mirror of
https://github.com/semantica-agi/semantica.git
synced 2026-09-04 04:01:07 +00:00
Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a41666d309 | ||
|
|
9cfa0b0d20 | ||
|
|
837654fc4f | ||
|
|
2daa937811 | ||
|
|
9321b9d27e | ||
|
|
d5a7ea9f9a | ||
|
|
b872b29628 | ||
|
|
bcc49f232d | ||
|
|
9e8db764d1 | ||
|
|
d9ed017b8c | ||
|
|
df42a015b0 | ||
|
|
9df54ffcd0 |
+4
-183
@@ -1,14 +1,9 @@
|
||||
/* ============================================================
|
||||
SEMANTICA DOCS — PREMIUM DESIGN SYSTEM
|
||||
SEMANTICA DOCS — DESIGN SYSTEM
|
||||
Dark-first (#080C10 bg, #10B981 emerald accent)
|
||||
Minimal, static styling — no decorative motion.
|
||||
============================================================ */
|
||||
|
||||
/* ── Keyframes ─────────────────────────────────────────────── */
|
||||
@keyframes pageFadeIn {
|
||||
from { opacity: 0; transform: translateY(6px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
/* ── Global ─────────────────────────────────────────────────── */
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
@@ -29,16 +24,7 @@ html {
|
||||
}
|
||||
::-webkit-scrollbar-thumb:hover { background: rgba(16, 185, 129, 0.4); }
|
||||
|
||||
/* ── Page entrance ──────────────────────────────────────────── */
|
||||
main,
|
||||
article,
|
||||
[class*="content-area"],
|
||||
[class*="ContentArea"],
|
||||
[class*="prose"] {
|
||||
animation: pageFadeIn 0.35s ease both;
|
||||
}
|
||||
|
||||
/* ── Focus rings ─────────────────────────────────────────────── */
|
||||
/* ── Focus rings (accessibility — kept) ─────────────────────── */
|
||||
*:focus-visible {
|
||||
outline: 2px solid rgba(16, 185, 129, 0.55) !important;
|
||||
outline-offset: 3px !important;
|
||||
@@ -59,7 +45,7 @@ h1::after {
|
||||
left: 0;
|
||||
width: 44px;
|
||||
height: 2px;
|
||||
background: linear-gradient(90deg, #10B981 0%, transparent 100%);
|
||||
background: #10B981;
|
||||
border-radius: 1px;
|
||||
}
|
||||
|
||||
@@ -71,9 +57,6 @@ article a,
|
||||
[class*="prose"] a {
|
||||
text-decoration-color: rgba(16, 185, 129, 0.35);
|
||||
text-underline-offset: 3px;
|
||||
transition:
|
||||
text-decoration-color 0.15s ease,
|
||||
color 0.15s ease;
|
||||
}
|
||||
|
||||
article a:hover,
|
||||
@@ -89,14 +72,6 @@ blockquote {
|
||||
padding: 0.9rem 1.2rem !important;
|
||||
font-style: italic;
|
||||
color: rgba(255, 255, 255, 0.68) !important;
|
||||
transition:
|
||||
border-color 0.2s ease,
|
||||
background-color 0.2s ease !important;
|
||||
}
|
||||
|
||||
blockquote:hover {
|
||||
border-left-color: rgba(16, 185, 129, 0.65) !important;
|
||||
background: rgba(16, 185, 129, 0.07) !important;
|
||||
}
|
||||
|
||||
/* ── HR / Divider ────────────────────────────────────────────── */
|
||||
@@ -123,165 +98,11 @@ table thead th {
|
||||
border-bottom: 1px solid rgba(16, 185, 129, 0.18) !important;
|
||||
}
|
||||
|
||||
table tbody tr {
|
||||
transition: background-color 0.15s ease;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
table tbody tr:hover {
|
||||
background-color: rgba(16, 185, 129, 0.06) !important;
|
||||
}
|
||||
|
||||
table tbody tr:hover td {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
table td,
|
||||
table th {
|
||||
transition: background-color 0.15s ease;
|
||||
}
|
||||
|
||||
/* ── CODE BLOCKS ─────────────────────────────────────────────── */
|
||||
pre,
|
||||
[class*="codeblock"],
|
||||
[class*="code-group"],
|
||||
[class*="CodeBlock"],
|
||||
[data-rehype-pretty-code-fragment] {
|
||||
transition:
|
||||
box-shadow 0.25s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
border-color 0.25s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
transform 0.25s cubic-bezier(0.4, 0, 0.2, 1) !important;
|
||||
}
|
||||
|
||||
pre:hover,
|
||||
[class*="codeblock"]:hover,
|
||||
[class*="CodeBlock"]:hover,
|
||||
[data-rehype-pretty-code-fragment]:hover {
|
||||
transform: translateY(-1px) !important;
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(16, 185, 129, 0.18),
|
||||
0 2px 12px rgba(16, 185, 129, 0.06),
|
||||
0 8px 32px rgba(0, 0, 0, 0.2) !important;
|
||||
border-color: rgba(16, 185, 129, 0.2) !important;
|
||||
}
|
||||
|
||||
/* ── CARDS ───────────────────────────────────────────────────── */
|
||||
[class*="card"],
|
||||
[class*="Card"],
|
||||
[data-card],
|
||||
.group\/card {
|
||||
transition:
|
||||
transform 0.22s ease,
|
||||
box-shadow 0.22s ease,
|
||||
border-color 0.22s ease !important;
|
||||
}
|
||||
|
||||
[class*="card"]:hover,
|
||||
[class*="Card"]:hover,
|
||||
[data-card]:hover,
|
||||
.group\/card:hover {
|
||||
transform: translateY(-3px) !important;
|
||||
box-shadow:
|
||||
0 8px 28px rgba(0, 0, 0, 0.18),
|
||||
0 0 0 1px rgba(16, 185, 129, 0.22) !important;
|
||||
border-color: rgba(16, 185, 129, 0.28) !important;
|
||||
}
|
||||
|
||||
/* ── CALLOUTS / ADMONITIONS ──────────────────────────────────── */
|
||||
[class*="callout"],
|
||||
[class*="Callout"],
|
||||
[class*="admonition"] {
|
||||
transition:
|
||||
box-shadow 0.2s ease,
|
||||
border-color 0.2s ease !important;
|
||||
}
|
||||
|
||||
[class*="callout"]:hover,
|
||||
[class*="Callout"]:hover,
|
||||
[class*="admonition"]:hover {
|
||||
box-shadow: 0 2px 16px rgba(16, 185, 129, 0.08) !important;
|
||||
border-color: rgba(16, 185, 129, 0.35) !important;
|
||||
}
|
||||
|
||||
/* ── STEPS ───────────────────────────────────────────────────── */
|
||||
[class*="step"],
|
||||
[class*="Step"] {
|
||||
transition: background-color 0.15s ease !important;
|
||||
}
|
||||
|
||||
[class*="step"]:hover,
|
||||
[class*="Step"]:hover {
|
||||
background-color: rgba(16, 185, 129, 0.04) !important;
|
||||
}
|
||||
|
||||
/* ── INLINE CODE ─────────────────────────────────────────────── */
|
||||
:not(pre) > code {
|
||||
transition:
|
||||
background-color 0.15s ease,
|
||||
color 0.15s ease !important;
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
:not(pre) > code:hover {
|
||||
background-color: rgba(16, 185, 129, 0.16) !important;
|
||||
}
|
||||
|
||||
/* ── NAVIGATION / SIDEBAR ────────────────────────────────────── */
|
||||
nav a,
|
||||
[class*="sidebar"] a,
|
||||
[class*="Sidebar"] a {
|
||||
transition: color 0.15s ease !important;
|
||||
text-decoration: none;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
nav a::after,
|
||||
[class*="sidebar"] a::after,
|
||||
[class*="Sidebar"] a::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: -1px;
|
||||
left: 0;
|
||||
width: 0;
|
||||
height: 1px;
|
||||
background: #10B981;
|
||||
transition: width 0.2s ease;
|
||||
}
|
||||
|
||||
nav a:hover::after,
|
||||
[class*="sidebar"] a:hover::after,
|
||||
[class*="Sidebar"] a:hover::after {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* ── TEXT / LIST ITEMS ───────────────────────────────────────── */
|
||||
ul > li,
|
||||
ol > li {
|
||||
border-radius: 3px;
|
||||
transition: background-color 0.12s ease;
|
||||
}
|
||||
|
||||
ul > li:hover,
|
||||
ol > li:hover {
|
||||
background-color: rgba(16, 185, 129, 0.04);
|
||||
}
|
||||
|
||||
/* ── PRIMARY BUTTON / CTA ────────────────────────────────────── */
|
||||
button[class*="primary"],
|
||||
a[class*="primary"],
|
||||
[class*="btn-primary"],
|
||||
[class*="ButtonPrimary"] {
|
||||
transition:
|
||||
box-shadow 0.2s ease,
|
||||
transform 0.2s ease !important;
|
||||
}
|
||||
|
||||
button[class*="primary"]:hover,
|
||||
a[class*="primary"]:hover,
|
||||
[class*="btn-primary"]:hover,
|
||||
[class*="ButtonPrimary"]:hover {
|
||||
box-shadow: 0 0 22px rgba(16, 185, 129, 0.28) !important;
|
||||
transform: translateY(-1px) !important;
|
||||
}
|
||||
|
||||
/* ── HIDE THEME TOGGLE ───────────────────────────────────────── */
|
||||
|
||||
+111
-80
@@ -38,18 +38,19 @@ This structure makes knowledge **searchable**, **connectable**, **queryable**, a
|
||||
Scanning text to find and classify real-world entities:
|
||||
|
||||
```python
|
||||
# Input: "Apple Inc. was founded by Steve Jobs in 1976 in Cupertino."
|
||||
{
|
||||
"entities": [
|
||||
{"text": "Apple Inc.", "type": "ORGANIZATION", "confidence": 0.98},
|
||||
{"text": "Steve Jobs", "type": "PERSON", "confidence": 0.99},
|
||||
{"text": "1976", "type": "DATE", "confidence": 0.95},
|
||||
{"text": "Cupertino", "type": "LOCATION", "confidence": 0.97}
|
||||
]
|
||||
}
|
||||
# "Apple Inc. was founded by Steve Jobs in 1976 in Cupertino."
|
||||
[
|
||||
Entity(text="Apple Inc.", label="ORG", start_char=0, end_char=10, confidence=0.98),
|
||||
Entity(text="Steve Jobs", label="PERSON", start_char=25, end_char=35, confidence=0.99),
|
||||
Entity(text="1976", label="DATE", start_char=39, end_char=43, confidence=0.95),
|
||||
Entity(text="Cupertino", label="GPE", start_char=47, end_char=56, confidence=0.97),
|
||||
]
|
||||
```
|
||||
|
||||
Each entity gets a type, confidence score, and a link to its source document. Three extraction methods are available:
|
||||
`NERExtractor(method=...).extract(text)` returns a list of `Entity` objects, each
|
||||
with a `label`, character offsets (`start_char` / `end_char`), a `confidence`
|
||||
score, and a `metadata` dict recording the extraction method. Three methods are
|
||||
available:
|
||||
|
||||
| Method | Speed | Accuracy | Requirements |
|
||||
| :------ | :----- | :-------- | :------------ |
|
||||
@@ -62,15 +63,19 @@ Each entity gets a type, confidence score, and a link to its source document. Th
|
||||
Finding how entities connect to each other:
|
||||
|
||||
```python
|
||||
{
|
||||
"relationships": [
|
||||
{"subject": "Steve Jobs", "predicate": "founded", "object": "Apple Inc.", "confidence": 0.92},
|
||||
{"subject": "Apple Inc.", "predicate": "located_in", "object": "Cupertino", "confidence": 0.89}
|
||||
]
|
||||
}
|
||||
jobs = Entity(text="Steve Jobs", label="PERSON", start_char=25, end_char=35)
|
||||
apple = Entity(text="Apple Inc.", label="ORG", start_char=0, end_char=10)
|
||||
|
||||
[
|
||||
Relation(subject=jobs, predicate="founded", object=apple, confidence=0.92),
|
||||
Relation(subject=apple, predicate="located_in", object=Entity(text="Cupertino", label="GPE", start_char=47, end_char=56), confidence=0.89),
|
||||
]
|
||||
```
|
||||
|
||||
Relationships can be extracted via rule-based methods, ML models, or LLMs: each producing typed triplets with confidence scores and source attribution.
|
||||
`RelationExtractor(method=...).extract(text, entities=entities)` returns a list of
|
||||
`Relation` objects: typed subject-predicate-object triples (the endpoints are
|
||||
`Entity` objects) with confidence scores and source attribution. Extraction runs
|
||||
via pattern rules, ML models, or LLMs.
|
||||
|
||||
|
||||
## Knowledge Graph vs. Vector Store
|
||||
@@ -94,9 +99,10 @@ Both store information for AI retrieval: but they're built for different jobs.
|
||||
```python
|
||||
from semantica.kg import GraphBuilder, PathFinder
|
||||
|
||||
graph = GraphBuilder(merge_entities=True).build(entities=entities, relationships=rels)
|
||||
finder = PathFinder()
|
||||
path = finder.dijkstra_shortest_path(graph, "Steve Jobs", "Tim Cook")
|
||||
graph = GraphBuilder(merge_entities=True).build(
|
||||
{"entities": entities, "relationships": rels}
|
||||
)
|
||||
path = PathFinder().dijkstra_shortest_path(graph, "Steve Jobs", "Tim Cook")
|
||||
```
|
||||
</Tab>
|
||||
|
||||
@@ -140,8 +146,16 @@ Both store information for AI retrieval: but they're built for different jobs.
|
||||
context = AgentContext(
|
||||
vector_store=VectorStore(backend="faiss", dimension=768),
|
||||
knowledge_graph=ContextGraph(advanced_analytics=True),
|
||||
graph_expansion=True,
|
||||
)
|
||||
result = context.query("Who founded Apple?", mode="graphrag")
|
||||
|
||||
# store() extracts entities and populates the graph + vector index
|
||||
context.store([{"content": "Steve Jobs co-founded Apple Inc. in 1976."}])
|
||||
|
||||
# retrieve() blends vector similarity with graph traversal
|
||||
results = context.retrieve("Who founded Apple?", use_graph=True, expand_graph=True)
|
||||
for r in results:
|
||||
print(r["score"], r["content"], r["source"])
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
@@ -221,70 +235,80 @@ Inferred: Steve Jobs has a connection to Cupertino
|
||||
Applies IF/THEN rules repeatedly until no new facts can be derived. Best for alert systems, compliance checks, and trigger-based workflows.
|
||||
|
||||
```python
|
||||
from semantica.reasoning import Reasoner, Rule, Fact, RuleType
|
||||
from semantica.reasoning import Reasoner
|
||||
|
||||
engine = Reasoner()
|
||||
engine.add_fact(Fact(subject="Alice", predicate="is_a", obj="Manager"))
|
||||
engine.add_rule(Rule(
|
||||
rule_type=RuleType.FORWARD_CHAIN,
|
||||
conditions=[{"subject": "?x", "predicate": "is_a", "object": "Manager"}],
|
||||
conclusion={"subject": "?x", "predicate": "has_authority", "object": "true"}
|
||||
))
|
||||
result = engine.infer()
|
||||
engine.add_fact("Manager(Alice)")
|
||||
engine.add_rule("IF Manager(?x) THEN HasAuthority(?x)")
|
||||
|
||||
results = engine.forward_chain() # list of InferenceResult
|
||||
for r in results:
|
||||
print(r.conclusion) # "HasAuthority(Alice)"
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Rete Network">
|
||||
Efficient pattern matching for large rule sets: the Rete algorithm avoids re-evaluating rules whose preconditions haven't changed. Best for thousands of rules over millions of facts.
|
||||
|
||||
```python
|
||||
from semantica.reasoning import ReteEngine
|
||||
from semantica.reasoning import ReteEngine, Rule, Fact
|
||||
|
||||
engine = ReteEngine()
|
||||
engine.load_rules("rules/domain_rules.json")
|
||||
results = engine.run(kg)
|
||||
engine.build_network([
|
||||
Rule(rule_id="r1", name="manager_authority",
|
||||
conditions=["Manager(?x)"], conclusion="HasAuthority(?x)"),
|
||||
])
|
||||
engine.add_fact(Fact(fact_id="f1", predicate="Manager", arguments=["Alice"]))
|
||||
|
||||
matches = engine.match_patterns()
|
||||
results = engine.execute_matches(matches) # ["HasAuthority(?x)"]
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Deductive & Abductive">
|
||||
**Deductive**: classical syllogistic reasoning from premises to guaranteed conclusions.
|
||||
|
||||
**Abductive**: infers the most likely explanation for observed evidence. Best for diagnostic and investigative use cases.
|
||||
<Tab title="LLM Reasoning">
|
||||
`GraphReasoner` answers open-ended questions over a knowledge graph with an
|
||||
LLM, returning a natural-language answer grounded in the graph's facts. Best
|
||||
for exploratory and investigative questions that fixed rules can't anticipate.
|
||||
|
||||
```python
|
||||
from semantica.reasoning import GraphReasoner
|
||||
|
||||
graph_reasoner = GraphReasoner(kg)
|
||||
graph_reasoner.add_rule({"if": [{"subject": "?a", "predicate": "parent_of", "object": "?b"}], "then": {"subject": "?a", "predicate": "ancestor_of", "object": "?b"}})
|
||||
inferences = graph_reasoner.infer(kg)
|
||||
reasoner = GraphReasoner(provider="openai", model="gpt-4o-mini")
|
||||
answer = reasoner.reason(kg, "Which suppliers are indirectly exposed to the Acme outage?")
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Datalog (v0.4.0)">
|
||||
Recursive Horn clause rules with fixpoint semantics: handles transitive closure and recursive relationships that forward chaining cannot express.
|
||||
|
||||
```python
|
||||
from semantica.reasoning import DatalogReasoner, DatalogFact, DatalogRule
|
||||
from semantica.reasoning import DatalogReasoner
|
||||
|
||||
reasoner = DatalogReasoner()
|
||||
reasoner.add_fact(DatalogFact("parent", ("alice", "bob")))
|
||||
reasoner.add_rule(DatalogRule("ancestor(?X, ?Y) :- parent(?X, ?Y)."))
|
||||
reasoner.evaluate()
|
||||
results = reasoner.query("ancestor(alice, ?Z)")
|
||||
reasoner.add_fact("parent(alice, bob)")
|
||||
reasoner.add_fact("parent(bob, charlie)")
|
||||
reasoner.add_rule("ancestor(X, Y) :- parent(X, Y).")
|
||||
reasoner.add_rule("ancestor(X, Z) :- parent(X, Y), ancestor(Y, Z).")
|
||||
|
||||
reasoner.derive_all()
|
||||
results = reasoner.query("ancestor(alice, ?Z)") # {"Z": "bob"} and {"Z": "charlie"}, order not guaranteed
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Engine Comparison">
|
||||
|
||||
| Engine | Description | Best For |
|
||||
| :------ | :----------- | :-------- |
|
||||
| Forward chaining | Applies rules until fixpoint | Alert systems, compliance checks |
|
||||
| Rete network | Efficient pattern matching | Large rule sets, high fact throughput |
|
||||
| Deductive | Classical syllogistic reasoning | Mathematical and logical inference |
|
||||
| Abductive | Most likely explanation | Diagnostics, investigation |
|
||||
| SPARQL | Query-based inference over RDF | Semantic web, ontology reasoning |
|
||||
| Datalog (v0.4.0) | Recursive Horn clause rules | Transitive closure, graph reachability |
|
||||
| Engine | Class | Best For |
|
||||
| :------ | :----- | :-------- |
|
||||
| Forward chaining | `Reasoner` | Alert systems, compliance checks |
|
||||
| Rete network | `ReteEngine` | Large rule sets, high fact throughput |
|
||||
| SPARQL expansion | `SPARQLReasoner` | Semantic web, ontology reasoning over RDF |
|
||||
| Datalog (v0.4.0) | `DatalogReasoner` | Transitive closure, graph reachability |
|
||||
| Temporal | `TemporalReasoningEngine` | Allen interval algebra, time-aware inference |
|
||||
| LLM over the graph | `GraphReasoner` | Open-ended, investigative questions |
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
All engines produce **explainable inference paths**: not black-box conclusions. Every derived fact includes the rules and premises that produced it.
|
||||
`Reasoner.forward_chain()` returns `InferenceResult` objects that carry the rule
|
||||
applied (`rule_used`) and the premises it fired on, and `ExplanationGenerator`
|
||||
turns one into a step-by-step natural-language justification: reasoning here is
|
||||
**not** a black box.
|
||||
|
||||
|
||||
## Temporal Intelligence
|
||||
@@ -313,11 +337,16 @@ Explore the semantic neighborhood of any entity in your graph: useful for unders
|
||||
```python
|
||||
from semantica.kg import SimilarityCalculator
|
||||
|
||||
calc = SimilarityCalculator()
|
||||
scores = calc.calculate_similarity(entity_a, entity_b)
|
||||
calc = SimilarityCalculator(method="cosine") # "cosine" | "euclidean" | "manhattan" | "correlation"
|
||||
|
||||
# Similarity for every unique pair of node embeddings: {(node_a, node_b): score}
|
||||
pairs = calc.pairwise_similarity({"apple": vec_apple, "google": vec_google, "nest": vec_nest})
|
||||
|
||||
# Or rank a set of embeddings by closeness to one query vector
|
||||
nearest = calc.find_most_similar(embeddings, query_embedding, top_k=10)
|
||||
```
|
||||
|
||||
**Features:** N×N semantic distance matrices, ego-mode visualization, distance band classification (`near` / `mid` / `far`), embedding cache optimization for large graphs.
|
||||
**Features:** N×N semantic distance matrices, ego-mode visualization, distance band classification (`direct` / `near` / `mid-range` / `distant`), embedding cache optimization for large graphs.
|
||||
|
||||
The [Visualization module](/reference/visualization) renders distance matrices as interactive heatmaps and ego-mode neighborhood graphs. The [Explorer](/reference/explorer) embeds distance intelligence directly in the browser dashboard.
|
||||
|
||||
@@ -341,11 +370,11 @@ Real-world data contains the same entity under many names: "Apple", "Apple Inc."
|
||||
```python
|
||||
from semantica.deduplication import DuplicateDetector, EntityMerger
|
||||
|
||||
detector = DuplicateDetector(similarity_threshold=0.85)
|
||||
duplicates = detector.detect_duplicates(entities)
|
||||
detector = DuplicateDetector(similarity_threshold=0.85)
|
||||
candidates = detector.detect_duplicates(entities)
|
||||
|
||||
merger = EntityMerger()
|
||||
deduplicated_entities = merger.merge_duplicates(entities)
|
||||
merger = EntityMerger()
|
||||
operations = merger.merge_duplicates(entities, strategy="keep_most_complete")
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
@@ -361,19 +390,21 @@ Every fact in Semantica links back to:
|
||||
- The **reasoning steps** that produced any inferred fact
|
||||
|
||||
<Note>
|
||||
This is W3C PROV-O compliant lineage: suitable for regulated industries that require audit trails (HIPAA, SOX, GDPR, FDA 21 CFR Part 11). Use `RDFExporter(include_provenance=True)` to embed provenance inline in any RDF export.
|
||||
This is W3C PROV-O compliant lineage: suitable for regulated industries that require audit trails (HIPAA, SOX, GDPR, FDA 21 CFR Part 11). `ProvenanceManager.export_prov(format="turtle")` serialises the recorded lineage as PROV-O RDF.
|
||||
</Note>
|
||||
|
||||
```python
|
||||
from semantica.provenance import ProvenanceManager
|
||||
|
||||
prov = ProvenanceManager()
|
||||
lineage = prov.get_entity_lineage("apple_inc")
|
||||
prov = ProvenanceManager()
|
||||
prov.track_entity("apple_inc", source="report.pdf",
|
||||
metadata={"extractor": "NamedEntityRecognizer", "confidence": 0.98})
|
||||
|
||||
print(f"Source: {lineage.source_document}")
|
||||
print(f"Method: {lineage.extraction_method}")
|
||||
print(f"Extracted: {lineage.timestamp}")
|
||||
print(f"Checksum: {lineage.checksum}")
|
||||
record = prov.get_provenance("apple_inc") # dict; use get_lineage() for the full chain
|
||||
print(record["source_document"])
|
||||
print(record["timestamp"])
|
||||
print(record["checksum"])
|
||||
print(record["metadata"]) # extractor, confidence, and any custom keys
|
||||
```
|
||||
|
||||
|
||||
@@ -456,27 +487,27 @@ Semantica is designed for extension. Any component: ingestor, extractor, graph b
|
||||
**Extension points available:** ingestors, parsers, normalizers, extractors, reasoning engines, export formats, vector store backends, graph store backends, visualization renderers.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="MethodRegistry: add domain-specific graph operations">
|
||||
<Accordion title="MethodRegistry: swap a built-in graph operation for your own">
|
||||
|
||||
`MethodRegistry` lets you register custom methods on knowledge graph objects by name: useful for adding domain-specific graph operations without subclassing.
|
||||
`method_registry` lets you register an alternative implementation for a
|
||||
knowledge-graph task (`build`, `analyze`, `centrality`, `resolve`, …) under a
|
||||
name, then select it wherever that task runs.
|
||||
|
||||
```python
|
||||
from semantica.kg import MethodRegistry
|
||||
from semantica.kg import method_registry
|
||||
from semantica.kg.methods import calculate_centrality
|
||||
|
||||
registry = MethodRegistry()
|
||||
|
||||
def find_supply_chain_hops(graph, source_node, max_hops=3):
|
||||
"""Custom BFS traversal for supply chain graphs."""
|
||||
def fast_centrality(graph, **kwargs):
|
||||
"""Custom centrality implementation."""
|
||||
...
|
||||
|
||||
# Register under a string key
|
||||
registry.register("supply_chain_hops", find_supply_chain_hops)
|
||||
# register(task, name, func)
|
||||
method_registry.register("centrality", "fast_centrality", fast_centrality)
|
||||
|
||||
# Call by name on any graph object
|
||||
result = registry.call("supply_chain_hops", kg, source_node="Supplier_A", max_hops=5)
|
||||
# The task wrappers consult method_registry, so the name is now selectable:
|
||||
scores = calculate_centrality(kg, method="fast_centrality")
|
||||
|
||||
# List all registered methods
|
||||
print(registry.list_methods()) # ["supply_chain_hops", ...]
|
||||
print(method_registry.list_all("centrality")) # {"centrality": ["fast_centrality", ...]}
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
|
||||
+30
-29
@@ -2,7 +2,7 @@
|
||||
"$schema": "https://mintlify.com/docs.json",
|
||||
"theme": "mint",
|
||||
"name": "Semantica",
|
||||
"description": "The Accountability and Context Layer for AI — Context Graphs · Decision Intelligence · Full Provenance",
|
||||
"description": "The Context and Semantic Layer for AI in High-Stakes Domains — Context Graphs · Decision Intelligence · Full Provenance",
|
||||
"colors": {
|
||||
"primary": "#10B981",
|
||||
"light": "#10B981",
|
||||
@@ -43,7 +43,7 @@
|
||||
"raiseIssue": true
|
||||
},
|
||||
"metadata": {
|
||||
"og:title": "Semantica — Accountability & Context Layer for AI",
|
||||
"og:title": "Semantica — Context & Semantic Layer for AI in High-Stakes Domains",
|
||||
"og:description": "Build explainable, auditable knowledge graphs with full provenance. Open source. MIT licensed.",
|
||||
"og:image": "/assets/img/semantica-logo.png",
|
||||
"twitter:card": "summary_large_image",
|
||||
@@ -121,6 +121,23 @@
|
||||
"pages": [
|
||||
"vector_stores/pgvector"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "FAQ",
|
||||
"pages": [
|
||||
"faq"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Community",
|
||||
"pages": [
|
||||
"community",
|
||||
"community-projects",
|
||||
"contributing-guide",
|
||||
"governance",
|
||||
"citation",
|
||||
"project-license"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -167,7 +184,17 @@
|
||||
"guides/policy-engine",
|
||||
"guides/visualization",
|
||||
"guides/distance-intelligence",
|
||||
"guides/graph-analytics",
|
||||
"guides/graph-analytics"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "API Reference",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Context & Intelligence",
|
||||
"pages": [
|
||||
"reference/context",
|
||||
"reference/kg",
|
||||
"reference/temporal",
|
||||
@@ -236,32 +263,6 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "FAQ",
|
||||
"groups": [
|
||||
{
|
||||
"group": "FAQ",
|
||||
"pages": [
|
||||
"faq"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Community",
|
||||
"pages": [
|
||||
"community",
|
||||
"community-projects",
|
||||
"contributing-guide",
|
||||
"governance",
|
||||
"citation",
|
||||
"project-license"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "Changelog",
|
||||
"href": "https://github.com/semantica-agi/semantica/releases"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
+41
-51
@@ -1,97 +1,87 @@
|
||||
---
|
||||
title: "Semantica"
|
||||
description: "The Accountability and Context Layer for AI: Context Graphs · Decision Intelligence · Full Provenance"
|
||||
description: "The Context and Semantic Layer for AI in High-Stakes Domains: Context Graphs · Decision Intelligence · Full Provenance"
|
||||
---
|
||||
|
||||
```bash
|
||||
pip install semantica
|
||||
```
|
||||
|
||||
Your AI agent just made a decision. Now someone needs to explain it.
|
||||
Most AI agents store embeddings, not meaning. They can't say why a fact was recalled, where it came from, or what led to a decision. In healthcare, finance, legal, and government, that lack of a traceable record blocks production deployment.
|
||||
|
||||
*What did it know at the time? Which facts shaped the outcome? Where did those facts come from? Has it made the same call before: and did that go well?*
|
||||
|
||||
If your stack can't answer those questions with a traceable record, you have a gap. Not a capability gap: an **accountability gap**. It's the reason AI hasn't landed at scale in healthcare, finance, legal, and government. And it's why teams building for those markets keep rebuilding the same guardrails from scratch.
|
||||
|
||||
**Semantica closes that gap.** It's the context and accountability layer that sits beneath your existing agent framework: not a replacement for LangChain or LlamaIndex, but the infrastructure that makes their outputs trustworthy.
|
||||
Semantica is the context and semantic layer for AI in high-stakes domains, sitting beneath your existing agent framework. It doesn't replace LangChain or LlamaIndex; it makes their outputs traceable.
|
||||
|
||||
|
||||
## The Problem Every Production AI Team Hits
|
||||
## What Most AI Stacks Are Missing
|
||||
|
||||
Powerful agents aren't automatically trustworthy ones. Five structural blind spots make modern AI systems impossible to deploy in regulated environments:
|
||||
|
||||
**No memory structure** — agents store embeddings, not meaning
|
||||
**No memory structure.** Agents store embeddings, not meaning.
|
||||
- No way to ask *why* a fact was recalled
|
||||
- No link from a recalled fact back to its source document
|
||||
- Context is a black box that resets on every run
|
||||
|
||||
**No decision trail** — agents act continuously but record nothing
|
||||
**No decision trail.** Agents act continuously but record nothing.
|
||||
- No history to hand to a regulator or auditor
|
||||
- No way to replay or reproduce a past decision
|
||||
- Debugging means re-running, not reviewing
|
||||
|
||||
**No provenance** — outputs can't be traced to source facts
|
||||
- In healthcare, finance, and legal: this is a hard compliance blocker
|
||||
**No provenance.** Outputs can't be traced to source facts.
|
||||
- A hard compliance blocker in healthcare, finance, and legal
|
||||
- No lineage from inference back to the original document
|
||||
- Impossible to demonstrate what the agent actually relied on
|
||||
- No way to demonstrate what the agent actually relied on
|
||||
|
||||
**No reasoning transparency** — black-box answers with no explanation
|
||||
- Impossible to validate the reasoning path
|
||||
- Impossible to contest a specific conclusion
|
||||
**No reasoning transparency.** Black-box answers with no explanation.
|
||||
- No way to validate the reasoning path
|
||||
- No way to contest a specific conclusion
|
||||
- No basis for improving or correcting future behavior
|
||||
|
||||
**No conflict detection** — contradictory facts silently coexist in vector stores
|
||||
**No conflict detection.** Contradictory facts silently coexist in vector stores.
|
||||
- No detection when two sources disagree
|
||||
- Outputs become inconsistent and unpredictable over time
|
||||
- Silent failures compound as the knowledge base grows
|
||||
|
||||
<Note>
|
||||
These aren't edge cases. They're why enterprise AI pilots stall: and why your compliance team keeps saying *not yet*.
|
||||
</Note>
|
||||
|
||||
|
||||
## What Semantica Adds to Your Stack
|
||||
|
||||
Semantica gives every agent the infrastructure it needs to be accountable. Drop it into your existing setup in minutes:
|
||||
Semantica gives every agent the infrastructure it needs to be accountable, and it drops into an existing setup in minutes.
|
||||
|
||||
**Context Graphs** — a structured, queryable graph of everything your agent knows, decides, and reasons about
|
||||
- Persistent across agent runs: no context loss between sessions
|
||||
**Context Graphs.** A structured, queryable graph of everything your agent knows, decides, and reasons about.
|
||||
- Persistent across agent runs, with no context loss between sessions
|
||||
- Queryable with SPARQL and full graph algorithms
|
||||
- Temporal model with `valid_from` / `valid_until` on nodes and edges
|
||||
- Point-in-time snapshots of the full knowledge state
|
||||
|
||||
**Decision Intelligence** — every decision is a first-class object in your system
|
||||
**Decision Intelligence.** Every decision is a first-class object in your system.
|
||||
- `record_decision()` captures full lifecycle and causal chain
|
||||
- Hybrid precedent search over past decisions for consistency
|
||||
- `analyze_decision_impact()` shows downstream consequences
|
||||
- Causal chain visualization from trigger to outcome
|
||||
|
||||
**Full Provenance** — every fact links to its source document and ingestion event
|
||||
**Full Provenance.** Every fact links to its source document and ingestion event.
|
||||
- W3C PROV-O compliant lineage across all modules
|
||||
- Full traceability from raw input to final inference
|
||||
- `recorded_at` stamping with OWL-Time export
|
||||
- Audit-ready for HIPAA, SOX, GDPR, FDA 21 CFR Part 11
|
||||
|
||||
**Reasoning Engines** — explainable reasoning paths, not black boxes
|
||||
**Reasoning Engines.** Explainable reasoning paths, not black boxes.
|
||||
- Forward chaining, Rete, deductive, abductive
|
||||
- SPARQL query-based inference over RDF graphs
|
||||
- Datalog with recursive Horn clause rules
|
||||
- Every conclusion backed by a traceable derivation path
|
||||
|
||||
**Temporal Intelligence** — your graph knows not just *what*, but *when*
|
||||
- Allen interval algebra: all 13 temporal relations
|
||||
**Temporal Intelligence.** Your graph knows not just *what*, but *when*.
|
||||
- Allen interval algebra covering all 13 temporal relations
|
||||
- Point-in-time queries over historical graph states
|
||||
- Temporal provenance stamping on every fact
|
||||
- OWL-Time export for standards-compliant archiving
|
||||
|
||||
**Ontology Hub** — full ontology lifecycle in the browser
|
||||
**Ontology Hub.** Full ontology lifecycle in the browser.
|
||||
- Visual editor for schema design and editing
|
||||
- SHACL Studio for constraint authoring and validation
|
||||
- Alignment authoring across multiple ontologies
|
||||
- Health dashboard and version control built in
|
||||
|
||||
<Tip>
|
||||
Works alongside any LLM provider and any agent framework: add it to an existing stack without changing your architecture.
|
||||
Works alongside any LLM provider and any agent framework. Add it to an existing stack without changing your architecture.
|
||||
</Tip>
|
||||
|
||||
<img src="/assets/img/diagrams/architecture-overview.svg" alt="Semantica four-layer architecture: Ingestion → Processing → Intelligence → Application" style={{ width: '100%', borderRadius: '12px', margin: '24px 0' }} />
|
||||
@@ -185,17 +175,17 @@ decision_id = context.record_decision(
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
- [Full Quickstart](/quickstart) — Step-by-step pipeline walkthrough
|
||||
- [Cookbook](/cookbook) — 40+ real-world Jupyter notebooks
|
||||
- [Join Discord](https://discord.gg/sV34vps5hH) — Community chat and support
|
||||
- [Full Quickstart](/quickstart): step-by-step pipeline walkthrough
|
||||
- [Cookbook](/cookbook): 40+ real-world Jupyter notebooks
|
||||
- [Join Discord](https://discord.gg/sV34vps5hH): community chat and support
|
||||
|
||||
|
||||
## Built for Where Mistakes Have Consequences
|
||||
## Industry Use Cases
|
||||
|
||||
Semantica was designed for domains where every decision must be explainable and every fact must be traceable.
|
||||
Semantica is used in domains where every decision must be explainable and every fact must be traceable.
|
||||
|
||||
<Warning>
|
||||
**This is system-level explainability, not foundation-model explainability.** Semantica does not expose, reconstruct, or explain what happens *inside* the LLM/foundation model — its internal reasoning or chain-of-thought stays opaque, as it does for any external system. What Semantica explains is *outside* the model: the context and data fed in, the decision produced, its provenance, the relevant relationships, the policies applied, and the full execution trail. See [Core Concepts](/concepts) for the full scope note.
|
||||
**This is system-level explainability, not foundation-model explainability.** Semantica does not expose, reconstruct, or explain what happens *inside* the LLM/foundation model. Its internal reasoning or chain-of-thought stays opaque, as it does for any external system. What Semantica explains is *outside* the model: the context and data fed in, the decision produced, its provenance, the relevant relationships, the policies applied, and the full execution trail. See [Core Concepts](/concepts) for the full scope note.
|
||||
</Warning>
|
||||
|
||||
**Healthcare & Life Sciences**
|
||||
@@ -256,7 +246,7 @@ Semantica was designed for domains where every decision must be explainable and
|
||||
- Knowledge graphs vs. vector stores: when to use each
|
||||
- What GraphRAG is and how Semantica implements it
|
||||
- How provenance and decision tracking work together
|
||||
- The accountability layer architecture
|
||||
- The context and semantic layer architecture
|
||||
</Step>
|
||||
<Step title="Go deep on any module">
|
||||
Every module has a dedicated [reference page](/reference/context) with:
|
||||
@@ -266,12 +256,12 @@ Semantica was designed for domains where every decision must be explainable and
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
- [Installation](/installation) — Get Semantica installed in under a minute
|
||||
- [Quickstart](/quickstart) — Build a complete knowledge graph pipeline in 5 minutes
|
||||
- [Core Concepts](/concepts) — The mental model behind the API
|
||||
- [API Reference](/reference/context) — Exact module, class, and method details
|
||||
- [Cookbook](/cookbook) — Domain notebooks for real-world use cases
|
||||
- [Changelog](https://github.com/semantica-agi/semantica/releases) — Release history
|
||||
- [Installation](/installation): get Semantica installed in under a minute
|
||||
- [Quickstart](/quickstart): build a complete knowledge graph pipeline in 5 minutes
|
||||
- [Core Concepts](/concepts): the mental model behind the API
|
||||
- [API Reference](/reference/context): exact module, class, and method details
|
||||
- [Cookbook](/cookbook): domain notebooks for real-world use cases
|
||||
- [Changelog](https://github.com/semantica-agi/semantica/releases): release history
|
||||
|
||||
|
||||
## Full Capabilities
|
||||
@@ -394,20 +384,20 @@ Semantica was designed for domains where every decision must be explainable and
|
||||
|
||||
## Why Semantica?
|
||||
|
||||
**Open Source, MIT** — No vendor lock-in. No paywalled features.
|
||||
**Open Source, MIT.** No vendor lock-in, no paywalled features.
|
||||
- Full source available on GitHub
|
||||
- Every line auditable by your security team
|
||||
- Fork, extend, and self-host with no restrictions
|
||||
- No telemetry, no usage reporting
|
||||
|
||||
**Production Ready** — Built for teams that can't afford surprises.
|
||||
**Production Ready.** Built for teams that can't afford surprises.
|
||||
- 1,000+ passing tests with full regression coverage
|
||||
- `PipelineValidator` catches configuration errors at startup
|
||||
- `FailureHandler` with exponential backoff and dead-letter queues
|
||||
- Ongoing security hardening: fixes shipped in every release ([CHANGELOG](https://github.com/semantica-agi/semantica/blob/main/CHANGELOG.md))
|
||||
- Ongoing security hardening, with fixes shipped in every release ([CHANGELOG](https://github.com/semantica-agi/semantica/blob/main/CHANGELOG.md))
|
||||
|
||||
**Modular by Design** — Import only what you need.
|
||||
**Modular by Design.** Import only what you need.
|
||||
- Use `NERExtractor` without a graph store
|
||||
- Use `ContextGraph` without vector storage
|
||||
- Every component independently swappable and testable
|
||||
- No framework lock-in: works with any agent stack
|
||||
- No framework lock-in, and works with any agent stack
|
||||
|
||||
@@ -416,6 +416,38 @@ class WeaviateStore:
|
||||
)
|
||||
raise ProcessingError(f"Failed to add objects: {str(e)}")
|
||||
|
||||
def delete_vectors(self, vector_ids: List[str], **options) -> Dict[str, Any]:
|
||||
"""Delete vectors (objects) from the collection by their ids.
|
||||
|
||||
Args:
|
||||
vector_ids: Object uuids to delete
|
||||
**options: Additional options (ignored, kept for API parity)
|
||||
|
||||
Returns:
|
||||
A dict with the number of successfully deleted objects
|
||||
(``delete_count``).
|
||||
"""
|
||||
if self.collection is None or not WEAVIATE_AVAILABLE:
|
||||
raise ProcessingError("Collection not initialized or Weaviate unavailable")
|
||||
|
||||
if not vector_ids:
|
||||
return {"delete_count": 0}
|
||||
|
||||
deleted = 0
|
||||
try:
|
||||
data = self.collection.data
|
||||
for vector_id in vector_ids:
|
||||
if not vector_id:
|
||||
continue
|
||||
# delete_by_id returns False (not an error) for a uuid that is
|
||||
# not present, and True when an object was deleted. Count only
|
||||
# actual deletes so delete_count never over-reports.
|
||||
if data.delete_by_id(vector_id):
|
||||
deleted += 1
|
||||
return {"delete_count": deleted}
|
||||
except Exception as e:
|
||||
raise ProcessingError(f"Failed to delete vectors: {str(e)}")
|
||||
|
||||
def get_vector(self, vector_id: str) -> Optional[np.ndarray]:
|
||||
"""Get vector by ID."""
|
||||
if self.collection is None or not WEAVIATE_AVAILABLE:
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
"""Tests for WeaviateStore.delete_vectors (#1374)."""
|
||||
|
||||
from unittest import TestCase
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from semantica.context.erasure import STATUS_ERASED, ErasureCoordinator
|
||||
from semantica.utils.exceptions import ProcessingError
|
||||
from semantica.vector_store import VectorStore
|
||||
from semantica.vector_store.weaviate_store import WeaviateStore
|
||||
|
||||
|
||||
class WeaviateStoreDeleteVectorsTest(TestCase):
|
||||
def setUp(self):
|
||||
self.patches = [
|
||||
patch("semantica.vector_store.weaviate_store.WEAVIATE_AVAILABLE", True)
|
||||
]
|
||||
for p in self.patches:
|
||||
p.start()
|
||||
|
||||
def tearDown(self):
|
||||
for p in reversed(self.patches):
|
||||
p.stop()
|
||||
|
||||
def _store(self, error=None):
|
||||
"""Return (store, data) where data records delete_by_id calls."""
|
||||
data = MagicMock()
|
||||
data.delete_by_id = MagicMock()
|
||||
coll = MagicMock()
|
||||
coll.data = data
|
||||
if error is not None:
|
||||
data.delete_by_id.side_effect = error
|
||||
store = WeaviateStore()
|
||||
store.collection = coll
|
||||
return store, data
|
||||
|
||||
def test_delete_single_id_calls_delete_by_id(self):
|
||||
store, data = self._store()
|
||||
ret = store.delete_vectors(["abc"])
|
||||
data.delete_by_id.assert_called_once_with("abc")
|
||||
self.assertEqual(ret, {"delete_count": 1})
|
||||
|
||||
def test_delete_many_ids_calls_each(self):
|
||||
store, data = self._store()
|
||||
ret = store.delete_vectors(["a", "b", "c"])
|
||||
self.assertEqual(data.delete_by_id.call_count, 3)
|
||||
self.assertEqual(ret, {"delete_count": 3})
|
||||
|
||||
def test_delete_skips_ids_that_report_missing(self):
|
||||
store, data = self._store()
|
||||
|
||||
def _fake(uuid):
|
||||
return uuid != "missing"
|
||||
|
||||
data.delete_by_id.side_effect = _fake
|
||||
ret = store.delete_vectors(["present", "missing", "also-here"])
|
||||
self.assertEqual(data.delete_by_id.call_count, 3)
|
||||
self.assertEqual(ret, {"delete_count": 2})
|
||||
|
||||
def test_delete_drops_empty_ids(self):
|
||||
store, data = self._store()
|
||||
store.delete_vectors(["", "abc"])
|
||||
data.delete_by_id.assert_called_once_with("abc")
|
||||
self.assertEqual(data.delete_by_id.call_count, 1)
|
||||
|
||||
def test_delete_empty_ids_is_noop(self):
|
||||
store, data = self._store()
|
||||
ret = store.delete_vectors([])
|
||||
self.assertEqual(ret, {"delete_count": 0})
|
||||
data.delete_by_id.assert_not_called()
|
||||
|
||||
def test_delete_without_collection_raises(self):
|
||||
store = WeaviateStore()
|
||||
with self.assertRaises(ProcessingError):
|
||||
store.delete_vectors(["a"])
|
||||
|
||||
def test_delete_backend_error_raises_processing_error(self):
|
||||
store, _ = self._store(error=RuntimeError("connection reset"))
|
||||
with self.assertRaises(ProcessingError):
|
||||
store.delete_vectors(["a"])
|
||||
|
||||
|
||||
class WeaviateErasureIntegrationTest(TestCase):
|
||||
"""ErasureCoordinator reaches the real WeaviateStore.delete_vectors path."""
|
||||
|
||||
def setUp(self):
|
||||
self._patch = patch(
|
||||
"semantica.vector_store.weaviate_store.WEAVIATE_AVAILABLE", True
|
||||
)
|
||||
self._patch.start()
|
||||
|
||||
def tearDown(self):
|
||||
self._patch.stop()
|
||||
|
||||
def _bind_weaviate_as_vector_store(self):
|
||||
vs = VectorStore(backend="weaviate", config={"dimension": 3})
|
||||
weaviate = WeaviateStore()
|
||||
data = MagicMock()
|
||||
coll = MagicMock()
|
||||
coll.data = data
|
||||
weaviate.collection = coll
|
||||
vs._backend_store = weaviate
|
||||
return vs, data
|
||||
|
||||
def test_erasure_reports_erased_when_delete_runs(self):
|
||||
vs, data = self._bind_weaviate_as_vector_store()
|
||||
coord = ErasureCoordinator(vector_store=vs)
|
||||
receipt = coord.erase_entity("customer-4471")
|
||||
data.delete_by_id.assert_called()
|
||||
self.assertEqual(receipt.stores["vectors"]["status"], STATUS_ERASED)
|
||||
|
||||
def test_erasure_reports_erased_when_nothing_was_found(self):
|
||||
"""delete_by_id returns False (404) for an id that is not in the store.
|
||||
|
||||
For erasure that still means the goal is met: nothing remains under
|
||||
that id. The receipt keeps the honest zero count in backend_result
|
||||
instead of raising a false failed status.
|
||||
"""
|
||||
vs, data = self._bind_weaviate_as_vector_store()
|
||||
data.delete_by_id.return_value = False
|
||||
coord = ErasureCoordinator(vector_store=vs)
|
||||
receipt = coord.erase_entity("customer-4471")
|
||||
self.assertEqual(receipt.stores["vectors"]["status"], STATUS_ERASED)
|
||||
self.assertEqual(
|
||||
receipt.stores["vectors"]["backend_result"], {"delete_count": 0}
|
||||
)
|
||||
|
||||
def test_erasure_backend_name_is_weaviate(self):
|
||||
vs, _ = self._bind_weaviate_as_vector_store()
|
||||
coord = ErasureCoordinator(vector_store=vs)
|
||||
receipt = coord.erase_entity("customer-4471")
|
||||
self.assertEqual(receipt.stores["vectors"]["backend"], "weaviate")
|
||||
|
||||
def test_facade_delete_vectors_forwards_to_weaviate(self):
|
||||
vs, data = self._bind_weaviate_as_vector_store()
|
||||
|
||||
def _fake(uuid):
|
||||
return uuid != "missing"
|
||||
|
||||
data.delete_by_id.side_effect = _fake
|
||||
ret = vs.delete_vectors(["present", "missing"])
|
||||
self.assertEqual(data.delete_by_id.call_count, 2)
|
||||
self.assertEqual(ret, {"delete_count": 1})
|
||||
Reference in New Issue
Block a user