mirror of
https://github.com/semantica-agi/semantica.git
synced 2026-09-04 04:01:07 +00:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f30c3f8ab0 | ||
|
|
b4b12ae705 |
@@ -12,63 +12,13 @@ on:
|
||||
- '**/*.md'
|
||||
pull_request:
|
||||
branches: [main]
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- 'docs_check.py'
|
||||
- '**/*.md'
|
||||
|
||||
jobs:
|
||||
# Detect whether this PR touches any source files (non-docs/non-markdown).
|
||||
# The result drives the `build` job's `if:` condition so that:
|
||||
# - docs-only PRs: `build` is skipped (satisfies the required check).
|
||||
# - code PRs: `build` runs exactly as before.
|
||||
# Push events (to main) keep their own paths-ignore above and never reach
|
||||
# this job, so the push optimization is unaffected.
|
||||
changes:
|
||||
runs-on: ubuntu-latest
|
||||
# Only needed for pull_request events; push events are pre-filtered above.
|
||||
if: github.event_name == 'pull_request'
|
||||
outputs:
|
||||
src: ${{ steps.filter.outputs.src }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
# Fetch enough history to compute the merge base against the PR base.
|
||||
fetch-depth: 0
|
||||
- name: Check for source changes
|
||||
id: filter
|
||||
run: |
|
||||
# List files changed in this PR relative to the true merge base.
|
||||
# Using three-dot merge-base diff so changes on the base branch that
|
||||
# are not part of this PR do not appear in the file list.
|
||||
# If every changed file matches docs/** or *.md (any depth) or
|
||||
# docs_check.py, this is a docs-only PR and src=false; otherwise
|
||||
# src=true.
|
||||
BASE="${{ github.event.pull_request.base.sha }}"
|
||||
HEAD="${{ github.event.pull_request.head.sha }}"
|
||||
MERGE_BASE=$(git merge-base "$BASE" "$HEAD")
|
||||
CHANGED=$(git diff --name-only "$MERGE_BASE" "$HEAD")
|
||||
echo "Changed files:"
|
||||
echo "$CHANGED"
|
||||
NON_DOCS=$(echo "$CHANGED" | grep -Ev '^(docs/|docs_check\.py|.*\.md$)' || true)
|
||||
if [ -n "$NON_DOCS" ]; then
|
||||
echo "src=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "src=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
build:
|
||||
needs: [changes]
|
||||
# For pull_request events:
|
||||
# - skip only when changes ran successfully and explicitly set src=false
|
||||
# (i.e. a confirmed docs-only PR).
|
||||
# - run when changes succeeded with src=true (source changes present).
|
||||
# - run when changes failed or was cancelled (fail-closed: missing output
|
||||
# must not silently skip the build).
|
||||
# For push/non-PR events: changes is skipped; always() prevents the build
|
||||
# from being skipped due to a skipped needs dependency.
|
||||
if: >-
|
||||
always() && (
|
||||
github.event_name != 'pull_request' ||
|
||||
needs.changes.result != 'success' ||
|
||||
needs.changes.outputs.src == 'true'
|
||||
)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
|
||||
@@ -13,65 +13,17 @@ on:
|
||||
- '**/*.md'
|
||||
pull_request:
|
||||
branches: [main]
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- 'mkdocs.yml'
|
||||
- 'requirements-docs.txt'
|
||||
- '**/*.md'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
# Detect whether this PR touches any source files (non-docs/non-markdown).
|
||||
# The result drives the `security-scan` job's `if:` condition so that:
|
||||
# - docs-only PRs: `security-scan` is skipped (satisfies the required check).
|
||||
# - code PRs: the full scan runs exactly as before.
|
||||
# Schedule and workflow_dispatch runs always skip this job and run the scan
|
||||
# unconditionally (the security-scan job's if: accounts for that below).
|
||||
# Push events (to main) keep their own paths-ignore above.
|
||||
changes:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'pull_request'
|
||||
outputs:
|
||||
src: ${{ steps.filter.outputs.src }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Check for source changes
|
||||
id: filter
|
||||
run: |
|
||||
# List files changed in this PR relative to the true merge base.
|
||||
# Using three-dot merge-base diff so changes on the base branch that
|
||||
# are not part of this PR do not appear in the file list.
|
||||
# If every changed file matches the docs/markdown paths-ignore list
|
||||
# (at any directory depth), this is a docs-only PR and src=false;
|
||||
# otherwise src=true.
|
||||
BASE="${{ github.event.pull_request.base.sha }}"
|
||||
HEAD="${{ github.event.pull_request.head.sha }}"
|
||||
MERGE_BASE=$(git merge-base "$BASE" "$HEAD")
|
||||
CHANGED=$(git diff --name-only "$MERGE_BASE" "$HEAD")
|
||||
echo "Changed files:"
|
||||
echo "$CHANGED"
|
||||
NON_DOCS=$(echo "$CHANGED" | grep -Ev '^(docs/|mkdocs\.yml$|requirements-docs\.txt$|.*\.md$)' || true)
|
||||
if [ -n "$NON_DOCS" ]; then
|
||||
echo "src=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "src=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
security-scan:
|
||||
# For pull_request events:
|
||||
# - skip only when changes ran successfully and explicitly set src=false
|
||||
# (i.e. a confirmed docs-only PR).
|
||||
# - run when changes succeeded with src=true (source changes present).
|
||||
# - run when changes failed or was cancelled (fail-closed: missing output
|
||||
# must not silently skip the security scan).
|
||||
# For schedule/workflow_dispatch/push: changes is skipped; always() ensures
|
||||
# the scan still runs unconditionally for those triggers.
|
||||
needs: [changes]
|
||||
if: >-
|
||||
always() && (
|
||||
github.event_name != 'pull_request' ||
|
||||
needs.changes.result != 'success' ||
|
||||
needs.changes.outputs.src == 'true'
|
||||
)
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
@@ -28,13 +28,7 @@
|
||||
|
||||
[](https://github.com/semantica-agi/semantica) [](https://github.com/semantica-agi/semantica/network/members) [](https://github.com/semantica-agi/semantica/graphs/contributors) [](https://pypi.org/project/semantica/) [](https://pepy.tech/project/semantica) [](https://www.python.org/) [](https://opensource.org/licenses/MIT) [](https://github.com/semantica-agi/semantica/actions) [](https://github.com/semantica-agi/semantica/actions/workflows/install-matrix.yml) [](https://scorecard.dev/viewer/?uri=github.com/semantica-agi/semantica) [](https://deepwiki.com/semantica-agi/semantica)
|
||||
|
||||
[](https://getsemantica.ai/)
|
||||
[](https://docs.getsemantica.ai/)
|
||||
[](https://discord.gg/sV34vps5hH)
|
||||
[](https://x.com/BuildSemantica)
|
||||
|
||||
[](https://www.youtube.com/watch?v=QfnNZg4-dZA)
|
||||
|
||||
[](https://getsemantica.ai/) [](https://docs.getsemantica.ai/) [](https://discord.gg/sV34vps5hH) [](https://x.com/BuildSemantica) [](https://www.youtube.com/watch?v=QfnNZg4-dZA) [](CHANGELOG.md)
|
||||
|
||||
```bash
|
||||
pip install semantica
|
||||
|
||||
@@ -185,7 +185,7 @@ Centralized `ConfigManager` with environment variable overrides. No magic defaul
|
||||
| **Deduplication v2** | `blocking_v2`, `hybrid_v2`, `semantic_v2`: up to 7x faster than v1 |
|
||||
| **Indexed search** | Explorer search at 0.004ms on 118k nodes (v0.5.0) |
|
||||
|
||||
- [Modules](/modules) — Full module documentation with code examples.
|
||||
- [Learning More](/learning-more) — Configuration reference, performance guide, and troubleshooting.
|
||||
- [Pipeline Reference](/reference/pipeline) — Pipeline orchestration, workers, and retry policies.
|
||||
- [Core Reference](/reference/core) — Framework lifecycle, plugin registry, and configuration.
|
||||
- [Modules](modules) — Full module documentation with code examples.
|
||||
- [Learning More](learning-more) — Configuration reference, performance guide, and troubleshooting.
|
||||
- [Pipeline Reference](reference/pipeline) — Pipeline orchestration, workers, and retry policies.
|
||||
- [Core Reference](reference/core) — Framework lifecycle, plugin registry, and configuration.
|
||||
|
||||
+183
-4
@@ -1,9 +1,14 @@
|
||||
/* ============================================================
|
||||
SEMANTICA DOCS — DESIGN SYSTEM
|
||||
SEMANTICA DOCS — PREMIUM 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;
|
||||
@@ -24,7 +29,16 @@ html {
|
||||
}
|
||||
::-webkit-scrollbar-thumb:hover { background: rgba(16, 185, 129, 0.4); }
|
||||
|
||||
/* ── Focus rings (accessibility — kept) ─────────────────────── */
|
||||
/* ── Page entrance ──────────────────────────────────────────── */
|
||||
main,
|
||||
article,
|
||||
[class*="content-area"],
|
||||
[class*="ContentArea"],
|
||||
[class*="prose"] {
|
||||
animation: pageFadeIn 0.35s ease both;
|
||||
}
|
||||
|
||||
/* ── Focus rings ─────────────────────────────────────────────── */
|
||||
*:focus-visible {
|
||||
outline: 2px solid rgba(16, 185, 129, 0.55) !important;
|
||||
outline-offset: 3px !important;
|
||||
@@ -45,7 +59,7 @@ h1::after {
|
||||
left: 0;
|
||||
width: 44px;
|
||||
height: 2px;
|
||||
background: #10B981;
|
||||
background: linear-gradient(90deg, #10B981 0%, transparent 100%);
|
||||
border-radius: 1px;
|
||||
}
|
||||
|
||||
@@ -57,6 +71,9 @@ 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,
|
||||
@@ -72,6 +89,14 @@ 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 ────────────────────────────────────────────── */
|
||||
@@ -98,11 +123,165 @@ 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 ───────────────────────────────────────── */
|
||||
|
||||
+13
-13
@@ -5,7 +5,7 @@ icon: "compass"
|
||||
---
|
||||
|
||||
<Info>
|
||||
Every module works independently — import only what you need. This page maps developer goals to starting points. The [Module Reference](/modules) covers every module in depth.
|
||||
Every module works independently — import only what you need. This page maps developer goals to starting points. The [Module Reference](modules) covers every module in depth.
|
||||
</Info>
|
||||
|
||||
## Quick Reference
|
||||
@@ -89,7 +89,7 @@ Pick your goal to see the minimum imports and a working skeleton.
|
||||
Pass `method="pattern"` to `NERExtractor` for zero-cost, zero-API-key extraction. Switch to `method="llm"` with any of the supported providers for higher recall.
|
||||
</Tip>
|
||||
|
||||
**Next:** [Quickstart →](/quickstart) — full pipeline with visualization and export.
|
||||
**Next:** [Quickstart →](quickstart) — full pipeline with visualization and export.
|
||||
</Tab>
|
||||
|
||||
<Tab title="Build GraphRAG">
|
||||
@@ -122,7 +122,7 @@ Pick your goal to see the minimum imports and a working skeleton.
|
||||
print(result["reasoning_path"]) # multi-hop trace
|
||||
```
|
||||
|
||||
**Next:** [Context module reference →](/reference/context)
|
||||
**Next:** [Context module reference →](reference/context)
|
||||
</Tab>
|
||||
|
||||
<Tab title="Add Agent Memory">
|
||||
@@ -163,7 +163,7 @@ Pick your goal to see the minimum imports and a working skeleton.
|
||||
`decision_tracking=True` is required. Without it, `record_decision()` raises `RuntimeError`.
|
||||
</Note>
|
||||
|
||||
**Next:** [Context module reference →](/reference/context)
|
||||
**Next:** [Context module reference →](reference/context)
|
||||
</Tab>
|
||||
|
||||
<Tab title="Track Provenance">
|
||||
@@ -195,7 +195,7 @@ Pick your goal to see the minimum imports and a working skeleton.
|
||||
diff = manager.diff("v1.0", "v1.1")
|
||||
```
|
||||
|
||||
**Next:** [Provenance reference →](/reference/provenance) · [Change Management reference →](/reference/change_management)
|
||||
**Next:** [Provenance reference →](reference/provenance) · [Change Management reference →](reference/change_management)
|
||||
</Tab>
|
||||
|
||||
<Tab title="Export">
|
||||
@@ -222,7 +222,7 @@ Pick your goal to see the minimum imports and a working skeleton.
|
||||
|
||||
**Formats:** Turtle · JSON-LD · N-Triples · RDF/XML · Parquet · Cypher · Arrow · OWL · CSV · ArangoDB AQL
|
||||
|
||||
**Next:** [Export module reference →](/reference/export)
|
||||
**Next:** [Export module reference →](reference/export)
|
||||
</Tab>
|
||||
|
||||
<Tab title="MCP — Claude / Cursor">
|
||||
@@ -268,7 +268,7 @@ Pick your goal to see the minimum imports and a working skeleton.
|
||||
Set `SEMANTICA_KG_PATH` to persist your graph across restarts. Without it, all data is lost when the server process exits.
|
||||
</Warning>
|
||||
|
||||
**Next:** [MCP Server reference →](/reference/mcp_server)
|
||||
**Next:** [MCP Server reference →](reference/mcp_server)
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -283,11 +283,11 @@ Pick your goal to see the minimum imports and a working skeleton.
|
||||
|
||||
Use **both together** via `AgentContext` (GraphRAG) to get grounded LLM responses where every claim traces back to a source node.
|
||||
|
||||
See also: [Core Concepts](/concepts)
|
||||
See also: [Core Concepts](concepts)
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="I just want to run something quickly." icon="rocket">
|
||||
Start with the [Quickstart](/quickstart). It builds a complete pipeline (ingest → parse → extract → graph → visualize → export) with no API key required.
|
||||
Start with the [Quickstart](quickstart). It builds a complete pipeline (ingest → parse → extract → graph → visualize → export) with no API key required.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="I'm adding Semantica to an existing agent — what's the minimum?" icon="plug">
|
||||
@@ -304,7 +304,7 @@ Pick your goal to see the minimum imports and a working skeleton.
|
||||
)
|
||||
```
|
||||
|
||||
[Context module reference →](/reference/context)
|
||||
[Context module reference →](reference/context)
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="I need a compliance-ready pipeline — what's the minimum stack?" icon="shield-check">
|
||||
@@ -322,6 +322,6 @@ Pick your goal to see the minimum imports and a working skeleton.
|
||||
|
||||
---
|
||||
|
||||
- [Quickstart](/quickstart) — Full pipeline in 5 minutes.
|
||||
- [Module Reference](/modules) — Every module with examples and common chains.
|
||||
- [API Reference](/reference/context) — Complete class and method documentation.
|
||||
- [Quickstart](quickstart) — Full pipeline in 5 minutes.
|
||||
- [Module Reference](modules) — Every module with examples and common chains.
|
||||
- [API Reference](reference/context) — Complete class and method documentation.
|
||||
|
||||
+2
-2
@@ -48,5 +48,5 @@ Published research using Semantica? [Let us know](https://github.com/semantica-a
|
||||
|
||||
## See Also
|
||||
|
||||
- [License](/project-license) — MIT License details.
|
||||
- [Community](/community) — Connect with the Semantica community.
|
||||
- [License](project-license) — MIT License details.
|
||||
- [Community](community) — Connect with the Semantica community.
|
||||
|
||||
+9
-9
@@ -24,7 +24,7 @@ After installation the following commands are available:
|
||||
| `semantica-mcp` | `semantica.mcp_server:main` | MCP server (stdio) for Claude Desktop, Cursor, Windsurf, and other MCP clients |
|
||||
|
||||
<Note>
|
||||
`semantica-explorer` requires `pip install semantica[explorer]`. Running it without that extra will immediately print an error and exit. See [Explorer Setup](/explorer-setup) for the full walkthrough.
|
||||
`semantica-explorer` requires `pip install semantica[explorer]`. Running it without that extra will immediately print an error and exit. See [Explorer Setup](explorer-setup) for the full walkthrough.
|
||||
</Note>
|
||||
|
||||
|
||||
@@ -52,8 +52,8 @@ python -c "import semantica; print(semantica.__version__)"
|
||||
- **semantica** — The general-purpose CLI. Use it for one-off pipeline runs, entity extraction, and graph operations from a shell script or CI job.
|
||||
- **semantica-server** — Starts the REST API server. Binds to `0.0.0.0:8000`. Use this when another service or application needs programmatic access to Semantica over HTTP.
|
||||
- **semantica-worker** — Background task processor. Run alongside `semantica-server` when you need async pipeline execution outside the request cycle. Start the server first, then start one or more workers pointing at the same backend.
|
||||
- **semantica-explorer** — Launches the browser dashboard. Requires `pip install semantica[explorer]`. Use this to explore a saved knowledge graph interactively. See [Explorer Setup](/explorer-setup).
|
||||
- **semantica-mcp** — Runs the MCP server over stdio. Configure it in your MCP client's settings file to expose all 15 tools and 3 resources to Claude Desktop, Cursor, Windsurf, or any MCP-aware client. See [MCP Server](/reference/mcp_server).
|
||||
- **semantica-explorer** — Launches the browser dashboard. Requires `pip install semantica[explorer]`. Use this to explore a saved knowledge graph interactively. See [Explorer Setup](explorer-setup).
|
||||
- **semantica-mcp** — Runs the MCP server over stdio. Configure it in your MCP client's settings file to expose all 15 tools and 3 resources to Claude Desktop, Cursor, Windsurf, or any MCP-aware client. See [MCP Server](reference/mcp_server).
|
||||
|
||||
|
||||
## Usage Examples
|
||||
@@ -116,7 +116,7 @@ python -c "import semantica; print(semantica.__version__)"
|
||||
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' | semantica-mcp
|
||||
```
|
||||
|
||||
You should receive a JSON-RPC response. See [MCP Server](/reference/mcp_server) for the full list of tools and resources.
|
||||
You should receive a JSON-RPC response. See [MCP Server](reference/mcp_server) for the full list of tools and resources.
|
||||
</Tab>
|
||||
<Tab title="Explorer">
|
||||
```bash
|
||||
@@ -124,7 +124,7 @@ python -c "import semantica; print(semantica.__version__)"
|
||||
semantica-explorer --graph my_graph.json
|
||||
```
|
||||
|
||||
See [Explorer Setup](/explorer-setup) for the full walkthrough including how to build and save a graph file.
|
||||
See [Explorer Setup](explorer-setup) for the full walkthrough including how to build and save a graph file.
|
||||
</Tab>
|
||||
<Tab title="Python module form">
|
||||
Every command also runs as a Python module: useful when the script directory is not on `PATH`:
|
||||
@@ -228,7 +228,7 @@ Install the [Microsoft Visual C++ Redistributable](https://aka.ms/vs/17/release/
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Explorer Setup](/explorer-setup) — Build a graph, save it, and launch the browser dashboard.
|
||||
- [MCP Server](/reference/mcp_server) — All 15 tools and 3 resources exposed over the MCP protocol.
|
||||
- [Installation](/installation) — Virtual environments, optional extras, and platform-specific notes.
|
||||
- [Quickstart](/quickstart) — End-to-end pipeline walkthrough with working code.
|
||||
- [Explorer Setup](explorer-setup) — Build a graph, save it, and launch the browser dashboard.
|
||||
- [MCP Server](reference/mcp_server) — All 15 tools and 3 resources exposed over the MCP protocol.
|
||||
- [Installation](installation) — Virtual environments, optional extras, and platform-specific notes.
|
||||
- [Quickstart](quickstart) — End-to-end pipeline walkthrough with working code.
|
||||
|
||||
@@ -109,12 +109,12 @@ def my_ingestor(source):
|
||||
method_registry.register("file", "my_format", my_ingestor)
|
||||
```
|
||||
|
||||
See [Architecture](/architecture#extension-points) for the full extension guide.
|
||||
See [Architecture](architecture#extension-points) for the full extension guide.
|
||||
|
||||
|
||||
## How to Contribute
|
||||
|
||||
- [Contributing Guide](/contributing-guide) — Submit code, documentation, tests, or cookbook notebooks.
|
||||
- [Contributing Guide](contributing-guide) — Submit code, documentation, tests, or cookbook notebooks.
|
||||
- [GitHub Issues](https://github.com/semantica-agi/semantica/issues) — Report bugs, request features, or propose integrations.
|
||||
- [Discord](https://discord.gg/sV34vps5hH) — Share what you're building with the community.
|
||||
- [GitHub Discussions](https://github.com/semantica-agi/semantica/discussions) — Long-form questions, design discussions, and ideas.
|
||||
|
||||
+5
-5
@@ -55,7 +55,7 @@ There's no single right way to contribute. Pick the path that fits your skills a
|
||||
- Review open pull requests
|
||||
- Share your Semantica projects in GitHub Discussions
|
||||
|
||||
See the [Contributing Guide](/contributing-guide) for the full development workflow.
|
||||
See the [Contributing Guide](contributing-guide) for the full development workflow.
|
||||
|
||||
|
||||
## Stay Connected
|
||||
@@ -68,7 +68,7 @@ See the [Contributing Guide](/contributing-guide) for the full development workf
|
||||
|
||||
## See Also
|
||||
|
||||
- [Contributing Guide](/contributing-guide) — Step-by-step guide for submitting PRs and setting up your dev environment.
|
||||
- [Community Projects](/community-projects) — Projects and integrations built by the community.
|
||||
- [FAQ](/faq) — Common questions answered.
|
||||
- [Governance](/governance) — How the project is run and decisions are made.
|
||||
- [Contributing Guide](contributing-guide) — Step-by-step guide for submitting PRs and setting up your dev environment.
|
||||
- [Community Projects](community-projects) — Projects and integrations built by the community.
|
||||
- [FAQ](faq) — Common questions answered.
|
||||
- [Governance](governance) — How the project is run and decisions are made.
|
||||
|
||||
+94
-125
@@ -5,19 +5,19 @@ icon: "book-open"
|
||||
---
|
||||
|
||||
<Info>
|
||||
New here? Start with [Getting Started](/getting-started) for hands-on examples, then return here for deeper understanding.
|
||||
New here? Start with [Getting Started](getting-started) for hands-on examples, then return here for deeper understanding.
|
||||
</Info>
|
||||
|
||||
Semantica transforms unstructured data (documents, web pages, reports, databases) into **knowledge graphs**: structured representations that AI systems can query, reason about, and trace back to sources.
|
||||
Semantica transforms unstructured data: documents, web pages, reports, databases: into **knowledge graphs**: structured representations that AI systems can query, reason about, and trace back to sources.
|
||||
|
||||
At its core, Semantica adds a context and semantic layer on top of your existing AI stack. It doesn't replace LangChain, LlamaIndex, or your LLM provider. It makes their outputs grounded, traceable, and auditable.
|
||||
At its core, Semantica adds a **context and accountability layer** on top of your existing AI stack. It doesn't replace LangChain, LlamaIndex, or your LLM provider: it makes their outputs **grounded**, **traceable**, and **auditable**.
|
||||
|
||||
- **Context Layer.** Knowledge graphs, GraphRAG retrieval, semantic embeddings, and temporal intelligence ground every LLM response in structured, queryable facts.
|
||||
- **Accountability Layer.** Provenance tracking, decision intelligence, conflict detection, and W3C PROV-O compliance make every claim in your AI stack auditable and explainable.
|
||||
- **Extension Layer.** `PluginRegistry` and `MethodRegistry` let you replace or augment any component (ingestors, extractors, reasoning engines, backends) without changing framework code.
|
||||
- **Context Layer** — Knowledge graphs, GraphRAG retrieval, semantic embeddings, and temporal intelligence ground every LLM response in structured, queryable facts.
|
||||
- **Accountability Layer** — Provenance tracking, decision intelligence, conflict detection, and W3C PROV-O compliance make every claim in your AI stack auditable and explainable.
|
||||
- **Extension Layer** — `PluginRegistry` and `MethodRegistry` let you replace or augment any component: ingestors, extractors, reasoning engines, backends: without changing framework code.
|
||||
|
||||
<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. In short, Semantica explains and audits *what the AI system did*, not the foundation model's private internal reasoning.
|
||||
**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. In short, Semantica explains and audits *what the AI system did*, not the foundation model's private internal reasoning.
|
||||
</Warning>
|
||||
|
||||
## Knowledge Graphs
|
||||
@@ -30,7 +30,7 @@ The foundation of everything in Semantica. A knowledge graph stores information
|
||||
- **Edges (relationships)**: `works_for`, `located_in`, `founded_by`
|
||||
- **Properties**: name, date, confidence score, source URL
|
||||
|
||||
This structure makes knowledge searchable, connectable, and queryable. Critically, it's explainable: every answer can be traced back to the facts and relationships that produced it.
|
||||
This structure makes knowledge **searchable**, **connectable**, **queryable**, and: critically: **explainable**: every answer can be traced back to the facts and relationships that produced it.
|
||||
|
||||
|
||||
## Entity Extraction (NER)
|
||||
@@ -38,19 +38,18 @@ This structure makes knowledge searchable, connectable, and queryable. Criticall
|
||||
Scanning text to find and classify real-world entities:
|
||||
|
||||
```python
|
||||
# "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),
|
||||
]
|
||||
# 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}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
`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:
|
||||
Each entity gets a type, confidence score, and a link to its source document. Three extraction methods are available:
|
||||
|
||||
| Method | Speed | Accuracy | Requirements |
|
||||
| :------ | :----- | :-------- | :------------ |
|
||||
@@ -63,19 +62,15 @@ available:
|
||||
Finding how entities connect to each other:
|
||||
|
||||
```python
|
||||
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": [
|
||||
{"subject": "Steve Jobs", "predicate": "founded", "object": "Apple Inc.", "confidence": 0.92},
|
||||
{"subject": "Apple Inc.", "predicate": "located_in", "object": "Cupertino", "confidence": 0.89}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
`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.
|
||||
Relationships can be extracted via rule-based methods, ML models, or LLMs: each producing typed triplets with confidence scores and source attribution.
|
||||
|
||||
|
||||
## Knowledge Graph vs. Vector Store
|
||||
@@ -99,10 +94,9 @@ 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}
|
||||
)
|
||||
path = PathFinder().dijkstra_shortest_path(graph, "Steve Jobs", "Tim Cook")
|
||||
graph = GraphBuilder(merge_entities=True).build(entities=entities, relationships=rels)
|
||||
finder = PathFinder()
|
||||
path = finder.dijkstra_shortest_path(graph, "Steve Jobs", "Tim Cook")
|
||||
```
|
||||
</Tab>
|
||||
|
||||
@@ -146,16 +140,8 @@ 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,
|
||||
)
|
||||
|
||||
# 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"])
|
||||
result = context.query("Who founded Apple?", mode="graphrag")
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
@@ -217,7 +203,7 @@ ontology = {
|
||||
}
|
||||
```
|
||||
|
||||
Semantica can auto-generate ontologies from your knowledge graph or import existing OWL/RDF/Turtle ontologies. The **Ontology Hub** (v0.5.0) adds a visual editor, SHACL Studio, alignment authoring, and a live health dashboard. See the [Ontology reference](/reference/ontology) for the full 6-stage generation pipeline.
|
||||
Semantica can auto-generate ontologies from your knowledge graph or import existing OWL/RDF/Turtle ontologies. The **Ontology Hub** (v0.5.0) adds a visual editor, SHACL Studio, alignment authoring, and a live health dashboard. See the [Ontology reference](reference/ontology) for the full 6-stage generation pipeline.
|
||||
|
||||
|
||||
## Reasoning & Inference
|
||||
@@ -235,80 +221,70 @@ 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
|
||||
from semantica.reasoning import Reasoner, Rule, Fact, RuleType
|
||||
|
||||
engine = Reasoner()
|
||||
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)"
|
||||
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()
|
||||
```
|
||||
</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, Rule, Fact
|
||||
from semantica.reasoning import ReteEngine
|
||||
|
||||
engine = ReteEngine()
|
||||
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)"]
|
||||
engine.load_rules("rules/domain_rules.json")
|
||||
results = engine.run(kg)
|
||||
```
|
||||
</Tab>
|
||||
<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.
|
||||
<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.
|
||||
|
||||
```python
|
||||
from semantica.reasoning import GraphReasoner
|
||||
|
||||
reasoner = GraphReasoner(provider="openai", model="gpt-4o-mini")
|
||||
answer = reasoner.reason(kg, "Which suppliers are indirectly exposed to the Acme outage?")
|
||||
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)
|
||||
```
|
||||
</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
|
||||
from semantica.reasoning import DatalogReasoner, DatalogFact, DatalogRule
|
||||
|
||||
reasoner = DatalogReasoner()
|
||||
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
|
||||
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)")
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Engine Comparison">
|
||||
|
||||
| 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 |
|
||||
| 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 |
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
`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.
|
||||
All engines produce **explainable inference paths**: not black-box conclusions. Every derived fact includes the rules and premises that produced it.
|
||||
|
||||
|
||||
## Temporal Intelligence
|
||||
@@ -337,18 +313,13 @@ Explore the semantic neighborhood of any entity in your graph: useful for unders
|
||||
```python
|
||||
from semantica.kg import SimilarityCalculator
|
||||
|
||||
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)
|
||||
calc = SimilarityCalculator()
|
||||
scores = calc.calculate_similarity(entity_a, entity_b)
|
||||
```
|
||||
|
||||
**Features:** N×N semantic distance matrices, ego-mode visualization, distance band classification (`direct` / `near` / `mid-range` / `distant`), embedding cache optimization for large graphs.
|
||||
**Features:** N×N semantic distance matrices, ego-mode visualization, distance band classification (`near` / `mid` / `far`), 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.
|
||||
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.
|
||||
|
||||
|
||||
## Deduplication & Entity Resolution
|
||||
@@ -370,11 +341,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)
|
||||
candidates = detector.detect_duplicates(entities)
|
||||
detector = DuplicateDetector(similarity_threshold=0.85)
|
||||
duplicates = detector.detect_duplicates(entities)
|
||||
|
||||
merger = EntityMerger()
|
||||
operations = merger.merge_duplicates(entities, strategy="keep_most_complete")
|
||||
merger = EntityMerger()
|
||||
deduplicated_entities = merger.merge_duplicates(entities)
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
@@ -390,21 +361,19 @@ 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). `ProvenanceManager.export_prov(format="turtle")` serialises the recorded lineage as PROV-O RDF.
|
||||
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.
|
||||
</Note>
|
||||
|
||||
```python
|
||||
from semantica.provenance import ProvenanceManager
|
||||
|
||||
prov = ProvenanceManager()
|
||||
prov.track_entity("apple_inc", source="report.pdf",
|
||||
metadata={"extractor": "NamedEntityRecognizer", "confidence": 0.98})
|
||||
prov = ProvenanceManager()
|
||||
lineage = prov.get_entity_lineage("apple_inc")
|
||||
|
||||
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
|
||||
print(f"Source: {lineage.source_document}")
|
||||
print(f"Method: {lineage.extraction_method}")
|
||||
print(f"Extracted: {lineage.timestamp}")
|
||||
print(f"Checksum: {lineage.checksum}")
|
||||
```
|
||||
|
||||
|
||||
@@ -444,7 +413,7 @@ When multiple sources disagree on the same fact, Semantica flags and resolves th
|
||||
- **Majority vote**: aggregate across all sources with ≥ 2 agreeing
|
||||
- **Manual review**: flag for human arbitration; continue pipeline without blocking
|
||||
|
||||
See the [Conflicts reference](/reference/conflicts) for `ConflictResolver`, `SourceTracker`, and `InvestigationGuideGenerator`.
|
||||
See the [Conflicts reference](reference/conflicts) for `ConflictResolver`, `SourceTracker`, and `InvestigationGuideGenerator`.
|
||||
|
||||
|
||||
## Custom Plugin Development
|
||||
@@ -487,32 +456,32 @@ 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: swap a built-in graph operation for your own">
|
||||
<Accordion title="MethodRegistry: add domain-specific graph operations">
|
||||
|
||||
`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.
|
||||
`MethodRegistry` lets you register custom methods on knowledge graph objects by name: useful for adding domain-specific graph operations without subclassing.
|
||||
|
||||
```python
|
||||
from semantica.kg import method_registry
|
||||
from semantica.kg.methods import calculate_centrality
|
||||
from semantica.kg import MethodRegistry
|
||||
|
||||
def fast_centrality(graph, **kwargs):
|
||||
"""Custom centrality implementation."""
|
||||
registry = MethodRegistry()
|
||||
|
||||
def find_supply_chain_hops(graph, source_node, max_hops=3):
|
||||
"""Custom BFS traversal for supply chain graphs."""
|
||||
...
|
||||
|
||||
# register(task, name, func)
|
||||
method_registry.register("centrality", "fast_centrality", fast_centrality)
|
||||
# Register under a string key
|
||||
registry.register("supply_chain_hops", find_supply_chain_hops)
|
||||
|
||||
# The task wrappers consult method_registry, so the name is now selectable:
|
||||
scores = calculate_centrality(kg, method="fast_centrality")
|
||||
# Call by name on any graph object
|
||||
result = registry.call("supply_chain_hops", kg, source_node="Supplier_A", max_hops=5)
|
||||
|
||||
print(method_registry.list_all("centrality")) # {"centrality": ["fast_centrality", ...]}
|
||||
# List all registered methods
|
||||
print(registry.list_methods()) # ["supply_chain_hops", ...]
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
- [Quickstart Tutorial](/quickstart): build a full pipeline with code.
|
||||
- [Modules Guide](/modules): every module explained with examples.
|
||||
- [API Reference](/reference/context): complete technical reference.
|
||||
- [Quickstart Tutorial](quickstart) — Build a full pipeline with code.
|
||||
- [Modules Guide](modules) — Every module explained with examples.
|
||||
- [API Reference](reference/context) — Complete technical reference.
|
||||
|
||||
@@ -85,5 +85,5 @@ All contributors are expected to follow the [Contributor Covenant Code of Conduc
|
||||
- [GitHub Discussions](https://github.com/semantica-agi/semantica/discussions)
|
||||
- [Discord](https://discord.gg/sV34vps5hH)
|
||||
|
||||
- [Community](/community) — Community guidelines and values.
|
||||
- [Governance](/governance) — How decisions are made and the project is run.
|
||||
- [Community](community) — Community guidelines and values.
|
||||
- [Governance](governance) — How decisions are made and the project is run.
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ icon: "flask"
|
||||
**Where to start:**
|
||||
- **New to Semantica**: begin with [Core Tutorials](#core-tutorials)
|
||||
- **Building an application**: see [Advanced Concepts](#advanced-concepts)
|
||||
- **Need installation help**: see the [Installation Guide](/installation)
|
||||
- **Need installation help**: see the [Installation Guide](installation)
|
||||
</Tip>
|
||||
|
||||
<Note>
|
||||
|
||||
+29
-30
@@ -2,7 +2,7 @@
|
||||
"$schema": "https://mintlify.com/docs.json",
|
||||
"theme": "mint",
|
||||
"name": "Semantica",
|
||||
"description": "The Context and Semantic Layer for AI in High-Stakes Domains — Context Graphs · Decision Intelligence · Full Provenance",
|
||||
"description": "The Accountability and Context Layer for AI — Context Graphs · Decision Intelligence · Full Provenance",
|
||||
"colors": {
|
||||
"primary": "#10B981",
|
||||
"light": "#10B981",
|
||||
@@ -43,7 +43,7 @@
|
||||
"raiseIssue": true
|
||||
},
|
||||
"metadata": {
|
||||
"og:title": "Semantica — Context & Semantic Layer for AI in High-Stakes Domains",
|
||||
"og:title": "Semantica — Accountability & Context Layer for AI",
|
||||
"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,23 +121,6 @@
|
||||
"pages": [
|
||||
"vector_stores/pgvector"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "FAQ",
|
||||
"pages": [
|
||||
"faq"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Community",
|
||||
"pages": [
|
||||
"community",
|
||||
"community-projects",
|
||||
"contributing-guide",
|
||||
"governance",
|
||||
"citation",
|
||||
"project-license"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -184,17 +167,7 @@
|
||||
"guides/policy-engine",
|
||||
"guides/visualization",
|
||||
"guides/distance-intelligence",
|
||||
"guides/graph-analytics"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "API Reference",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Context & Intelligence",
|
||||
"pages": [
|
||||
"guides/graph-analytics",
|
||||
"reference/context",
|
||||
"reference/kg",
|
||||
"reference/temporal",
|
||||
@@ -263,6 +236,32 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -6,7 +6,7 @@ icon: "map"
|
||||
|
||||
**`semantica-explorer`** is an **interactive browser dashboard** for knowledge graph exploration. You give it a graph file, it starts a local server, and opens a browser tab where you can search nodes, find paths, inspect provenance, and run analytics: no code required after launch.
|
||||
|
||||
This page covers everything needed to go from zero to a running Explorer. For the full REST API reference and endpoint catalogue, see [Explorer Reference](/reference/explorer).
|
||||
This page covers everything needed to go from zero to a running Explorer. For the full REST API reference and endpoint catalogue, see [Explorer Reference](reference/explorer).
|
||||
|
||||
|
||||
## Prerequisites
|
||||
@@ -27,7 +27,7 @@ Verify:
|
||||
semantica-explorer --help
|
||||
```
|
||||
|
||||
You should see the usage message with the four available flags. If you see `command not found`, activate your virtual environment first. See [CLI Setup](/cli-setup#troubleshooting) for PATH help.
|
||||
You should see the usage message with the four available flags. If you see `command not found`, activate your virtual environment first. See [CLI Setup](cli-setup#troubleshooting) for PATH help.
|
||||
|
||||
|
||||
## Minimal End-to-End Example
|
||||
@@ -264,7 +264,7 @@ Once running, Explorer exposes a REST API and dashboard for:
|
||||
|
||||
The full endpoint catalogue is documented in the Swagger UI at `/docs` and in the reference page below.
|
||||
|
||||
- [Explorer Reference](/reference/explorer) — Every REST endpoint, WebSocket events, analytics, and all supported flags.
|
||||
- [CLI Setup](/cli-setup) — All five Semantica executables and when to use each one.
|
||||
- [Context Module](/reference/context) — Full documentation for ContextGraph: build, query, save, and load.
|
||||
- [Quickstart](/quickstart) — End-to-end pipeline: ingest → extract → build graph → export.
|
||||
- [Explorer Reference](reference/explorer) — Every REST endpoint, WebSocket events, analytics, and all supported flags.
|
||||
- [CLI Setup](cli-setup) — All five Semantica executables and when to use each one.
|
||||
- [Context Module](reference/context) — Full documentation for ContextGraph: build, query, save, and load.
|
||||
- [Quickstart](quickstart) — End-to-end pipeline: ingest → extract → build graph → export.
|
||||
|
||||
+3
-3
@@ -93,7 +93,7 @@ pip install --upgrade semantica
|
||||
pip install semantica
|
||||
```
|
||||
|
||||
See [Installation](/installation) for virtual environment setup, optional extras (`[gpu]`, `[all]`, provider-specific), and platform-specific troubleshooting.
|
||||
See [Installation](installation) for virtual environment setup, optional extras (`[gpu]`, `[all]`, provider-specific), and platform-specific troubleshooting.
|
||||
|
||||
</Accordion>
|
||||
|
||||
@@ -173,7 +173,7 @@ This includes PyTorch with CUDA, FAISS GPU, and CuPy.
|
||||
<Accordion title="How does Semantica handle large datasets?" icon="layer-group">
|
||||
|
||||
- **Batching**: process documents in configurable chunks to control memory usage
|
||||
- **Parallel processing**: the `semantica.pipeline` module can run independent, parallel-safe steps in the same dependency layer concurrently (see the [Pipeline guide](/guides/pipeline))
|
||||
- **Parallel processing**: the `semantica.pipeline` module can run independent, parallel-safe steps in the same dependency layer concurrently (see the [Pipeline guide](guides/pipeline))
|
||||
- **Delta processing**: update graphs incrementally without full recompute on new data
|
||||
- **Persistent backends**: swap in-memory NetworkX for Neo4j, FalkorDB, or Apache AGE for large-scale production graphs
|
||||
|
||||
@@ -350,4 +350,4 @@ set PYTHONIOENCODING=utf-8
|
||||
|
||||
- [Discord](https://discord.gg/sV34vps5hH) — Community chat and live support.
|
||||
- [GitHub Issues](https://github.com/semantica-agi/semantica/issues) — Bug reports and feature requests.
|
||||
- [Contributing](/contributing-guide) — Help improve Semantica.
|
||||
- [Contributing](contributing-guide) — Help improve Semantica.
|
||||
|
||||
+37
-45
@@ -5,7 +5,7 @@ icon: "rocket"
|
||||
---
|
||||
|
||||
<Tip>
|
||||
Already installed? Jump straight to [Quickstart](/quickstart). Need setup help first? See [Installation](/installation).
|
||||
Already installed? Jump straight to [Quickstart](quickstart). Need setup help first? See [Installation](installation).
|
||||
</Tip>
|
||||
|
||||
## What You Can Build
|
||||
@@ -52,15 +52,15 @@ icon: "rocket"
|
||||
|
||||
| Track | You want to... | Start with |
|
||||
| :----- | :-------------- | :--------- |
|
||||
| **Knowledge Graph** | Turn documents into structured, queryable graphs | [Quickstart → Step 1](/quickstart) |
|
||||
| **Agent Context** | Give your AI agent persistent memory and decision tracking | [Context reference](/reference/context) |
|
||||
| **GraphRAG** | Ground LLM answers in structured knowledge | [Concepts → GraphRAG](/concepts#graphrag) |
|
||||
| **MCP Integration** | Use Semantica from Claude Desktop or VS Code | [MCP Server](/reference/mcp_server) |
|
||||
| **Knowledge Graph** | Turn documents into structured, queryable graphs | [Quickstart → Step 1](quickstart) |
|
||||
| **Agent Context** | Give your AI agent persistent memory and decision tracking | [Context reference](reference/context) |
|
||||
| **GraphRAG** | Ground LLM answers in structured knowledge | [Concepts → GraphRAG](concepts#graphrag) |
|
||||
| **MCP Integration** | Use Semantica from Claude Desktop or VS Code | [MCP Server](reference/mcp_server) |
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Run the pipeline">
|
||||
The full 6-step pipeline: ingest, parse, extract, build, visualize, export: is in the [Quickstart](/quickstart). Takes under 5 minutes with pattern-based extraction (no API key required).
|
||||
The full 6-step pipeline: ingest, parse, extract, build, visualize, export: is in the [Quickstart](quickstart). Takes under 5 minutes with pattern-based extraction (no API key required).
|
||||
|
||||
<Note>
|
||||
An LLM API key is **optional** for the quickstart. Pattern-based extraction works out of the box: upgrade to LLM extraction for higher accuracy when you're ready.
|
||||
@@ -84,13 +84,13 @@ icon: "rocket"
|
||||
# 1. Ingest
|
||||
sources = FileIngestor().ingest("data/report.pdf")
|
||||
|
||||
# 2. Parse (extract_text returns a plain string for any supported format)
|
||||
text = DocumentParser().extract_text(sources[0].path)
|
||||
# 2. Parse
|
||||
parsed = DocumentParser().parse(sources[0])
|
||||
|
||||
# 3. Extract (extractors take text, return Entity / Relation objects)
|
||||
# 3. Extract
|
||||
ner = NERExtractor(method="pattern") # no API key needed
|
||||
entities = ner.extract(text)
|
||||
relationships = RelationExtractor(method="pattern").extract(text, entities=entities)
|
||||
entities = ner.extract(parsed)
|
||||
relationships = RelationExtractor().extract(parsed, entities=entities)
|
||||
|
||||
# 4. Build
|
||||
graph = GraphBuilder(merge_entities=True).build(
|
||||
@@ -99,7 +99,7 @@ icon: "rocket"
|
||||
print(f"{len(graph['entities'])} nodes, {len(graph['relationships'])} edges")
|
||||
```
|
||||
|
||||
**Next:** [Full pipeline walkthrough →](/quickstart)
|
||||
**Next:** [Full pipeline walkthrough →](quickstart)
|
||||
</Tab>
|
||||
|
||||
<Tab title="Agent Context">
|
||||
@@ -131,7 +131,7 @@ icon: "rocket"
|
||||
precedents = context.find_precedents("model selection", limit=5)
|
||||
```
|
||||
|
||||
**Next:** [Context module reference →](/reference/context)
|
||||
**Next:** [Context module reference →](reference/context)
|
||||
</Tab>
|
||||
|
||||
<Tab title="GraphRAG">
|
||||
@@ -144,32 +144,24 @@ icon: "rocket"
|
||||
context = AgentContext(
|
||||
vector_store=VectorStore(backend="faiss", dimension=768),
|
||||
knowledge_graph=ContextGraph(advanced_analytics=True),
|
||||
graph_expansion=True, # blend graph traversal into retrieval
|
||||
max_expansion_hops=3, # how far to walk from the seed nodes
|
||||
)
|
||||
|
||||
# store() runs extraction and populates both the vector index and the graph
|
||||
context.store([
|
||||
{"content": "Steve Wozniak co-founded Apple with Steve Jobs in 1976."},
|
||||
{"content": "Tony Fadell led the iPod team at Apple, then founded Nest."},
|
||||
])
|
||||
# Load your knowledge graph
|
||||
context.load_graph("company_kg.json")
|
||||
|
||||
# GraphRAG retrieval: seed from vector matches, expand along graph edges
|
||||
results = context.retrieve(
|
||||
# Multi-hop GraphRAG query
|
||||
result = context.query(
|
||||
"What companies were founded by people who worked at Apple?",
|
||||
use_graph=True,
|
||||
expand_graph=True,
|
||||
mode="graphrag",
|
||||
reasoning=True,
|
||||
)
|
||||
for r in results:
|
||||
print(f"[{r['score']:.3f}] {r['content'][:70]} (source: {r['source']})")
|
||||
|
||||
# Every claim links back to a source node
|
||||
for claim in result.claims:
|
||||
print(f"{claim.text} → source: {claim.source_node}")
|
||||
```
|
||||
|
||||
Each result carries `content`, `score`, `source`, and `metadata`. For a
|
||||
grounded natural-language answer plus an auditable traversal, use
|
||||
`context.query_with_reasoning(query, llm_provider=...)` — it returns
|
||||
`response`, `reasoning_path`, `sources`, and `confidence`.
|
||||
|
||||
**Next:** [GraphRAG concepts →](/concepts#graphrag)
|
||||
**Next:** [GraphRAG concepts →](concepts#graphrag)
|
||||
</Tab>
|
||||
|
||||
<Tab title="MCP Integration">
|
||||
@@ -193,7 +185,7 @@ icon: "rocket"
|
||||
|
||||
15 tools available instantly: extract entities, query graph, record decisions, run reasoning, export results.
|
||||
|
||||
**Next:** [MCP Server reference →](/reference/mcp_server)
|
||||
**Next:** [MCP Server reference →](reference/mcp_server)
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -202,29 +194,29 @@ icon: "rocket"
|
||||
|
||||
Semantica uses a modular, layered architecture: import only what you need.
|
||||
|
||||
- **[Input Layer](/reference/ingest)** — Load and prepare data from any source. Modules: `ingest`, `parse`, `split`, `normalize`
|
||||
- **[Semantic Layer](/reference/semantic_extract)** — Extract meaning from raw text. Modules: `semantic_extract`, `kg`, `ontology`, `reasoning`
|
||||
- **[Storage Layer](/reference/vector_store)** — Persist knowledge for retrieval. Modules: `embeddings`, `vector_store`, `graph_store`, `triplet_store`
|
||||
- **[Quality Layer](/reference/deduplication)** — Validate and deduplicate. Modules: `deduplication`, `conflicts`
|
||||
- **[Context Layer](/reference/context)** — Track decisions and lineage. Modules: `context`, `provenance`, `change_management`
|
||||
- **[Output Layer](/reference/export)** — Deliver results downstream. Modules: `export`, `visualization`, `pipeline`, `explorer`
|
||||
- **[Input Layer](reference/ingest)** — Load and prepare data from any source. Modules: `ingest`, `parse`, `split`, `normalize`
|
||||
- **[Semantic Layer](reference/semantic_extract)** — Extract meaning from raw text. Modules: `semantic_extract`, `kg`, `ontology`, `reasoning`
|
||||
- **[Storage Layer](reference/vector_store)** — Persist knowledge for retrieval. Modules: `embeddings`, `vector_store`, `graph_store`, `triplet_store`
|
||||
- **[Quality Layer](reference/deduplication)** — Validate and deduplicate. Modules: `deduplication`, `conflicts`
|
||||
- **[Context Layer](reference/context)** — Track decisions and lineage. Modules: `context`, `provenance`, `change_management`
|
||||
- **[Output Layer](reference/export)** — Deliver results downstream. Modules: `export`, `visualization`, `pipeline`, `explorer`
|
||||
|
||||
|
||||
## Which Module Do I Need?
|
||||
|
||||
See the [Choose the Right Module](/choose-your-module) guide — it maps 35+ developer goals to the right starting point across all 27 modules, with working code for the most common paths.
|
||||
See the [Choose the Right Module](choose-your-module) guide — it maps 35+ developer goals to the right starting point across all 27 modules, with working code for the most common paths.
|
||||
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Core Concepts](/concepts) — Knowledge graphs, ontologies, and reasoning explained in depth.
|
||||
- [Quickstart Tutorial](/quickstart) — Full 6-step pipeline walkthrough with working code.
|
||||
- [Module Reference](/modules) — Every module, class, and common chain explained.
|
||||
- [API Reference](/reference/context) — Complete module documentation for every class and method.
|
||||
- [Core Concepts](concepts) — Knowledge graphs, ontologies, and reasoning explained in depth.
|
||||
- [Quickstart Tutorial](quickstart) — Full 6-step pipeline walkthrough with working code.
|
||||
- [Module Reference](modules) — Every module, class, and common chain explained.
|
||||
- [API Reference](reference/context) — Complete module documentation for every class and method.
|
||||
|
||||
|
||||
## Help
|
||||
|
||||
- [Discord](https://discord.gg/sV34vps5hH) — Ask questions, share projects, get community support.
|
||||
- [GitHub Issues](https://github.com/semantica-agi/semantica/issues) — Report bugs or request features.
|
||||
- [FAQ](/faq) — Common questions answered.
|
||||
- [FAQ](faq) — Common questions answered.
|
||||
|
||||
+4
-4
@@ -214,7 +214,7 @@ A vulnerability in XML parsers that allows attackers to read arbitrary files or
|
||||
|
||||
## See Also
|
||||
|
||||
- [Core Concepts](/concepts) — Deeper explanation of key ideas with code examples.
|
||||
- [Getting Started](/getting-started) — First working examples: no prior graph experience required.
|
||||
- [Modules Guide](/modules) — All 27 modules explained with code and pipeline chains.
|
||||
- [API Reference](/reference/context) — Complete technical reference for every class and method.
|
||||
- [Core Concepts](concepts) — Deeper explanation of key ideas with code examples.
|
||||
- [Getting Started](getting-started) — First working examples: no prior graph experience required.
|
||||
- [Modules Guide](modules) — All 27 modules explained with code and pipeline chains.
|
||||
- [API Reference](reference/context) — Complete technical reference for every class and method.
|
||||
|
||||
+3
-3
@@ -74,10 +74,10 @@ Semantica follows **Semantic Versioning** (`MAJOR.MINOR.PATCH`):
|
||||
|
||||
## License
|
||||
|
||||
MIT License: see [LICENSE](https://github.com/semantica-agi/semantica/blob/main/LICENSE) and the [License page](/project-license).
|
||||
MIT License: see [LICENSE](https://github.com/semantica-agi/semantica/blob/main/LICENSE) and the [License page](project-license).
|
||||
|
||||
|
||||
## See Also
|
||||
|
||||
- [Contributing](/contributing-guide) — How to submit changes.
|
||||
- [Community](/community) — Community guidelines and channels.
|
||||
- [Contributing](contributing-guide) — How to submit changes.
|
||||
- [Community](community) — Community guidelines and channels.
|
||||
|
||||
@@ -46,7 +46,7 @@ Agent Memory provides persistent storage and intelligent retrieval of informatio
|
||||
- Simple retrieval tasks where relationships between entities don't matter
|
||||
|
||||
<Info>
|
||||
This guide covers the memory layer. For graph-enriched traversal and entity linking, see [Context Graphs](/guides/context-graphs). For decision accountability — recording, auditing, and causally tracing what the agent chose — see [Decision Intelligence](/guides/decision-intelligence).
|
||||
This guide covers the memory layer. For graph-enriched traversal and entity linking, see [Context Graphs](context-graphs). For decision accountability — recording, auditing, and causally tracing what the agent chose — see [Decision Intelligence](decision-intelligence).
|
||||
</Info>
|
||||
|
||||
## Setting Up a Persistent Memory Context
|
||||
@@ -657,10 +657,10 @@ print("Total memories: {}".format(s.get("total_items", 0)))
|
||||
|
||||
## Related Guides
|
||||
|
||||
- [Context Graphs](/guides/context-graphs) — How the underlying `ContextGraph` stores entity nodes and decision nodes; temporal interval reasoning; deduplication before node insertion; ontology from graph.
|
||||
- [Decision Intelligence](/guides/decision-intelligence) — Recording decisions as graph nodes with causal chains and policy gating.
|
||||
- [Multi-Agent Systems](/guides/multi-agent) — Coordinating multiple agents through a shared `AgentContext` and save/load handoffs.
|
||||
- [LLM Integrations](/guides/llm-integrations) — Configuring the LLM provider passed to `query_with_reasoning()`.
|
||||
- [Context Graphs](context-graphs) — How the underlying `ContextGraph` stores entity nodes and decision nodes; temporal interval reasoning; deduplication before node insertion; ontology from graph.
|
||||
- [Decision Intelligence](decision-intelligence) — Recording decisions as graph nodes with causal chains and policy gating.
|
||||
- [Multi-Agent Systems](multi-agent) — Coordinating multiple agents through a shared `AgentContext` and save/load handoffs.
|
||||
- [LLM Integrations](llm-integrations) — Configuring the LLM provider passed to `query_with_reasoning()`.
|
||||
- [Deduplication Guide](deduplication) — Full reference for `DuplicateDetector`, `EntityMerger`, similarity methods, and cluster strategies.
|
||||
- [Ontology Management](ontology) — Generate and validate OWL ontologies from the knowledge graph; export to Turtle, OWL/XML, JSON-LD.
|
||||
- [Context Module Reference](../reference/context) — Full API: `AgentContext`, `AgentMemory`, `MemoryItem`, `ContextRetriever`.
|
||||
|
||||
@@ -496,8 +496,8 @@ print("Model v1.1 verified and approved for production.")
|
||||
|
||||
## Related Guides
|
||||
|
||||
- [Context Graphs](/guides/context-graphs) — `ContextGraph.to_dict()` feeds `create_snapshot()`
|
||||
- [Context Graphs](context-graphs) — `ContextGraph.to_dict()` feeds `create_snapshot()`
|
||||
- [Ontology Management](ontology) — pair ontology versioning with graph versioning for a complete schema + data audit trail
|
||||
- [SHACL Validation](/guides/shacl-validation) — validate graph data at each version gate before snapshotting
|
||||
- [SHACL Validation](shacl-validation) — validate graph data at each version gate before snapshotting
|
||||
- [Provenance](provenance) — combine change management with W3C PROV-O lineage for a full audit trail
|
||||
- [Visualization](visualization) — `TemporalVisualizer.visualize_snapshot_comparison()` and `visualize_metrics_evolution()` render version diffs as interactive charts
|
||||
|
||||
@@ -69,7 +69,7 @@ flowchart TD
|
||||
2. **Conflict Detection** — Call `detect_entity_conflicts()` to surface all property disagreements at once, or `detect_value_conflicts()` to target a specific property.
|
||||
3. **Resolution** — For each conflict, apply a strategy (`CREDIBILITY_WEIGHTED`, `MOST_RECENT`, `VOTING`, etc.) or route it for expert review (`EXPERT_REVIEW`).
|
||||
4. **Persist Canonical Values** — Write resolved values back to your canonical entities or graph store. See [Persisting resolved values](#persisting-resolved-values).
|
||||
5. **SHACL Validation** — Enforce structural constraints on the resolved graph to confirm it satisfies your ontology. See [SHACL Validation](/guides/shacl-validation).
|
||||
5. **SHACL Validation** — Enforce structural constraints on the resolved graph to confirm it satisfies your ontology. See [SHACL Validation](shacl-validation).
|
||||
|
||||
## Quick Start: A Beginner Example
|
||||
|
||||
@@ -698,6 +698,6 @@ Calling `set_resolution_rule()` for every entity-property pair just to apply the
|
||||
|
||||
- [Deduplication](deduplication) — remove duplicate nodes before running conflict detection
|
||||
- [Provenance](provenance) — track which source each resolved value came from, and verify the audit trail cryptographically
|
||||
- [SHACL Validation](/guides/shacl-validation) — enforce structural constraints after conflicts are resolved
|
||||
- [Change Management](/guides/change-management) — snapshot the graph before and after conflict resolution runs
|
||||
- [SHACL Validation](shacl-validation) — enforce structural constraints after conflicts are resolved
|
||||
- [Change Management](change-management) — snapshot the graph before and after conflict resolution runs
|
||||
- [Ontology Management](ontology) — align entity types to a shared vocabulary to reduce type conflicts at the schema level
|
||||
|
||||
@@ -50,7 +50,7 @@ A context graph is a property graph that stores entities as **nodes** and relati
|
||||
- Cases where setup complexity exceeds the relationship complexity
|
||||
|
||||
<Info>
|
||||
ContextGraph is an **in-memory data structure**. All nodes, edges, and metadata are stored in Python dictionaries and lists. For standalone graphs, persist state with `save_to_file()`. When using `AgentContext`, call `AgentContext.save()` instead — it saves the graph, the FAISS vector index, and memory in one step. For analytical operations on top of a populated graph — centrality rankings, community detection, node embeddings, link prediction — see the [Graph Analytics guide](/guides/graph-analytics). For recording and querying decisions stored as nodes, see the [Decision Intelligence guide](/guides/decision-intelligence).
|
||||
ContextGraph is an **in-memory data structure**. All nodes, edges, and metadata are stored in Python dictionaries and lists. For standalone graphs, persist state with `save_to_file()`. When using `AgentContext`, call `AgentContext.save()` instead — it saves the graph, the FAISS vector index, and memory in one step. For analytical operations on top of a populated graph — centrality rankings, community detection, node embeddings, link prediction — see the [Graph Analytics guide](graph-analytics). For recording and querying decisions stored as nodes, see the [Decision Intelligence guide](decision-intelligence).
|
||||
</Info>
|
||||
|
||||
## Constructing the Graph
|
||||
@@ -704,8 +704,8 @@ for n in stress_reach:
|
||||
|
||||
## Related Guides
|
||||
|
||||
- [Graph Analytics](/guides/graph-analytics) — centrality rankings, community detection, node embeddings, and link prediction on a populated `ContextGraph`
|
||||
- [Decision Intelligence](/guides/decision-intelligence) — recording decisions as typed nodes, causal chain analysis, precedent search, and policy enforcement
|
||||
- [Graph Analytics](graph-analytics) — centrality rankings, community detection, node embeddings, and link prediction on a populated `ContextGraph`
|
||||
- [Decision Intelligence](decision-intelligence) — recording decisions as typed nodes, causal chain analysis, precedent search, and policy enforcement
|
||||
- [Ingest](ingest) — loading data from PDFs, APIs, databases, STIX bundles, and RSS feeds into the graph
|
||||
- [Deduplication](deduplication) — detecting and merging near-duplicate nodes before insertion to prevent graph fragmentation
|
||||
- [Reasoning](reasoning) — temporal interval algebra (Allen relations), forward/backward chaining, and SPARQL over the knowledge graph
|
||||
|
||||
@@ -638,8 +638,8 @@ results = context.find_precedents("APT29 infrastructure attribution", limit=5)
|
||||
|
||||
## Related Guides
|
||||
|
||||
- [Context Graphs](/guides/context-graphs) — how `ContextGraph` stores decision nodes and causal edges
|
||||
- [Distance Intelligence](/guides/distance-intelligence) — `trace_decision_causality()` annotates causal chains with confidence decay and distance bands
|
||||
- [Context Graphs](context-graphs) — how `ContextGraph` stores decision nodes and causal edges
|
||||
- [Distance Intelligence](distance-intelligence) — `trace_decision_causality()` annotates causal chains with confidence decay and distance bands
|
||||
- [Provenance](provenance) — W3C PROV-O audit trail that wraps decision records in standards-compliant provenance
|
||||
- [MCP Server](/guides/mcp-server) — expose decision recording and precedent search to LLM agents via the `record_decision` and `find_precedents` tools
|
||||
- [Change Management](/guides/change-management) — checkpoint decision state with `flush_checkpoint()` for versioned snapshots
|
||||
- [MCP Server](mcp-server) — expose decision recording and precedent search to LLM agents via the `record_decision` and `find_precedents` tools
|
||||
- [Change Management](change-management) — checkpoint decision state with `flush_checkpoint()` for versioned snapshots
|
||||
|
||||
@@ -612,7 +612,7 @@ The similarity threshold controls sensitivity. Start at 0.7 and examine false po
|
||||
## Related Guides
|
||||
|
||||
- [Ingest Anything](ingest) — multi-source ingestion creates the duplicates this module resolves
|
||||
- [Context Graphs](/guides/context-graphs) — store deduplicated entities directly in the knowledge graph
|
||||
- [Conflict Resolution](/guides/conflict-resolution) — after merging, reconcile disagreeing property values on the canonical entity
|
||||
- [Context Graphs](context-graphs) — store deduplicated entities directly in the knowledge graph
|
||||
- [Conflict Resolution](conflict-resolution) — after merging, reconcile disagreeing property values on the canonical entity
|
||||
- [Provenance](provenance) — track merge lineage so every canonical entity traces back to its original sources
|
||||
- [Pipeline](pipeline) — chain ingest, deduplicate, and store as a `PipelineBuilder` workflow
|
||||
|
||||
@@ -557,8 +557,8 @@ for chain in chains:
|
||||
|
||||
## Related Guides
|
||||
|
||||
- [Context Graphs](/guides/context-graphs) — `ContextGraph` node and edge model; `add_edge(weight=...)` feeds confidence decay
|
||||
- [Graph Analytics](/guides/graph-analytics) — centrality, community detection, Node2Vec embeddings, link prediction
|
||||
- [Agent Memory](/guides/agent-memory) — proximity-blended retrieval (`proximity_weight`) integrates distance intelligence into memory search
|
||||
- [Decision Intelligence](/guides/decision-intelligence) — `trace_decision_causality()` for causal chains with distance annotations
|
||||
- [Context Graphs](context-graphs) — `ContextGraph` node and edge model; `add_edge(weight=...)` feeds confidence decay
|
||||
- [Graph Analytics](graph-analytics) — centrality, community detection, Node2Vec embeddings, link prediction
|
||||
- [Agent Memory](agent-memory) — proximity-blended retrieval (`proximity_weight`) integrates distance intelligence into memory search
|
||||
- [Decision Intelligence](decision-intelligence) — `trace_decision_causality()` for causal chains with distance annotations
|
||||
- [Reasoning & Rules](reasoning) — `TemporalReasoningEngine` for Allen interval algebra over time-bounded graph nodes
|
||||
|
||||
@@ -443,8 +443,8 @@ For semantic reasoning and ontology work, OWL/XML is the format — it is the on
|
||||
|
||||
## Related Guides
|
||||
|
||||
- [Context Graphs](/guides/context-graphs) — the `ContextGraph` object whose `to_dict()` feeds all exports
|
||||
- [Context Graphs](context-graphs) — the `ContextGraph` object whose `to_dict()` feeds all exports
|
||||
- [Ontology Management](ontology) — export OWL ontologies generated from your graph
|
||||
- [Reasoning & Rules](reasoning) — reasoning results can be exported as RDF triples
|
||||
- [Change Management](/guides/change-management) — snapshot a graph before exporting to prove the export was made from a verified state
|
||||
- [Change Management](change-management) — snapshot a graph before exporting to prove the export was made from a verified state
|
||||
- [Pipeline](pipeline) — chain ingest, extract, and export in a single `PipelineBuilder`
|
||||
|
||||
@@ -310,7 +310,7 @@ for node1, node2, score in predictions:
|
||||
A score above 0.8 is worth analyst review — these aren't random; they're edges the topology of the existing graph strongly implies. Scores below 0.5 are noise. The sweet spot for human review is 0.6–0.8: plausible but not yet confirmed.
|
||||
|
||||
<Info>
|
||||
Link prediction is also available on `Decision` nodes through `DecisionQuery.predict_decision_relationships(decision_id, top_k)`. See the [Decision Intelligence guide](/guides/decision-intelligence) for how to surface causal relationships between past decisions.
|
||||
Link prediction is also available on `Decision` nodes through `DecisionQuery.predict_decision_relationships(decision_id, top_k)`. See the [Decision Intelligence guide](decision-intelligence) for how to surface causal relationships between past decisions.
|
||||
</Info>
|
||||
|
||||
## Understanding Your Decision History
|
||||
@@ -538,7 +538,7 @@ print(f"\n{len(result['communities'])} exposure clusters "
|
||||
|
||||
## Related Guides
|
||||
|
||||
- [Context Graphs](/guides/context-graphs) — building and querying the underlying `ContextGraph`
|
||||
- [Context Graphs](context-graphs) — building and querying the underlying `ContextGraph`
|
||||
- [Visualization](visualization) — render centrality rankings and community clusters as interactive dashboards
|
||||
- [Decision Intelligence](/guides/decision-intelligence) — link prediction and structural similarity applied to decision nodes
|
||||
- [GraphRAG](/guides/graphrag) — using analytics results to ground LLM generation in the most contextually relevant subgraph
|
||||
- [Decision Intelligence](decision-intelligence) — link prediction and structural similarity applied to decision nodes
|
||||
- [GraphRAG](graphrag) — using analytics results to ground LLM generation in the most contextually relevant subgraph
|
||||
|
||||
+42
-55
@@ -1,9 +1,9 @@
|
||||
---
|
||||
title: "GraphRAG: Graph-Augmented Retrieval"
|
||||
title: "GraphRAG — Graph-Augmented Retrieval"
|
||||
description: "Go beyond vector search: retrieve facts, trace reasoning paths, and ground LLM responses in your knowledge graph."
|
||||
---
|
||||
|
||||
GraphRAG combines vector similarity with knowledge graph traversal so retrieval finds structurally connected facts, not just text that sounds related. When a `ContextGraph` is attached to `AgentContext`, every retrieval call automatically blends semantic search with multi-hop graph expansion, and `query_with_reasoning()` returns an auditable reasoning path alongside the LLM answer.
|
||||
GraphRAG combines vector similarity with knowledge graph traversal so retrieval finds structurally connected facts, not just text that sounds related. When a `ContextGraph` is attached to `AgentContext`, every retrieval call automatically blends semantic search with multi-hop graph expansion — and `query_with_reasoning()` returns an auditable reasoning path alongside the LLM answer.
|
||||
|
||||
## What Is GraphRAG?
|
||||
|
||||
@@ -11,7 +11,7 @@ GraphRAG (Graph-Augmented Retrieval-Augmented Generation) enhances traditional R
|
||||
|
||||
**GraphRAG vs. traditional vector-only RAG:** Vector RAG finds documents similar to your query text. GraphRAG finds documents similar to your query AND documents connected to those through entity relationships, even if they don't mention your query terms directly.
|
||||
|
||||
**The role of graph traversal:** Starting from entities found in vector-similar documents, GraphRAG expands outward through relationship edges to discover related facts. This reveals connections that pure text similarity would miss, like finding that a threat actor targets healthcare by following the path: Actor → Tool → Victim Organization → Industry Sector.
|
||||
**The role of graph traversal:** Starting from entities found in vector-similar documents, GraphRAG expands outward through relationship edges to discover related facts. This reveals connections that pure text similarity would miss — like finding that a threat actor targets healthcare by following the path: Actor → Tool → Victim Organization → Industry Sector.
|
||||
|
||||
## Why Use GraphRAG?
|
||||
|
||||
@@ -96,7 +96,7 @@ context = AgentContext(
|
||||
)
|
||||
```
|
||||
|
||||
Now ingest your documents. `store()` with `extract_entities=True` runs the full extraction pipeline internally (Named Entity Recognition, relation extraction, and entity linking) and populates both the vector index and the graph simultaneously:
|
||||
Now ingest your documents. `store()` with `extract_entities=True` runs the full extraction pipeline internally — Named Entity Recognition (NER), relation extraction, and entity linking — and populates both the vector index and the graph simultaneously:
|
||||
|
||||
```python
|
||||
intel_documents = [
|
||||
@@ -132,17 +132,16 @@ stats = context.store(
|
||||
print("Graph built: {} nodes, {} edges".format(
|
||||
stats["graph_nodes"], stats["graph_edges"]
|
||||
))
|
||||
# Graph built: 18 nodes, 14 edges
|
||||
# Nodes: APT29, HAMMERTOSS, NATO, LifeCare, AS59796, CISA Sector 6, ...
|
||||
# Edges: deployed, observed_on, classified_as, targets, operates_in, ...
|
||||
```
|
||||
|
||||
`store()` returns a dict with `stored_count`, `memory_ids`, `graph_nodes`, and
|
||||
`graph_edges`. The extracted nodes (APT29, HAMMERTOSS, LifeCare, AS59796, …) and
|
||||
edges (`deployed`, `observed_on`, `classified_as`, …) now span all four documents.
|
||||
|
||||
The graph now contains a connected subgraph linking APT29 to healthcare infrastructure across four document boundaries, something that would be invisible to a pure vector search.
|
||||
The graph now contains a connected subgraph linking APT29 to healthcare infrastructure across four document boundaries — something that would be invisible to a pure vector search.
|
||||
|
||||
## Retrieving the relevant subgraph
|
||||
|
||||
With the graph populated, a plain `retrieve()` call already does more than vector search. When `use_graph=True`, the retriever seeds the graph traversal from the top-k vector matches and expands outward by following edges. Expansion depth is set once, by `max_expansion_hops` on the `AgentContext` constructor:
|
||||
With the graph populated, a plain `retrieve()` call already does more than vector search. When `use_graph=True`, the retriever seeds the graph traversal from the top-k vector matches and expands outward by following edges, collecting connected facts within `max_hops`:
|
||||
|
||||
```python
|
||||
results = context.retrieve(
|
||||
@@ -150,6 +149,7 @@ results = context.retrieve(
|
||||
use_graph=True,
|
||||
max_results=10,
|
||||
expand_graph=True,
|
||||
max_hops=3,
|
||||
)
|
||||
|
||||
for r in results:
|
||||
@@ -169,25 +169,17 @@ Notice the top results: while pure vector search might rank connected facts lowe
|
||||
When you know specifically which entity you want to anchor the traversal to, pass `anchor_node`:
|
||||
|
||||
```python
|
||||
# Anchor on APT29 explicitly: proximity scores are calculated from this node
|
||||
# Anchor on APT29 explicitly — proximity scores are calculated from this node
|
||||
apt29_intel = context.retrieve(
|
||||
"C2 infrastructure beaconing patterns",
|
||||
use_graph=True,
|
||||
anchor_node="APT29",
|
||||
proximity_weight=0.7, # strongly favour nodes close to APT29
|
||||
max_hops=3, # with an anchor, this bounds the proximity radius
|
||||
max_hops=3,
|
||||
max_results=8,
|
||||
)
|
||||
```
|
||||
|
||||
<Note>
|
||||
`max_hops` on `retrieve()` only takes effect when `anchor_node` is set: it
|
||||
bounds the proximity radius used for scoring and drops results farther than
|
||||
`max_hops` from the anchor. Without an `anchor_node` it is ignored. It does
|
||||
**not** change how far graph expansion reaches: that is fixed by
|
||||
`max_expansion_hops` on the constructor.
|
||||
</Note>
|
||||
|
||||
## Getting a grounded LLM answer with a reasoning path
|
||||
|
||||
`retrieve()` gives you the grounded context. `query_with_reasoning()` goes one step further: it passes that subgraph context to an LLM and returns the answer together with the multi-hop path the retrieval system traced through the graph. That path is your audit trail.
|
||||
@@ -195,7 +187,7 @@ apt29_intel = context.retrieve(
|
||||
```python
|
||||
from semantica.llms import LiteLLM
|
||||
|
||||
llm = LiteLLM(model="anthropic/claude-sonnet-5")
|
||||
llm = LiteLLM(model="anthropic/claude-sonnet-4-20250514")
|
||||
|
||||
result = context.query_with_reasoning(
|
||||
"What are APT29's known TTPs against healthcare infrastructure, "
|
||||
@@ -205,7 +197,7 @@ result = context.query_with_reasoning(
|
||||
max_hops=3,
|
||||
)
|
||||
|
||||
# The LLM answer, grounded in graph-retrieved context, not training memory
|
||||
# The LLM answer — grounded in graph-retrieved context, not training memory
|
||||
print(result["response"])
|
||||
|
||||
# The multi-hop trace: APT29 → deployed → HAMMERTOSS → observed_on → LifeCare → ...
|
||||
@@ -221,7 +213,7 @@ for src in result["sources"]:
|
||||
print(" [{:.3f}] {}".format(src["score"], src["content"][:80]))
|
||||
```
|
||||
|
||||
The `reasoning_path` field is what separates GraphRAG from a black-box LLM call. When an analyst asks "how do you know APT29 targeted healthcare?", you can show them the exact traversal the system made across your own documents, not a claim the model generated from training data.
|
||||
The `reasoning_path` field is what separates GraphRAG from a black-box LLM call. When an analyst asks "how do you know APT29 targeted healthcare?", you can show them the exact traversal the system made across your own documents — not a claim the model generated from training data.
|
||||
|
||||
The full return structure from `query_with_reasoning()`:
|
||||
|
||||
@@ -240,11 +232,11 @@ The full return structure from `query_with_reasoning()`:
|
||||
|
||||
<Tabs>
|
||||
|
||||
<Tab title="Defense: CTI/Threat">
|
||||
<Tab title="Defense — CTI/Threat">
|
||||
|
||||
Multi-INT intelligence fusion: OSINT threat feeds, NVD CVE data, and HUMINT summaries ingested into a single graph, then queried with multi-hop reasoning to trace C2 infrastructure chains and attribute campaigns to specific actors.
|
||||
|
||||
In classified environments the graph can be partitioned by data handling caveat: each `AgentContext` operates over the subset of documents cleared for the querying user. The `reasoning_path` output doubles as a sanitisable audit trail for downgraded reporting.
|
||||
In classified environments the graph can be partitioned by data handling caveat — each `AgentContext` operates over the subset of documents cleared for the querying user. The `reasoning_path` output doubles as a sanitisable audit trail for downgraded reporting.
|
||||
|
||||
```python
|
||||
from semantica.context import AgentContext, ContextGraph
|
||||
@@ -281,7 +273,7 @@ context.store(
|
||||
link_entities=True,
|
||||
)
|
||||
|
||||
llm = LiteLLM(model="anthropic/claude-sonnet-5")
|
||||
llm = LiteLLM(model="anthropic/claude-sonnet-4-20250514")
|
||||
result = context.query_with_reasoning(
|
||||
"Trace the C2 infrastructure chain for APT29 operations targeting "
|
||||
"ITAR-controlled contractors in 2025. Include IP ranges, ASNs, and TTPs.",
|
||||
@@ -308,11 +300,11 @@ proximate = context.retrieve(
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="Security: SOC/Incident">
|
||||
<Tab title="Security — SOC/Incident">
|
||||
|
||||
Security operations: real-time alert triage against a graph containing hosts, CVEs, user accounts, runbooks, and historical incidents. GraphRAG retrieves the relevant runbook and similar past incidents in a single call, reducing mean-time-to-respond.
|
||||
|
||||
The `decision_tracking=True` flag records every triage query as an auditable decision, with the full context that was provided to the LLM. That's essential for post-incident review and SOC metrics.
|
||||
The `decision_tracking=True` flag records every triage query as an auditable decision, with the full context that was provided to the LLM — essential for post-incident review and SOC metrics.
|
||||
|
||||
```python
|
||||
from semantica.context import AgentContext, ContextGraph
|
||||
@@ -351,7 +343,7 @@ Parent: wmiprvse.exe
|
||||
Sigma match: T1053.005 Scheduled Task/Job
|
||||
"""
|
||||
|
||||
llm = LiteLLM(model="anthropic/claude-sonnet-5")
|
||||
llm = LiteLLM(model="anthropic/claude-sonnet-4-20250514")
|
||||
triage = soc_context.query_with_reasoning(
|
||||
"Triage this SIEM alert and identify the correct response runbook:\n{}".format(alert_text),
|
||||
llm_provider=llm,
|
||||
@@ -377,7 +369,7 @@ for inc in similar:
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="Life Science: Clinical/Pharma">
|
||||
<Tab title="Life Science — Clinical/Pharma">
|
||||
|
||||
Clinical decision support: FDA drug labels, clinical guidelines, and trial summaries ingested into a graph where drug-enzyme-metabolite-interaction chains become traversable paths. A three-hop query (drug → enzyme → metabolite → contraindication) surfaces interaction risks that no single document would make explicit.
|
||||
|
||||
@@ -425,7 +417,7 @@ Patient: 68F, AF, CKD stage 3b (eGFR 32). On warfarin (INR target 2.0–3.0).
|
||||
Presenting for elective hip replacement. Concurrent: amiodarone 200mg, atorvastatin 40mg.
|
||||
"""
|
||||
|
||||
llm = LiteLLM(model="anthropic/claude-sonnet-5")
|
||||
llm = LiteLLM(model="anthropic/claude-sonnet-4-20250514")
|
||||
answer = clinical_context.query_with_reasoning(
|
||||
"What is the evidence-based warfarin bridging protocol for this patient "
|
||||
"given CKD and amiodarone interaction risk?\n\n{}".format(patient_context),
|
||||
@@ -451,7 +443,7 @@ contra_chain = clinical_context.retrieve(
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="Banking: Risk/Compliance">
|
||||
<Tab title="Banking — Risk/Compliance">
|
||||
|
||||
Regulatory compliance: Basel III (CRE20), BCBS 239, SR 11-7, and EBA IRRBB guidelines ingested as a graph where regulation articles cross-reference each other as edges. Multi-hop queries traverse those cross-references automatically, so a question about commercial real estate RWA pulls the relevant CRE20 paragraphs and the BCBS 239 data quality requirements that govern their calculation in a single call.
|
||||
|
||||
@@ -474,17 +466,12 @@ compliance_context = AgentContext(
|
||||
retention_days=2555, # 7-year regulatory retention
|
||||
)
|
||||
|
||||
# In production the text comes from a parsed file, e.g. FileIngestor().ingest_file(path).text;
|
||||
# inline strings here for brevity
|
||||
basel_cre20_text = (
|
||||
"CRE20.32: For income-producing real estate where repayment depends on "
|
||||
"property cash flows, RWA = exposure × risk weight, where risk weight "
|
||||
"is determined by LTV bucket per Table CRE20.3..."
|
||||
)
|
||||
bcbs239_text = (
|
||||
"Principle 3: Risk data should be accurate and have a single authoritative source. "
|
||||
"Where data is aggregated across systems, reconciliation must be documented..."
|
||||
)
|
||||
# In production these come from ingest_file() — shown as strings here for brevity
|
||||
basel_cre20_text = "CRE20.32: For income-producing real estate where repayment depends on "
|
||||
"property cash flows, RWA = exposure × risk weight, where risk weight "
|
||||
"is determined by LTV bucket per Table CRE20.3..."
|
||||
bcbs239_text = "Principle 3: Risk data should be accurate and have a single authoritative source. "
|
||||
"Where data is aggregated across systems, reconciliation must be documented..."
|
||||
|
||||
compliance_context.store(
|
||||
[
|
||||
@@ -495,7 +482,7 @@ compliance_context.store(
|
||||
extract_relationships=True,
|
||||
)
|
||||
|
||||
llm = LiteLLM(model="anthropic/claude-sonnet-5")
|
||||
llm = LiteLLM(model="anthropic/claude-sonnet-4-20250514")
|
||||
answer = compliance_context.query_with_reasoning(
|
||||
"Under Basel III CRE20, what are the RWA calculation requirements for "
|
||||
"commercial real estate exposures with LTV > 80%? "
|
||||
@@ -509,7 +496,7 @@ print(answer["response"])
|
||||
print("Regulatory sources cited: {}".format(answer["num_sources"]))
|
||||
print("Confidence: {:.1%}".format(answer["confidence"]))
|
||||
|
||||
# The reasoning path is the audit log: show it to the regulator
|
||||
# The reasoning path is the audit log — show it to the regulator
|
||||
print("\n--- Reasoning Path (audit log) ---")
|
||||
print(answer["reasoning_path"])
|
||||
```
|
||||
@@ -537,18 +524,18 @@ The `hybrid_alpha` parameter set in the `AgentContext` constructor establishes a
|
||||
When targeting a specific `anchor_node`, you can apply `proximity_weight` in `retrieve()` to dynamically blend structural distance from the anchor into the final score:
|
||||
|
||||
```python
|
||||
# Anchor node provided: let vector semantics lead, graph proximity only slightly boosts
|
||||
# Anchor node provided — let vector semantics lead, graph proximity only slightly boosts
|
||||
results = context.retrieve(
|
||||
query, use_graph=True, anchor_node="APT29", proximity_weight=0.2
|
||||
)
|
||||
|
||||
# Known-entity tracing: topology drives the retrieval
|
||||
# Known-entity tracing — topology drives the retrieval
|
||||
results = context.retrieve(
|
||||
query, use_graph=True, anchor_node="APT29", proximity_weight=0.8
|
||||
)
|
||||
```
|
||||
|
||||
Each additional expansion hop exponentially increases the subgraph size. Practical defaults by domain:
|
||||
Each additional hop in `max_hops` exponentially increases the subgraph size. Practical defaults by domain:
|
||||
|
||||
```text
|
||||
General Q&A max_expansion_hops=2 (95% of useful facts within 2 hops)
|
||||
@@ -557,7 +544,7 @@ Drug interactions max_expansion_hops=3 (drug → enzyme → metabolite
|
||||
Regulatory cross-ref max_expansion_hops=2 (rule → article → article)
|
||||
```
|
||||
|
||||
Expansion depth is a constructor setting only (`max_expansion_hops`); there is no per-call override on `retrieve()`. `query_with_reasoning()` does take a per-call `max_hops` argument.
|
||||
Set globally in the constructor; override per call with the `max_hops` argument to `retrieve()`.
|
||||
|
||||
## How GraphRAG works internally
|
||||
|
||||
@@ -589,9 +576,9 @@ The vector search and graph traversal run independently, then their scores are f
|
||||
|
||||
## Related Guides
|
||||
|
||||
- [Semantic Extraction](/guides/semantic-extraction): build the graph from raw unstructured text
|
||||
- [Agent Memory](/guides/agent-memory): store, retrieve, and persist agent memories
|
||||
- [Context Graphs](/guides/context-graphs): build and traverse the knowledge graph directly
|
||||
- [Reasoning](/guides/reasoning): derive new facts and run inference rules over the graph
|
||||
- [Decision Intelligence](/guides/decision-intelligence): causal chains, policy enforcement, decision tracking
|
||||
- [LLM Integrations](/guides/llm-integrations): connect Groq, OpenAI, Anthropic, HuggingFace, and 100+ more
|
||||
- [Semantic Extraction](semantic-extraction) — build the graph from raw unstructured text
|
||||
- [Agent Memory](agent-memory) — store, retrieve, and persist agent memories
|
||||
- [Context Graphs](context-graphs) — build and traverse the knowledge graph directly
|
||||
- [Reasoning](reasoning) — derive new facts and run inference rules over the graph
|
||||
- [Decision Intelligence](decision-intelligence) — causal chains, policy enforcement, decision tracking
|
||||
- [LLM Integrations](llm-integrations) — connect Groq, OpenAI, Anthropic, HuggingFace, and 100+ more
|
||||
|
||||
@@ -951,8 +951,8 @@ print(f"Compliance graph: {graph.stats()['node_count']} nodes, "
|
||||
## Related Guides
|
||||
|
||||
- [Pipeline](pipeline) — chain ingest steps with `PipelineBuilder` for automated, retryable, parallelised workflows
|
||||
- [Context Graphs](/guides/context-graphs) — storing and querying the entities you ingest as a typed property graph
|
||||
- [Semantic Extraction](/guides/semantic-extraction) — NER, relation extraction, and triplet extraction from ingested text
|
||||
- [Context Graphs](context-graphs) — storing and querying the entities you ingest as a typed property graph
|
||||
- [Semantic Extraction](semantic-extraction) — NER, relation extraction, and triplet extraction from ingested text
|
||||
- [Provenance](provenance) — tracking the origin document, confidence score, and ingestion timestamp for every extracted entity
|
||||
- [Databricks Integration](../integrations/databricks) — Unity Catalog setup, PAT/OAuth M2M authentication, and lineage introspection
|
||||
- [Snowflake Integration](../integrations/snowflake) — warehouse setup and password/key-pair/OAuth authentication
|
||||
|
||||
@@ -275,20 +275,20 @@ print(data)
|
||||
|
||||
**LiteLLM** is a universal adapter that provides a single interface to over 100 different LLM providers, including Anthropic Claude, Azure OpenAI, AWS Bedrock, Google Vertex AI, and local Ollama instances. It acts as a translation layer, converting your unified API calls into provider-specific requests, enabling easy switching between providers without code changes.
|
||||
|
||||
`LiteLLM` is the Swiss Army knife. It wraps the `litellm` library, which speaks to every major provider using a unified completion API. The model string encodes both provider and model name: `"anthropic/claude-sonnet-5"`, `"azure/gpt-4o"`, `"bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0"`, `"ollama/llama3.2"`. Change the string, change the provider — no other code changes needed.
|
||||
`LiteLLM` is the Swiss Army knife. It wraps the `litellm` library, which speaks to every major provider using a unified completion API. The model string encodes both provider and model name: `"anthropic/claude-sonnet-4-20250514"`, `"azure/gpt-4o"`, `"bedrock/anthropic.claude-3-5-sonnet-20241022-v2:0"`, `"ollama/llama3.2"`. Change the string, change the provider — no other code changes needed.
|
||||
|
||||
```python
|
||||
from semantica.llms import LiteLLM
|
||||
|
||||
# Anthropic Claude — highest accuracy for complex reasoning
|
||||
llm = LiteLLM(model="anthropic/claude-sonnet-5")
|
||||
llm = LiteLLM(model="anthropic/claude-sonnet-4-20250514")
|
||||
# Reads ANTHROPIC_API_KEY from environment
|
||||
|
||||
# Azure OpenAI — compliance and data-residency requirements
|
||||
llm = LiteLLM(model="azure/gpt-4o", api_key="YOUR_AZURE_KEY")
|
||||
|
||||
# AWS Bedrock — existing cloud agreement, no new vendor
|
||||
llm = LiteLLM(model="bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0")
|
||||
llm = LiteLLM(model="bedrock/anthropic.claude-3-5-sonnet-20241022-v2:0")
|
||||
|
||||
# Google Vertex AI
|
||||
llm = LiteLLM(model="vertex_ai/gemini-1.5-pro")
|
||||
@@ -306,7 +306,7 @@ The environment-variable convention for each provider: `ANTHROPIC_API_KEY`, `AZU
|
||||
import os
|
||||
|
||||
PROVIDER_MAP = {
|
||||
"prod": "anthropic/claude-sonnet-5",
|
||||
"prod": "anthropic/claude-sonnet-4-20250514",
|
||||
"staging": "openai/gpt-4o-mini",
|
||||
"local": "ollama/llama3.2",
|
||||
"azure": "azure/gpt-4o",
|
||||
@@ -378,7 +378,7 @@ print("FAST: {} (conf={:.0%})".format(fast_result["response"], fast_result["con
|
||||
|
||||
# Tier 2: deep answer with Claude if confidence is below threshold
|
||||
if fast_result["confidence"] < 0.85:
|
||||
deep_llm = LiteLLM(model="anthropic/claude-sonnet-5")
|
||||
deep_llm = LiteLLM(model="anthropic/claude-sonnet-4-20250514")
|
||||
deep_result = context.query_with_reasoning(
|
||||
query, llm_provider=deep_llm, max_results=15, max_hops=3
|
||||
)
|
||||
@@ -574,7 +574,7 @@ print("TRIAGE: {} (conf={:.0%})".format(triage["response"], triage["confidence"]
|
||||
|
||||
# Tier 2: escalate to Claude for deep analysis if Tier 1 is uncertain
|
||||
if triage["confidence"] < 0.88:
|
||||
deep_llm = LiteLLM(model="anthropic/claude-sonnet-5")
|
||||
deep_llm = LiteLLM(model="anthropic/claude-sonnet-4-20250514")
|
||||
deep = context.query_with_reasoning(
|
||||
"Full MITRE ATT&CK analysis of this alert: identify the attack chain, "
|
||||
"blast radius, affected systems, and recommended containment steps.",
|
||||
@@ -630,7 +630,7 @@ for d in drugs:
|
||||
# trastuzumab (conf=0.98), pertuzumab (conf=0.97), docetaxel (conf=0.96)
|
||||
|
||||
# Report synthesis with Claude — switch to azure/gpt-4o for HIPAA by changing one string
|
||||
report_llm = LiteLLM(model="anthropic/claude-sonnet-5")
|
||||
report_llm = LiteLLM(model="anthropic/claude-sonnet-4-20250514")
|
||||
# For HIPAA-constrained Azure deployment:
|
||||
# report_llm = LiteLLM(model="azure/gpt-4o", api_key="YOUR_AZURE_KEY")
|
||||
|
||||
@@ -682,7 +682,7 @@ question = (
|
||||
|
||||
# Two-provider consensus — same query, same graph, different LLMs
|
||||
gpt4o = OpenAI(model="gpt-4o", api_key="YOUR_OAI_KEY")
|
||||
claude = LiteLLM(model="anthropic/claude-sonnet-5")
|
||||
claude = LiteLLM(model="anthropic/claude-sonnet-4-20250514")
|
||||
|
||||
answer_a = context.query_with_reasoning(question, llm_provider=gpt4o, max_results=10)
|
||||
answer_b = context.query_with_reasoning(question, llm_provider=claude, max_results=10)
|
||||
@@ -719,7 +719,7 @@ for src in best["sources"]:
|
||||
|
||||
## Related Guides
|
||||
|
||||
- [Agent Memory](/guides/agent-memory) — using `query_with_reasoning()` with any LLM provider for graph-grounded retrieval
|
||||
- [Multi-Agent Systems](/guides/multi-agent) — wiring different LLM providers to different agent tiers in a shared-graph pipeline
|
||||
- [Semantic Extraction](/guides/semantic-extraction) — LLM-powered NER, relation extraction, event detection, and triplet extraction
|
||||
- [GraphRAG](/guides/graphrag) — multi-hop graph reasoning with `query_with_reasoning()`
|
||||
- [Agent Memory](agent-memory) — using `query_with_reasoning()` with any LLM provider for graph-grounded retrieval
|
||||
- [Multi-Agent Systems](multi-agent) — wiring different LLM providers to different agent tiers in a shared-graph pipeline
|
||||
- [Semantic Extraction](semantic-extraction) — LLM-powered NER, relation extraction, event detection, and triplet extraction
|
||||
- [GraphRAG](graphrag) — multi-hop graph reasoning with `query_with_reasoning()`
|
||||
|
||||
@@ -343,7 +343,7 @@ The result is a fully auditable credit decision trail with precedent links, read
|
||||
## Related Guides
|
||||
|
||||
- [Reasoning & Rules](reasoning) — the engine behind the `run_reasoning` tool
|
||||
- [Decision Intelligence](/guides/decision-intelligence) — how decisions are stored as causal graph nodes
|
||||
- [Context Graphs](/guides/context-graphs) — the graph that `add_entity` and `add_relationship` write to
|
||||
- [Decision Intelligence](decision-intelligence) — how decisions are stored as causal graph nodes
|
||||
- [Context Graphs](context-graphs) — the graph that `add_entity` and `add_relationship` write to
|
||||
- [Export & Serialization](export) — all export formats available via `export_graph`
|
||||
- [Ontology Management](ontology) — generate OWL ontologies from the graph built via MCP
|
||||
|
||||
@@ -55,7 +55,7 @@ Semantica coordinates agents through shared context (memory and knowledge graphs
|
||||
Semantica coordinates multiple agents through a shared `ContextGraph` — agents read and write to the same graph, or hand off serialized state via `save()` and `load()`, with no message broker required. Use this pattern when splitting work across ingestion, enrichment, reasoning, and reporting roles that must share a single evidence base.
|
||||
|
||||
<Info>
|
||||
This guide covers multi-agent coordination. For the memory layer each agent uses internally, see [Agent Memory](/guides/agent-memory). For graph traversal and entity linking, see [Context Graphs](/guides/context-graphs). For decision recording and precedent matching, see [Decision Intelligence](/guides/decision-intelligence).
|
||||
This guide covers multi-agent coordination. For the memory layer each agent uses internally, see [Agent Memory](agent-memory). For graph traversal and entity linking, see [Context Graphs](context-graphs). For decision recording and precedent matching, see [Decision Intelligence](decision-intelligence).
|
||||
</Info>
|
||||
|
||||
## The Three Coordination Patterns
|
||||
@@ -197,7 +197,7 @@ reasoning_agent.load("./pipeline/enriched_intel/")
|
||||
# All memories, graph nodes, and vector embeddings from both ingestion agents are now available.
|
||||
|
||||
# Use a high-capability model for the synthesis step
|
||||
llm = LiteLLM(model="anthropic/claude-sonnet-5")
|
||||
llm = LiteLLM(model="anthropic/claude-sonnet-4-20250514")
|
||||
|
||||
synthesis = reasoning_agent.query_with_reasoning(
|
||||
"Summarize the APT29 exploitation of CVE-2024-3400: affected products, "
|
||||
@@ -428,7 +428,7 @@ tier1.store(
|
||||
|
||||
# --- Tier 2: deep investigation when Tier 1 confidence is low ---
|
||||
if triage["confidence"] < 0.90:
|
||||
deep_llm = LiteLLM(model="anthropic/claude-sonnet-5")
|
||||
deep_llm = LiteLLM(model="anthropic/claude-sonnet-4-20250514")
|
||||
|
||||
investigation = tier2.query_with_reasoning(
|
||||
"Full MITRE ATT&CK analysis of incident {}. "
|
||||
@@ -533,7 +533,7 @@ t1.start(); t2.start()
|
||||
t1.join(); t2.join()
|
||||
|
||||
# Chief agent synthesizes across literature and experimental data
|
||||
llm = LiteLLM(model="anthropic/claude-sonnet-5")
|
||||
llm = LiteLLM(model="anthropic/claude-sonnet-4-20250514")
|
||||
|
||||
synthesis = chief.query_with_reasoning(
|
||||
"Identify the top two candidate compounds for KRAS G12C NSCLC that show "
|
||||
@@ -576,7 +576,7 @@ credit_officer = make_desk_agent()
|
||||
committee_chair = make_desk_agent()
|
||||
|
||||
app_id = "LOAN-2025-88421"
|
||||
llm = LiteLLM(model="anthropic/claude-sonnet-5")
|
||||
llm = LiteLLM(model="anthropic/claude-sonnet-4-20250514")
|
||||
|
||||
# --- Risk Desk: PD/LGD/EL analysis ---
|
||||
risk_desk.store(
|
||||
@@ -679,7 +679,7 @@ context.retrieve("...", user_id="analyst-jsmith")
|
||||
|
||||
## Related Guides
|
||||
|
||||
- [Agent Memory](/guides/agent-memory) — memory storage, retrieval, persistence, and the working memory window each agent uses internally
|
||||
- [Context Graphs](/guides/context-graphs) — build and traverse the shared `ContextGraph` directly; temporal interval reasoning; entity deduplication before node insertion
|
||||
- [Decision Intelligence](/guides/decision-intelligence) — record and trace decisions across agent handoffs with causal chain analysis
|
||||
- [LLM Integrations](/guides/llm-integrations) — configure the LLM provider passed to `query_with_reasoning()` in each agent
|
||||
- [Agent Memory](agent-memory) — memory storage, retrieval, persistence, and the working memory window each agent uses internally
|
||||
- [Context Graphs](context-graphs) — build and traverse the shared `ContextGraph` directly; temporal interval reasoning; entity deduplication before node insertion
|
||||
- [Decision Intelligence](decision-intelligence) — record and trace decisions across agent handoffs with causal chain analysis
|
||||
- [LLM Integrations](llm-integrations) — configure the LLM provider passed to `query_with_reasoning()` in each agent
|
||||
|
||||
@@ -297,7 +297,7 @@ export_rdf(ontology, "cyber_threat.jsonld", format="jsonld")
|
||||
export_rdf(ontology, "cyber_threat.nt", format="ntriples")
|
||||
```
|
||||
|
||||
The exported Turtle file is the input to Semantica's SHACL validation pipeline. See the [SHACL Validation](/guides/shacl-validation) guide for how to generate constraint shapes from this ontology and run them against live graph data.
|
||||
The exported Turtle file is the input to Semantica's SHACL validation pipeline. See the [SHACL Validation](shacl-validation) guide for how to generate constraint shapes from this ontology and run them against live graph data.
|
||||
|
||||
---
|
||||
|
||||
@@ -477,7 +477,7 @@ regs = [
|
||||
]
|
||||
|
||||
# Use an LLM to extract the conceptual model from regulatory prose
|
||||
llm_gen = LLMOntologyGenerator(provider="anthropic", model="claude-sonnet-5")
|
||||
llm_gen = LLMOntologyGenerator(provider="anthropic", model="claude-sonnet-4-20250514")
|
||||
ontology = llm_gen.generate_ontology_from_text(
|
||||
"\n\n".join(r.text[:8000] for r in regs) # token-safe excerpt per document
|
||||
)
|
||||
@@ -503,8 +503,8 @@ else:
|
||||
|
||||
## Related Guides
|
||||
|
||||
- [SHACL Validation](/guides/shacl-validation) — generate W3C SHACL constraint shapes from your ontology and validate live graph data against them
|
||||
- [SHACL Validation](shacl-validation) — generate W3C SHACL constraint shapes from your ontology and validate live graph data against them
|
||||
- [Reasoning & Rules](reasoning) — apply forward/backward-chaining rules over your ontology to derive new facts
|
||||
- [Export & Serialization](export) — export graphs to RDF, GraphML, CSV, and Neo4j Cypher
|
||||
- [Semantic Extraction](/guides/semantic-extraction) — extract entities and relationships that feed ontology generation
|
||||
- [Context Graphs](/guides/context-graphs) — the knowledge graph that ontology generation reads from
|
||||
- [Semantic Extraction](semantic-extraction) — extract entities and relationships that feed ontology generation
|
||||
- [Context Graphs](context-graphs) — the knowledge graph that ontology generation reads from
|
||||
|
||||
@@ -127,7 +127,7 @@ engine = ExecutionEngine(max_workers=4, retry_on_failure=True)
|
||||
result = engine.execute_pipeline(pipeline)
|
||||
|
||||
print(f"Success: {result.success}")
|
||||
print(f"Output: {result.output}") # the final step's return value, e.g. {"node_count": ..., "edge_count": ...}
|
||||
print(f"Output: {result.output}") # {"node_count": 312, "edge_count": 847}
|
||||
print(f"Duration: {result.metrics['execution_time']:.2f}s")
|
||||
print(f"Steps completed: {result.metrics['steps_executed']}")
|
||||
```
|
||||
@@ -197,9 +197,7 @@ engine = ExecutionEngine(
|
||||
max_workers = 4,
|
||||
retry_on_failure = True,
|
||||
)
|
||||
# ExecutionEngine builds its own FailureHandler; replace it with the configured one
|
||||
engine.failure_handler = handler
|
||||
# The engine now calls engine.failure_handler.get_retry_policy(step.step_type) on failure
|
||||
# The engine uses handler.get_retry_policy(step.step_type) when a step fails
|
||||
```
|
||||
|
||||
`handler.classify_error()` distinguishes `ValidationError` (low severity, usually don't retry), `ProcessingError` (high severity), and timeout/connection errors (medium severity, always retry). You can inspect the classification:
|
||||
@@ -719,6 +717,6 @@ print(f"Compliance delta update: {result.output}")
|
||||
## Related Guides
|
||||
|
||||
- [Ingest](ingest) — all source types for the ingest step: PDFs, APIs, databases, RSS feeds, STIX directories, and streams
|
||||
- [Semantic Extraction](/guides/semantic-extraction) — NER, relation extraction, triplet extraction, and event detection for the extract step
|
||||
- [Context Graphs](/guides/context-graphs) — building and querying the `ContextGraph` that the store step populates
|
||||
- [Semantic Extraction](semantic-extraction) — NER, relation extraction, triplet extraction, and event detection for the extract step
|
||||
- [Context Graphs](context-graphs) — building and querying the `ContextGraph` that the store step populates
|
||||
- [Provenance](provenance) — tracking the origin document, confidence score, and pipeline run ID for every extracted entity
|
||||
|
||||
@@ -662,9 +662,9 @@ print("Policy updated to v2.4.0")
|
||||
|
||||
## Related Guides
|
||||
|
||||
- [Decision Intelligence](/guides/decision-intelligence) — `record_decision()`, causal chains, and precedent search — the decisions that `check_compliance()` evaluates
|
||||
- [Decision Intelligence](decision-intelligence) — `record_decision()`, causal chains, and precedent search — the decisions that `check_compliance()` evaluates
|
||||
- [Reasoning & Rules](reasoning) — complement policy rules with formal inference for logical conflict detection
|
||||
- [SHACL Validation](/guides/shacl-validation) — enforce structural constraints on policy nodes themselves
|
||||
- [Change Management](/guides/change-management) — version-snapshot the policy graph alongside the knowledge graph
|
||||
- [SHACL Validation](shacl-validation) — enforce structural constraints on policy nodes themselves
|
||||
- [Change Management](change-management) — version-snapshot the policy graph alongside the knowledge graph
|
||||
- [Provenance](provenance) — W3C PROV-O lineage for every policy decision and exception
|
||||
- [MCP Server](/guides/mcp-server) — expose `record_decision` and `find_precedents` as MCP tools for AI agents
|
||||
- [MCP Server](mcp-server) — expose `record_decision` and `find_precedents` as MCP tools for AI agents
|
||||
|
||||
@@ -659,7 +659,7 @@ Note: the banking example above passes `agent_id="credit_data_service_v2"` to `t
|
||||
|
||||
## Related Guides
|
||||
|
||||
- [Semantic Extraction](/guides/semantic-extraction) — the NER and relation extraction pipeline that auto-generates provenance entries for every extracted entity
|
||||
- [Conflict Resolution](/guides/conflict-resolution) — provenance property sources feed directly into conflict detection; every resolved value is traceable to its source
|
||||
- [Semantic Extraction](semantic-extraction) — the NER and relation extraction pipeline that auto-generates provenance entries for every extracted entity
|
||||
- [Conflict Resolution](conflict-resolution) — provenance property sources feed directly into conflict detection; every resolved value is traceable to its source
|
||||
- [Deduplication](deduplication) — merge operations are recorded in merge history; pair with provenance for a complete lineage from source to canonical entity
|
||||
- [Provenance Reference](../reference/provenance) — full storage backend API, `InMemoryStorage`, `SQLiteStorage`, and `ProvenanceEntry` schema
|
||||
|
||||
@@ -838,9 +838,9 @@ if proof:
|
||||
|
||||
## Related Guides
|
||||
|
||||
- [Semantic Extraction](/guides/semantic-extraction) — extract the entities and relationships that populate the graph facts you reason over
|
||||
- [GraphRAG](/guides/graphrag) — retrieve graph-grounded context for LLM responses
|
||||
- [Semantic Extraction](semantic-extraction) — extract the entities and relationships that populate the graph facts you reason over
|
||||
- [GraphRAG](graphrag) — retrieve graph-grounded context for LLM responses
|
||||
- [Ontology Management](ontology) — generate OWL ontologies to give your rules formal semantics
|
||||
- [Decision Intelligence](/guides/decision-intelligence) — record and trace inferred decisions through the full causal chain
|
||||
- [Context Graphs](/guides/context-graphs) — the knowledge graph that reasoning operates over
|
||||
- [MCP Server](/guides/mcp-server) — expose `run_reasoning` as a tool for Claude and other agents
|
||||
- [Decision Intelligence](decision-intelligence) — record and trace inferred decisions through the full causal chain
|
||||
- [Context Graphs](context-graphs) — the knowledge graph that reasoning operates over
|
||||
- [MCP Server](mcp-server) — expose `run_reasoning` as a tool for Claude and other agents
|
||||
|
||||
@@ -71,7 +71,7 @@ This pipeline transforms documents like "APT29 deployed HAMMERTOSS malware targe
|
||||
`semantica.semantic_extract` turns unstructured text into structured graph-ready output: it identifies named entities, extracts relationships between them, detects time-anchored events, resolves coreferences, and serialises everything as RDF triplets. Use it to populate a `ContextGraph` from raw documents — intelligence reports, clinical notes, regulatory filings, or any free-text corpus.
|
||||
|
||||
<Info>
|
||||
Extracted entities and relationships feed into `ContextGraph` via `AgentContext.store()`. For how they are attributed back to source documents, see the [Provenance Guide](provenance). For how the populated graph is queried and traversed, see [Context Graphs](/guides/context-graphs).
|
||||
Extracted entities and relationships feed into `ContextGraph` via `AgentContext.store()`. For how they are attributed back to source documents, see the [Provenance Guide](provenance). For how the populated graph is queried and traversed, see [Context Graphs](context-graphs).
|
||||
</Info>
|
||||
|
||||
## Step 1 — Named Entity Recognition: who and what is in the text
|
||||
@@ -100,15 +100,14 @@ ner = NamedEntityRecognizer(
|
||||
methods=["llm", "ml", "pattern"],
|
||||
confidence_threshold=0.75,
|
||||
provider="anthropic",
|
||||
llm_model="claude-sonnet-5",
|
||||
llm_model="claude-sonnet-4-6",
|
||||
)
|
||||
entities = ner.extract_entities(report)
|
||||
|
||||
for e in entities:
|
||||
print("[{:>5.2f}] {:15s} {}".format(e.confidence, e.label, e.text))
|
||||
|
||||
# Illustrative output — exact labels and scores depend on the method and model.
|
||||
# Abbreviated:
|
||||
# Expected output (abbreviated):
|
||||
# [ 0.94] THREAT_ACTOR GAMMA-7
|
||||
# [ 0.91] THREAT_ACTOR DELTA-3
|
||||
# [ 0.97] MALWARE HAMMERTOSS
|
||||
@@ -263,18 +262,16 @@ from semantica.semantic_extract import TripletExtractor
|
||||
tri = TripletExtractor(
|
||||
method="llm",
|
||||
provider="anthropic",
|
||||
llm_model="claude-sonnet-5",
|
||||
llm_model="claude-sonnet-4-6",
|
||||
include_temporal=True, # attach time context to triplets when available
|
||||
include_provenance=True, # embed source document reference in each triplet
|
||||
validate=False, # return raw triplets; validate explicitly below
|
||||
)
|
||||
|
||||
# Feed in the entities and relations you already extracted — the extractor
|
||||
# uses them to constrain what it produces
|
||||
# uses them to constrain and validate what it produces
|
||||
triplets = tri.extract_triplets(report, entities, relations)
|
||||
|
||||
# Filter malformed triplets before serialisation
|
||||
# (extract_triplets validates automatically unless validate=False, as above)
|
||||
valid = tri.validate_triplets(triplets)
|
||||
print("Valid: {}/{}".format(len(valid), len(triplets)))
|
||||
|
||||
@@ -323,7 +320,7 @@ def ingest_intel_report(
|
||||
methods=[method, "pattern"],
|
||||
confidence_threshold=0.70,
|
||||
provider="anthropic",
|
||||
llm_model="claude-sonnet-5",
|
||||
llm_model="claude-sonnet-4-6",
|
||||
)
|
||||
entities = ner.extract_entities(text)
|
||||
classified = ner.classify_entities(entities)
|
||||
@@ -338,7 +335,7 @@ def ingest_intel_report(
|
||||
relation_types=["deployed", "targets", "exploits", "operates_from", "provided_to"],
|
||||
confidence_threshold=0.65,
|
||||
provider="anthropic",
|
||||
llm_model="claude-sonnet-5",
|
||||
llm_model="claude-sonnet-4-6",
|
||||
)
|
||||
relations = rel.extract_relations(text, entities)
|
||||
|
||||
@@ -350,10 +347,9 @@ def ingest_intel_report(
|
||||
tri = TripletExtractor(
|
||||
method=method,
|
||||
provider="anthropic",
|
||||
llm_model="claude-sonnet-5",
|
||||
llm_model="claude-sonnet-4-6",
|
||||
include_temporal=True,
|
||||
include_provenance=True,
|
||||
validate=False, # keep raw triplets so the summary can report rejections
|
||||
)
|
||||
triplets = tri.extract_triplets(text, entities, relations)
|
||||
valid = tri.validate_triplets(triplets)
|
||||
@@ -381,7 +377,6 @@ def ingest_intel_report(
|
||||
"coref_chains": len(chains),
|
||||
"relations": len(relations),
|
||||
"events": len(events),
|
||||
"triplets_total": len(triplets),
|
||||
"triplets_valid": len(valid),
|
||||
"graph_nodes": graph_stats.get("graph_nodes", 0),
|
||||
"graph_edges": graph_stats.get("graph_edges", 0),
|
||||
@@ -407,7 +402,7 @@ for text, doc_id in reports:
|
||||
summary["relations"],
|
||||
summary["events"],
|
||||
summary["triplets_valid"],
|
||||
summary["triplets_total"],
|
||||
len(summary["rdf_turtle"]),
|
||||
))
|
||||
```
|
||||
|
||||
@@ -426,7 +421,7 @@ ner = NamedEntityRecognizer(
|
||||
methods=["llm", "pattern"],
|
||||
confidence_threshold=0.75,
|
||||
provider="anthropic",
|
||||
llm_model="claude-sonnet-5",
|
||||
llm_model="claude-sonnet-4-6",
|
||||
)
|
||||
entities = ner.extract_entities(fintel_text)
|
||||
grouped = ner.classify_entities(entities)
|
||||
@@ -443,14 +438,14 @@ rel = RelationExtractor(
|
||||
relation_types=["operates_from", "deployed", "targets", "exploits"],
|
||||
confidence_threshold=0.70,
|
||||
provider="anthropic",
|
||||
llm_model="claude-sonnet-5",
|
||||
llm_model="claude-sonnet-4-6",
|
||||
)
|
||||
relations = rel.extract_relations(fintel_text, entities)
|
||||
|
||||
tri = TripletExtractor(
|
||||
method="llm",
|
||||
provider="anthropic",
|
||||
llm_model="claude-sonnet-5",
|
||||
llm_model="claude-sonnet-4-6",
|
||||
include_temporal=True,
|
||||
include_provenance=True,
|
||||
)
|
||||
@@ -549,14 +544,14 @@ rel = RelationExtractor(
|
||||
relation_types=["treats", "causes_adverse_event", "has_efficacy", "evaluated_in"],
|
||||
confidence_threshold=0.65,
|
||||
provider="anthropic",
|
||||
llm_model="claude-sonnet-5",
|
||||
llm_model="claude-sonnet-4-6",
|
||||
)
|
||||
relations = rel.extract_relations(paper, entities)
|
||||
|
||||
tri = TripletExtractor(
|
||||
method="llm",
|
||||
provider="anthropic",
|
||||
llm_model="claude-sonnet-5",
|
||||
llm_model="claude-sonnet-4-6",
|
||||
triplet_types=["treats", "has_efficacy", "causes_adverse_event"],
|
||||
include_temporal=True,
|
||||
include_provenance=True,
|
||||
@@ -600,7 +595,7 @@ ner = NamedEntityRecognizer(
|
||||
methods=["llm", "ml", "pattern"],
|
||||
confidence_threshold=0.70,
|
||||
provider="anthropic",
|
||||
llm_model="claude-sonnet-5",
|
||||
llm_model="claude-sonnet-4-6",
|
||||
)
|
||||
entities = ner.extract_entities(credit_memo)
|
||||
grouped = ner.classify_entities(entities)
|
||||
@@ -617,14 +612,14 @@ rel = RelationExtractor(
|
||||
relation_types=["guaranteed_by", "secured_by", "classified_as", "exposed_to"],
|
||||
confidence_threshold=0.65,
|
||||
provider="anthropic",
|
||||
llm_model="claude-sonnet-5",
|
||||
llm_model="claude-sonnet-4-6",
|
||||
)
|
||||
relations = rel.extract_relations(credit_memo, entities)
|
||||
|
||||
tri = TripletExtractor(
|
||||
method="llm",
|
||||
provider="anthropic",
|
||||
llm_model="claude-sonnet-5",
|
||||
llm_model="claude-sonnet-4-6",
|
||||
include_temporal=True,
|
||||
include_provenance=True,
|
||||
)
|
||||
@@ -669,8 +664,8 @@ The fallback behaviour is automatic: if the primary method returns an empty list
|
||||
## Related Guides
|
||||
|
||||
- [Provenance Guide](provenance) — track every extracted entity and chunk back to its source document
|
||||
- [Agent Memory Guide](/guides/agent-memory) — store extracted knowledge as searchable agent memories with graph enrichment
|
||||
- [Context Graphs Guide](/guides/context-graphs) — how extracted entities populate `ContextGraph` nodes and edges
|
||||
- [GraphRAG Guide](/guides/graphrag) — retrieve facts from the populated graph to ground LLM responses
|
||||
- [Agent Memory Guide](agent-memory) — store extracted knowledge as searchable agent memories with graph enrichment
|
||||
- [Context Graphs Guide](context-graphs) — how extracted entities populate `ContextGraph` nodes and edges
|
||||
- [GraphRAG Guide](graphrag) — retrieve facts from the populated graph to ground LLM responses
|
||||
- [Reasoning Guide](reasoning) — derive new facts, run SPARQL queries, and apply inference rules over the extracted graph
|
||||
- [Semantic Extract Reference](../reference/semantic_extract) — full API for all extractor classes, providers, and validators
|
||||
|
||||
@@ -740,5 +740,5 @@ def validate_before_publish(data_graph_str: str, ontology: dict) -> None:
|
||||
- [Ontology Management](ontology) — generate the OWL ontology that SHACL shapes are derived from
|
||||
- [Reasoning & Rules](reasoning) — complement SHACL structural constraints with logical inference rules
|
||||
- [Export & Serialization](export) — serialize graph data to Turtle/RDF/XML for `run_shacl_validation` input
|
||||
- [Conflict Resolution](/guides/conflict-resolution) — detect and resolve data conflicts before SHACL validation
|
||||
- [Change Management](/guides/change-management) — version-gate SHACL shapes alongside ontology versions
|
||||
- [Conflict Resolution](conflict-resolution) — detect and resolve data conflicts before SHACL validation
|
||||
- [Change Management](change-management) — version-gate SHACL shapes alongside ontology versions
|
||||
|
||||
@@ -614,8 +614,8 @@ fig.write_html("out.html") # manual export
|
||||
|
||||
## Related Guides
|
||||
|
||||
- [Context Graphs](/guides/context-graphs) — `graph.to_dict()` is the primary input for `KGVisualizer`
|
||||
- [Context Graphs](context-graphs) — `graph.to_dict()` is the primary input for `KGVisualizer`
|
||||
- [Ontology Management](ontology) — `OntologyVisualizer` renders ontologies produced by `OntologyGenerator`
|
||||
- [Change Management](/guides/change-management) — `TemporalVersionManager` snapshots feed `visualize_metrics_evolution()` and `visualize_snapshot_comparison()`
|
||||
- [Graph Analytics](/guides/graph-analytics) — centrality scores, community dicts, and connectivity results that feed the `AnalyticsVisualizer`
|
||||
- [Change Management](change-management) — `TemporalVersionManager` snapshots feed `visualize_metrics_evolution()` and `visualize_snapshot_comparison()`
|
||||
- [Graph Analytics](graph-analytics) — centrality scores, community dicts, and connectivity results that feed the `AnalyticsVisualizer`
|
||||
- [Export & Serialization](export) — export the same graph to GraphML, GEXF, or DOT for Gephi and Graphviz
|
||||
|
||||
+304
-25
@@ -1,31 +1,109 @@
|
||||
---
|
||||
title: "Welcome to Semantica"
|
||||
description: "The Context and Semantic Layer for AI in High-Stakes Domains: Context Graphs · Decision Intelligence · Full Provenance"
|
||||
title: "Semantica"
|
||||
description: "The Accountability and Context Layer for AI: Context Graphs · Decision Intelligence · Full Provenance"
|
||||
---
|
||||
|
||||
```bash
|
||||
pip install semantica
|
||||
```
|
||||
|
||||
Most AI agents run on embeddings, not meaning. A similarity score has no structure, no relationships, and no way to explain why a result came back.
|
||||
Your AI agent just made a decision. Now someone needs to explain it.
|
||||
|
||||
Semantica is the semantic and context layer underneath your LLM, vector store, and agent framework: deterministic infrastructure, not a model. Graph construction, reasoning, and provenance all run without an LLM in the loop. It turns fragmented enterprise data into a structured, queryable context graph and knowledge graph, governed by ontologies, taxonomies, and controlled vocabularies (OWL, SHACL, SKOS), so your data's meaning is explicit rather than approximated by an embedding.
|
||||
*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?*
|
||||
|
||||
Provenance and audit trails aren't a bolt-on. They fall out naturally once your data has that structure, so the same graph that powers retrieval and reasoning also gives you a straight answer when a regulator asks why.
|
||||
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.
|
||||
|
||||
## What you get
|
||||
**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.
|
||||
|
||||
- **[Context graphs](/guides/context-graphs)**: a persistent, queryable graph of everything your agent knows, decides, and reasons about
|
||||
- **Decision intelligence**: `record_decision()` captures the full lifecycle and causal chain of every decision
|
||||
- **[Full provenance](/guides/provenance)**: every fact links back to its source, W3C PROV-O compliant and audit-ready for HIPAA, SOX, and GDPR
|
||||
- **[Explainable reasoning](/guides/reasoning)**: forward chaining, Datalog, and SPARQL, each with a derivation path you can inspect
|
||||
- **Temporal intelligence**: Allen interval algebra and point-in-time snapshots, so the graph knows not just *what* but *when*
|
||||
|
||||
## The Problem Every Production AI Team Hits
|
||||
|
||||
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 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 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 lineage from inference back to the original document
|
||||
- Impossible 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 basis for improving or correcting future behavior
|
||||
|
||||
**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:
|
||||
|
||||
**Context Graphs** — a structured, queryable graph of everything your agent knows, decides, and reasons about
|
||||
- Persistent across agent runs: 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
|
||||
- `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
|
||||
- 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
|
||||
- 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
|
||||
- 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
|
||||
- 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, and ingests directly from enterprise data platforms like Databricks, SAP, Salesforce, and Snowflake. 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>
|
||||
|
||||
## Try it
|
||||
<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' }} />
|
||||
|
||||
|
||||
## See It In Action
|
||||
|
||||
One pip install. A few lines to connect your agent. Everything else becomes traceable.
|
||||
|
||||
```bash
|
||||
pip install semantica
|
||||
```
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
@@ -107,28 +185,229 @@ decision_id = context.record_decision(
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
## Start here
|
||||
- [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
|
||||
|
||||
Semantica was designed for 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.
|
||||
</Warning>
|
||||
|
||||
**Healthcare & Life Sciences**
|
||||
- Clinical decision support with full audit trails
|
||||
- Drug interaction and contraindication graphs
|
||||
- Patient safety event tracking and root-cause analysis
|
||||
- HIPAA-compliant provenance chains out of the box
|
||||
|
||||
**Finance & Risk**
|
||||
- Fraud detection knowledge graphs
|
||||
- Risk assessment trails built to survive an audit
|
||||
- SOX, GDPR, and MiFID II compliance infrastructure
|
||||
- Model decision lineage for regulatory reporting
|
||||
|
||||
**Legal & Compliance**
|
||||
- Evidence-backed research with every cited fact provenance-linked
|
||||
- Contract analysis with traceable clause extraction
|
||||
- Regulatory change tracking across jurisdictions
|
||||
- Full reasoning paths ready for court-admissible documentation
|
||||
|
||||
**Cybersecurity**
|
||||
- Threat attribution graphs linking actors, TTPs, and indicators
|
||||
- Incident response timelines with full event provenance
|
||||
- Security audit trails across the complete kill chain
|
||||
- MITRE ATT&CK-aligned knowledge graph integration
|
||||
|
||||
**Government & Defense**
|
||||
- Policy decision trails from brief to outcome
|
||||
- Classified information handling with provenance chains
|
||||
- Chain-of-custody scrutiny for intelligence reporting
|
||||
- Air-gapped deployment with local LLM support
|
||||
|
||||
**Critical Infrastructure**
|
||||
- Power grid state tracking with temporal intelligence
|
||||
- Transportation safety event graphs
|
||||
- Emergency response coordination with decision audit trails
|
||||
- Consequence modeling for high-stakes operational decisions
|
||||
|
||||
|
||||
## Start Here
|
||||
|
||||
<Steps>
|
||||
<Step title="Install">
|
||||
<Step title="Install Semantica">
|
||||
```bash
|
||||
pip install semantica
|
||||
```
|
||||
Optional extras: `[all]`, `[neo4j]`, `[pinecone]`. See [Installation](/installation).
|
||||
See [Installation](installation) for optional extras (`[all]`, `[neo4j]`, `[pinecone]`) and environment setup.
|
||||
</Step>
|
||||
<Step title="Build a pipeline">
|
||||
Follow the [Quickstart](/quickstart) to ingest documents, extract entities, build a graph, and record a decision in 5 minutes.
|
||||
<Step title="Run the Quickstart">
|
||||
Build a complete knowledge graph pipeline in [5 minutes](quickstart):
|
||||
- Ingest documents from any source
|
||||
- Extract entities and relationships
|
||||
- Build and query the graph
|
||||
- Record and trace a decision
|
||||
</Step>
|
||||
<Step title="Learn the model">
|
||||
[Core Concepts](/concepts) covers knowledge graphs vs. vector stores, GraphRAG, and how provenance and decisions fit together.
|
||||
<Step title="Learn the mental model">
|
||||
[Core Concepts](concepts) covers:
|
||||
- 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
|
||||
</Step>
|
||||
<Step title="Go deep">
|
||||
Every module has a [reference page](/reference/context) with full API docs and runnable examples.
|
||||
<Step title="Go deep on any module">
|
||||
Every module has a dedicated [reference page](reference/context) with:
|
||||
- Full class and method documentation
|
||||
- Parameter tables with types and defaults
|
||||
- Runnable code examples for each feature
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
More: the [Cookbook](/cookbook) for real-world notebooks, [Discord](https://discord.gg/sV34vps5hH) for help.
|
||||
- [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
|
||||
|
||||
<AccordionGroup>
|
||||
|
||||
<Accordion title="Context & Decision Intelligence" icon="brain">
|
||||
|
||||
### Context Graphs
|
||||
|
||||
- Structured, persistent graph of entities, relationships, and decisions
|
||||
- Temporal model with `valid_from` / `valid_until` on every node and edge
|
||||
- Point-in-time queries across historical graph states
|
||||
- Distance Intelligence: semantic neighborhoods and N×N distance matrices
|
||||
|
||||
### Decision Tracking
|
||||
|
||||
- `record_decision()` with full lifecycle management and causal chains
|
||||
- Hybrid similarity search over past decisions for consistency enforcement
|
||||
- `analyze_decision_impact()` and `analyze_decision_influence()` for consequence modeling
|
||||
- Ego-mode exploration for targeted neighborhood investigation
|
||||
|
||||
<Accordion title="Full module list">
|
||||
`semantica.ingest`, `semantica.parse`, `semantica.split`, `semantica.normalize`, `semantica.semantic_extract`, `semantica.kg`, `semantica.ontology`, `semantica.reasoning`, `semantica.embeddings`, `semantica.vector_store`, `semantica.graph_store`, `semantica.triplet_store`, `semantica.context`, `semantica.provenance`, `semantica.change_management`, `semantica.deduplication`, `semantica.conflicts`, `semantica.export`, `semantica.visualization`, `semantica.pipeline`, `semantica.seed`, `semantica.llms`, `semantica.mcp_server`, `semantica.explorer`, `semantica.evals`, `semantica.utils`, `semantica.core`. See the [API Reference](/reference/context) for full docs on each.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Knowledge Engineering" icon="diagram-project">
|
||||
|
||||
### Entity & Relation Extraction
|
||||
|
||||
- Named entity recognition: pattern, ML, or LLM methods
|
||||
- Typed triplet extraction via LLM or rule-based pipelines
|
||||
- Event extraction with temporal and causal linking
|
||||
|
||||
### Ontology & Schema
|
||||
|
||||
- Ontology Hub: visual editor, SHACL Studio, alignments, health dashboard
|
||||
- Deduplication v2: `blocking_v2`, `hybrid_v2`, `semantic_v2`: up to 7x faster
|
||||
- Datalog reasoning: recursive Horn clause rules with fixpoint semantics
|
||||
- SPARQL reasoning: query-based inference over RDF graphs
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Provenance & Auditability" icon="shield-check">
|
||||
|
||||
### Lineage Tracking
|
||||
|
||||
- W3C PROV-O lineage across all modules: every fact has a source
|
||||
- `recorded_at` stamping with full OWL-Time export
|
||||
- Change management with SHA-256 checksums and version control
|
||||
- Full audit trails from ingestion event to final inference
|
||||
|
||||
### Compliance Infrastructure
|
||||
|
||||
- HIPAA: patient data handling with audit-ready provenance chains
|
||||
- SOX / MiFID II: financial decision records with full traceability
|
||||
- GDPR: data lineage for subject access and right-to-erasure workflows
|
||||
- FDA 21 CFR Part 11: electronic records and signature compliance
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Data Ingestion & Export" icon="database">
|
||||
|
||||
### Ingestion Formats
|
||||
|
||||
- Documents: PDF, DOCX, HTML, PPTX, Docling layout analysis
|
||||
- Structured data: JSON, CSV, Excel, Parquet, XML
|
||||
- Sources: web crawl, SQL, Snowflake, feeds, email, code repositories, MCP
|
||||
|
||||
### Vector Stores
|
||||
|
||||
- FAISS, Pinecone, Weaviate, Qdrant, Milvus, PgVector, in-memory
|
||||
|
||||
### Graph Stores
|
||||
|
||||
- Neo4j, FalkorDB, Apache AGE, Amazon Neptune
|
||||
|
||||
### Export Formats
|
||||
|
||||
- RDF: Turtle, JSON-LD, N-Triples, RDF/XML
|
||||
- Tabular: Parquet, CSV, Arrow
|
||||
- Graph: GraphML, GEXF, DOT, ArangoDB AQL
|
||||
- Ontology: OWL, SKOS, SHACL
|
||||
|
||||
</Accordion>
|
||||
|
||||
</AccordionGroup>
|
||||
|
||||
|
||||
## Module Reference
|
||||
|
||||
| Module | What it provides |
|
||||
| :-------- | :----------------- |
|
||||
| `semantica.context` | Context graphs, agent memory, decision tracking, causal analysis, precedent search |
|
||||
| `semantica.kg` | KG construction, graph algorithms, temporal model, Allen interval algebra |
|
||||
| `semantica.semantic_extract` | NER, relation extraction, event extraction, triplet generation |
|
||||
| `semantica.reasoning` | Forward chaining, Rete, deductive, abductive, SPARQL, Datalog |
|
||||
| `semantica.ontology` | SHACL, SKOS, alignments, diff/migration, auto-generation, OWL/RDF |
|
||||
| `semantica.explorer` | FastAPI Knowledge Explorer, Ontology Hub, Distance Intelligence, SHACL Studio |
|
||||
| `semantica.mcp_server` | MCP stdio server: 15 tools for Claude Desktop, VS Code, Cursor, Windsurf, Cline |
|
||||
| `semantica.vector_store` | FAISS, Pinecone, Weaviate, Qdrant, Milvus, PgVector |
|
||||
| `semantica.graph_store` | Neo4j, FalkorDB, Apache AGE, Amazon Neptune |
|
||||
| `semantica.triplet_store` | In-memory and persistent RDF triple store with SPARQL |
|
||||
| `semantica.ingest` | Files, web, feeds, databases, Snowflake, Parquet, XML, MCP |
|
||||
| `semantica.parse` | Document parsing: PDF, DOCX, HTML, PPTX, Docling layout analysis |
|
||||
| `semantica.split` | Text chunking: sentence, paragraph, token, semantic boundary strategies |
|
||||
| `semantica.normalize` | Text normalization, entity canonicalization, whitespace and encoding cleanup |
|
||||
| `semantica.embeddings` | Sentence-Transformers, FastEmbed, OpenAI, BGE, Ollama local embeddings |
|
||||
| `semantica.pipeline` | Pipeline DSL, parallel workers, retry policies, failure handling |
|
||||
| `semantica.export` | RDF, Parquet, ArangoDB AQL, CSV, OWL, Arrow, GraphML, GEXF, DOT |
|
||||
| `semantica.visualization` | Programmatic graph rendering: force, hierarchical, circular, spring layouts |
|
||||
| `semantica.deduplication` | Entity deduplication v1/v2, similarity scoring, blocking, merging |
|
||||
| `semantica.conflicts` | Conflict detection and resolution across overlapping knowledge sources |
|
||||
| `semantica.provenance` | W3C PROV-O lineage tracking, source attribution, audit trails |
|
||||
| `semantica.change_management` | Version control with SHA-256 checksums, diff, rollback |
|
||||
| `semantica.llms` | Groq, OpenAI, Anthropic, Gemini, Ollama, DeepSeek, Novita AI, LiteLLM, HuggingFace |
|
||||
| `semantica.seed` | Foundation graph seeding from CSV, JSON, SQL, API, and RDF sources |
|
||||
| `semantica.evals` | Evaluation harness: KG quality, extraction F1, pipeline benchmarking, regression tracking |
|
||||
| `semantica.core` | Orchestration, ConfigManager, LifecycleManager, PluginRegistry, MethodRegistry |
|
||||
| `semantica.utils` | Logging, validation, progress tracking, hash utilities, nested dict helpers |
|
||||
|
||||
|
||||
## Why Semantica?
|
||||
|
||||
**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.
|
||||
- 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))
|
||||
|
||||
**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
|
||||
|
||||
@@ -183,6 +183,6 @@ Install the [Microsoft Visual C++ Redistributable](https://aka.ms/vs/17/release/
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Getting Started](/getting-started) — Understand what Semantica does before you build.
|
||||
- [Build the Pipeline](/quickstart) — Follow the end-to-end workflow with code.
|
||||
- [Browse Examples](/cookbook) — See notebook examples organized by use case.
|
||||
- [Getting Started](getting-started) — Understand what Semantica does before you build.
|
||||
- [Build the Pipeline](quickstart) — Follow the end-to-end workflow with code.
|
||||
- [Browse Examples](cookbook) — See notebook examples organized by use case.
|
||||
|
||||
@@ -193,7 +193,7 @@ if not connector.test_connection():
|
||||
## See Also
|
||||
|
||||
- [Ingest Module](../reference/ingest) — Full DatabricksIngestor and all other ingestors.
|
||||
- [Snowflake Integration](/integrations/snowflake) — Companion connector for a Snowflake + Databricks hybrid estate.
|
||||
- [Snowflake Integration](snowflake) — Companion connector for a Snowflake + Databricks hybrid estate.
|
||||
- [Pipeline](../reference/pipeline) — Use Databricks ingestion as a pipeline step.
|
||||
- [Installation](../installation) — All optional dependency extras.
|
||||
- [Knowledge Graph](../reference/kg) — Build a KG from ingested Databricks data.
|
||||
|
||||
@@ -12,13 +12,13 @@ icon: "link"
|
||||
pip install "semantica[langchain]"
|
||||
```
|
||||
|
||||
Requires `langchain-core >= 0.3`. If langchain-core is not installed, the integration still imports. Every class carries the full Semantica API and degrades gracefully (`build()` returns `None`; branch on `LANGCHAIN_AVAILABLE`).
|
||||
Requires `langchain-core >= 0.3`. If langchain-core is not installed, the integration still imports — every class carries the full Semantica API and degrades gracefully (`build()` returns `None`; branch on `LANGCHAIN_AVAILABLE`).
|
||||
|
||||
## Components at a Glance
|
||||
|
||||
- **SemanticaRetriever** (`BaseRetriever`): hybrid-search seeds retrieval, then graph edges are walked `hops` steps (default 2) for GraphRAG-style results.
|
||||
- **SemanticaVectorStore** (`VectorStore`): `add_texts` / `similarity_search` / `similarity_search_with_score` / `from_texts` over `HybridSearch`.
|
||||
- **SemanticaKGTool** / **SemanticaDecisionTool** (`BaseTool` subclasses): `semantica_query_graph` and `semantica_query_decisions` for LangGraph / tool-calling agents.
|
||||
- **SemanticaRetriever** — `BaseRetriever`: hybrid-search seeds retrieval, then graph edges are walked `hops` steps (default 2) for GraphRAG-style results.
|
||||
- **SemanticaVectorStore** — `VectorStore`: `add_texts` / `similarity_search` / `similarity_search_with_score` / `from_texts` over `HybridSearch`.
|
||||
- **SemanticaKGTool** / **SemanticaDecisionTool** — `BaseTool` subclasses: `semantica_query_graph` and `semantica_query_decisions` for LangGraph / tool-calling agents.
|
||||
|
||||
## Component Details
|
||||
|
||||
|
||||
@@ -370,7 +370,7 @@ Common causes of authentication failures:
|
||||
## See Also
|
||||
|
||||
- [Ingest Module](../reference/ingest) — Full `SalesforceIngestor` API and all other ingestors.
|
||||
- [Snowflake Integration](/integrations/snowflake) — Relational warehouse connector with a similar design.
|
||||
- [Databricks Integration](/integrations/databricks) — Lakehouse connector.
|
||||
- [Snowflake Integration](snowflake) — Relational warehouse connector with a similar design.
|
||||
- [Databricks Integration](databricks) — Lakehouse connector.
|
||||
- [Installation](../installation) — All optional dependency extras.
|
||||
- [Knowledge Graph](../reference/kg) — Build a KG from ingested Salesforce data.
|
||||
|
||||
@@ -172,7 +172,7 @@ if not connector.test_connection():
|
||||
## See Also
|
||||
|
||||
- [Ingest Module](../reference/ingest) — Full SnowflakeIngestor and all other ingestors.
|
||||
- [Databricks Integration](/integrations/databricks) — Companion connector for a Snowflake + Databricks hybrid estate.
|
||||
- [Databricks Integration](databricks) — Companion connector for a Snowflake + Databricks hybrid estate.
|
||||
- [Pipeline](../reference/pipeline) — Use Snowflake ingestion as a pipeline step.
|
||||
- [Installation](../installation) — All optional dependency extras.
|
||||
- [Knowledge Graph](../reference/kg) — Build a KG from ingested Snowflake data.
|
||||
|
||||
+13
-13
@@ -9,9 +9,9 @@ Whether you're running your first pipeline or deploying Semantica in production,
|
||||
|
||||
## Learning Paths
|
||||
|
||||
- **Beginner (1–2 hrs)** — New to Semantica and knowledge graphs. [Start with Installation →](/installation)
|
||||
- **Intermediate (4–6 hrs)** — Comfortable with basics, building real applications. [Start with Modules →](/modules)
|
||||
- **Advanced (8+ hrs)** — Enterprise deployments, customization, and extension. [Start with Architecture →](/architecture)
|
||||
- **Beginner (1–2 hrs)** — New to Semantica and knowledge graphs. [Start with Installation →](installation)
|
||||
- **Intermediate (4–6 hrs)** — Comfortable with basics, building real applications. [Start with Modules →](modules)
|
||||
- **Advanced (8+ hrs)** — Enterprise deployments, customization, and extension. [Start with Architecture →](architecture)
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Beginner (1–2 hrs)">
|
||||
@@ -19,16 +19,16 @@ Whether you're running your first pipeline or deploying Semantica in production,
|
||||
|
||||
<Steps>
|
||||
<Step title="Set up your environment">
|
||||
[Installation Guide](/installation): virtual environments, optional extras, platform-specific fixes.
|
||||
[Installation Guide](installation): virtual environments, optional extras, platform-specific fixes.
|
||||
</Step>
|
||||
<Step title="Understand the core ideas">
|
||||
[Core Concepts](/concepts): what knowledge graphs are, how embeddings work, what extraction does.
|
||||
[Core Concepts](concepts): what knowledge graphs are, how embeddings work, what extraction does.
|
||||
</Step>
|
||||
<Step title="Run your first example">
|
||||
[Getting Started](/getting-started): 5-minute code walkthrough with pattern-based extraction (no API key needed).
|
||||
[Getting Started](getting-started): 5-minute code walkthrough with pattern-based extraction (no API key needed).
|
||||
</Step>
|
||||
<Step title="Build your first knowledge graph">
|
||||
[Quickstart Tutorial](/quickstart): full 6-step pipeline from ingestion to visualization.
|
||||
[Quickstart Tutorial](quickstart): full 6-step pipeline from ingestion to visualization.
|
||||
</Step>
|
||||
<Step title="Explore interactively">
|
||||
[Welcome to Semantica notebook](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/01_Welcome_to_Semantica.ipynb): Jupyter walkthrough of every module.
|
||||
@@ -40,13 +40,13 @@ Whether you're running your first pipeline or deploying Semantica in production,
|
||||
|
||||
<Steps>
|
||||
<Step title="Learn every module">
|
||||
[Modules Guide](/modules): all 27 modules with code examples and common pipeline chains.
|
||||
[Modules Guide](modules): all 27 modules with code examples and common pipeline chains.
|
||||
</Step>
|
||||
<Step title="Build production knowledge graphs">
|
||||
[Building Knowledge Graphs notebook](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/07_Building_Knowledge_Graphs.ipynb): multi-source, deduplication, conflict resolution.
|
||||
</Step>
|
||||
<Step title="Add semantic search">
|
||||
[Embedding Generation notebook](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/12_Embedding_Generation.ipynb): generating embeddings, provider and model switching, dimensions. Then [Vector Store notebook](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/13_Vector_Store.ipynb): storing and searching vectors for retrieval.
|
||||
[Embeddings notebook](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/09_Embeddings.ipynb): providers, pooling strategies, vector stores.
|
||||
</Step>
|
||||
<Step title="Multi-source integration">
|
||||
[Multi-Source Data Integration notebook](https://github.com/semantica-agi/semantica/blob/main/cookbook/advanced/06_Multi_Source_Data_Integration.ipynb) for multi-source patterns.
|
||||
@@ -58,7 +58,7 @@ Whether you're running your first pipeline or deploying Semantica in production,
|
||||
|
||||
<Steps>
|
||||
<Step title="Understand the architecture">
|
||||
[Architecture Guide](/architecture): four-layer design, extension points, and design decisions.
|
||||
[Architecture Guide](architecture): four-layer design, extension points, and design decisions.
|
||||
</Step>
|
||||
<Step title="Temporal intelligence">
|
||||
[Temporal Graphs notebook](https://github.com/semantica-agi/semantica/blob/main/cookbook/advanced/10_Temporal_Knowledge_Graphs.ipynb): `valid_from`/`valid_until`, Allen interval algebra, point-in-time queries.
|
||||
@@ -236,6 +236,6 @@ The `blocking_v2`, `hybrid_v2`, and `semantic_v2` strategies reduce O(n²) compa
|
||||
- **Graph exports**: encrypt sensitive exports at rest; use the v0.5.0 SSRF-safe `base_url` validation when configuring custom LLM gateways
|
||||
- **XML ingestion**: always use `XMLIngestor` (v0.5.0), which uses the XXE-safe lxml backend; never parse untrusted XML with the standard library parser
|
||||
|
||||
- [Cookbook](/cookbook) — Interactive Jupyter notebooks from beginner to advanced.
|
||||
- [FAQ](/faq) — Common questions answered.
|
||||
- [API Reference](/reference/core) — Complete technical documentation.
|
||||
- [Cookbook](cookbook) — Interactive Jupyter notebooks from beginner to advanced.
|
||||
- [FAQ](faq) — Common questions answered.
|
||||
- [API Reference](reference/core) — Complete technical documentation.
|
||||
|
||||
+148
-189
@@ -9,7 +9,7 @@ icon: "puzzle-piece"
|
||||
</Info>
|
||||
|
||||
<Tip>
|
||||
Not sure which module to use? The [Choose the Right Module](/choose-your-module) guide maps 35+ developer goals to modules with code examples — start there if you're orienting for the first time.
|
||||
Not sure which module to use? The [Choose the Right Module](choose-your-module) guide maps 35+ developer goals to modules with code examples — start there if you're orienting for the first time.
|
||||
</Tip>
|
||||
|
||||
Semantica is organized into **27 modules** across six logical layers. Each module is independently importable: you never pay for what you don't use.
|
||||
@@ -28,9 +28,7 @@ Semantica is organized into **27 modules** across six logical layers. Each modul
|
||||
|
||||
### Ingest
|
||||
|
||||
Loads data from files, web, databases, and streams. Each ingestor returns its own
|
||||
result type (`FileIngestor` → `FileObject`, `WebIngestor` → `WebContent`, …);
|
||||
document-oriented ones expose a `.text` payload and `.metadata`.
|
||||
Loads data from files, web, databases, and streams into a unified `SourceDocument` format.
|
||||
|
||||
```python
|
||||
from semantica.ingest import FileIngestor, WebIngestor, ParquetIngestor, XMLIngestor, DatabricksIngestor
|
||||
@@ -39,7 +37,7 @@ from semantica.ingest import FileIngestor, WebIngestor, ParquetIngestor, XMLInge
|
||||
ingestor = FileIngestor()
|
||||
documents = ingestor.ingest_directory("data/")
|
||||
|
||||
# Web page: returns a WebContent with .text, .title, .links, .metadata
|
||||
# Web crawl
|
||||
web_ingestor = WebIngestor()
|
||||
page = web_ingestor.ingest_url("https://example.com")
|
||||
|
||||
@@ -69,13 +67,13 @@ Extracts structured text and layout metadata from raw documents.
|
||||
```python
|
||||
from semantica.parse import DocumentParser, DoclingParser
|
||||
|
||||
# Standard parser: all common formats. parse() takes a path, returns a dict
|
||||
# Standard parser: all common formats
|
||||
parser = DocumentParser()
|
||||
parsed = parser.parse("document.pdf") # {"full_text": ..., "metadata": ..., ...}
|
||||
parsed = parser.parse_document("document.pdf")
|
||||
|
||||
# Advanced parser (pip install semantica[parse-docling]): tables, OCR, layout
|
||||
parser = DoclingParser(export_format="markdown", enable_ocr=True)
|
||||
parsed = parser.parse("data/annual_report.pdf") # dict with full_text, tables, pages
|
||||
# Advanced parser: multi-column PDFs, merged-cell tables, OCR
|
||||
parser = DoclingParser(extract_tables=True, extract_images=True, output_format="markdown")
|
||||
parsed = parser.parse("data/annual_report.pdf")
|
||||
```
|
||||
|
||||
**Available parsers:** `DocumentParser`, `DoclingParser`, `CodeParser`, `CSVParser`, `DocxParser`, `EmailParser`, `ExcelParser`, `HTMLParser`, `ImageParser`, `JSONParser`, `MCPParser`, `MediaParser`, `PDFParser`, `PPTXParser`, `StructuredDataParser`, `WebParser`, `XMLParser`
|
||||
@@ -87,12 +85,11 @@ Chunks text for embedding and RAG pipelines with awareness of semantic boundarie
|
||||
```python
|
||||
from semantica.split import TextSplitter
|
||||
|
||||
# chunk_size / chunk_overlap are constructor arguments
|
||||
splitter = TextSplitter(method="semantic_transformer", chunk_size=1000, chunk_overlap=200)
|
||||
chunks = splitter.split(text)
|
||||
splitter = TextSplitter(method="semantic_transformer")
|
||||
chunks = splitter.split(text, chunk_size=1000, chunk_overlap=200)
|
||||
```
|
||||
|
||||
**Chunking methods:** `recursive`, `token`, `sentence`, `paragraph`, `semantic_transformer`, `entity_aware`, `relation_aware`, `graph_based`, `ontology_aware`, `hierarchical`, `community_detection`, `centrality_based`, `llm`
|
||||
**Chunking strategies:** `recursive`, `semantic_transformer`, `entity_aware`, `relation_aware`, `sliding_window`, `structural`
|
||||
|
||||
### Normalize
|
||||
|
||||
@@ -118,18 +115,17 @@ Named entity recognition, relation extraction, and triplet generation.
|
||||
```python
|
||||
from semantica.semantic_extract import NERExtractor, RelationExtractor, TripletExtractor
|
||||
|
||||
# LLM method: provider + llm_model select the backend; the API key comes from the env
|
||||
ner = NERExtractor(method="llm", provider="groq", llm_model="llama-3.3-70b-versatile")
|
||||
entities = ner.extract("Apple Inc. was founded by Steve Jobs.") # list[Entity]
|
||||
ner = NERExtractor(method="llm", llm_provider=llm)
|
||||
entities = ner.extract("Apple Inc. was founded by Steve Jobs.")
|
||||
|
||||
rel = RelationExtractor(method="llm", provider="groq", llm_model="llama-3.3-70b-versatile")
|
||||
relationships = rel.extract(text, entities=entities) # list[Relation]
|
||||
rel = RelationExtractor(method="llm", llm_provider=llm)
|
||||
relationships = rel.extract(text, entities=entities)
|
||||
|
||||
trip = TripletExtractor(method="pattern")
|
||||
triplets = trip.extract(text) # list[Triplet]
|
||||
trip = TripletExtractor(method="llm", llm_provider=llm)
|
||||
triplets = trip.extract(text)
|
||||
```
|
||||
|
||||
**Extraction methods:** `"pattern"` (no API key), `"ml"` (local spaCy model), `"llm"` (any of the 9 supported providers)
|
||||
**Extraction methods:** `"pattern"` (no API key), `"ml"` (local model), `"llm"` (any of the 8 supported providers)
|
||||
|
||||
**Additional extractors:** `CoreferenceResolver`, `EventDetector`, `SemanticAnalyzer`, `SemanticNetworkExtractor`
|
||||
|
||||
@@ -141,17 +137,17 @@ Graph construction, graph algorithms, temporal model, and distance intelligence.
|
||||
from semantica.kg import GraphBuilder, GraphAnalyzer, TemporalGraphQuery, SimilarityCalculator
|
||||
from datetime import datetime
|
||||
|
||||
# Build: build() takes a {"entities": ..., "relationships": ...} dict
|
||||
# Build
|
||||
builder = GraphBuilder(merge_entities=True)
|
||||
kg = builder.build({"entities": entities, "relationships": relationships})
|
||||
kg = builder.build(entities=entities, relationships=relationships)
|
||||
|
||||
# Temporal graphs (v0.4.0)
|
||||
query_engine = TemporalGraphQuery(enable_temporal_reasoning=True)
|
||||
snapshot = query_engine.query_at_time(kg, query="", at_time=datetime(2021, 6, 15))
|
||||
|
||||
# Semantic similarity (v0.5.0): operates on embedding vectors
|
||||
calc = SimilarityCalculator(method="cosine")
|
||||
score = calc.cosine_similarity(vec_a, vec_b)
|
||||
# Semantic similarity (v0.5.0)
|
||||
calc = SimilarityCalculator()
|
||||
scores = calc.calculate_similarity(entity_a, entity_b)
|
||||
```
|
||||
|
||||
**Graph algorithms available:** centrality calculation, community detection, connectivity analysis, entity resolution, link prediction, path finding, similarity calculation
|
||||
@@ -179,23 +175,19 @@ Derives new facts from existing knowledge using multiple inference strategies.
|
||||
```python
|
||||
from semantica.reasoning import Reasoner, DatalogReasoner
|
||||
|
||||
# Forward chaining: facts and rules as predicate(args) / IF-THEN strings
|
||||
# Rule-based reasoning
|
||||
engine = Reasoner()
|
||||
engine.add_fact("Manager(Alice)")
|
||||
engine.add_rule("IF Manager(?x) THEN HasAuthority(?x)")
|
||||
results = engine.forward_chain() # list[InferenceResult] with .conclusion, .rule_used
|
||||
engine.apply_transitivity("located_in")
|
||||
engine.apply_symmetry("knows")
|
||||
result = engine.infer()
|
||||
|
||||
# Datalog: recursive Horn clause rules (v0.4.0)
|
||||
datalog = DatalogReasoner()
|
||||
datalog.add_fact("parent(tom, bob)")
|
||||
datalog.add_fact("parent(bob, ann)")
|
||||
datalog.add_rule("ancestor(X, Y) :- parent(X, Y).")
|
||||
datalog = DatalogEngine()
|
||||
datalog.add_rule("ancestor(X, Z) :- parent(X, Y), ancestor(Y, Z).")
|
||||
datalog.derive_all()
|
||||
results = datalog.query("ancestor(tom, ?Z)") # [{"Z": "bob"}, {"Z": "ann"}], order not guaranteed
|
||||
results = datalog.query("ancestor(alice, ?)")
|
||||
```
|
||||
|
||||
**Engines:** `Reasoner` (forward/backward chaining), `ReteEngine`, `SPARQLReasoner`, `DatalogReasoner`, `TemporalReasoningEngine`, `GraphReasoner` (LLM)
|
||||
**Engines:** forward chaining, Rete network, deductive, abductive, SPARQL, Datalog: all produce explainable inference paths
|
||||
|
||||
|
||||
## Storage
|
||||
@@ -207,9 +199,9 @@ Generates and manages vector embeddings for semantic similarity.
|
||||
```python
|
||||
from semantica.embeddings import EmbeddingGenerator
|
||||
|
||||
generator = EmbeddingGenerator()
|
||||
embeddings = generator.generate_embeddings(["text1", "text2"]) # np.ndarray
|
||||
similarity = generator.compare_embeddings(embeddings[0], embeddings[1])
|
||||
generator = EmbeddingGenerator(model="sentence-transformers")
|
||||
embeddings = generator.generate(["text1", "text2"])
|
||||
similarity = generator.similarity(embeddings[0], embeddings[1])
|
||||
```
|
||||
|
||||
**Supported models:** Sentence-Transformers, FastEmbed, OpenAI, BGE
|
||||
@@ -223,18 +215,12 @@ Multi-backend vector database with hybrid search support.
|
||||
```python
|
||||
from semantica.vector_store import VectorStore
|
||||
|
||||
store = VectorStore(backend="faiss", dimension=768)
|
||||
|
||||
# Raw vectors
|
||||
ids = store.store_vectors(embeddings) # returns generated ids
|
||||
hits = store.search_vectors(query_vector, k=10)
|
||||
|
||||
# Or store text and let the store embed it
|
||||
store.add_documents(["Apple was founded in 1976.", "Google was founded in 1998."])
|
||||
results = store.search("tech company founding dates", limit=10)
|
||||
store = VectorStore(backend="faiss", dimension=768)
|
||||
store.add_vectors(embeddings, ids)
|
||||
results = store.search(query_vector, top_k=10)
|
||||
```
|
||||
|
||||
**Backends:** FAISS, Pinecone, Weaviate, Qdrant, Milvus, PgVector, SQLite, in-memory
|
||||
**Backends:** FAISS, Pinecone, Weaviate, Qdrant, Milvus, PgVector, in-memory
|
||||
|
||||
**Search modes:** semantic top-k, hybrid (vector + keyword), metadata-filtered
|
||||
|
||||
@@ -246,8 +232,8 @@ Connects to graph databases for persistent, query-able storage.
|
||||
from semantica.graph_store import GraphStore
|
||||
|
||||
store = GraphStore(backend="neo4j")
|
||||
store.add_nodes([{"id": "acme", "type": "Organization", "properties": {"name": "Acme"}}])
|
||||
store.add_edges([{"source": "alice", "target": "acme", "type": "works_for"}])
|
||||
store.add_nodes(entities)
|
||||
store.add_edges(relationships)
|
||||
results = store.query("MATCH (n)-[r]->(m) RETURN n, r, m")
|
||||
```
|
||||
|
||||
@@ -260,9 +246,9 @@ RDF triple-based storage with SPARQL query support.
|
||||
```python
|
||||
from semantica.triplet_store import TripletStore
|
||||
|
||||
store = TripletStore(backend="oxigraph")
|
||||
store.add_triplets(triplets) # list of Triplet objects (or add_triplet for one)
|
||||
results = store.execute_query("SELECT ?s ?p ?o WHERE { ?s ?p ?o }")
|
||||
store = TripletStore(backend="blazegraph")
|
||||
store.add_triplets(subject, predicate, obj)
|
||||
results = store.sparql("SELECT ?s ?p ?o WHERE { ?s ?p ?o }")
|
||||
```
|
||||
|
||||
**Backends:** Oxigraph (embedded), Blazegraph, Apache Jena, RDF4J
|
||||
@@ -275,18 +261,15 @@ results = store.execute_query("SELECT ?s ?p ?o WHERE { ?s ?p ?o }")
|
||||
Detects, scores, and merges duplicate entities across sources.
|
||||
|
||||
```python
|
||||
from semantica.deduplication import DuplicateDetector, EntityMerger
|
||||
from semantica.deduplication import EntityResolver
|
||||
|
||||
detector = DuplicateDetector(similarity_threshold=0.85)
|
||||
candidates = detector.detect_duplicates(entities)
|
||||
|
||||
merger = EntityMerger()
|
||||
operations = merger.merge_duplicates(entities, strategy="keep_most_complete")
|
||||
resolver = EntityResolver()
|
||||
merged = resolver.resolve(entities, strategy="semantic_v2")
|
||||
```
|
||||
|
||||
**v2 candidate-generation modes** (`blocking_v2`, `hybrid_v2`, `semantic_v2`) are up to 7x faster than v1.
|
||||
**v2 strategies** (`blocking_v2`, `hybrid_v2`, `semantic_v2`) are up to 7x faster than v1.
|
||||
|
||||
**Components:** `DuplicateDetector`, `EntityMerger`, `ClusterBuilder`, `MergeStrategyManager`
|
||||
**Components:** `EntityResolver`, `DuplicateDetector`, `EntityMerger`, `SimilarityCalculator`, `ClusterBuilder`
|
||||
|
||||
**`DuplicateDetector` options:** `max_results`, `top_k_per_entity`, `min_similarity`, `sort_by`
|
||||
|
||||
@@ -295,13 +278,14 @@ operations = merger.merge_duplicates(entities, strategy="keep_most_complete")
|
||||
Detects and resolves fact conflicts across overlapping knowledge sources.
|
||||
|
||||
```python
|
||||
from semantica.conflicts import ConflictDetector, ConflictResolver
|
||||
from semantica.conflicts import ConflictDetector
|
||||
|
||||
conflicts = ConflictDetector().detect_conflicts(entities) # list of entity dicts
|
||||
resolved = ConflictResolver().resolve_conflicts(conflicts, strategy="most_recent")
|
||||
detector = ConflictDetector()
|
||||
conflicts = detector.detect_conflicts(kg)
|
||||
resolved = detector.resolve(conflicts, strategy="most_recent")
|
||||
```
|
||||
|
||||
**Detection types:** value conflicts, type conflicts, relationship conflicts, temporal conflicts, logical conflicts
|
||||
**Detection types:** value conflicts, type conflicts, temporal conflicts, logical conflicts
|
||||
|
||||
**Resolution strategies:** prefer most recent, prefer most reliable source, majority vote, flag for manual review
|
||||
|
||||
@@ -314,7 +298,6 @@ Agent context graphs, decision tracking, causal chains, and precedent search.
|
||||
|
||||
```python
|
||||
from semantica.context import AgentContext, ContextGraph
|
||||
from semantica.vector_store import VectorStore
|
||||
|
||||
context = AgentContext(
|
||||
vector_store=VectorStore(backend="faiss", dimension=768),
|
||||
@@ -345,7 +328,7 @@ W3C PROV-O compliant lineage tracking across all modules.
|
||||
from semantica.provenance import ProvenanceManager
|
||||
|
||||
manager = ProvenanceManager()
|
||||
manager.track_entity("entity_1", source="document.pdf", metadata={"type": "person"})
|
||||
manager.track_entity("entity_1", "document.pdf", "person")
|
||||
lineage = manager.get_lineage("entity_1")
|
||||
```
|
||||
|
||||
@@ -381,8 +364,8 @@ RDFExporter().export(graph, file_path="graph.ttl", format="turtle")
|
||||
# Analytics
|
||||
ParquetExporter().export(graph, file_path="output/graph.parquet")
|
||||
|
||||
# ArangoDB: writes AQL INSERT statements to the given path
|
||||
ArangoAQLExporter().export(graph, file_path="graph.aql")
|
||||
# ArangoDB
|
||||
aql = ArangoAQLExporter().export(graph)
|
||||
```
|
||||
|
||||
**Export formats:** RDF (Turtle, JSON-LD, N-Triples, XML), Parquet, ArangoDB AQL, CSV, OWL, Arrow, LPG, YAML, distance matrices
|
||||
@@ -407,24 +390,16 @@ viz.visualize_network(graph, output="html", file_path="graph.html")
|
||||
Pipeline DSL with parallel workers, retry policies, and failure handling.
|
||||
|
||||
```python
|
||||
from semantica.pipeline import PipelineBuilder, ExecutionEngine
|
||||
from semantica.ingest import FileIngestor
|
||||
from semantica.semantic_extract import NERExtractor
|
||||
from semantica.pipeline import Pipeline
|
||||
|
||||
builder = PipelineBuilder()
|
||||
|
||||
# Each step type dispatches to a handler you register (or supply explicitly)
|
||||
builder.register_step_handler("ingest", lambda data, **c: FileIngestor().ingest(c["source"]))
|
||||
builder.register_step_handler("extract", lambda docs, **c: NERExtractor(method="pattern").extract(docs[0].text))
|
||||
|
||||
builder.add_step("ingest", step_type="ingest", source="data/")
|
||||
builder.add_step("extract", step_type="extract")
|
||||
|
||||
pipeline = builder.connect_steps("ingest", "extract").build(name="docs_to_entities")
|
||||
result = ExecutionEngine().execute_pipeline(pipeline)
|
||||
pipeline = Pipeline()
|
||||
pipeline.add_step("ingest", FileIngestor())
|
||||
pipeline.add_step("extract", NERExtractor())
|
||||
pipeline.add_step("build", GraphBuilder())
|
||||
result = pipeline.run("data/")
|
||||
```
|
||||
|
||||
**Components:** `PipelineBuilder`, `Pipeline`, `ExecutionEngine`, `FailureHandler`, `PipelineValidator`, `ParallelismManager`, `ResourceScheduler`
|
||||
**Components:** `Pipeline`, `PipelineBuilder`, `ExecutionEngine`, `FailureHandler`, `PipelineValidator`, `ParallelismManager`, `ResourceScheduler`
|
||||
|
||||
### Explorer
|
||||
|
||||
@@ -453,7 +428,7 @@ llm = OpenAI(model="gpt-4o", api_key=os.getenv("OPENAI_API_KEY"))
|
||||
llm = LiteLLM(model="anthropic/claude-opus-4-7", api_key=os.getenv("ANTHROPIC_API_KEY"))
|
||||
```
|
||||
|
||||
**Supported providers:** OpenAI, Anthropic, Google Gemini, Groq, Ollama, DeepSeek, Novita AI, HuggingFace, plus LiteLLM (100+ models via one interface)
|
||||
**Supported providers:** OpenAI, Anthropic, Google Gemini, Groq, Ollama, DeepSeek, Novita AI, LiteLLM (20+ models via one interface)
|
||||
|
||||
### MCP Server
|
||||
|
||||
@@ -470,43 +445,44 @@ python -m semantica.mcp_server
|
||||
Bootstrap knowledge graphs from verified structured sources: fixed-point reference data, controlled vocabularies, and domain anchors.
|
||||
|
||||
```python
|
||||
from semantica.seed import SeedDataManager
|
||||
from semantica.seed import SeedManager
|
||||
|
||||
seed = SeedDataManager()
|
||||
seed = SeedManager()
|
||||
seed.populate(kg, dataset="companies", count=100)
|
||||
|
||||
# Load trusted reference data from CSV / JSON / a database / an API
|
||||
seed_data = seed.load_from_csv("seed_data/industries.csv", entity_type="Industry")
|
||||
|
||||
# Merge seed data with extraction output (seed values win on conflict by default)
|
||||
combined = seed.integrate_with_extracted(
|
||||
{"entities": seed_data, "relationships": []},
|
||||
{"entities": extracted_entities, "relationships": extracted_relationships},
|
||||
merge_strategy="seed_first",
|
||||
)
|
||||
# Load domain seeds from file or built-in datasets
|
||||
seed.load_from_file("seed_data/industries.json")
|
||||
seed.inject(kg) # merges seed nodes without duplicating existing entities
|
||||
```
|
||||
|
||||
**Use cases:** anchoring extraction with known entities, pre-populating ontology classes, deterministic test graph generation.
|
||||
|
||||
### Evals
|
||||
|
||||
Scores decision-intelligence outputs (decision records, audit trails, reasoning
|
||||
text) with a registry of deterministic and model-backed evaluators plus a small
|
||||
run harness.
|
||||
Evaluation framework for measuring KG quality, extraction accuracy, and pipeline performance.
|
||||
|
||||
```python
|
||||
from semantica.evals import evaluate, list_evaluators
|
||||
from semantica.evals import KGEvaluator, ExtractionEvaluator, PipelineEvaluator, RegressionTracker
|
||||
|
||||
list_evaluators()
|
||||
# ['decision_scores', 'exact_match', 'keyword_check', 'length_range',
|
||||
# 'levenshtein', 'llm_as_judge', 'numeric_range', 'regex_match', 'rouge',
|
||||
# 'temporal_range']
|
||||
# KG quality
|
||||
report = KGEvaluator().evaluate(kg, ontology=ontology)
|
||||
print(f"Completeness: {report.completeness:.2%} Consistency: {report.consistency:.2%}")
|
||||
|
||||
cases = [("apple", "aple"), ("night", "nacht")]
|
||||
summary = evaluate(cases, evaluators=["levenshtein"])
|
||||
print(summary.total, summary.passed, summary.pass_rate)
|
||||
# Extraction accuracy
|
||||
report = ExtractionEvaluator().evaluate_ner(predictions=extracted, gold_standard=annotated)
|
||||
print(f"Precision: {report.precision:.3f} Recall: {report.recall:.3f} F1: {report.f1:.3f}")
|
||||
|
||||
# Pipeline throughput and latency
|
||||
metrics = PipelineEvaluator().benchmark(pipeline, data="data/", bench_runs=5)
|
||||
print(f"Throughput: {metrics.docs_per_second:.1f} docs/sec")
|
||||
|
||||
# Regression tracking across runs
|
||||
tracker = RegressionTracker(db_path="eval_history.db")
|
||||
run_id = tracker.record_run(pipeline_version="v1.2.0", metrics=metrics)
|
||||
diff = tracker.compare(run_id, baseline_run_id="run_abc123")
|
||||
```
|
||||
|
||||
**Public API:** `evaluate(cases, evaluators, config=None)`, `list_evaluators()`, `get_evaluator(name)`, and the `EvalMetric` / `CaseResult` / `EvalSummary` result types. See the [Evals reference](/reference/evals).
|
||||
**Components:** `KGEvaluator`, `ExtractionEvaluator`, `PipelineEvaluator`, `RegressionTracker`
|
||||
|
||||
### Core
|
||||
|
||||
@@ -515,20 +491,20 @@ Base classes, shared data models, and the plugin registry used across all module
|
||||
```python
|
||||
from semantica.core import Semantica, PluginRegistry, ConfigManager
|
||||
|
||||
# ConfigManager loads a Config; Config.get() does dotted lookups
|
||||
config = ConfigManager().load_from_file("config.yaml")
|
||||
batch = config.get("processing.batch_size", default=32)
|
||||
|
||||
# Top-level orchestrator: pass the Config object (or a dict), not a path
|
||||
sem = Semantica(config=config)
|
||||
# Top-level orchestrator
|
||||
sem = Semantica(config_path="config.yaml")
|
||||
sem.initialize()
|
||||
|
||||
# Plugin registry: register custom components under a name
|
||||
# Plugin registry: register custom components
|
||||
registry = PluginRegistry()
|
||||
registry.register_plugin("my_ingestor", MyCustomIngestor, version="1.0.0")
|
||||
registry.register("my_ingestor", MyCustomIngestor)
|
||||
|
||||
# Config management
|
||||
config = ConfigManager(config_path="config.yaml")
|
||||
batch = config.get("processing.batch_size", default=32)
|
||||
```
|
||||
|
||||
**Components:** `Semantica`, `PluginRegistry`, `ConfigManager`, `Config`, `LifecycleManager`, `HealthStatus`, `MethodRegistry`
|
||||
**Components:** `Semantica`, `PluginRegistry`, `ConfigManager`, `LifecycleManager`, `HealthMonitor`, `Config`
|
||||
|
||||
### Utils
|
||||
|
||||
@@ -556,13 +532,11 @@ from semantica.semantic_extract import NERExtractor, RelationExtractor
|
||||
from semantica.kg import GraphBuilder
|
||||
|
||||
sources = FileIngestor().ingest("data/")
|
||||
text = DocumentParser().parse(sources[0].path)["full_text"]
|
||||
ner = NERExtractor(method="llm", provider="groq", llm_model="llama-3.3-70b-versatile")
|
||||
rel = RelationExtractor(method="llm", provider="groq", llm_model="llama-3.3-70b-versatile")
|
||||
entities = ner.extract(text)
|
||||
relationships = rel.extract(text, entities=entities)
|
||||
parsed = DocumentParser().parse(sources[0])
|
||||
entities = NERExtractor(method="llm", llm_provider=llm).extract(parsed)
|
||||
relationships = RelationExtractor(method="llm", llm_provider=llm).extract(parsed, entities=entities)
|
||||
graph = GraphBuilder(merge_entities=True).build(
|
||||
{"entities": entities, "relationships": relationships}
|
||||
entities=entities, relationships=relationships
|
||||
)
|
||||
```
|
||||
|
||||
@@ -581,20 +555,16 @@ from semantica.vector_store import VectorStore
|
||||
context = AgentContext(
|
||||
vector_store=VectorStore(backend="faiss", dimension=768),
|
||||
knowledge_graph=ContextGraph(advanced_analytics=True),
|
||||
graph_expansion=True,
|
||||
)
|
||||
context.load_graph("company_kg.json")
|
||||
|
||||
# store() extracts entities and populates the graph + vector index
|
||||
context.store([{"content": "Steve Wozniak co-founded Apple with Steve Jobs."}])
|
||||
|
||||
# retrieve() blends vector similarity with multi-hop graph traversal
|
||||
results = context.retrieve(
|
||||
result = context.query(
|
||||
"What companies did Apple alumni found?",
|
||||
use_graph=True,
|
||||
expand_graph=True,
|
||||
mode="graphrag",
|
||||
reasoning=True,
|
||||
)
|
||||
for r in results:
|
||||
print(f"[{r['score']:.3f}] {r['content']} (source: {r['source']})")
|
||||
for claim in result.claims:
|
||||
print(f"{claim.text} → {claim.source_node}")
|
||||
```
|
||||
|
||||
**Best for:** question-answering systems, RAG with source attribution, research assistants
|
||||
@@ -636,22 +606,18 @@ precedents = context.find_precedents("model selection", limit=5)
|
||||
|
||||
```python
|
||||
from semantica.ingest import FileIngestor
|
||||
from semantica.parse import DocumentParser
|
||||
from semantica.semantic_extract import NERExtractor
|
||||
from semantica.kg import GraphBuilder
|
||||
from semantica.provenance import ProvenanceManager
|
||||
from semantica.export import RDFExporter
|
||||
|
||||
sources = FileIngestor().ingest("records/")
|
||||
ner = NERExtractor(method="llm", provider="groq", llm_model="llama-3.3-70b-versatile")
|
||||
entities = ner.extract(DocumentParser().parse(sources[0].path)["full_text"])
|
||||
graph = GraphBuilder(merge_entities=True).build({"entities": entities, "relationships": []})
|
||||
|
||||
entities = NERExtractor(method="llm", llm_provider=llm).extract(sources)
|
||||
graph = GraphBuilder(merge_entities=True).build(entities=entities, relationships=[])
|
||||
prov = ProvenanceManager()
|
||||
prov.track_entity("entity_id", source="records/filing.pdf", metadata={"extractor": "llm"})
|
||||
lineage = prov.get_lineage("entity_id")
|
||||
lineage = prov.get_entity_lineage("entity_id")
|
||||
|
||||
RDFExporter().export(graph, file_path="audit.ttl", format="turtle")
|
||||
RDFExporter(include_provenance=True).export(graph, file_path="audit.ttl", format="turtle")
|
||||
```
|
||||
|
||||
**Best for:** HIPAA, SOX, GDPR, FDA 21 CFR Part 11 deployments
|
||||
@@ -666,25 +632,18 @@ RDFExporter().export(graph, file_path="audit.ttl", format="turtle")
|
||||
from semantica.ingest import WebIngestor
|
||||
from semantica.normalize import TextNormalizer
|
||||
from semantica.semantic_extract import NERExtractor, RelationExtractor
|
||||
from semantica.graph_store import GraphStore
|
||||
from semantica.kg import GraphBuilder
|
||||
from semantica.graph_store import Neo4jStore
|
||||
|
||||
ingestor = WebIngestor()
|
||||
pages = WebIngestor(max_depth=2).ingest("https://example.com")
|
||||
normalizer = TextNormalizer()
|
||||
ner = NERExtractor(method="pattern")
|
||||
rel = RelationExtractor(method="pattern")
|
||||
store = Neo4jStore(uri="bolt://localhost:7687", user="neo4j", password="password")
|
||||
|
||||
# The generic GraphStore wrapper exposes the add_nodes/add_edges interface
|
||||
# GraphBuilder persists through; a raw Neo4jStore does not
|
||||
store = GraphStore(backend="neo4j", uri="bolt://localhost:7687", user="neo4j", password="password")
|
||||
builder = GraphBuilder(merge_entities=True, graph_store=store)
|
||||
|
||||
for url in ["https://example.com/a", "https://example.com/b"]:
|
||||
page = ingestor.ingest_url(url) # WebContent, has .text
|
||||
for page in pages:
|
||||
text = normalizer.normalize_text(page.text)
|
||||
entities = ner.extract(text)
|
||||
relationships = rel.extract(text, entities=entities)
|
||||
builder.build({"entities": entities, "relationships": relationships})
|
||||
entities = NERExtractor().extract(text)
|
||||
relationships = RelationExtractor().extract(text, entities=entities)
|
||||
store.add_nodes(entities)
|
||||
store.add_edges(relationships)
|
||||
```
|
||||
|
||||
**Best for:** competitive intelligence, news monitoring, research aggregation
|
||||
@@ -721,34 +680,34 @@ versioner.create_snapshot(kg, "2024-Q1", author="user@example.com", description=
|
||||
|
||||
| Module | Purpose | Key Classes |
|
||||
| :------ | :------- | :----------- |
|
||||
| [ingest](/reference/ingest) | Data ingestion | `FileIngestor`, `WebIngestor`, `ParquetIngestor`, `XMLIngestor` |
|
||||
| [parse](/reference/parse) | Document parsing | `DocumentParser`, `DoclingParser` |
|
||||
| [split](/reference/split) | Text chunking | `TextSplitter` |
|
||||
| [normalize](/reference/normalize) | Data cleaning | `TextNormalizer`, `EntityNormalizer`, `LanguageDetector` |
|
||||
| [semantic_extract](/reference/semantic_extract) | NER & relation extraction | `NERExtractor`, `RelationExtractor`, `TripletExtractor`, `SemanticAnalyzer`, `SemanticNetworkExtractor`, `ExtractionValidator` |
|
||||
| [kg](/reference/kg) | Graph construction | `GraphBuilder`, `TemporalGraphQuery`, `SimilarityCalculator` |
|
||||
| [ontology](/reference/ontology) | Schema management | `OntologyGenerator`, `SHACLGenerator` |
|
||||
| [reasoning](/reference/reasoning) | Logical inference | `Reasoner`, `DatalogReasoner` |
|
||||
| [embeddings](/reference/embeddings) | Vector embeddings | `EmbeddingGenerator` |
|
||||
| [vector_store](/reference/vector_store) | Vector database | `VectorStore` |
|
||||
| [graph_store](/reference/graph_store) | Graph database | `GraphStore` |
|
||||
| [triplet_store](/reference/triplet_store) | RDF triple store | `TripletStore` |
|
||||
| [deduplication](/reference/deduplication) | Entity resolution | `DuplicateDetector`, `EntityMerger`, `ClusterBuilder`, `MergeStrategyManager` |
|
||||
| [conflicts](/reference/conflicts) | Conflict resolution | `ConflictDetector`, `ConflictResolver`, `SourceTracker` |
|
||||
| [context](/reference/context) | Agent context & decisions | `AgentContext`, `ContextGraph` |
|
||||
| [provenance](/reference/provenance) | W3C PROV-O lineage | `ProvenanceManager` |
|
||||
| [change_management](/reference/change_management) | Version control | `TemporalVersionManager` |
|
||||
| [export](/reference/export) | Data export | `RDFExporter`, `ParquetExporter` |
|
||||
| [visualization](/reference/visualization) | Graph visualization | `KGVisualizer` |
|
||||
| [pipeline](/reference/pipeline) | Workflow orchestration | `Pipeline`, `PipelineBuilder` |
|
||||
| [explorer](/reference/explorer) | Knowledge Explorer UI | `semantica-explorer --graph <file>` |
|
||||
| [llms](/reference/llms) | LLM providers | `Groq`, `OpenAI`, `create_provider` |
|
||||
| [mcp_server](/reference/mcp_server) | MCP stdio server | `python -m semantica.mcp_server` |
|
||||
| [seed](/reference/seed) | KG bootstrapping from structured sources | `SeedDataManager` |
|
||||
| [evals](/reference/evals) | Decision-intelligence evaluation | `evaluate`, `list_evaluators`, `EvalSummary` |
|
||||
| [core](/reference/core) | Base classes & registry | `Semantica`, `ConfigManager`, `PluginRegistry`, `LifecycleManager` |
|
||||
| [utils](/reference/utils) | Shared utilities | `helpers`, `validators` |
|
||||
| [ingest](reference/ingest) | Data ingestion | `FileIngestor`, `WebIngestor`, `ParquetIngestor`, `XMLIngestor` |
|
||||
| [parse](reference/parse) | Document parsing | `DocumentParser`, `DoclingParser` |
|
||||
| [split](reference/split) | Text chunking | `TextSplitter` |
|
||||
| [normalize](reference/normalize) | Data cleaning | `TextNormalizer`, `EntityNormalizer`, `LanguageDetector` |
|
||||
| [semantic_extract](reference/semantic_extract) | NER & relation extraction | `NERExtractor`, `RelationExtractor`, `TripletExtractor`, `SemanticAnalyzer`, `SemanticNetworkExtractor`, `ExtractionValidator` |
|
||||
| [kg](reference/kg) | Graph construction | `GraphBuilder`, `TemporalGraphQuery`, `SimilarityCalculator` |
|
||||
| [ontology](reference/ontology) | Schema management | `OntologyGenerator`, `SHACLGenerator` |
|
||||
| [reasoning](reference/reasoning) | Logical inference | `Reasoner`, `DatalogReasoner` |
|
||||
| [embeddings](reference/embeddings) | Vector embeddings | `EmbeddingGenerator` |
|
||||
| [vector_store](reference/vector_store) | Vector database | `VectorStore` |
|
||||
| [graph_store](reference/graph_store) | Graph database | `GraphStore` |
|
||||
| [triplet_store](reference/triplet_store) | RDF triple store | `TripletStore` |
|
||||
| [deduplication](reference/deduplication) | Entity resolution | `EntityResolver`, `DuplicateDetector`, `ClusterBuilder`, `MergeStrategyManager` |
|
||||
| [conflicts](reference/conflicts) | Conflict resolution | `ConflictDetector` |
|
||||
| [context](reference/context) | Agent context & decisions | `AgentContext`, `ContextGraph` |
|
||||
| [provenance](reference/provenance) | W3C PROV-O lineage | `ProvenanceManager` |
|
||||
| [change_management](reference/change_management) | Version control | `TemporalVersionManager` |
|
||||
| [export](reference/export) | Data export | `RDFExporter`, `ParquetExporter` |
|
||||
| [visualization](reference/visualization) | Graph visualization | `KGVisualizer` |
|
||||
| [pipeline](reference/pipeline) | Workflow orchestration | `Pipeline`, `PipelineBuilder` |
|
||||
| [explorer](reference/explorer) | Knowledge Explorer UI | `semantica-explorer --graph <file>` |
|
||||
| [llms](reference/llms) | LLM providers | `Groq`, `OpenAI`, `create_provider` |
|
||||
| [mcp_server](reference/mcp_server) | MCP stdio server | `python -m semantica.mcp_server` |
|
||||
| [seed](reference/seed) | KG bootstrapping from structured sources | `SeedManager` |
|
||||
| [evals](reference/evals) | Quality evaluation | `KGEvaluator`, `ExtractionEvaluator`, `PipelineEvaluator`, `RegressionTracker` |
|
||||
| [core](reference/core) | Base classes & registry | `Semantica`, `ConfigManager`, `PluginRegistry`, `LifecycleManager` |
|
||||
| [utils](reference/utils) | Shared utilities | `helpers`, `validators` |
|
||||
|
||||
- [Getting Started](/getting-started) — Your first knowledge graph in 5 minutes.
|
||||
- [Cookbook](/cookbook) — 40+ domain notebooks with real-world examples.
|
||||
- [API Reference](/reference/context) — Full technical documentation.
|
||||
- [Getting Started](getting-started) — Your first knowledge graph in 5 minutes.
|
||||
- [Cookbook](cookbook) — 40+ domain notebooks with real-world examples.
|
||||
- [API Reference](reference/context) — Full technical documentation.
|
||||
|
||||
@@ -76,5 +76,5 @@ By contributing to Semantica, you agree that your contributions will be licensed
|
||||
|
||||
## See Also
|
||||
|
||||
- [Contributing](/contributing-guide) — How to contribute to the project.
|
||||
- [Citation](/citation) — How to cite Semantica in research.
|
||||
- [Contributing](contributing-guide) — How to contribute to the project.
|
||||
- [Citation](citation) — How to cite Semantica in research.
|
||||
|
||||
+12
-14
@@ -78,13 +78,11 @@ from semantica.parse import DocumentParser
|
||||
parser = DocumentParser()
|
||||
parsed = parser.parse(sources[0].path) # parse() takes a path string
|
||||
|
||||
print(parsed["full_text"][:200]) # extracted text
|
||||
print(parsed["metadata"]) # document properties (fields vary by format)
|
||||
print(parsed["text"][:200]) # extracted text
|
||||
print(parsed["metadata"]) # file_path, encoding, size, and format-specific keys
|
||||
```
|
||||
|
||||
`parse()` returns a `dict`. `full_text` and `metadata` are present for every
|
||||
format; other keys depend on the parser (`pages` for PDF, `tables` and
|
||||
`paragraphs` for DOCX, `tables` for `DoclingParser`).
|
||||
`parse()` returns a `dict` with `text`, `full_text`, and `metadata` keys.
|
||||
|
||||
<Tip>
|
||||
For PDFs with tables, charts, or multi-column layouts, use `DoclingParser` (`pip install semantica[parse-docling]`): it applies advanced layout analysis and returns structured table data alongside text.
|
||||
@@ -109,7 +107,7 @@ Identify named entities and extract typed relationships between them.
|
||||
```python Pattern-based (fast, no API key)
|
||||
from semantica.semantic_extract import NERExtractor, RelationExtractor
|
||||
|
||||
text = parsed["full_text"]
|
||||
text = parsed["text"]
|
||||
|
||||
ner = NERExtractor(method="pattern")
|
||||
entities = ner.extract(text)
|
||||
@@ -124,7 +122,7 @@ relationships = rel.extract(text, entities=entities)
|
||||
from semantica.semantic_extract import NERExtractor, RelationExtractor
|
||||
|
||||
# Reads GROQ_API_KEY from the environment; provider/llm_model select the backend
|
||||
text = parsed["full_text"]
|
||||
text = parsed["text"]
|
||||
|
||||
ner = NERExtractor(method="llm", provider="groq", llm_model="llama-3.3-70b-versatile")
|
||||
entities = ner.extract(text)
|
||||
@@ -279,7 +277,7 @@ builder = GraphBuilder(merge_entities=True)
|
||||
|
||||
all_entities, all_rels = [], []
|
||||
for source in FileIngestor().ingest("data/reports/"):
|
||||
text = parser.parse(source.path)["full_text"]
|
||||
text = parser.parse(source.path)["text"]
|
||||
entities = ner.extract(text)
|
||||
rels = rel.extract(text, entities=entities)
|
||||
all_entities.extend(entities)
|
||||
@@ -415,14 +413,14 @@ store = GraphStore(backend="neo4j", uri="bolt://localhost:7687",
|
||||
builder = GraphBuilder(merge_entities=True, graph_store=store)
|
||||
|
||||
for info in ingestor.scan_directory("data/reports/", recursive=True):
|
||||
text = parser.parse(info["path"])["full_text"] # one document loaded at a time
|
||||
text = parser.parse(info["path"])["text"] # one document loaded at a time
|
||||
entities = ner.extract(text)
|
||||
rels = rel.extract(text, entities=entities)
|
||||
builder.build({"entities": entities, "relationships": rels})
|
||||
```
|
||||
|
||||
For multi-step orchestration with configurable parallelism, see the
|
||||
[Pipeline guide](/guides/pipeline).
|
||||
[Pipeline guide](guides/pipeline).
|
||||
|
||||
</Accordion>
|
||||
|
||||
@@ -454,7 +452,7 @@ pip install --upgrade semantica
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Core Concepts](/concepts) — Knowledge graphs, ontologies, reasoning engines: the mental model behind Semantica.
|
||||
- [Module Reference](/modules) — Every module explained with key classes and common chains.
|
||||
- [API Reference](/reference/context) — Complete documentation for every module, class, and parameter.
|
||||
- [Cookbook](/cookbook) — 40+ interactive Jupyter notebooks with real-world datasets.
|
||||
- [Core Concepts](concepts) — Knowledge graphs, ontologies, reasoning engines: the mental model behind Semantica.
|
||||
- [Module Reference](modules) — Every module explained with key classes and common chains.
|
||||
- [API Reference](reference/context) — Complete documentation for every module, class, and parameter.
|
||||
- [Cookbook](cookbook) — 40+ interactive Jupyter notebooks with real-world datasets.
|
||||
|
||||
@@ -351,6 +351,6 @@ for record in history:
|
||||
</AccordionGroup>
|
||||
|
||||
- [Provenance](provenance) — W3C PROV-O lineage tracking.
|
||||
- [Knowledge Graph](/reference/kg) — The graph being versioned.
|
||||
- [Knowledge Graph](kg) — The graph being versioned.
|
||||
- [Export](export) — Export versioned snapshots.
|
||||
- [Conflicts](/reference/conflicts) — Detect conflicts introduced between versions.
|
||||
- [Conflicts](conflicts) — Detect conflicts introduced between versions.
|
||||
|
||||
@@ -453,4 +453,4 @@ class InvestigationStep:
|
||||
- [Deduplication](deduplication) — Resolve duplicate entities before conflict detection.
|
||||
- [Ontology](ontology) — Logical conflicts use SHACL shapes and ontology axioms.
|
||||
- [Provenance](provenance) — Track which source each conflicting fact came from.
|
||||
- [Knowledge Graph](/reference/kg) — The graph being checked for conflicts.
|
||||
- [Knowledge Graph](kg) — The graph being checked for conflicts.
|
||||
|
||||
+21
-21
@@ -30,28 +30,28 @@ icon: "brain"
|
||||
|
||||
## What You Get
|
||||
|
||||
- **AgentContext**: memory, decision tracking, and graph-backed retrieval behind one API
|
||||
- **AgentContext** — Memory, decision tracking, and graph-backed retrieval behind one API
|
||||
- Conversation history and checkpoint diffing
|
||||
- Persist and restore full context state to disk
|
||||
- **ContextGraph**: thread-safe in-memory knowledge graph
|
||||
- **ContextGraph** — Thread-safe in-memory knowledge graph
|
||||
- PageRank, centrality, community detection, temporal validity
|
||||
- Cross-graph navigation and link traversal
|
||||
- **AgentMemory**: embedding-backed memory with retention policy
|
||||
- **AgentMemory** — Embedding-backed memory with retention policy
|
||||
- LRU eviction at configurable `max_memory_size`
|
||||
- Per-conversation history isolation
|
||||
- **DecisionRecorder**: records decisions with causal chains and confidence scores
|
||||
- **DecisionRecorder** — Records decisions with causal chains and confidence scores
|
||||
- Temporal validity windows (`valid_from` / `valid_until`)
|
||||
- Cross-system context capture on every decision
|
||||
- **PolicyEngine**: versioned policy storage in the knowledge graph
|
||||
- **PolicyEngine** — Versioned policy storage in the knowledge graph
|
||||
- Compliance checking against recorded decisions
|
||||
- Policy exception tracking with approver audit trail
|
||||
- **EntityLinker**: maps entity text to stable URIs
|
||||
- **EntityLinker** — Maps entity text to stable URIs
|
||||
- Creates typed links between entity IDs
|
||||
- Prevents "Apple", "Apple Inc.", "AAPL" becoming separate nodes
|
||||
- **ContextRetriever**: fuses vector similarity, graph traversal, and agent memory
|
||||
- **ContextRetriever** — Fuses vector similarity, graph traversal, and agent memory
|
||||
- Richer context than pure vector search
|
||||
- Configurable `hybrid_alpha` and expansion hops
|
||||
- **CausalChainAnalyzer**: traces upstream causes and downstream effects of any decision
|
||||
- **CausalChainAnalyzer** — Traces upstream causes and downstream effects of any decision
|
||||
- Explainability paths with relationship types
|
||||
- Configurable depth and direction
|
||||
|
||||
@@ -273,7 +273,7 @@ icon: "brain"
|
||||
</Tip>
|
||||
|
||||
<Tip>
|
||||
**Persist your context between runs.** `VectorStore` does not auto-persist; passing `index_path=` to its constructor is a no-op. Call `context.save("agent_state/")` to write memory, the vector index, and the graph to disk, and `context.load("agent_state/")` on the next process to restore them. See the "Persist & Restore" tab under [Real-World Patterns](#real-world-patterns) below.
|
||||
**Persist your context between runs.** `VectorStore` does not auto-persist — passing `index_path=` to its constructor is a no-op. Call `context.save("agent_state/")` to write memory, the vector index, and the graph to disk, and `context.load("agent_state/")` on the next process to restore them. See the "Persist & Restore" tab under [Real-World Patterns](#real-world-patterns) below.
|
||||
</Tip>
|
||||
|
||||
### Memory Methods
|
||||
@@ -449,7 +449,7 @@ print("Nodes: {}, Edges: {}".format(stats["node_count"], stats["edge_count"]))
|
||||
`ContextGraph` exposes a full Distance Intelligence API for exploring semantic neighborhoods and blending proximity into retrieval.
|
||||
|
||||
<Info>
|
||||
Full Distance Intelligence reference (distance matrices, API endpoints, embedding cache, Explorer UI) is covered in the dedicated [Distance Intelligence](/reference/distance) page. This section documents the context-layer API.
|
||||
Full Distance Intelligence reference — distance matrices, API endpoints, embedding cache, Explorer UI — is covered in the dedicated [Distance Intelligence](distance) page. This section documents the context-layer API.
|
||||
</Info>
|
||||
|
||||
### Neighbors with Distance Metadata
|
||||
@@ -480,7 +480,7 @@ for n in neighbors:
|
||||
| Added field | Type | Description |
|
||||
| :---------- | :---- | :----------- |
|
||||
| `distance_band` | `str` | `"direct"` (1 hop) / `"near"` (2) / `"mid-range"` (3–4) / `"distant"` (5+) |
|
||||
| `confidence_decay` | `float` | `edge_weight ^ hop_count`; decays with each hop |
|
||||
| `confidence_decay` | `float` | `edge_weight ^ hop_count` — decays with each hop |
|
||||
| `path_to_anchor` | `List[str]` | Shortest path from anchor node to this neighbor |
|
||||
| `hop_count` | `int` | BFS depth from anchor |
|
||||
|
||||
@@ -659,7 +659,7 @@ if not receipt.complete:
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Check the receipt. The call returning is not proof the data is gone. FAISS,
|
||||
Check the receipt — the call returning is not proof the data is gone. FAISS,
|
||||
Milvus, and Weaviate expose no delete method, so erasure cannot be completed on
|
||||
those backends today; the receipt reports `unsupported` rather than a success it
|
||||
did not achieve.
|
||||
@@ -687,9 +687,9 @@ At least one store is required; a store that is not supplied reports
|
||||
|
||||
| Status | Meaning |
|
||||
| :--- | :--- |
|
||||
| `erased` | Reached, data removed. On the vectors leg this means the store accepted the delete for the ids given; backends offer no portable existence check, so it is not a count of embeddings that were really there |
|
||||
| `erased` | Reached, data removed. On the vectors leg this means the store accepted the delete for the ids given — backends offer no portable existence check, so it is not a count of embeddings that were really there |
|
||||
| `not_found` | Reached, held nothing for this entity |
|
||||
| `not_configured` | No such store was bound: normal, not a failure |
|
||||
| `not_configured` | No such store was bound — normal, not a failure |
|
||||
| `unsupported` | The store cannot delete at all; retrying will not help |
|
||||
| `failed` | The store was reached and the deletion did not succeed |
|
||||
|
||||
@@ -721,7 +721,7 @@ receipt.to_dict()
|
||||
# }
|
||||
```
|
||||
|
||||
Erasure runs outward-in: vectors, then memory, then the graph. The tombstone is
|
||||
Erasure runs outward-in — vectors, then memory, then the graph. The tombstone is
|
||||
the durable attestation that an erasure happened, so it is written last: a crash
|
||||
mid-cascade leaves the node present and the receipt incomplete, rather than a
|
||||
tombstone claiming more than actually happened. A store that raises is recorded
|
||||
@@ -1087,10 +1087,10 @@ class EntityLink:
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
- [Vector Store](/reference/vector_store): embedding storage backend for memory retrieval.
|
||||
- [Knowledge Graph](/reference/kg): graph algorithms and analytics used inside ContextGraph.
|
||||
- [Reasoning](/guides/reasoning): logical inference layered on top of context.
|
||||
- [Provenance](/guides/provenance): W3C PROV-O lineage for every stored fact.
|
||||
- [Vector Store](vector_store) — Embedding storage backend for memory retrieval.
|
||||
- [Knowledge Graph](kg) — Graph algorithms and analytics used inside ContextGraph.
|
||||
- [Reasoning](reasoning) — Logical inference layered on top of context.
|
||||
- [Provenance](provenance) — W3C PROV-O lineage for every stored fact.
|
||||
|
||||
- [Context Module](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/19_Context_Module.ipynb): memory and decision tracking · Intermediate
|
||||
- [Advanced Context Engineering](https://github.com/semantica-agi/semantica/blob/main/cookbook/advanced/11_Advanced_Context_Engineering.ipynb): production FAISS + Neo4j setup · Advanced
|
||||
- [Context Module](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/19_Context_Module.ipynb) — Memory and decision tracking · Intermediate
|
||||
- [Advanced Context Engineering](https://github.com/semantica-agi/semantica/blob/main/cookbook/advanced/11_Advanced_Context_Engineering.ipynb) — Production FAISS + Neo4j setup · Advanced
|
||||
|
||||
@@ -227,6 +227,6 @@ result = build_knowledge_base(sources=["doc.pdf"], method="fast")
|
||||
</Tip>
|
||||
|
||||
- [Pipeline](pipeline) — Pipeline execution and step orchestration.
|
||||
- [Utils](/reference/utils) — Shared utilities used by Core internally.
|
||||
- [Utils](utils) — Shared utilities used by Core internally.
|
||||
- [Getting Started](../getting-started) — Learn the basics before using Core.
|
||||
- [LLMs](/reference/llms) — Configure LLM providers via ConfigManager.
|
||||
- [LLMs](llms) — Configure LLM providers via ConfigManager.
|
||||
|
||||
@@ -437,7 +437,7 @@ result = calculate_similarity(entity_a, entity_b, method="drug_name")
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
- [Conflicts](/reference/conflicts) — Detect value conflicts between non-duplicate entities.
|
||||
- [Knowledge Graph](/reference/kg) — GraphBuilder uses deduplication during construction.
|
||||
- [Normalize](/reference/normalize) — Normalize entity names before deduplication.
|
||||
- [Conflicts](conflicts) — Detect value conflicts between non-duplicate entities.
|
||||
- [Knowledge Graph](kg) — GraphBuilder uses deduplication during construction.
|
||||
- [Normalize](normalize) — Normalize entity names before deduplication.
|
||||
- [Provenance](provenance) — Track merged entity lineage.
|
||||
|
||||
@@ -607,7 +607,9 @@ The Knowledge Explorer embeds Distance Intelligence directly in the browser dash
|
||||
The 10× cache improvement applies when the graph is unchanged between requests. In write-heavy pipelines where nodes are added continuously, cache hit rates will be lower. Use `force_refresh=False` (default) for read-heavy Explorer usage and `force_refresh=True` for batch pipeline contexts.
|
||||
</Note>
|
||||
|
||||
- [Context Module](/reference/context) — `ContextGraph.get_neighbors()` and proximity-blended retrieval.
|
||||
- [Knowledge Graph Module](/reference/kg) — `NodeEmbedder`, `SimilarityCalculator`, and graph analytics.
|
||||
- [Context Module](context) — `ContextGraph.get_neighbors()` and proximity-blended retrieval.
|
||||
- [Knowledge Graph Module](kg) — `NodeEmbedder`, `SimilarityCalculator`, and graph analytics.
|
||||
- [Visualization](visualization) — Programmatic distance heatmaps and ego-mode graph renders.
|
||||
- [Explorer](/reference/explorer) — Knowledge Explorer with built-in Distance Intelligence dashboard.
|
||||
- [Explorer](explorer) — Knowledge Explorer with built-in Distance Intelligence dashboard.
|
||||
|
||||
- [Distance Intelligence](https://github.com/semantica-agi/semantica/blob/main/cookbook/advanced/12_Distance_Intelligence.ipynb) — Semantic neighborhoods and distance matrices · Advanced
|
||||
|
||||
@@ -619,7 +619,7 @@ providers = check_available_providers()
|
||||
# → {"sentence_transformers": True, "fastembed": True, "openai": False}
|
||||
```
|
||||
|
||||
- [Vector Store](/reference/vector_store) — Store and search the generated embeddings.
|
||||
- [Split](/reference/split) — Chunk text before embedding for better retrieval quality.
|
||||
- [KG Module](/reference/kg) — Distance Intelligence uses graph embeddings for semantic neighbourhoods.
|
||||
- [Vector Store](vector_store) — Store and search the generated embeddings.
|
||||
- [Split](split) — Chunk text before embedding for better retrieval quality.
|
||||
- [KG Module](kg) — Distance Intelligence uses graph embeddings for semantic neighbourhoods.
|
||||
- [Deduplication](deduplication) — Semantic deduplication uses embedding distance for entity resolution.
|
||||
|
||||
+46
-206
@@ -1,224 +1,64 @@
|
||||
---
|
||||
title: "Evals Module"
|
||||
description: "Score decision records, audit trails, and reasoning output with deterministic and model-backed evaluators plus a small run harness."
|
||||
description: "Evaluation framework for measuring Knowledge Graph quality, extraction accuracy, and pipeline performance: coming soon."
|
||||
icon: "chart-line"
|
||||
---
|
||||
|
||||
`semantica.evals` measures the quality of decision intelligence outputs. It takes
|
||||
the decisions, audit trails, and reasoning text your pipeline produces and scores
|
||||
them against expectations you define, returning a structured summary you can log,
|
||||
assert on in tests, or track across runs.
|
||||
**`semantica.evals`** is planned as a comprehensive evaluation framework for measuring **extraction accuracy, graph quality, and pipeline performance**.
|
||||
|
||||
- A registry of named evaluators, from exact string matching to ROUGE overlap and
|
||||
LLM-as-judge
|
||||
- `decision_scores`, a composite evaluator for `Decision` objects that checks
|
||||
outcome, confidence bounds, required fields, provenance, and (optionally)
|
||||
policy compliance
|
||||
- A `evaluate()` runner that applies several evaluators to a list of cases and
|
||||
aggregates pass / fail / error counts
|
||||
- Per-evaluator **objectives** that let you override an evaluator's built-in
|
||||
verdict at the run level
|
||||
<Warning>
|
||||
**`semantica.evals` is not yet implemented.** The module is a placeholder with `__all__ = []`. No classes or functions are available for import. This page describes the planned API only.
|
||||
</Warning>
|
||||
|
||||
<Note>
|
||||
The module is versioned separately from the package: `semantica.evals.__version__`
|
||||
is `"0.1.0"`. The public surface described here is stable, but expect additive
|
||||
changes (new evaluators, new objective options) before it reaches 1.0.
|
||||
</Note>
|
||||
## Planned Features
|
||||
|
||||
## Public API
|
||||
When released, `semantica.evals` will provide:
|
||||
|
||||
| Name | Kind | Role |
|
||||
| :--- | :--- | :--- |
|
||||
| `evaluate(cases, evaluators, config=None, target_fn=None)` | function | Run named evaluators over each case, return an `EvalSummary` |
|
||||
| `list_evaluators()` | function | Sorted names of every registered evaluator |
|
||||
| `get_evaluator(name)` | function | Look up a single evaluator function by name |
|
||||
| `EvalMetric` | dataclass (frozen) | One evaluator's result: `score`, `passed`, `meta` |
|
||||
| `CaseResult` | namedtuple | One case's result: `case_id`, `status`, `metrics`, `details` |
|
||||
| `EvalSummary` | dataclass | Aggregate across cases: `total`, `passed`, `failed`, `errors`, `pass_rate`, `cases` |
|
||||
|
||||
```python
|
||||
import semantica.evals as evals
|
||||
from semantica.evals import evaluate, list_evaluators, get_evaluator
|
||||
```
|
||||
|
||||
## Built-in evaluators
|
||||
|
||||
Every evaluator is a plain function `fn(actual, expected, config=None) -> EvalMetric`
|
||||
registered under a stable name. `list_evaluators()` returns the current set:
|
||||
|
||||
```python
|
||||
>>> list_evaluators()
|
||||
['decision_scores', 'exact_match', 'keyword_check', 'length_range',
|
||||
'levenshtein', 'llm_as_judge', 'numeric_range', 'regex_match', 'rouge',
|
||||
'temporal_range']
|
||||
```
|
||||
|
||||
| Name | Passes when | Relevant `config` keys |
|
||||
| :--- | :--- | :--- |
|
||||
| `exact_match` | `actual == expected` | none |
|
||||
| `regex_match` | `re.search(expected, actual)` matches | none |
|
||||
| `keyword_check` | every required term appears in `actual` (word-boundary) | `required` (falls back to `expected`) |
|
||||
| `numeric_range` | `min <= actual <= max` | `min`, `max` (both required) |
|
||||
| `temporal_range` | ISO datetime `actual` falls in `[min, max]` | `min`, `max` as ISO strings (both required) |
|
||||
| `length_range` | `min <= len(actual) <= max` | `min` (default 0), `max` (required) |
|
||||
| `levenshtein` | normalized similarity `>= threshold` | `threshold` (default 0.8) |
|
||||
| `rouge` | ROUGE-1 F1 `> 0` and `>= threshold` | `threshold` (default 0.0) |
|
||||
| `llm_as_judge` | caller-supplied `judge_fn(actual, expected)` returns truthy | `judge_fn` (required callable) |
|
||||
| `decision_scores` | all configured sub-checks on a `Decision` pass | see below |
|
||||
|
||||
An evaluator that cannot run (bad regex, unparseable datetime, no `judge_fn`) returns an
|
||||
`EvalMetric` with an `"error"` key in `meta` rather than raising. Evaluators that
|
||||
require numeric bounds (`numeric_range`, `length_range`) instead return a failing
|
||||
metric with a `"reason"` key when the bound is missing — they do not raise and do
|
||||
not set `"error"`.
|
||||
|
||||
### `decision_scores`
|
||||
|
||||
`decision_scores` accepts a `Decision` (from `semantica.context.decision_models`)
|
||||
or its dict form and runs a set of field-level and governance checks. The score is
|
||||
the fraction of checks that passed; `passed` is `True` only when all of them did.
|
||||
|
||||
| Sub-check | Controlled by |
|
||||
| Planned Class | Role |
|
||||
| :--- | :--- |
|
||||
| Outcome matches | `expected_outcome` in config, or the case's `expected`; **skipped** when neither is set |
|
||||
| Confidence in range | `min_confidence` (default 0.0), `max_confidence` (default 1.0); always run |
|
||||
| `decision_maker`, `reasoning`, `scenario` non-empty | always run |
|
||||
| Provenance present in metadata | `provenance_key` (default `"provenance"`); always run |
|
||||
| Policy compliance | `policy_engine` and `policy_id` both set; skipped otherwise |
|
||||
| `KGEvaluator` | Completeness, consistency, schema compliance, coverage, and orphan node detection |
|
||||
| `ExtractionEvaluator` | NER precision / recall / F1 and relation extraction metrics against gold datasets |
|
||||
| `PipelineBenchmark` | Throughput (docs/sec), per-step latency, peak memory, and error rate |
|
||||
| `RegressionTracker` | Record runs and compare metrics across commits or config changes |
|
||||
| `EvalReport` | Structured report: `{scores, regressions, recommendations}` |
|
||||
| `DeduplicationEvaluator` | Merge precision, false positive / false negative rates |
|
||||
| `ReasoningEvaluator` | Inference accuracy, rule coverage, and derivation depth |
|
||||
|
||||
Passing `causal_chain_exists` in config raises `NotImplementedError`. That key is a
|
||||
reserved slot for a future release.
|
||||
## Current Workaround
|
||||
|
||||
## Running an evaluation
|
||||
|
||||
`evaluate()` takes a list of cases and a list of evaluator names. A case is either
|
||||
a `(expected, actual)` tuple or a dict:
|
||||
Until `semantica.evals` ships, use `semantica.ontology.OntologyEvaluator` for ontology quality metrics:
|
||||
|
||||
```python
|
||||
{
|
||||
"id": "loan-001", # optional, generated if absent
|
||||
"expected": ..., # optional; some evaluators read it, some don't
|
||||
"actual": ..., # the value under test
|
||||
"config": {...}, # optional, per-evaluator settings for this case
|
||||
"target_fn": callable, # optional, called with the case to produce `actual`
|
||||
}
|
||||
from semantica.ontology import OntologyEvaluator
|
||||
|
||||
evaluator = OntologyEvaluator()
|
||||
|
||||
# evaluate_ontology takes the ontology dict only
|
||||
result = evaluator.evaluate_ontology(ontology)
|
||||
|
||||
print("Coverage: ", result.coverage_score)
|
||||
print("Completeness:", result.completeness_score)
|
||||
print("Gaps: ", result.gaps)
|
||||
print("Suggestions: ", result.suggestions)
|
||||
|
||||
# Full report with class granularity and relation completeness
|
||||
report = evaluator.generate_report(ontology)
|
||||
print("Coverage score: ", report["evaluation"]["coverage_score"])
|
||||
print("Completeness score:", report["evaluation"]["completeness_score"])
|
||||
print("Relation coverage: ", report["relation_completeness"]["relation_coverage"])
|
||||
```
|
||||
|
||||
If `actual` is missing, the runner calls the case's `target_fn` (or the
|
||||
`target_fn` passed to `evaluate()`) to produce it. Per-case `config` is deep-merged
|
||||
over the top-level `config`, so a case can override one evaluator's settings
|
||||
without discarding the rest.
|
||||
`EvaluationResult` fields returned by `evaluate_ontology()`:
|
||||
|
||||
```python
|
||||
from datetime import datetime
|
||||
| Field | Type | Description |
|
||||
| :----- | :---- | :----------- |
|
||||
| `coverage_score` | `float` | Fraction of competency questions answerable by the ontology |
|
||||
| `completeness_score` | `float` | Average of class and property completeness scores |
|
||||
| `gaps` | `List[str]` | Identified gaps in coverage |
|
||||
| `suggestions` | `List[str]` | Improvement suggestions |
|
||||
| `metrics` | `dict` | Detailed sub-metrics |
|
||||
|
||||
from semantica.context.decision_models import Decision
|
||||
from semantica.evals import evaluate
|
||||
|
||||
decision = Decision(
|
||||
decision_id="d-1",
|
||||
category="loan",
|
||||
scenario="loan-request",
|
||||
reasoning="vetted against lending policy v3",
|
||||
outcome="approve",
|
||||
confidence=0.87,
|
||||
timestamp=datetime.now(),
|
||||
decision_maker="approver-a",
|
||||
metadata={"provenance": "workflow:loan/v3"},
|
||||
)
|
||||
|
||||
cases = [
|
||||
{
|
||||
"id": "loan-001",
|
||||
"actual": decision,
|
||||
"config": {
|
||||
"decision_scores": {
|
||||
"expected_outcome": "approve",
|
||||
"min_confidence": 0.7,
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
summary = evaluate(cases, ["decision_scores"])
|
||||
print(summary.pass_rate) # 1.0
|
||||
```
|
||||
|
||||
Evaluators run independently per case. If one raises, that case's `status` becomes
|
||||
`"error"` and the exception text is captured in the metric's `meta`; the rest of
|
||||
the run continues.
|
||||
|
||||
## Objectives
|
||||
|
||||
By default each evaluator decides its own pass / fail. An **objective** overrides
|
||||
that verdict at the run level, keyed by evaluator name under `config`:
|
||||
|
||||
```python
|
||||
# Raise levenshtein's bar from its default 0.8 to 0.9
|
||||
evaluate(
|
||||
[("apple", "aple")],
|
||||
evaluators=["levenshtein"],
|
||||
config={"levenshtein": {"objective": {"direction": "maximize", "threshold": 0.9}}},
|
||||
)
|
||||
|
||||
# Lower is better
|
||||
evaluate(
|
||||
[("night", "nacht")],
|
||||
evaluators=["levenshtein"],
|
||||
config={"levenshtein": {"objective": {"direction": "minimize", "threshold": 0.5}}},
|
||||
)
|
||||
|
||||
# Expect the metric NOT to match
|
||||
evaluate(
|
||||
[("ok", "ok")],
|
||||
evaluators=["exact_match"],
|
||||
config={"exact_match": {"objective": {"expect": False}}},
|
||||
)
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
- `maximize` with `threshold`: pass iff `score >= threshold`. `maximize` with no
|
||||
threshold is a no-op and the evaluator's own verdict stands.
|
||||
- `minimize` with `threshold`: pass iff `score <= threshold`. `minimize`
|
||||
**requires** a threshold; omitting it raises `ValueError`.
|
||||
- `expect` (`True` / `False`): pass iff `bool(score)` equals it. Cannot be combined
|
||||
with `direction` or `threshold`, and must be a real boolean.
|
||||
- A metric that already carries an `"error"` in its `meta` is unaffected by any
|
||||
objective.
|
||||
- Invalid objective config is validated for every case before any evaluator runs,
|
||||
so a bad objective fails the whole run up front rather than partway through.
|
||||
|
||||
## Reading the summary
|
||||
|
||||
```python
|
||||
summary = evaluate(cases, ["decision_scores"])
|
||||
|
||||
summary.total, summary.passed, summary.failed, summary.errors
|
||||
summary.pass_rate # passed / total, or 1.0 for an empty case list
|
||||
|
||||
for case in summary.cases:
|
||||
print(case.case_id, case.status) # status: "pass" | "fail" | "error"
|
||||
for name, metric in case.metrics.items():
|
||||
print(name, metric.score, metric.passed)
|
||||
print(metric.meta.get("reasons", {})) # per-sub-check failure reasons
|
||||
```
|
||||
|
||||
`EvalMetric` is frozen (`score: float`, `passed: bool`, `meta: dict`). `CaseResult`
|
||||
is a namedtuple, and `EvalSummary` is a plain dataclass, so all three are
|
||||
straightforward to serialize for logging or regression tracking.
|
||||
|
||||
## Notes
|
||||
|
||||
- `llm_as_judge` needs `config["judge_fn"]`, a callable
|
||||
`judge_fn(actual, expected) -> bool` you supply. No LLM backend is imported
|
||||
unless you pass one in.
|
||||
- `decision_scores` governance checks are opt-in: policy compliance is only
|
||||
evaluated when both `policy_engine` and `policy_id` are present.
|
||||
|
||||
## See also
|
||||
|
||||
- [Decision Intelligence](/guides/decision-intelligence) — producing the `Decision` records this module scores
|
||||
- [Reasoning](/reference/reasoning) — inference output that reasoning-text evaluators can measure
|
||||
- [Policy Engine](/guides/policy-engine) — the `policy_engine` used by `decision_scores`
|
||||
- [Ontology Evaluator](/reference/ontology) — separate tooling for ontology quality metrics
|
||||
- [Semantic Extract](semantic_extract) — Extraction module.
|
||||
- [Knowledge Graph](kg) — Graph quality assessment.
|
||||
- [Pipeline](pipeline) — Pipeline performance metrics.
|
||||
- [Ontology Evaluator](ontology) — Available now for ontology quality metrics.
|
||||
|
||||
@@ -403,7 +403,7 @@ Semantic neighborhood requires node embeddings stored in node properties (keys `
|
||||
**Session state lost after restart**
|
||||
Session state is in-memory only. Use `POST /api/export` to save a JSON snapshot before shutting down.
|
||||
|
||||
- [Context](/reference/context) — Build and save the ContextGraph that Explorer loads.
|
||||
- [Context](context) — Build and save the ContextGraph that Explorer loads.
|
||||
- [Ontology](ontology) — Programmatic ontology management and SHACL generation.
|
||||
- [Visualization](visualization) — Programmatic graph rendering without the Explorer server.
|
||||
- [Export](export) — Export to RDF, Parquet, and other formats without launching a server.
|
||||
|
||||
@@ -394,7 +394,7 @@ The `export_csv` convenience function delegates to `CSVExporter.export()`. For p
|
||||
**Match your export format to your consumer.** Neo4j → `cypher`; ArangoDB → `aql`; Gephi/yEd → `graphml` or `gexf`; semantic web tools → `turtle` or `json-ld`; analytics pipelines → `parquet`; zero-copy IPC → `arrow`.
|
||||
</Tip>
|
||||
|
||||
- [Triplet Store](/reference/triplet_store) — Store RDF exports in a SPARQL-queryable backend.
|
||||
- [Triplet Store](triplet_store) — Store RDF exports in a SPARQL-queryable backend.
|
||||
- [Ontology](ontology) — Export OWL ontologies.
|
||||
- [Provenance](provenance) — Include provenance metadata in RDF exports.
|
||||
- [Pipeline](pipeline) — Add export as a final pipeline step.
|
||||
|
||||
@@ -503,7 +503,7 @@ stats = store.get_stats()
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
- [KG Module](/reference/kg) — Build the graph before persisting it.
|
||||
- [Triplet Store](/reference/triplet_store) — RDF triple store for semantic web and SPARQL queries.
|
||||
- [KG Module](kg) — Build the graph before persisting it.
|
||||
- [Triplet Store](triplet_store) — RDF triple store for semantic web and SPARQL queries.
|
||||
- [Visualization](visualization) — Visualize graphs stored in any backend.
|
||||
- [Context](/reference/context) — AgentContext uses GraphStore for memory retrieval.
|
||||
- [Context](context) — AgentContext uses GraphStore for memory retrieval.
|
||||
|
||||
@@ -646,7 +646,7 @@ from semantica.ingest import ingest_file
|
||||
result = ingest_file("source_path", method="my_format")
|
||||
```
|
||||
|
||||
- [Parse](/reference/parse) — Parse raw sources into structured text and tables.
|
||||
- [Parse](parse) — Parse raw sources into structured text and tables.
|
||||
- [Pipeline](pipeline) — Orchestrate ingest as the first pipeline step.
|
||||
- [Snowflake Integration](../integrations/snowflake) — Snowflake-specific setup and authentication guide.
|
||||
- [Databricks Integration](../integrations/databricks) — Databricks Unity Catalog setup, authentication, and lineage guide.
|
||||
|
||||
@@ -75,10 +75,10 @@ kg = builder.build({"entities": entities, "relationships": relationships})
|
||||
## Temporal Knowledge Graphs (v0.4.0+)
|
||||
|
||||
<Info>
|
||||
Full temporal reference including `BiTemporalFact`, `TemporalReasoningEngine`, Allen interval algebra, and `TemporalNormalizer` is covered in the dedicated [Temporal Intelligence](/reference/temporal) page. This section documents the KG-layer temporal API.
|
||||
Full temporal reference including `BiTemporalFact`, `TemporalReasoningEngine`, Allen interval algebra, and `TemporalNormalizer` is covered in the dedicated [Temporal Intelligence](temporal) page. This section documents the KG-layer temporal API.
|
||||
</Info>
|
||||
|
||||
The temporal stack — see the [Temporal Intelligence](/reference/temporal) page for the full reference.
|
||||
The temporal stack — see the [Temporal Intelligence](temporal) page for the full reference.
|
||||
|
||||
### Building a Temporal Graph
|
||||
|
||||
@@ -264,7 +264,7 @@ versioner.verify_checksum(past_kg)
|
||||
```
|
||||
|
||||
<Tip>
|
||||
See the [Temporal Intelligence](/reference/temporal) reference for the full class API, domain examples (personnel changes, policy evolution, financial timelines), and configuration options.
|
||||
See the [Temporal Intelligence](temporal) reference for the full class API, domain examples (personnel changes, policy evolution, financial timelines), and configuration options.
|
||||
</Tip>
|
||||
|
||||
|
||||
@@ -475,10 +475,10 @@ kg:
|
||||
default_validity: infinite
|
||||
```
|
||||
|
||||
- [Graph Store](/reference/graph_store) — Persist graphs in Neo4j, FalkorDB, or Apache AGE.
|
||||
- [Semantic Extract](/reference/semantic_extract) — Source of entities and relationships fed to GraphBuilder.
|
||||
- [Graph Store](graph_store) — Persist graphs in Neo4j, FalkorDB, or Apache AGE.
|
||||
- [Semantic Extract](semantic_extract) — Source of entities and relationships fed to GraphBuilder.
|
||||
- [Visualization](visualization) — Visualize knowledge graphs interactively.
|
||||
- [Conflicts](/reference/conflicts) — Conflict detection and resolution.
|
||||
- [Conflicts](conflicts) — Conflict detection and resolution.
|
||||
|
||||
### Cookbooks
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ from semantica.llms import Groq, OpenAI, LiteLLM, HuggingFaceLLM
|
||||
from semantica.llms import LiteLLM
|
||||
|
||||
llm = LiteLLM(
|
||||
model="anthropic/claude-sonnet-5",
|
||||
model="anthropic/claude-sonnet-4-20250514",
|
||||
api_key=os.getenv("ANTHROPIC_API_KEY"),
|
||||
temperature=0.0,
|
||||
)
|
||||
@@ -198,7 +198,7 @@ llm = Groq(api_key=os.getenv("GROQ_API_KEY"), model="llama-3.1-8b-instant")
|
||||
# Method 3: Multiple providers via LiteLLM
|
||||
providers = {
|
||||
"fast": LiteLLM(model="groq/llama-3.1-8b-instant", api_key=os.getenv("GROQ_API_KEY")),
|
||||
"smart": LiteLLM(model="anthropic/claude-sonnet-5", api_key=os.getenv("ANTHROPIC_API_KEY"))
|
||||
"smart": LiteLLM(model="anthropic/claude-sonnet-4-20250514", api_key=os.getenv("ANTHROPIC_API_KEY"))
|
||||
}
|
||||
```
|
||||
|
||||
@@ -252,7 +252,7 @@ from semantica.llms import LiteLLM
|
||||
# pip install "semantica[llm-litellm]"
|
||||
|
||||
# Anthropic Claude
|
||||
llm = LiteLLM(model="anthropic/claude-opus-4-7", api_key=os.getenv("ANTHROPIC_API_KEY"))
|
||||
llm = LiteLLM(model="anthropic/claude-opus-4-5", api_key=os.getenv("ANTHROPIC_API_KEY"))
|
||||
|
||||
# Google Gemini
|
||||
llm = LiteLLM(model="gemini/gemini-1.5-pro", api_key=os.getenv("GOOGLE_API_KEY"))
|
||||
@@ -267,7 +267,7 @@ llm = LiteLLM(model="deepseek/deepseek-chat", api_key=os.getenv("DEEP
|
||||
llm = LiteLLM(model="azure/gpt-4o", api_key=os.getenv("AZURE_API_KEY"))
|
||||
|
||||
# AWS Bedrock
|
||||
llm = LiteLLM(model="bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0")
|
||||
llm = LiteLLM(model="bedrock/anthropic.claude-3-5-sonnet-20241022-v2:0")
|
||||
|
||||
# Novita AI
|
||||
llm = LiteLLM(model="novita/deepseek/deepseek-v3.2", api_key=os.getenv("NOVITA_API_KEY"))
|
||||
@@ -297,12 +297,12 @@ from semantica.llms import LiteLLM
|
||||
|
||||
# Pattern: LiteLLM(model="<provider>/<model-name>")
|
||||
providers = {
|
||||
"Anthropic": LiteLLM(model="anthropic/claude-opus-4-7", api_key=os.getenv("ANTHROPIC_API_KEY")),
|
||||
"Anthropic": LiteLLM(model="anthropic/claude-opus-4-5", api_key=os.getenv("ANTHROPIC_API_KEY")),
|
||||
"Gemini": LiteLLM(model="gemini/gemini-1.5-pro", api_key=os.getenv("GOOGLE_API_KEY")),
|
||||
"Ollama": LiteLLM(model="ollama/llama3.2:3b", api_base="http://localhost:11434"),
|
||||
"DeepSeek": LiteLLM(model="deepseek/deepseek-chat", api_key=os.getenv("DEEPSEEK_API_KEY")),
|
||||
"Azure": LiteLLM(model="azure/gpt-4o", api_key=os.getenv("AZURE_API_KEY")),
|
||||
"Bedrock": LiteLLM(model="bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0"),
|
||||
"Bedrock": LiteLLM(model="bedrock/anthropic.claude-3-5-sonnet-20241022-v2:0"),
|
||||
"Cohere": LiteLLM(model="cohere/command-r-plus", api_key=os.getenv("COHERE_API_KEY")),
|
||||
"Novita AI": LiteLLM(model="novita/deepseek/deepseek-v3.2", api_key=os.getenv("NOVITA_API_KEY")),
|
||||
}
|
||||
@@ -416,7 +416,7 @@ for text in texts:
|
||||
| :---------- | :--------------------------- | :----------- |
|
||||
| **Entity Extraction** | `Groq("llama-3.3-70b-versatile")` | Fast, good accuracy for structured tasks |
|
||||
| **Relation Extraction** | `OpenAI("gpt-4o")` | Best at complex relationship reasoning |
|
||||
| **Complex Analysis** | `LiteLLM("anthropic/claude-sonnet-5")` | Highest reasoning capability |
|
||||
| **Complex Analysis** | `LiteLLM("anthropic/claude-sonnet-4-20250514")` | Highest reasoning capability |
|
||||
| **High Volume/Cost** | `LiteLLM("deepseek/deepseek-chat")` | Lowest cost per token |
|
||||
|
||||
### Error Handling
|
||||
@@ -439,7 +439,7 @@ extractor = NERExtractor(
|
||||
)
|
||||
```
|
||||
|
||||
- [Semantic Extract](/reference/semantic_extract) — Use LLMs for NER and relation extraction.
|
||||
- [Semantic Extract](semantic_extract) — Use LLMs for NER and relation extraction.
|
||||
- [Agno Integration](../integrations/agno) — LLM providers in Agno multi-agent teams.
|
||||
- [Reasoning](reasoning) — LLM-backed deductive and abductive reasoning.
|
||||
- [Context](/reference/context) — GraphRAG uses LLMs for reasoning over knowledge graphs.
|
||||
- [Context](context) — GraphRAG uses LLMs for reasoning over knowledge graphs.
|
||||
|
||||
@@ -45,7 +45,7 @@ python -m semantica.mcp_server
|
||||
- **Zero Infrastructure** — Runs over stdio: no server, no port, no Docker required. One config block to activate in any MCP client.
|
||||
- **Persistent Graphs** — Point `SEMANTICA_KG_PATH` at a saved graph file to reload it automatically on every server startup.
|
||||
- **Decision Intelligence** — Record decisions, find precedents via hybrid similarity search, and trace causal chains across agent runs.
|
||||
- **REST Alternative** — The [Explorer](/reference/explorer) module offers a full HTTP API and browser dashboard if you prefer programmatic access.
|
||||
- **REST Alternative** — The [Explorer](explorer) module offers a full HTTP API and browser dashboard if you prefer programmatic access.
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -493,7 +493,7 @@ The MCP server exposes three readable resources:
|
||||
| `semantica://decisions/list` | All recorded decisions (up to 50) |
|
||||
| `semantica://schema/info` | Server version and available tools |
|
||||
|
||||
- [Context](/reference/context) — The ContextGraph that the MCP server operates on.
|
||||
- [Semantic Extract](/reference/semantic_extract) — NER and relation extraction powering the MCP tools.
|
||||
- [Context](context) — The ContextGraph that the MCP server operates on.
|
||||
- [Semantic Extract](semantic_extract) — NER and relation extraction powering the MCP tools.
|
||||
- [Reasoning](reasoning) — Forward-chaining engine behind run_reasoning.
|
||||
- [Agno Integration](../integrations/agno) — Use Semantica inside Agno multi-agent teams.
|
||||
|
||||
@@ -584,7 +584,7 @@ normalized = normalize_text("Apple Inc.", method="expand_suffixes")
|
||||
# → "Apple Incorporated"
|
||||
```
|
||||
|
||||
- [Parse](/reference/parse) — Parse documents before normalization.
|
||||
- [Split](/reference/split) — Chunk normalized text for embedding.
|
||||
- [Parse](parse) — Parse documents before normalization.
|
||||
- [Split](split) — Chunk normalized text for embedding.
|
||||
- [Deduplication](deduplication) — Resolve duplicate entities after normalization.
|
||||
- [Pipeline](pipeline) — Include normalization as a named pipeline step.
|
||||
|
||||
@@ -287,6 +287,6 @@ ontology_data = ingest_ontology("schema.jsonld") # JSON-LD
|
||||
</Note>
|
||||
|
||||
- [Reasoning](reasoning) — Apply inference rules over ontology axioms.
|
||||
- [Knowledge Graph](/reference/kg) — The graph being modeled by the ontology.
|
||||
- [Knowledge Graph](kg) — The graph being modeled by the ontology.
|
||||
- [Export](export) — Export ontologies as RDF, OWL, or JSON-LD.
|
||||
- [Conflicts](/reference/conflicts) — Detect ontology constraint violations.
|
||||
- [Conflicts](conflicts) — Detect ontology constraint violations.
|
||||
|
||||
@@ -298,6 +298,6 @@ for source in sources:
|
||||
</Note>
|
||||
|
||||
- [Ingest](ingest) — Load files before parsing.
|
||||
- [Split](/reference/split) — Chunk parsed text for embedding and extraction.
|
||||
- [Split](split) — Chunk parsed text for embedding and extraction.
|
||||
- [Docling Integration](../integrations/docling) — Full Docling integration setup guide.
|
||||
- [Semantic Extract](/reference/semantic_extract) — Extract entities and relations from parsed text.
|
||||
- [Semantic Extract](semantic_extract) — Extract entities and relations from parsed text.
|
||||
|
||||
@@ -497,7 +497,7 @@ result = engine.execute_pipeline(
|
||||
|
||||
## SPARQL CONSTRUCT Template Steps
|
||||
|
||||
Use the `"construct_template"` step type to render and execute a [SPARQL CONSTRUCT template](/reference/triplet_store#sparql-construct-templates) as part of a pipeline. `store_backend` and `construct_template_registry` are execution-time resources, not step config — pass them to `execute_pipeline()`, the same way `delta_mode` steps receive `version_manager` and `triplet_store`:
|
||||
Use the `"construct_template"` step type to render and execute a [SPARQL CONSTRUCT template](triplet_store#sparql-construct-templates) as part of a pipeline. `store_backend` and `construct_template_registry` are execution-time resources, not step config — pass them to `execute_pipeline()`, the same way `delta_mode` steps receive `version_manager` and `triplet_store`:
|
||||
|
||||
```python
|
||||
from semantica.pipeline import PipelineBuilder, ExecutionEngine
|
||||
@@ -589,6 +589,6 @@ StepStatus.SKIPPED # Skipped due to FailureHandler "skip" strategy
|
||||
</AccordionGroup>
|
||||
|
||||
- [Ingest](ingest) — First step in most pipelines.
|
||||
- [Semantic Extract](/reference/semantic_extract) — Core extraction step.
|
||||
- [Knowledge Graph](/reference/kg) — Graph construction step.
|
||||
- [Semantic Extract](semantic_extract) — Core extraction step.
|
||||
- [Knowledge Graph](kg) — Graph construction step.
|
||||
- [Export](export) — Final output step.
|
||||
|
||||
@@ -522,7 +522,7 @@ Provenance tracking in Semantica produces the following audit artifacts:
|
||||
`ProvenanceManager` does not include built-in Turtle or JSON-LD serialization. Use `entry.to_dict()` and `get_lineage()` to retrieve provenance data, then serialize with your preferred RDF library if W3C PROV-O RDF output is required.
|
||||
</Note>
|
||||
|
||||
- [Change Management](/reference/change_management) — Version control and snapshot audit trails.
|
||||
- [Change Management](change_management) — Version control and snapshot audit trails.
|
||||
- [Ingest](ingest) — Provenance begins at the ingestion stage.
|
||||
- [Export](export) — Include provenance metadata in RDF exports.
|
||||
- [Context](/reference/context) — Decision provenance via AgentContext.
|
||||
- [Context](context) — Decision provenance via AgentContext.
|
||||
|
||||
@@ -323,7 +323,7 @@ all_facts = datalog.derive_all()
|
||||
|
||||
# Query with variable pattern: variables start with uppercase or ?
|
||||
results = datalog.query("ancestor(alice, ?Z)")
|
||||
# → a list of binding dicts: [{"Z": "bob"}, {"Z": "charlie"}, {"Z": "dave"}] (order not guaranteed)
|
||||
# → [{"Z": "bob"}, {"Z": "charlie"}, {"Z": "dave"}]
|
||||
|
||||
# Clear and start over
|
||||
datalog.clear()
|
||||
@@ -482,7 +482,7 @@ step.confidence # float
|
||||
`GraphReasoner` requires a configured LLM provider. If the provider fails to initialize, `reason()` returns an error string instead of raising. Check `reasoner.provider is not None` before calling if you need to surface failures explicitly.
|
||||
</Warning>
|
||||
|
||||
- [Knowledge Graph](/reference/kg) — The knowledge graph being reasoned over.
|
||||
- [Knowledge Graph](kg) — The knowledge graph being reasoned over.
|
||||
- [Ontology](ontology) — Ontology axioms and SHACL constraints for logical reasoning.
|
||||
- [Triplet Store](/reference/triplet_store) — RDF backend for SPARQL-based reasoning.
|
||||
- [Context](/reference/context) — Reasoning integrated into agent decision intelligence.
|
||||
- [Triplet Store](triplet_store) — RDF backend for SPARQL-based reasoning.
|
||||
- [Context](context) — Reasoning integrated into agent decision intelligence.
|
||||
|
||||
@@ -322,6 +322,6 @@ export SEMANTICA_SEED_MERGE_STRATEGY=seed_first
|
||||
</Tip>
|
||||
|
||||
- [Ingest](ingest) — Load unstructured data alongside seed data.
|
||||
- [Knowledge Graph](/reference/kg) — The target graph that seed data populates.
|
||||
- [Knowledge Graph](kg) — The target graph that seed data populates.
|
||||
- [Deduplication](deduplication) — Handle duplicates during seed-extracted merge.
|
||||
- [Pipeline](pipeline) — Incorporate seed loading as a named pipeline step.
|
||||
|
||||
@@ -410,7 +410,7 @@ triplets = trip.extract(text)
|
||||
| `ml` | Fast | Free | High | Limited |
|
||||
| `llm` | Medium | API cost | Highest | Yes (schema) |
|
||||
|
||||
- [LLM Providers](/reference/llms) — Configure which LLM is used for extraction.
|
||||
- [Knowledge Graph](/reference/kg) — Build graphs from extracted entities and relationships.
|
||||
- [Parse Module](/reference/parse) — Parse documents before extraction.
|
||||
- [LLM Providers](llms) — Configure which LLM is used for extraction.
|
||||
- [Knowledge Graph](kg) — Build graphs from extracted entities and relationships.
|
||||
- [Parse Module](parse) — Parse documents before extraction.
|
||||
- [Deduplication](deduplication) — Resolve duplicate entities after extraction.
|
||||
|
||||
@@ -373,7 +373,7 @@ for chunk in chunks:
|
||||
|
||||
For the full pipeline orchestration API, see the [Pipeline reference](pipeline).
|
||||
|
||||
- [Parse](/reference/parse) — Parse documents before chunking: produces sections and metadata.
|
||||
- [Embeddings](/reference/embeddings) — Embed chunks for vector search and semantic chunking.
|
||||
- [Semantic Extract](/reference/semantic_extract) — Extract entities and relations from individual chunks.
|
||||
- [Parse](parse) — Parse documents before chunking: produces sections and metadata.
|
||||
- [Embeddings](embeddings) — Embed chunks for vector search and semantic chunking.
|
||||
- [Semantic Extract](semantic_extract) — Extract entities and relations from individual chunks.
|
||||
- [Pipeline](pipeline) — Integrate splitting as a named pipeline step.
|
||||
|
||||
@@ -874,8 +874,8 @@ kg:
|
||||
engine: allen # allen | point_in_time_only
|
||||
```
|
||||
|
||||
- [Knowledge Graph Module](/reference/kg) — Core graph construction, `GraphBuilder`, analytics.
|
||||
- [Context Module](/reference/context) — Decision temporal windows and `find_active_nodes()`.
|
||||
- [Knowledge Graph Module](kg) — Core graph construction, `GraphBuilder`, analytics.
|
||||
- [Context Module](context) — Decision temporal windows and `find_active_nodes()`.
|
||||
- [Provenance](provenance) — W3C PROV-O lineage stamped alongside temporal metadata.
|
||||
- [Export](export) — OWL, Turtle, JSON-LD, and Parquet export with temporal annotations.
|
||||
|
||||
|
||||
@@ -564,4 +564,4 @@ for row in result.bindings:
|
||||
- [Export](export) — Export knowledge graphs to RDF formats.
|
||||
- [Ontology](ontology) — Load OWL ontologies and store as RDF triples.
|
||||
- [Reasoning](reasoning) — SPARQL-based property chain inference.
|
||||
- [Graph Store](/reference/graph_store) — Property graph alternative for Cypher queries.
|
||||
- [Graph Store](graph_store) — Property graph alternative for Cypher queries.
|
||||
|
||||
@@ -222,5 +222,5 @@ from semantica.utils import read_json_file
|
||||
config = read_json_file("config.json")
|
||||
```
|
||||
|
||||
- [Core](/reference/core) — Framework orchestration that uses Utils internally.
|
||||
- [Core](core) — Framework orchestration that uses Utils internally.
|
||||
- [Pipeline](pipeline) — Uses ProgressTracker for per-step tracking.
|
||||
|
||||
@@ -588,7 +588,7 @@ store.create_index(index_type="pq", metric="L2", m=8)
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
- [Embeddings](/reference/embeddings) — Generate the vectors stored here.
|
||||
- [Context](/reference/context) — AgentContext uses VectorStore for memory retrieval.
|
||||
- [Split](/reference/split) — Chunk documents before embedding and storing.
|
||||
- [Embeddings](embeddings) — Generate the vectors stored here.
|
||||
- [Context](context) — AgentContext uses VectorStore for memory retrieval.
|
||||
- [Split](split) — Chunk documents before embedding and storing.
|
||||
- [Ingest](ingest) — Ingest documents before embedding and storing.
|
||||
|
||||
@@ -288,9 +288,9 @@ For a full browser-based UI with search, path finding, and the Ontology Hub, lau
|
||||
semantica-explorer --graph my_graph.json
|
||||
```
|
||||
|
||||
See the [Explorer reference](/reference/explorer) for the full feature set and REST API.
|
||||
See the [Explorer reference](explorer) for the full feature set and REST API.
|
||||
|
||||
- [Knowledge Graph](/reference/kg) — The graph being visualized.
|
||||
- [Knowledge Graph](kg) — The graph being visualized.
|
||||
- [Ontology](ontology) — Visualize ontology class structure.
|
||||
- [Embeddings](/reference/embeddings) — Generate the embeddings visualized here.
|
||||
- [Explorer](/reference/explorer) — Full interactive Knowledge Explorer UI.
|
||||
- [Embeddings](embeddings) — Generate the embeddings visualized here.
|
||||
- [Explorer](explorer) — Full interactive Knowledge Explorer UI.
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview",
|
||||
"test:graph-store": "node --test tests/graphStore.multi-edge.test.mjs",
|
||||
"test:graph-workspace": "node --import tsx --test tests/markdownContentViewer.test.ts tests/graphSceneState.display.test.ts tests/temporalLifecycle.test.ts tests/deterministicExplorerRendering.test.ts tests/smallGraphLayout.test.ts tests/realtimeGraphAttributes.test.ts tests/ontologyEditorModel.test.ts",
|
||||
"test:graph-workspace": "node --import tsx --test tests/markdownContentViewer.test.ts tests/graphSceneState.display.test.ts tests/temporalLifecycle.test.ts tests/deterministicExplorerRendering.test.ts tests/smallGraphLayout.test.ts tests/realtimeGraphAttributes.test.ts",
|
||||
"test:deterministic-e2e": "node --import tsx --test tests/deterministicExplorerRendering.e2e.ts",
|
||||
"test:plugin-registry": "node --import tsx --test tests/pluginRegistry.temporal.test.mjs"
|
||||
},
|
||||
|
||||
+1
-13
@@ -93,18 +93,6 @@ const navItems: NavItem[] = [
|
||||
{ id: 'ontology-hub', label: 'Ontology Hub', hint: 'Schema governance, registry, and vocabulary management', icon: GitMerge },
|
||||
];
|
||||
|
||||
function readInitialWorkspace(): WorkspaceId {
|
||||
try {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
if (params.has("ontologyTab") || params.has("ontologyEntity")) {
|
||||
return "ontology-hub";
|
||||
}
|
||||
} catch {
|
||||
// Default to the welcome screen when URL state is unavailable.
|
||||
}
|
||||
return "welcome";
|
||||
}
|
||||
|
||||
const shellStyles = `
|
||||
:root {
|
||||
--app-bg: #07111f;
|
||||
@@ -1785,7 +1773,7 @@ function WelcomeScreen({
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
const [activeWorkspace, setActiveWorkspace] = useState<WorkspaceId>(readInitialWorkspace);
|
||||
const [activeWorkspace, setActiveWorkspace] = useState<WorkspaceId>('welcome');
|
||||
const [exploreView, setExploreView] = useState<ExploreView>('graph');
|
||||
const [analyzeView, setAnalyzeView] = useState<AnalyzeView>('reasoning');
|
||||
const [enrichView, setEnrichView] = useState<EnrichView>('import');
|
||||
|
||||
@@ -8,10 +8,8 @@ import {
|
||||
useNodesState,
|
||||
useEdgesState,
|
||||
MarkerType,
|
||||
Handle,
|
||||
Position,
|
||||
} from "@xyflow/react";
|
||||
import type { Connection, Edge, Node, ReactFlowInstance } from "@xyflow/react";
|
||||
import type { Connection, Edge, Node } from "@xyflow/react";
|
||||
import "@xyflow/react/dist/style.css";
|
||||
import {
|
||||
Plus,
|
||||
@@ -24,20 +22,10 @@ import {
|
||||
Pencil,
|
||||
Trash2,
|
||||
} from "lucide-react";
|
||||
import { loadOntologyEntityOwner, loadOntologyGraph } from "./api";
|
||||
import type { OntologyGraphEdge, OntologyGraphNode } from "./api";
|
||||
import {
|
||||
classifyNodeType,
|
||||
inferOntologyUri,
|
||||
isEditableEntityType,
|
||||
ONTOLOGY_MINIMAP_THEME,
|
||||
} from "./ontologyEditorModel";
|
||||
import type { EditorEntityType, RegistryEntry } from "./ontologyEditorModel";
|
||||
|
||||
type OntologyNodeData = {
|
||||
label?: string;
|
||||
type?: string;
|
||||
entityType?: EditorEntityType;
|
||||
};
|
||||
|
||||
type OntologyNode = Node<OntologyNodeData>;
|
||||
@@ -46,57 +34,12 @@ type OntologyEdge = Edge<Record<string, unknown>>;
|
||||
const nodeTypes = {
|
||||
classNode: ({ data }: { data: OntologyNodeData }) => (
|
||||
<div style={classNodeStyle}>
|
||||
<Handle type="target" position={Position.Left} style={handleStyle} />
|
||||
<div style={classNodeHeader}>{data.label}</div>
|
||||
<div style={classNodeSub}>{data.type}</div>
|
||||
<Handle type="source" position={Position.Right} style={handleStyle} />
|
||||
</div>
|
||||
),
|
||||
};
|
||||
|
||||
const handleStyle: React.CSSProperties = {
|
||||
width: 8,
|
||||
height: 8,
|
||||
border: "1px solid rgba(235, 243, 255, 0.8)",
|
||||
background: "#4aa3ff",
|
||||
};
|
||||
|
||||
const ontologyFlowThemeCss = `
|
||||
.ontology-editor-flow .react-flow__controls {
|
||||
overflow: hidden;
|
||||
border: 1px solid rgba(127, 208, 255, 0.2);
|
||||
border-radius: 9px;
|
||||
background: rgba(6, 13, 26, 0.96);
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.38);
|
||||
}
|
||||
|
||||
.ontology-editor-flow .react-flow__controls-button {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
background: transparent;
|
||||
border-bottom-color: rgba(127, 208, 255, 0.14);
|
||||
color: #8fa8c6;
|
||||
transition: color 140ms ease, background 140ms ease;
|
||||
}
|
||||
|
||||
.ontology-editor-flow .react-flow__controls-button:hover {
|
||||
background: rgba(74, 163, 255, 0.14);
|
||||
color: #ebf3ff;
|
||||
}
|
||||
|
||||
.ontology-editor-flow .react-flow__controls-button:focus-visible {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
outline: 2px solid #7fd0ff;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
.ontology-editor-flow .react-flow__controls-button:disabled {
|
||||
background: rgba(3, 9, 18, 0.32);
|
||||
color: #40566f;
|
||||
}
|
||||
`;
|
||||
|
||||
const classNodeStyle: React.CSSProperties = {
|
||||
padding: "12px 16px",
|
||||
borderRadius: "8px",
|
||||
@@ -136,95 +79,17 @@ interface DraftDiff {
|
||||
annotation_changes: Record<string, Record<string, any>>;
|
||||
}
|
||||
|
||||
function requestedEntityUri(): string {
|
||||
try {
|
||||
return new URLSearchParams(window.location.search).get("ontologyEntity") || "";
|
||||
} catch {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
function nodeLabel(node: OntologyGraphNode): string {
|
||||
const explicit = String(node.content || node.properties?.["rdfs:label"] || "").trim();
|
||||
if (explicit && explicit !== node.id) {
|
||||
return explicit;
|
||||
}
|
||||
const trimmed = node.id.replace(/[/#]+$/, "");
|
||||
return trimmed.split("#").pop() || trimmed.split("/").pop() || node.id;
|
||||
}
|
||||
|
||||
function classifyEditorNode(node: OntologyGraphNode): OntologyNodeData["entityType"] {
|
||||
return classifyNodeType(node.type);
|
||||
}
|
||||
|
||||
function layoutEditorNodes(inputNodes: OntologyNode[]): OntologyNode[] {
|
||||
const properties = inputNodes.filter((node) => node.data.entityType === "property");
|
||||
const targets = inputNodes.filter((node) => (
|
||||
node.data.entityType === "class" || node.data.entityType === "external"
|
||||
));
|
||||
const context = inputNodes.filter((node) => (
|
||||
node.data.entityType !== "property"
|
||||
&& node.data.entityType !== "class"
|
||||
&& node.data.entityType !== "external"
|
||||
));
|
||||
const height = Math.max(360, Math.max(properties.length, targets.length) * 180);
|
||||
const positions = new Map<string, { x: number; y: number }>();
|
||||
|
||||
properties.forEach((node, index) => {
|
||||
positions.set(node.id, { x: 0, y: ((index + 1) * height) / (properties.length + 1) });
|
||||
});
|
||||
targets.forEach((node, index) => {
|
||||
positions.set(node.id, { x: 600, y: ((index + 1) * height) / (targets.length + 1) });
|
||||
});
|
||||
context.forEach((node, index) => {
|
||||
positions.set(node.id, { x: 300 + index * 220, y: height + 120 });
|
||||
});
|
||||
|
||||
return inputNodes.map((node) => ({
|
||||
...node,
|
||||
position: positions.get(node.id) || node.position,
|
||||
}));
|
||||
}
|
||||
|
||||
function buildEditorElements(apiNodes: OntologyGraphNode[], apiEdges: OntologyGraphEdge[]) {
|
||||
const sortedNodes = [...apiNodes].sort((left, right) => {
|
||||
const typeDelta = left.type.localeCompare(right.type);
|
||||
return typeDelta || left.id.localeCompare(right.id);
|
||||
});
|
||||
const nodes = layoutEditorNodes(sortedNodes.map((node) => ({
|
||||
id: node.id,
|
||||
type: "classNode",
|
||||
position: { x: 0, y: 0 },
|
||||
data: {
|
||||
label: nodeLabel(node),
|
||||
type: node.type,
|
||||
entityType: classifyEditorNode(node),
|
||||
},
|
||||
})));
|
||||
const edges: OntologyEdge[] = apiEdges.map((edge, index) => ({
|
||||
id: edge.id || `${edge.source}:${edge.type}:${edge.target}:${index}`,
|
||||
source: edge.source,
|
||||
target: edge.target,
|
||||
label: edge.type,
|
||||
type: "default",
|
||||
markerEnd: { type: MarkerType.ArrowClosed },
|
||||
style: { stroke: "rgba(127, 208, 255, 0.72)", strokeWidth: 1.5 },
|
||||
labelStyle: { fill: "#c8dcf5", fontSize: 11, fontWeight: 600 },
|
||||
labelBgStyle: { fill: "#07111f", fillOpacity: 0.9 },
|
||||
}));
|
||||
return { nodes, edges };
|
||||
interface RegistryEntry {
|
||||
uri: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export function OntologyEditor() {
|
||||
const [nodes, setNodes, onNodesChange] = useNodesState<OntologyNode>([]);
|
||||
const [edges, setEdges, onEdgesChange] = useEdgesState<OntologyEdge>([]);
|
||||
const [selectedElement, setSelectedElement] = useState<OntologyNode | OntologyEdge | null>(null);
|
||||
const hasDetailPanel = selectedElement !== null;
|
||||
const [registry, setRegistry] = useState<RegistryEntry[]>([]);
|
||||
const [ontologyUri, setOntologyUri] = useState<string>("");
|
||||
const [flowInstance, setFlowInstance] = useState<ReactFlowInstance<OntologyNode, OntologyEdge> | null>(null);
|
||||
const [isLoadingGraph, setIsLoadingGraph] = useState(false);
|
||||
const [graphError, setGraphError] = useState("");
|
||||
const [draftDiff, setDraftDiff] = useState<DraftDiff>({
|
||||
added_classes: [],
|
||||
removed_classes: [],
|
||||
@@ -243,18 +108,12 @@ export function OntologyEditor() {
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
const requested = requestedEntityUri();
|
||||
Promise.all([
|
||||
fetch("/api/ontology/registry").then((response) => (response.ok ? response.json() : [])),
|
||||
requested
|
||||
? loadOntologyEntityOwner(requested).catch(() => undefined)
|
||||
: Promise.resolve(undefined),
|
||||
])
|
||||
.then(([entries, explicitOwner]: [RegistryEntry[], string | undefined]) => {
|
||||
fetch("/api/ontology/registry")
|
||||
.then((response) => (response.ok ? response.json() : []))
|
||||
.then((entries: RegistryEntry[]) => {
|
||||
if (cancelled) return;
|
||||
setRegistry(entries);
|
||||
const inferredOntology = inferOntologyUri(entries, requested, explicitOwner);
|
||||
setOntologyUri((current) => current || inferredOntology || entries[0]?.uri || "");
|
||||
setOntologyUri((current) => current || entries[0]?.uri || "");
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Failed to load ontology registry:", error);
|
||||
@@ -264,47 +123,6 @@ export function OntologyEditor() {
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!ontologyUri) {
|
||||
setNodes([]);
|
||||
setEdges([]);
|
||||
setSelectedElement(null);
|
||||
return;
|
||||
}
|
||||
|
||||
const controller = new AbortController();
|
||||
setIsLoadingGraph(true);
|
||||
setGraphError("");
|
||||
loadOntologyGraph(ontologyUri, controller.signal)
|
||||
.then((payload) => {
|
||||
const elements = buildEditorElements(payload.nodes, payload.edges);
|
||||
setNodes(elements.nodes);
|
||||
setEdges(elements.edges);
|
||||
const requested = requestedEntityUri();
|
||||
setSelectedElement(elements.nodes.find((node) => node.id === requested) || null);
|
||||
})
|
||||
.catch((error) => {
|
||||
if (controller.signal.aborted) return;
|
||||
setNodes([]);
|
||||
setEdges([]);
|
||||
setSelectedElement(null);
|
||||
setGraphError(error instanceof Error ? error.message : "Failed to load ontology graph");
|
||||
})
|
||||
.finally(() => {
|
||||
if (!controller.signal.aborted) setIsLoadingGraph(false);
|
||||
});
|
||||
|
||||
return () => controller.abort();
|
||||
}, [ontologyUri, setEdges, setNodes]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!flowInstance || nodes.length === 0) return;
|
||||
const frame = window.requestAnimationFrame(() => {
|
||||
void flowInstance.fitView({ padding: 0.22, duration: 320, maxZoom: 1.25 });
|
||||
});
|
||||
return () => window.cancelAnimationFrame(frame);
|
||||
}, [flowInstance, hasDetailPanel, nodes.length, ontologyUri]);
|
||||
|
||||
const onConnect = useCallback(
|
||||
(params: Connection) => setEdges((eds) => addEdge({ ...params, markerEnd: { type: MarkerType.ArrowClosed } }, eds)),
|
||||
[setEdges]
|
||||
@@ -316,7 +134,7 @@ export function OntologyEditor() {
|
||||
id: newId,
|
||||
type: "classNode",
|
||||
position: { x: Math.random() * 400, y: Math.random() * 300 },
|
||||
data: { label: "NewClass", type: "owl:Class", entityType: "class" },
|
||||
data: { label: "NewClass", type: "owl:Class" },
|
||||
};
|
||||
setNodes((nds) => [...nds, newNode]);
|
||||
setDraftDiff((prev) => ({
|
||||
@@ -352,7 +170,7 @@ export function OntologyEditor() {
|
||||
id: newId,
|
||||
type: "classNode",
|
||||
position: { x: Math.random() * 400, y: Math.random() * 300 },
|
||||
data: { label: "NewIndividual", type: "owl:NamedIndividual", entityType: "external" },
|
||||
data: { label: "NewIndividual", type: "owl:NamedIndividual" },
|
||||
};
|
||||
setNodes((nds) => [...nds, newNode]);
|
||||
}, [setNodes]);
|
||||
@@ -372,21 +190,13 @@ export function OntologyEditor() {
|
||||
}, []);
|
||||
|
||||
const autoLayout = useCallback(() => {
|
||||
setNodes(layoutEditorNodes(nodes));
|
||||
const layoutNodes = nodes.map((node, index) => ({
|
||||
...node,
|
||||
position: { x: (index % 4) * 200, y: Math.floor(index / 4) * 150 },
|
||||
}));
|
||||
setNodes(layoutNodes);
|
||||
}, [nodes, setNodes]);
|
||||
|
||||
const selectNode = useCallback((node: OntologyNode) => {
|
||||
setSelectedElement(node);
|
||||
try {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
params.set("ontologyTab", "editor");
|
||||
params.set("ontologyEntity", node.id);
|
||||
window.history.replaceState(null, "", `?${params.toString()}`);
|
||||
} catch {
|
||||
// URL state is optional; the editor selection still works without it.
|
||||
}
|
||||
}, []);
|
||||
|
||||
const saveDraft = useCallback(async () => {
|
||||
if (!ontologyUri) {
|
||||
alert("Please select an ontology first");
|
||||
@@ -437,11 +247,12 @@ export function OntologyEditor() {
|
||||
...prev,
|
||||
removed_properties: [...prev.removed_properties, target.id],
|
||||
}));
|
||||
} else if (isEditableEntityType(target.data.entityType)) {
|
||||
} else {
|
||||
setNodes((nds) => nds.filter((n) => n.id !== target.id));
|
||||
setDraftDiff((prev) => target.data.entityType === "property"
|
||||
? { ...prev, removed_properties: [...prev.removed_properties, target.id] }
|
||||
: { ...prev, removed_classes: [...prev.removed_classes, target.id] });
|
||||
setDraftDiff((prev) => ({
|
||||
...prev,
|
||||
removed_classes: [...prev.removed_classes, target.id],
|
||||
}));
|
||||
}
|
||||
setSelectedElement(null);
|
||||
}
|
||||
@@ -450,21 +261,16 @@ export function OntologyEditor() {
|
||||
|
||||
const renameSelected = useCallback(() => {
|
||||
const target = showContext?.element ?? selectedElement;
|
||||
if (target && !("source" in target) && isEditableEntityType(target.data.entityType)) {
|
||||
if (target && !("source" in target)) {
|
||||
const newLabel = prompt("Enter new name:", String(target.data.label ?? ""));
|
||||
if (newLabel) {
|
||||
setNodes((nds) =>
|
||||
nds.map((n) => (n.id === target.id ? { ...n, data: { ...n.data, label: newLabel } } : n))
|
||||
);
|
||||
setDraftDiff((prev) => target.data.entityType === "property"
|
||||
? {
|
||||
...prev,
|
||||
modified_properties: { ...prev.modified_properties, [target.id]: { label: newLabel } },
|
||||
}
|
||||
: {
|
||||
...prev,
|
||||
modified_classes: { ...prev.modified_classes, [target.id]: { label: newLabel } },
|
||||
});
|
||||
setDraftDiff((prev) => ({
|
||||
...prev,
|
||||
modified_classes: { ...prev.modified_classes, [target.id]: { label: newLabel } },
|
||||
}));
|
||||
}
|
||||
}
|
||||
setShowContext(null);
|
||||
@@ -533,10 +339,11 @@ export function OntologyEditor() {
|
||||
};
|
||||
|
||||
const detailPanelStyle: React.CSSProperties = {
|
||||
flex: "0 0 320px",
|
||||
position: "absolute",
|
||||
right: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
width: "320px",
|
||||
minWidth: "320px",
|
||||
boxSizing: "border-box",
|
||||
background: "rgba(9, 19, 34, 0.95)",
|
||||
borderLeft: "1px solid rgba(140, 192, 255, 0.12)",
|
||||
padding: "20px",
|
||||
@@ -546,24 +353,11 @@ export function OntologyEditor() {
|
||||
|
||||
return (
|
||||
<div style={{ display: "flex", flexDirection: "column", height: "100%", background: "#07111f" }}>
|
||||
<style>{ontologyFlowThemeCss}</style>
|
||||
<div style={toolbarStyle}>
|
||||
<select
|
||||
aria-label="Active ontology"
|
||||
value={ontologyUri}
|
||||
onChange={(event) => {
|
||||
setOntologyUri(event.target.value);
|
||||
setSelectedElement(null);
|
||||
try {
|
||||
// Drop the previous ontology's entity from the URL, or a reload
|
||||
// would resolve the stale ID and jump back to that ontology.
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
params.delete("ontologyEntity");
|
||||
window.history.replaceState(null, "", `?${params.toString()}`);
|
||||
} catch {
|
||||
// URL state is optional; switching ontologies still works.
|
||||
}
|
||||
}}
|
||||
onChange={(event) => setOntologyUri(event.target.value)}
|
||||
style={selectStyle}
|
||||
>
|
||||
<option value="">Select ontology...</option>
|
||||
@@ -604,75 +398,43 @@ export function OntologyEditor() {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div style={{ display: "flex", flex: 1, minHeight: 0, minWidth: 0 }}>
|
||||
<div style={{ flex: 1, minHeight: 0, minWidth: 0, position: "relative" }}>
|
||||
<ReactFlow
|
||||
className="ontology-editor-flow"
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
onNodesChange={onNodesChange}
|
||||
onEdgesChange={onEdgesChange}
|
||||
onConnect={onConnect}
|
||||
onInit={setFlowInstance}
|
||||
onNodeClick={(_, node) => selectNode(node)}
|
||||
onEdgeClick={(_, edge) => setSelectedElement(edge)}
|
||||
onNodeContextMenu={handleNodeContextMenu}
|
||||
onEdgeContextMenu={handleEdgeContextMenu}
|
||||
nodeTypes={nodeTypes}
|
||||
fitView
|
||||
style={{ background: "#07111f" }}
|
||||
>
|
||||
<Background color="#1a2d3d" gap={20} />
|
||||
<Controls />
|
||||
<MiniMap {...ONTOLOGY_MINIMAP_THEME} />
|
||||
</ReactFlow>
|
||||
<div style={{ flex: 1, position: "relative" }}>
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
onNodesChange={onNodesChange}
|
||||
onEdgesChange={onEdgesChange}
|
||||
onConnect={onConnect}
|
||||
onNodeClick={(_, node) => setSelectedElement(node)}
|
||||
onEdgeClick={(_, edge) => setSelectedElement(edge)}
|
||||
onNodeContextMenu={handleNodeContextMenu}
|
||||
onEdgeContextMenu={handleEdgeContextMenu}
|
||||
nodeTypes={nodeTypes}
|
||||
fitView
|
||||
style={{ background: "#07111f" }}
|
||||
>
|
||||
<Background color="#1a2d3d" gap={20} />
|
||||
<Controls />
|
||||
<MiniMap nodeColor="#4aa3ff" maskColor="rgba(0,0,0,0.6)" />
|
||||
</ReactFlow>
|
||||
|
||||
{isLoadingGraph && (
|
||||
<div style={canvasMessageStyle}>Loading ontology structure…</div>
|
||||
)}
|
||||
{!isLoadingGraph && graphError && (
|
||||
<div style={{ ...canvasMessageStyle, color: "#ff9a8d" }}>{graphError}</div>
|
||||
)}
|
||||
{!isLoadingGraph && !graphError && ontologyUri && nodes.length === 0 && (
|
||||
<div style={canvasMessageStyle}>This ontology has no editable classes or properties.</div>
|
||||
)}
|
||||
|
||||
{showContext && (
|
||||
<div style={{ ...contextMenuStyle, left: showContext.x, top: showContext.y }}>
|
||||
{"source" in showContext.element || isEditableEntityType(showContext.element.data.entityType) ? (
|
||||
<>
|
||||
{!("source" in showContext.element) && (
|
||||
<div style={contextItemStyle} onClick={renameSelected}>
|
||||
<Pencil size={14} />
|
||||
Rename
|
||||
</div>
|
||||
)}
|
||||
<div style={contextItemStyle} onClick={deleteSelected}>
|
||||
<Trash2 size={14} />
|
||||
Delete
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div style={{ ...contextItemStyle, cursor: "default", color: "#8fa8c6" }}>
|
||||
This term is read-only
|
||||
</div>
|
||||
)}
|
||||
{showContext && (
|
||||
<div style={{ ...contextMenuStyle, left: showContext.x, top: showContext.y }}>
|
||||
<div style={contextItemStyle} onClick={renameSelected}>
|
||||
<Pencil size={14} />
|
||||
Rename
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div style={contextItemStyle} onClick={deleteSelected}>
|
||||
<Trash2 size={14} />
|
||||
Delete
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{selectedElement && (
|
||||
<div style={detailPanelStyle}>
|
||||
<h3 style={{ margin: "0 0 16px", color: "#ebf3ff", fontSize: "16px" }}>
|
||||
{"source" in selectedElement
|
||||
? "Relationship Details"
|
||||
: selectedElement.data.entityType === "property"
|
||||
? "Property Details"
|
||||
: selectedElement.data.entityType === "ontology"
|
||||
? "Ontology Details"
|
||||
: selectedElement.data.entityType === "external"
|
||||
? "External Term Details"
|
||||
: "Class Details"}
|
||||
{"source" in selectedElement ? "Property Details" : "Class Details"}
|
||||
</h3>
|
||||
<div style={{ marginBottom: "12px" }}>
|
||||
<label style={{ display: "block", color: "#8fa8c6", fontSize: "12px", marginBottom: "4px" }}>
|
||||
@@ -691,9 +453,7 @@ export function OntologyEditor() {
|
||||
<input
|
||||
type="text"
|
||||
value={String(selectedElement.data.label ?? "")}
|
||||
readOnly={!isEditableEntityType(selectedElement.data.entityType)}
|
||||
onChange={(e) => {
|
||||
if (!isEditableEntityType(selectedElement.data.entityType)) return;
|
||||
setNodes((nds) =>
|
||||
nds.map((n) =>
|
||||
n.id === selectedElement.id
|
||||
@@ -703,19 +463,10 @@ export function OntologyEditor() {
|
||||
);
|
||||
setDraftDiff((prev) => ({
|
||||
...prev,
|
||||
...(selectedElement.data.entityType === "property"
|
||||
? {
|
||||
modified_properties: {
|
||||
...prev.modified_properties,
|
||||
[selectedElement.id]: { label: e.target.value },
|
||||
},
|
||||
}
|
||||
: {
|
||||
modified_classes: {
|
||||
...prev.modified_classes,
|
||||
[selectedElement.id]: { label: e.target.value },
|
||||
},
|
||||
}),
|
||||
modified_classes: {
|
||||
...prev.modified_classes,
|
||||
[selectedElement.id]: { label: e.target.value },
|
||||
},
|
||||
}));
|
||||
}}
|
||||
style={{
|
||||
@@ -745,17 +496,3 @@ export function OntologyEditor() {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const canvasMessageStyle: React.CSSProperties = {
|
||||
position: "absolute",
|
||||
left: "50%",
|
||||
top: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
padding: "10px 14px",
|
||||
borderRadius: "8px",
|
||||
border: "1px solid rgba(127, 208, 255, 0.18)",
|
||||
background: "rgba(3, 9, 18, 0.9)",
|
||||
color: "#8fa8c6",
|
||||
fontSize: "13px",
|
||||
pointerEvents: "none",
|
||||
};
|
||||
|
||||
@@ -9,32 +9,6 @@ import type {
|
||||
ShaclValidationResponse,
|
||||
} from "./types";
|
||||
|
||||
export type OntologyGraphNode = {
|
||||
id: string;
|
||||
type: string;
|
||||
content?: string;
|
||||
properties?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type OntologyGraphEdge = {
|
||||
id?: string;
|
||||
source: string;
|
||||
target: string;
|
||||
type: string;
|
||||
weight?: number;
|
||||
properties?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type OntologyGraphResponse = {
|
||||
uri: string;
|
||||
nodes: OntologyGraphNode[];
|
||||
edges: OntologyGraphEdge[];
|
||||
};
|
||||
|
||||
export type OntologyEntityOwner = {
|
||||
source_ontology?: string;
|
||||
};
|
||||
|
||||
async function parseResponse<T>(response: Response): Promise<T> {
|
||||
if (!response.ok) {
|
||||
let detail = `Request failed with status ${response.status}`;
|
||||
@@ -57,18 +31,6 @@ export async function loadOntologyRegistry(): Promise<OntologyEntry[]> {
|
||||
return parseResponse<OntologyEntry[]>(await fetch("/api/ontology/registry"));
|
||||
}
|
||||
|
||||
export async function loadOntologyGraph(uri: string, signal?: AbortSignal): Promise<OntologyGraphResponse> {
|
||||
return parseResponse<OntologyGraphResponse>(
|
||||
await fetch(`/api/ontology/graph?uri=${encodeURIComponent(uri)}`, { signal }),
|
||||
);
|
||||
}
|
||||
|
||||
export async function loadOntologyEntityOwner(uri: string): Promise<string | undefined> {
|
||||
const response = await fetch(`/api/ontology/entity/${encodeURIComponent(uri)}`);
|
||||
if (!response.ok) return undefined;
|
||||
return (await response.json() as OntologyEntityOwner).source_ontology;
|
||||
}
|
||||
|
||||
export async function loadAlignments(uri?: string): Promise<OntologyAlignment[]> {
|
||||
const query = uri ? `?uri=${encodeURIComponent(uri)}` : "";
|
||||
return parseResponse<OntologyAlignment[]>(await fetch(`/api/ontology/alignments${query}`));
|
||||
|
||||
@@ -38,7 +38,6 @@ function readTabParam(): OntologyHubTab {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const raw = params.get(TAB_PARAM);
|
||||
if (raw && TABS.some((t) => t.id === raw)) return raw as OntologyHubTab;
|
||||
if (params.get("ontologyEntity")) return "editor";
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
@@ -117,3 +116,4 @@ export function OntologyWorkspace({ onJumpToGraphNode }: OntologyWorkspaceProps)
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
export type EditorEntityType = "ontology" | "class" | "property" | "external";
|
||||
|
||||
export type RegistryEntry = {
|
||||
uri: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
export const ONTOLOGY_MINIMAP_THEME = {
|
||||
bgColor: "#0b1625",
|
||||
maskColor: "rgba(7, 17, 31, 0.72)",
|
||||
maskStrokeColor: "#5faeff",
|
||||
maskStrokeWidth: 2,
|
||||
nodeColor: "#2d7fd3",
|
||||
nodeStrokeColor: "#9acbff",
|
||||
nodeStrokeWidth: 1,
|
||||
style: {
|
||||
border: "1px solid #29435c",
|
||||
borderRadius: 6,
|
||||
boxShadow: "0 4px 16px rgba(0, 0, 0, 0.32)",
|
||||
},
|
||||
} as const;
|
||||
|
||||
// The backend emits node types in compact (owl:Class) or full IRI
|
||||
// (http://www.w3.org/2002/07/owl#Class) form; classification must accept both.
|
||||
const FULL_IRI_PREFIXES: Array<[string, string]> = [
|
||||
["http://www.w3.org/2002/07/owl#", "owl:"],
|
||||
["http://www.w3.org/2000/01/rdf-schema#", "rdfs:"],
|
||||
["http://www.w3.org/2004/02/skos/core#", "skos:"],
|
||||
];
|
||||
|
||||
export function compactNodeType(type: string): string {
|
||||
for (const [iri, prefix] of FULL_IRI_PREFIXES) {
|
||||
if (type.startsWith(iri)) {
|
||||
return `${prefix}${type.slice(iri.length)}`;
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
export function classifyNodeType(rawType: string): EditorEntityType {
|
||||
const type = compactNodeType(rawType);
|
||||
if (type === "owl:Ontology") return "ontology";
|
||||
if (type === "owl:Class" || type === "rdfs:Class") return "class";
|
||||
if (type.includes("Property")) return "property";
|
||||
return "external";
|
||||
}
|
||||
|
||||
function ownsByNamespace(entityUri: string, ontologyUri: string): boolean {
|
||||
const stem = ontologyUri.replace(/[/#]+$/, "");
|
||||
return entityUri === ontologyUri
|
||||
|| entityUri.startsWith(`${stem}#`)
|
||||
|| entityUri.startsWith(`${stem}/`);
|
||||
}
|
||||
|
||||
export function inferOntologyUri(
|
||||
entries: RegistryEntry[],
|
||||
entityUri: string,
|
||||
explicitOwner?: string,
|
||||
): string | undefined {
|
||||
if (explicitOwner && entries.some((entry) => entry.uri === explicitOwner)) {
|
||||
return explicitOwner;
|
||||
}
|
||||
return [...entries]
|
||||
.filter((entry) => ownsByNamespace(entityUri, entry.uri))
|
||||
.sort((left, right) => right.uri.length - left.uri.length)[0]?.uri;
|
||||
}
|
||||
|
||||
export function isEditableEntityType(entityType?: EditorEntityType): boolean {
|
||||
return entityType === "class" || entityType === "property";
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import {
|
||||
classifyNodeType,
|
||||
compactNodeType,
|
||||
inferOntologyUri,
|
||||
isEditableEntityType,
|
||||
ONTOLOGY_MINIMAP_THEME,
|
||||
} from "../src/workspaces/OntologyWorkspace/ontologyEditorModel";
|
||||
|
||||
const registry = [
|
||||
{ uri: "https://example.test/foo", name: "Foo" },
|
||||
{ uri: "https://example.test/foo/nested", name: "Nested" },
|
||||
];
|
||||
|
||||
test("ontology inference requires a URI delimiter and prefers the closest namespace", () => {
|
||||
assert.equal(inferOntologyUri(registry, "https://example.test/foobar/Class"), undefined);
|
||||
assert.equal(
|
||||
inferOntologyUri(registry, "https://example.test/foo/nested#Class"),
|
||||
"https://example.test/foo/nested",
|
||||
);
|
||||
});
|
||||
|
||||
test("explicit scheme ownership wins when an entity uses another namespace", () => {
|
||||
assert.equal(
|
||||
inferOntologyUri(registry, "https://vocabulary.test/Class", "https://example.test/foo"),
|
||||
"https://example.test/foo",
|
||||
);
|
||||
});
|
||||
|
||||
test("only draft-supported class and property nodes are editable", () => {
|
||||
assert.equal(isEditableEntityType("class"), true);
|
||||
assert.equal(isEditableEntityType("property"), true);
|
||||
assert.equal(isEditableEntityType("ontology"), false);
|
||||
assert.equal(isEditableEntityType("external"), false);
|
||||
});
|
||||
|
||||
test("the ontology minimap has an explicit dark, high-contrast theme", () => {
|
||||
assert.equal(ONTOLOGY_MINIMAP_THEME.bgColor, "#0b1625");
|
||||
assert.equal(ONTOLOGY_MINIMAP_THEME.maskStrokeColor, "#5faeff");
|
||||
assert.equal(ONTOLOGY_MINIMAP_THEME.nodeStrokeColor, "#9acbff");
|
||||
assert.match(ONTOLOGY_MINIMAP_THEME.style.border, /#29435c/);
|
||||
});
|
||||
|
||||
test("node types classify identically in compact and full IRI form", () => {
|
||||
const cases: Array<[string, string, string]> = [
|
||||
["owl:Ontology", "http://www.w3.org/2002/07/owl#Ontology", "ontology"],
|
||||
["owl:Class", "http://www.w3.org/2002/07/owl#Class", "class"],
|
||||
["rdfs:Class", "http://www.w3.org/2000/01/rdf-schema#Class", "class"],
|
||||
["owl:ObjectProperty", "http://www.w3.org/2002/07/owl#ObjectProperty", "property"],
|
||||
["owl:DatatypeProperty", "http://www.w3.org/2002/07/owl#DatatypeProperty", "property"],
|
||||
["owl:AnnotationProperty", "http://www.w3.org/2002/07/owl#AnnotationProperty", "property"],
|
||||
];
|
||||
for (const [compact, fullIri, expected] of cases) {
|
||||
assert.equal(classifyNodeType(compact), expected, compact);
|
||||
assert.equal(classifyNodeType(fullIri), expected, fullIri);
|
||||
}
|
||||
assert.equal(classifyNodeType("owl:NamedIndividual"), "external");
|
||||
assert.equal(classifyNodeType("http://www.w3.org/2004/02/skos/core#Concept"), "external");
|
||||
});
|
||||
|
||||
test("compactNodeType leaves unknown namespaces untouched", () => {
|
||||
assert.equal(compactNodeType("https://example.org/custom#Thing"), "https://example.org/custom#Thing");
|
||||
assert.equal(compactNodeType("owl:Class"), "owl:Class");
|
||||
});
|
||||
+7
-36
@@ -47,11 +47,7 @@ dependencies = [
|
||||
"numpy>=2.0.2",
|
||||
"pandas>=1.3.0",
|
||||
"scipy>=1.13.1",
|
||||
# scikit-learn dropped Python 3.9 support at 1.7.0 (requires_python >=3.10),
|
||||
# so an unqualified >=1.7.2 floor is unsatisfiable on 3.9. Cap 3.9 to the
|
||||
# last 3.9-compatible release line; 3.10+ is left unconstrained.
|
||||
"scikit-learn>=1.6.1,<1.7.0; python_version < '3.10'",
|
||||
"scikit-learn>=1.7.2; python_version >= '3.10'",
|
||||
"scikit-learn>=1.7.2",
|
||||
"umap-learn>=0.5.12",
|
||||
# thinc (spacy's core dep) dropped Python 3.9 wheels at 8.3.10, and later
|
||||
# spacy patch releases (3.8.8+) require thinc>=8.3.9-only-on-3.10+ ranges,
|
||||
@@ -70,49 +66,24 @@ dependencies = [
|
||||
"seaborn>=0.13.2",
|
||||
"plotly>=6.8.0",
|
||||
"ipywidgets>=8.0.0",
|
||||
# requests dropped Python 3.9 support at 2.33.0 (requires_python >=3.10),
|
||||
# so an unqualified >=2.34.2 floor is unsatisfiable on 3.9. Cap 3.9 to the
|
||||
# last 3.9-compatible release; 3.10+ is left unconstrained.
|
||||
"requests>=2.32.5,<2.33.0; python_version < '3.10'",
|
||||
"requests>=2.34.2; python_version >= '3.10'",
|
||||
"requests>=2.34.2",
|
||||
"GitPython>=3.1.58",
|
||||
# chardet dropped Python 3.9 support at 6.0.0 (requires_python >=3.10), so
|
||||
# an unqualified >=7.4.3 floor is unsatisfiable on 3.9. Cap 3.9 to the last
|
||||
# 3.9-compatible release; 3.10+ is left unconstrained.
|
||||
"chardet>=5.2.0,<6.0.0; python_version < '3.10'",
|
||||
"chardet>=7.4.3; python_version >= '3.10'",
|
||||
"chardet>=7.4.3",
|
||||
"protobuf>=5.29.1,<8.0",
|
||||
# grpcio dropped Python 3.9 support at 1.81.0 (requires_python >=3.10), so
|
||||
# an unqualified >=1.81.1 floor is unsatisfiable on 3.9. Cap 3.9 to the last
|
||||
# 3.9-compatible release; 3.10+ is left unconstrained.
|
||||
"grpcio>=1.80.0,<1.81.0; python_version < '3.10'",
|
||||
"grpcio>=1.81.1; python_version >= '3.10'",
|
||||
"grpcio>=1.81.1",
|
||||
"beautifulsoup4>=4.15.0",
|
||||
"lxml>=6.1.1",
|
||||
"python-docx>=1.2.0",
|
||||
"openpyxl>=3.1.5",
|
||||
# pillow dropped Python 3.9 support at 12.0.0 (requires_python >=3.10), so
|
||||
# an unqualified >=12.2.0 floor is unsatisfiable on 3.9. Cap 3.9 to the last
|
||||
# 3.9-compatible release; 3.10+ is left unconstrained.
|
||||
"pillow>=11.3.0,<12.0.0; python_version < '3.10'",
|
||||
"pillow>=12.2.0; python_version >= '3.10'",
|
||||
"pillow>=12.2.0",
|
||||
"librosa>=0.9.0",
|
||||
"opencv-python>=4.13.0.92",
|
||||
"faiss-cpu>=1.7.0",
|
||||
"fastembed>=0.2.0",
|
||||
# onnxruntime stopped shipping cp39 wheels at 1.20.0 (its PyPI metadata
|
||||
# still claims requires_python >=3.9, but no matching wheel exists), so an
|
||||
# unqualified >=1.20.1 floor is unsatisfiable on 3.9. Cap 3.9 to the last
|
||||
# release with a cp39 wheel; 3.10+ is left unconstrained.
|
||||
"onnxruntime>=1.19.2,<1.20.0; python_version < '3.10'",
|
||||
"onnxruntime>=1.20.1; python_version >= '3.10'",
|
||||
"onnxruntime>=1.20.1",
|
||||
"tokenizers>=0.15.0",
|
||||
"pydantic>=2.13.4",
|
||||
# click dropped Python 3.9 support at 8.2.0 (requires_python >=3.10), so an
|
||||
# unqualified >=8.4.2 floor is unsatisfiable on 3.9. Cap 3.9 to the last
|
||||
# 3.9-compatible release; 3.10+ is left unconstrained.
|
||||
"click>=8.1.8,<8.2.0; python_version < '3.10'",
|
||||
"click>=8.4.2; python_version >= '3.10'",
|
||||
"click>=8.4.2",
|
||||
"rich>=12.5.0",
|
||||
"tqdm>=4.68.3",
|
||||
"pyyaml>=6.0",
|
||||
|
||||
@@ -234,12 +234,6 @@ class EntityDetailResponse(BaseModel):
|
||||
properties: Dict[str, Any] = Field(default_factory=dict)
|
||||
|
||||
|
||||
class OntologyGraphResponse(BaseModel):
|
||||
uri: str
|
||||
nodes: List[Dict[str, Any]] = Field(default_factory=list)
|
||||
edges: List[Dict[str, Any]] = Field(default_factory=list)
|
||||
|
||||
|
||||
class SKOSScheme(BaseModel):
|
||||
uri: str
|
||||
title: str
|
||||
@@ -670,7 +664,6 @@ def _convert_ontology_to_graph(ontology_dict: Dict[str, Any]) -> Tuple[List[Dict
|
||||
"rdfs:label": cls.get("label", cls.get("name", "")),
|
||||
"rdfs:comment": cls.get("description", ""),
|
||||
"uri": cls_uri,
|
||||
"scheme_uri": ontology_uri,
|
||||
},
|
||||
}
|
||||
nodes.append(node)
|
||||
@@ -687,21 +680,14 @@ def _convert_ontology_to_graph(ontology_dict: Dict[str, Any]) -> Tuple[List[Dict
|
||||
# Add property nodes and edges
|
||||
for prop in ontology_dict.get("properties", []):
|
||||
prop_uri = prop.get("uri", f"temp:prop:{uuid.uuid4().hex[:12]}")
|
||||
property_type = {
|
||||
"object": "owl:ObjectProperty",
|
||||
"data": "owl:DatatypeProperty",
|
||||
"datatype": "owl:DatatypeProperty",
|
||||
"annotation": "owl:AnnotationProperty",
|
||||
}.get(str(prop.get("type", "object")).lower(), "owl:ObjectProperty")
|
||||
node = {
|
||||
"id": prop_uri,
|
||||
"type": property_type,
|
||||
"type": f"owl:{prop.get('type', 'Object').title()}Property",
|
||||
"content": prop.get("name", prop.get("label", "")),
|
||||
"properties": {
|
||||
"rdfs:label": prop.get("label", prop.get("name", "")),
|
||||
"rdfs:comment": prop.get("description", ""),
|
||||
"uri": prop_uri,
|
||||
"scheme_uri": ontology_uri,
|
||||
},
|
||||
}
|
||||
nodes.append(node)
|
||||
@@ -762,38 +748,14 @@ def _node_source_ontology(node: Dict[str, Any]) -> Optional[str]:
|
||||
)
|
||||
|
||||
|
||||
def _node_belongs_to_ontology(
|
||||
node: Dict[str, Any],
|
||||
ontology_uri: str,
|
||||
known_ontology_uris: Optional[set[str]] = None,
|
||||
) -> bool:
|
||||
def _node_belongs_to_ontology(node: Dict[str, Any], ontology_uri: str) -> bool:
|
||||
nid = node.get("id", "")
|
||||
if nid == ontology_uri:
|
||||
return True
|
||||
owner = _node_source_ontology(node)
|
||||
if owner:
|
||||
return owner == ontology_uri
|
||||
if known_ontology_uris:
|
||||
namespace_owners = [
|
||||
candidate
|
||||
for candidate in known_ontology_uris
|
||||
if nid == candidate
|
||||
or nid.startswith(
|
||||
(candidate.rstrip("#/") + "#", candidate.rstrip("#/") + "/")
|
||||
)
|
||||
]
|
||||
if namespace_owners and max(namespace_owners, key=len) != ontology_uri:
|
||||
return False
|
||||
if _node_source_ontology(node) == ontology_uri:
|
||||
return True
|
||||
stem = ontology_uri.rstrip("#/")
|
||||
if not nid.startswith((stem + "#", stem + "/")):
|
||||
return False
|
||||
# Prefix ownership only extends to names minted directly in the
|
||||
# ontology's namespace (<stem>#Term or <stem>/Term). Any further
|
||||
# delimiter marks a nested vocabulary (<stem>/child#Term,
|
||||
# <stem>/child/Term), which must not be absorbed into the parent
|
||||
# until it is registered or carries an explicit owner.
|
||||
local_name = nid[len(stem) + 1 :]
|
||||
return "#" not in local_name and "/" not in local_name
|
||||
return nid.startswith((stem + "#", stem + "/"))
|
||||
|
||||
|
||||
def _is_ontology_entity(node: Dict[str, Any]) -> bool:
|
||||
@@ -1259,8 +1221,7 @@ def _parse_rdf_sync(content: bytes, fmt: str) -> tuple:
|
||||
metadata.setdefault("description", str(obj))
|
||||
break
|
||||
|
||||
synthetic_uri = "uri" not in metadata
|
||||
if synthetic_uri:
|
||||
if "uri" not in metadata:
|
||||
metadata["uri"] = f"urn:semantica:onto:{uuid.uuid4().hex[:8]}"
|
||||
metadata.setdefault("name", metadata["uri"].rsplit("/", 1)[-1].rsplit("#", 1)[-1] or "Unnamed")
|
||||
metadata["triple_count"] = len(g)
|
||||
@@ -1307,20 +1268,6 @@ def _parse_rdf_sync(content: bytes, fmt: str) -> tuple:
|
||||
"weight": 1.0,
|
||||
})
|
||||
|
||||
if synthetic_uri:
|
||||
# No owl:Ontology / skos:ConceptScheme declaration exists, so the
|
||||
# synthetic registry URI shares no namespace with any node. Ownership
|
||||
# must be recorded explicitly, and the editor needs a matching graph
|
||||
# node, or the registered ontology resolves to an empty core and 404s.
|
||||
for node in nodes:
|
||||
node["properties"].setdefault("scheme_uri", metadata["uri"])
|
||||
nodes.append({
|
||||
"id": metadata["uri"],
|
||||
"type": "owl:Ontology",
|
||||
"content": metadata["name"],
|
||||
"properties": {"rdfs:label": metadata["name"], "uri": metadata["uri"]},
|
||||
})
|
||||
|
||||
return nodes, edges, metadata
|
||||
|
||||
|
||||
@@ -1821,107 +1768,6 @@ async def search_entities(
|
||||
return results
|
||||
|
||||
|
||||
@router.get("/graph", response_model=OntologyGraphResponse)
|
||||
async def get_ontology_graph(
|
||||
request: Request,
|
||||
uri: str = Query(..., min_length=1),
|
||||
session: GraphSession = Depends(get_session),
|
||||
):
|
||||
"""Return the editable schema subgraph for one registered ontology."""
|
||||
registry = _get_registry(request)
|
||||
ontology_nodes: List[Dict[str, Any]] = []
|
||||
for node_type in _ONTOLOGY_TYPES:
|
||||
nodes, _ = await asyncio.to_thread(
|
||||
session.get_nodes, node_type=node_type, skip=0, limit=2**63 - 1
|
||||
)
|
||||
ontology_nodes.extend(nodes)
|
||||
known_ontology_uris = set(registry) | {
|
||||
str(node.get("id", "")) for node in ontology_nodes if node.get("id")
|
||||
}
|
||||
if uri not in known_ontology_uris:
|
||||
raise HTTPException(status_code=404, detail="Ontology not found in registry.")
|
||||
|
||||
schema_types = _CLASS_TYPES | _PROPERTY_TYPES | _CONCEPT_TYPES | _ONTOLOGY_TYPES
|
||||
candidates_by_id: Dict[str, Dict[str, Any]] = {}
|
||||
for node_type in schema_types:
|
||||
nodes, _ = await asyncio.to_thread(
|
||||
session.get_nodes, node_type=node_type, skip=0, limit=2**63 - 1
|
||||
)
|
||||
candidates_by_id.update(
|
||||
(str(node.get("id", "")), node) for node in nodes if node.get("id")
|
||||
)
|
||||
|
||||
core_node_ids = {
|
||||
str(node.get("id", ""))
|
||||
for node in candidates_by_id.values()
|
||||
if _node_belongs_to_ontology(node, uri, known_ontology_uris)
|
||||
}
|
||||
if not core_node_ids:
|
||||
raise HTTPException(status_code=404, detail="Ontology graph not found.")
|
||||
|
||||
structure_edge_types = {
|
||||
"rdf:type",
|
||||
"rdfs:subClassOf",
|
||||
"rdfs:domain",
|
||||
"rdfs:range",
|
||||
"owl:disjointWith",
|
||||
"owl:equivalentClass",
|
||||
"owl:equivalentProperty",
|
||||
"owl:inverseOf",
|
||||
"skos:broader",
|
||||
"skos:narrower",
|
||||
"skos:related",
|
||||
}
|
||||
selected_edges: List[Dict[str, Any]] = []
|
||||
for edge_type in structure_edge_types:
|
||||
edges, _ = await asyncio.to_thread(
|
||||
session.get_edges,
|
||||
edge_type=edge_type,
|
||||
skip=0,
|
||||
limit=2**63 - 1,
|
||||
)
|
||||
# Keep only edges whose source is a core node: the requested ontology
|
||||
# may reference outward (e.g. rdfs:range to an external vocabulary),
|
||||
# but an unrelated ontology's property pointing at a core class must
|
||||
# not leak inward.
|
||||
selected_edges.extend(
|
||||
edge for edge in edges
|
||||
if str(edge.get("source", "")) in core_node_ids
|
||||
)
|
||||
if (
|
||||
len(core_node_ids) > _MAX_ANALYSIS_NODES
|
||||
or len(selected_edges) > _MAX_ANALYSIS_NODES
|
||||
):
|
||||
raise HTTPException(
|
||||
status_code=413,
|
||||
detail=(
|
||||
"Ontology editor graph exceeds the maximum size "
|
||||
f"({_MAX_ANALYSIS_NODES} nodes or edges)."
|
||||
),
|
||||
)
|
||||
|
||||
selected_node_ids = set(core_node_ids)
|
||||
for edge in selected_edges:
|
||||
selected_node_ids.add(str(edge.get("source", "")))
|
||||
selected_node_ids.add(str(edge.get("target", "")))
|
||||
|
||||
selected_nodes = [candidates_by_id[node_id] for node_id in core_node_ids]
|
||||
for node_id in selected_node_ids - core_node_ids:
|
||||
external = await asyncio.to_thread(session.get_node, node_id)
|
||||
if external is not None:
|
||||
selected_nodes.append(external)
|
||||
selected_nodes.sort(key=lambda node: str(node.get("id", "")))
|
||||
selected_edges.sort(
|
||||
key=lambda edge: (
|
||||
str(edge.get("source", "")),
|
||||
str(edge.get("type", "")),
|
||||
str(edge.get("target", "")),
|
||||
str(edge.get("id", "")),
|
||||
)
|
||||
)
|
||||
return OntologyGraphResponse(uri=uri, nodes=selected_nodes, edges=selected_edges)
|
||||
|
||||
|
||||
@router.get("/entity/{entity_uri:path}", response_model=EntityDetailResponse)
|
||||
async def get_entity_detail(
|
||||
entity_uri: str,
|
||||
|
||||
@@ -253,7 +253,7 @@ class GraphAnalyzer:
|
||||
graph,
|
||||
start_time=None,
|
||||
end_time=None,
|
||||
metrics=None,
|
||||
metrics=["node_count", "edge_count", "density", "communities"],
|
||||
interval=None,
|
||||
**options,
|
||||
):
|
||||
@@ -271,8 +271,6 @@ class GraphAnalyzer:
|
||||
Returns:
|
||||
Evolution analysis results with time series data
|
||||
"""
|
||||
if metrics is None:
|
||||
metrics = ["node_count", "edge_count", "density", "communities"]
|
||||
self.logger.info("Analyzing temporal evolution")
|
||||
|
||||
from .temporal_query import TemporalGraphQuery
|
||||
|
||||
@@ -35,7 +35,7 @@ Example Usage:
|
||||
>>> llm = LiteLLM(model="openai/gpt-4o", api_key="your-key")
|
||||
>>> response = llm.generate("Hello, world!")
|
||||
>>> # Or use other providers via LiteLLM
|
||||
>>> llm = LiteLLM(model="anthropic/claude-sonnet-5")
|
||||
>>> llm = LiteLLM(model="anthropic/claude-sonnet-4-20250514")
|
||||
>>> response = llm.generate("Hello, world!")
|
||||
>>>
|
||||
>>> # Anthropic provider
|
||||
|
||||
@@ -31,7 +31,7 @@ class LiteLLM:
|
||||
Provides unified interface to 100+ LLM providers through LiteLLM library.
|
||||
Supports providers like OpenAI, Anthropic, Groq, Azure, Bedrock, Vertex AI, etc.
|
||||
|
||||
Model format: "provider/model-name" (e.g., "openai/gpt-4o", "anthropic/claude-sonnet-5", "groq/llama-3.1-8b-instant")
|
||||
Model format: "provider/model-name" (e.g., "openai/gpt-4o", "anthropic/claude-sonnet-4-20250514", "groq/llama-3.1-8b-instant")
|
||||
|
||||
Example:
|
||||
>>> from semantica.llms import LiteLLM
|
||||
@@ -39,7 +39,7 @@ class LiteLLM:
|
||||
>>> response = llm.generate("What is AI?")
|
||||
>>>
|
||||
>>> # Use with different providers
|
||||
>>> llm = LiteLLM(model="anthropic/claude-sonnet-5")
|
||||
>>> llm = LiteLLM(model="anthropic/claude-sonnet-4-20250514")
|
||||
>>> response = llm.generate("Hello!")
|
||||
"""
|
||||
|
||||
@@ -54,7 +54,7 @@ class LiteLLM:
|
||||
|
||||
Args:
|
||||
model: Model identifier in format "provider/model-name"
|
||||
Examples: "openai/gpt-4o", "anthropic/claude-sonnet-5",
|
||||
Examples: "openai/gpt-4o", "anthropic/claude-sonnet-4-20250514",
|
||||
"groq/llama-3.1-8b-instant", "azure/gpt-4", etc.
|
||||
api_key: API key (optional, can use environment variables)
|
||||
**kwargs: Additional LiteLLM options (temperature, max_tokens, etc.)
|
||||
|
||||
@@ -37,29 +37,6 @@ from .naming_conventions import NamingConventions
|
||||
from .relationship_utils import build_entity_aliases, resolve_relationship_endpoint_type
|
||||
|
||||
|
||||
# Top-level entity keys that describe structure or provenance rather than
|
||||
# business attributes. GraphBuilder and EntityMerger attach these to entity
|
||||
# dicts (relationships list, nested properties/metadata maps, merge history),
|
||||
# so they must not be inferred as datatype properties. Each key mirrors what
|
||||
# the framework actually writes to a merged entity top level
|
||||
# (see MergeStrategyManager._merge_entities merged_entity dict and GraphBuilder).
|
||||
_CONTROL_FIELDS = frozenset(
|
||||
{
|
||||
"id",
|
||||
"type",
|
||||
"entity_type",
|
||||
"text",
|
||||
"label",
|
||||
"confidence",
|
||||
"properties",
|
||||
"relationships",
|
||||
"metadata",
|
||||
"merged_from",
|
||||
"merge_strategy",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class PropertyGenerator:
|
||||
"""
|
||||
Property generation engine for ontologies.
|
||||
@@ -370,7 +347,7 @@ class PropertyGenerator:
|
||||
|
||||
for entity in entities:
|
||||
for key, value in entity.items():
|
||||
if key in _CONTROL_FIELDS:
|
||||
if key in ["id", "type", "entity_type", "text", "label", "confidence"]:
|
||||
continue
|
||||
|
||||
# Infer type
|
||||
|
||||
@@ -375,7 +375,7 @@ class HierarchicalChunker:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
levels: Optional[List[str]] = None,
|
||||
levels: List[str] = ["section", "paragraph", "sentence"],
|
||||
chunk_sizes: Optional[List[int]] = None,
|
||||
**kwargs,
|
||||
):
|
||||
@@ -387,8 +387,6 @@ class HierarchicalChunker:
|
||||
chunk_sizes: Chunk sizes for each level
|
||||
**kwargs: Additional options
|
||||
"""
|
||||
if levels is None:
|
||||
levels = ["section", "paragraph", "sentence"]
|
||||
self.levels = levels
|
||||
self.chunk_sizes = chunk_sizes or [2000, 1000, 500]
|
||||
self.options = kwargs
|
||||
|
||||
@@ -1402,7 +1402,7 @@ def split_embedding_semantic(
|
||||
|
||||
def split_hierarchical(
|
||||
text: str,
|
||||
levels: Optional[List[str]] = None,
|
||||
levels: List[str] = ["section", "paragraph", "sentence"],
|
||||
chunk_sizes: Optional[List[int]] = None,
|
||||
**kwargs,
|
||||
) -> List[Chunk]:
|
||||
@@ -1420,8 +1420,6 @@ def split_hierarchical(
|
||||
"""
|
||||
if chunk_sizes is None:
|
||||
chunk_sizes = [2000, 1000, 500]
|
||||
if levels is None:
|
||||
levels = ["section", "paragraph", "sentence"]
|
||||
|
||||
# Start with largest level
|
||||
if "section" in levels:
|
||||
|
||||
@@ -416,38 +416,6 @@ 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:
|
||||
|
||||
@@ -12,11 +12,7 @@ from semantica.context.context_graph import ContextGraph
|
||||
pytest.importorskip("fastapi")
|
||||
|
||||
from semantica.explorer.app import create_app # noqa: E402
|
||||
from semantica.explorer.routes.ontology import ( # noqa: E402
|
||||
OntologyEntry,
|
||||
_convert_ontology_to_graph,
|
||||
_node_belongs_to_ontology,
|
||||
)
|
||||
from semantica.explorer.routes.ontology import OntologyEntry # noqa: E402
|
||||
from semantica.explorer.session import GraphSession # noqa: E402
|
||||
|
||||
from starlette.testclient import TestClient # noqa: E402
|
||||
@@ -135,181 +131,6 @@ def test_health_returns_dimensions_and_issues(client):
|
||||
assert isinstance(payload["issues"], list)
|
||||
|
||||
|
||||
def test_ontology_graph_returns_editable_schema_nodes_and_edges(client):
|
||||
response = client.get(
|
||||
"/api/ontology/graph",
|
||||
params={"uri": "http://example.org/onto-a"},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
payload = response.json()
|
||||
node_ids = {node["id"] for node in payload["nodes"]}
|
||||
assert "http://example.org/onto-a" in node_ids
|
||||
assert "http://example.org/onto-a#Person" in node_ids
|
||||
assert "http://example.org/onto-a#name" in node_ids
|
||||
assert any(
|
||||
edge["source"] == "http://example.org/onto-a#name"
|
||||
and edge["target"] == "http://example.org/onto-a#Person"
|
||||
and edge["type"] == "rdfs:domain"
|
||||
for edge in payload["edges"]
|
||||
)
|
||||
|
||||
|
||||
def test_ontology_graph_rejects_unregistered_namespace(client):
|
||||
response = client.get(
|
||||
"/api/ontology/graph",
|
||||
params={"uri": "http://example.org"},
|
||||
)
|
||||
|
||||
assert response.status_code == 404
|
||||
|
||||
|
||||
def test_ontology_graph_excludes_separately_registered_nested_ontology(client):
|
||||
graph = client.app.state.session.graph
|
||||
nested = "http://example.org/onto-a/nested"
|
||||
nested_class = f"{nested}#PrivateClass"
|
||||
graph.add_node(nested, node_type="owl:Ontology", content="Nested Ontology")
|
||||
graph.add_node(nested_class, node_type="owl:Class", content="Private Class")
|
||||
|
||||
response = client.get(
|
||||
"/api/ontology/graph",
|
||||
params={"uri": "http://example.org/onto-a"},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
node_ids = {node["id"] for node in response.json()["nodes"]}
|
||||
assert nested not in node_ids
|
||||
assert nested_class not in node_ids
|
||||
|
||||
|
||||
def test_ontology_graph_prefers_explicit_ownership_over_uri_namespace(client):
|
||||
graph = client.app.state.session.graph
|
||||
explicit_member = "http://unrelated.example/Person"
|
||||
graph.add_node(
|
||||
explicit_member,
|
||||
node_type="owl:Class",
|
||||
content="Explicit Member",
|
||||
scheme_uri="http://example.org/onto-a",
|
||||
)
|
||||
|
||||
response = client.get(
|
||||
"/api/ontology/graph",
|
||||
params={"uri": "http://example.org/onto-a"},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert explicit_member in {node["id"] for node in response.json()["nodes"]}
|
||||
|
||||
|
||||
def test_ontology_graph_excludes_inward_edges_from_other_ontologies(client):
|
||||
graph = client.app.state.session.graph
|
||||
foreign_prop = "http://example.org/onto-b#recordOf"
|
||||
graph.add_node(
|
||||
foreign_prop,
|
||||
node_type="owl:ObjectProperty",
|
||||
content="record of",
|
||||
scheme_uri="http://example.org/onto-b",
|
||||
)
|
||||
# onto-b's property points its domain at onto-a's class: an inward
|
||||
# reference that must not pull the foreign property into onto-a's graph.
|
||||
graph.add_edge(foreign_prop, "http://example.org/onto-a#Person", edge_type="rdfs:domain")
|
||||
|
||||
response = client.get(
|
||||
"/api/ontology/graph",
|
||||
params={"uri": "http://example.org/onto-a"},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
payload = response.json()
|
||||
assert foreign_prop not in {node["id"] for node in payload["nodes"]}
|
||||
assert all(edge["source"] != foreign_prop for edge in payload["edges"])
|
||||
|
||||
|
||||
def test_ontology_graph_excludes_unregistered_nested_namespace(client):
|
||||
graph = client.app.state.session.graph
|
||||
nested_class = "http://example.org/onto-a/vocab#Term"
|
||||
graph.add_node(nested_class, node_type="owl:Class", content="Nested Term")
|
||||
|
||||
response = client.get(
|
||||
"/api/ontology/graph",
|
||||
params={"uri": "http://example.org/onto-a"},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert nested_class not in {node["id"] for node in response.json()["nodes"]}
|
||||
|
||||
|
||||
def test_node_belongs_to_ontology_nested_namespace_matrix():
|
||||
parent = "http://example.org/onto-a"
|
||||
child = "http://example.org/onto-a/nested"
|
||||
|
||||
def node(node_id):
|
||||
return {"id": node_id, "properties": {}}
|
||||
|
||||
assert _node_belongs_to_ontology(node(f"{parent}#Person"), parent, {parent})
|
||||
assert _node_belongs_to_ontology(node(f"{parent}/Person"), parent, {parent})
|
||||
# An unregistered nested namespace is not absorbed into the parent,
|
||||
# whether fragment-based or path-based
|
||||
assert not _node_belongs_to_ontology(node(f"{child}#Term"), parent, {parent})
|
||||
assert not _node_belongs_to_ontology(node(f"{child}/Term"), parent, {parent})
|
||||
# Once registered, the nested namespace owns its nodes
|
||||
assert not _node_belongs_to_ontology(node(f"{child}#Term"), parent, {parent, child})
|
||||
assert _node_belongs_to_ontology(node(f"{child}#Term"), child, {parent, child})
|
||||
assert _node_belongs_to_ontology(node(f"{child}/Term"), child, {parent, child})
|
||||
|
||||
|
||||
def test_load_fallback_import_without_declaration_is_editable(client):
|
||||
turtle = """
|
||||
@prefix ex: <http://data.example.org/people#> .
|
||||
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
|
||||
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
|
||||
|
||||
ex:Employee a rdfs:Class ;
|
||||
rdfs:label "Employee" .
|
||||
ex:manager a rdf:Property ;
|
||||
rdfs:label "manager" .
|
||||
"""
|
||||
with patch(
|
||||
"semantica.ingest.ontology_ingestor.OntologyIngestor.ingest_ontology",
|
||||
side_effect=RuntimeError("force fallback parser"),
|
||||
):
|
||||
loaded = client.post(
|
||||
"/api/ontology/load",
|
||||
json={"content": turtle, "format": "turtle"},
|
||||
)
|
||||
assert loaded.status_code == 200
|
||||
uri = loaded.json()["uri"]
|
||||
assert uri.startswith("urn:semantica:onto:")
|
||||
|
||||
response = client.get("/api/ontology/graph", params={"uri": uri})
|
||||
assert response.status_code == 200
|
||||
payload = response.json()
|
||||
node_ids = {node["id"] for node in payload["nodes"]}
|
||||
assert uri in node_ids
|
||||
assert "http://data.example.org/people#Employee" in node_ids
|
||||
|
||||
|
||||
def test_ontology_graph_ignores_unrelated_data_when_enforcing_size_limit(client):
|
||||
graph = client.app.state.session.graph
|
||||
for index in range(5_001):
|
||||
graph.add_node(
|
||||
f"urn:unrelated:{index}",
|
||||
node_type="owl:Class",
|
||||
content="Unrelated",
|
||||
scheme_uri="http://example.org/onto-b",
|
||||
)
|
||||
|
||||
response = client.get(
|
||||
"/api/ontology/graph",
|
||||
params={"uri": "http://example.org/onto-a"},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "http://example.org/onto-a#Person" in {
|
||||
node["id"] for node in response.json()["nodes"]
|
||||
}
|
||||
|
||||
|
||||
def test_shacl_generate_and_shapes(client):
|
||||
response = client.post(
|
||||
"/api/ontology/shacl/generate",
|
||||
@@ -875,29 +696,6 @@ def test_ontology_load_does_not_swallow_422_from_ingestor_success_path(client):
|
||||
fallback_parse.assert_not_called()
|
||||
|
||||
|
||||
def test_convert_ontology_uses_standard_property_types_and_scheme_uri():
|
||||
ontology_uri = "http://example.org/onto"
|
||||
nodes, _ = _convert_ontology_to_graph(
|
||||
{
|
||||
"uri": ontology_uri,
|
||||
"name": "Example Ontology",
|
||||
"classes": [
|
||||
{"uri": f"{ontology_uri}#Person", "name": "Person"},
|
||||
],
|
||||
"properties": [
|
||||
{"uri": f"{ontology_uri}#name", "name": "name", "type": "data"},
|
||||
{"uri": f"{ontology_uri}#knows", "name": "knows", "type": "object"},
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
by_id = {node["id"]: node for node in nodes}
|
||||
assert by_id[f"{ontology_uri}#Person"]["properties"]["scheme_uri"] == ontology_uri
|
||||
assert by_id[f"{ontology_uri}#name"]["type"] == "owl:DatatypeProperty"
|
||||
assert by_id[f"{ontology_uri}#knows"]["type"] == "owl:ObjectProperty"
|
||||
assert by_id[f"{ontology_uri}#name"]["properties"]["scheme_uri"] == ontology_uri
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# refresh_ontology — single combined add_nodes_and_edges() coverage (#775)
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -991,3 +789,5 @@ def test_refresh_ontology_missing_source_url_returns_422(client):
|
||||
response = client.post(f"/api/ontology/{encoded_uri}/refresh")
|
||||
assert response.status_code == 422
|
||||
assert "source url" in response.json()["detail"].lower()
|
||||
|
||||
|
||||
|
||||
@@ -242,107 +242,6 @@ class TestGraphAnalyzer(unittest.TestCase):
|
||||
self.mock_connectivity.analyze_connectivity.assert_called_once()
|
||||
mock_metrics.assert_called_once()
|
||||
|
||||
class TestAnalyzeTemporalEvolutionMutableDefault(unittest.TestCase):
|
||||
"""Regression tests for fix: replace mutable default argument in
|
||||
GraphAnalyzer.analyze_temporal_evolution (metrics=[...] -> None).
|
||||
|
||||
TemporalGraphQuery is imported lazily inside the method body
|
||||
(``from .temporal_query import TemporalGraphQuery``), so it is patched
|
||||
at its definition site: ``semantica.kg.temporal_query.TemporalGraphQuery``.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.mock_tracker_patcher = patch("semantica.kg.graph_analyzer.get_progress_tracker")
|
||||
self.mock_get_tracker = self.mock_tracker_patcher.start()
|
||||
self.mock_get_tracker.return_value = MagicMock()
|
||||
|
||||
self.mock_centrality_patcher = patch("semantica.kg.graph_analyzer.CentralityCalculator")
|
||||
self.mock_centrality_patcher.start()
|
||||
|
||||
self.mock_community_patcher = patch("semantica.kg.graph_analyzer.CommunityDetector")
|
||||
self.mock_community_patcher.start()
|
||||
|
||||
self.mock_connectivity_patcher = patch("semantica.kg.graph_analyzer.ConnectivityAnalyzer")
|
||||
self.mock_connectivity_patcher.start()
|
||||
|
||||
# TemporalGraphQuery is imported *inside* the method body, so patch it
|
||||
# at the definition module rather than at the caller module.
|
||||
self.mock_tq_patcher = patch(
|
||||
"semantica.kg.temporal_query.TemporalGraphQuery", autospec=False
|
||||
)
|
||||
mock_tq_cls = self.mock_tq_patcher.start()
|
||||
self.mock_tq = MagicMock()
|
||||
self.mock_tq.analyze_evolution.return_value = {"snapshots": []}
|
||||
mock_tq_cls.return_value = self.mock_tq
|
||||
|
||||
def tearDown(self):
|
||||
patch.stopall()
|
||||
|
||||
def _make_analyzer(self):
|
||||
return GraphAnalyzer()
|
||||
|
||||
def test_default_metrics_value_is_canonical(self):
|
||||
"""When metrics=None, the four canonical metric names must be used."""
|
||||
analyzer = self._make_analyzer()
|
||||
graph = {"entities": [], "relationships": []}
|
||||
result = analyzer.analyze_temporal_evolution(graph)
|
||||
|
||||
self.assertEqual(
|
||||
sorted(result["metrics_tracked"]),
|
||||
sorted(["node_count", "edge_count", "density", "communities"]),
|
||||
)
|
||||
|
||||
def test_default_metrics_independent_across_calls(self):
|
||||
"""Mutating the returned metrics_tracked list must not affect the next call."""
|
||||
analyzer = self._make_analyzer()
|
||||
graph = {"entities": [], "relationships": []}
|
||||
|
||||
result1 = analyzer.analyze_temporal_evolution(graph)
|
||||
# Mutate the returned list in-place.
|
||||
result1["metrics_tracked"].append("MUTATED")
|
||||
|
||||
result2 = analyzer.analyze_temporal_evolution(graph)
|
||||
self.assertNotIn(
|
||||
"MUTATED",
|
||||
result2["metrics_tracked"],
|
||||
"Mutable default leaked: 'MUTATED' appeared in the second call's metrics list",
|
||||
)
|
||||
|
||||
def test_result_contains_metrics_tracked_key(self):
|
||||
"""Return value must include 'metrics_tracked' with the default list."""
|
||||
analyzer = self._make_analyzer()
|
||||
graph = {"entities": [], "relationships": []}
|
||||
result = analyzer.analyze_temporal_evolution(graph)
|
||||
|
||||
self.assertIn("metrics_tracked", result)
|
||||
self.assertEqual(
|
||||
sorted(result["metrics_tracked"]),
|
||||
sorted(["node_count", "edge_count", "density", "communities"]),
|
||||
)
|
||||
|
||||
def test_explicit_metrics_override_is_respected(self):
|
||||
"""Explicitly passed metrics must be forwarded and reflected in the return value."""
|
||||
analyzer = self._make_analyzer()
|
||||
graph = {"entities": [], "relationships": []}
|
||||
custom = ["node_count"]
|
||||
result = analyzer.analyze_temporal_evolution(graph, metrics=custom)
|
||||
|
||||
self.assertEqual(result["metrics_tracked"], custom)
|
||||
|
||||
def test_explicit_metrics_mutation_does_not_affect_default(self):
|
||||
"""Mutating the list passed as an explicit argument must not corrupt
|
||||
a subsequent default call."""
|
||||
analyzer = self._make_analyzer()
|
||||
graph = {"entities": [], "relationships": []}
|
||||
|
||||
explicit = ["node_count"]
|
||||
analyzer.analyze_temporal_evolution(graph, metrics=explicit)
|
||||
explicit.append("MUTATED")
|
||||
|
||||
result = analyzer.analyze_temporal_evolution(graph)
|
||||
self.assertNotIn("MUTATED", result["metrics_tracked"])
|
||||
|
||||
|
||||
class TestTemporalGraphQuery(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.mock_tracker_patcher = patch("semantica.utils.progress_tracker.get_progress_tracker")
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
"""Framework/control entity keys must not be inferred as datatype properties.
|
||||
|
||||
The _CONTROL_FIELDS skip set mirrors exactly what the framework writes to a
|
||||
merged entity's top level (see MergeStrategyManager._merge_entities): no extra
|
||||
guesses, so business attributes that merely share a common name (e.g. source)
|
||||
keep getting inferred.
|
||||
"""
|
||||
|
||||
from semantica.deduplication.merge_strategy import MergeStrategyManager
|
||||
from semantica.ontology.property_generator import PropertyGenerator
|
||||
|
||||
|
||||
def _merged_entity():
|
||||
"""Run a real merge so the entity carries the framework's actual top-level keys."""
|
||||
manager = MergeStrategyManager(default_strategy="keep_most_complete")
|
||||
result = manager.merge_entities(
|
||||
[
|
||||
{"id": "b1", "name": "Hangzhou Branch", "type": "ORG", "employee_count": 120},
|
||||
{"id": "b2", "name": "Hangzhou Branch", "type": "ORG"},
|
||||
]
|
||||
)
|
||||
return result.merged_entity
|
||||
|
||||
|
||||
def test_framework_fields_not_inferred_as_data_properties():
|
||||
entity = _merged_entity()
|
||||
classes = [{"name": "Organization", "metadata": {"inferred_from": "ORG"}}]
|
||||
|
||||
properties = PropertyGenerator().infer_properties([entity], [], classes)
|
||||
|
||||
names = {p["name"] for p in properties}
|
||||
for framed in (
|
||||
"properties",
|
||||
"relationships",
|
||||
"metadata",
|
||||
"merged_from",
|
||||
"merge_strategy",
|
||||
):
|
||||
assert framed not in names, f"framework field {framed} leaked as a property"
|
||||
|
||||
|
||||
def test_business_attributes_still_inferred():
|
||||
entity = _merged_entity()
|
||||
classes = [{"name": "Organization", "metadata": {"inferred_from": "ORG"}}]
|
||||
|
||||
properties = PropertyGenerator().infer_properties([entity], [], classes)
|
||||
|
||||
names = {p["name"] for p in properties}
|
||||
assert "name" in names
|
||||
assert "metadata" not in names
|
||||
|
||||
|
||||
def test_source_field_still_inferred_as_business_attribute():
|
||||
"""A top-level 'source' is a business attribute, not a framework field."""
|
||||
entity = {
|
||||
"id": "b1",
|
||||
"name": "Hangzhou Branch",
|
||||
"type": "ORG",
|
||||
"source": "doc-42",
|
||||
}
|
||||
classes = [{"name": "Organization", "metadata": {"inferred_from": "ORG"}}]
|
||||
|
||||
properties = PropertyGenerator().infer_properties([entity], [], classes)
|
||||
|
||||
names = {p["name"] for p in properties}
|
||||
assert "source" in names
|
||||
|
||||
|
||||
def test_unmerged_graphbuilder_entities_infer_business_attributes():
|
||||
"""Flat entities from GraphBuilder (merge_entities=False) must not lose business
|
||||
attributes through _CONTROL_FIELDS: name and domain-specific fields must be
|
||||
inferred, and none of the framework keys should appear in the output."""
|
||||
from semantica.kg.graph_builder import GraphBuilder
|
||||
|
||||
builder = GraphBuilder(merge_entities=False, resolve_conflicts=False)
|
||||
graph = builder.build(
|
||||
{
|
||||
"entities": [
|
||||
{"id": "c1", "name": "Chengdu Plant", "type": "ORG", "headcount": 300},
|
||||
{"id": "c2", "name": "Wuhan Plant", "type": "ORG", "headcount": 450},
|
||||
],
|
||||
"relationships": [],
|
||||
}
|
||||
)
|
||||
entities = graph["entities"]
|
||||
classes = [{"name": "Organization", "metadata": {"inferred_from": "ORG"}}]
|
||||
|
||||
properties = PropertyGenerator().infer_properties(entities, [], classes)
|
||||
|
||||
names = {p["name"] for p in properties}
|
||||
assert "name" in names, "name must be inferred from flat GraphBuilder entities"
|
||||
assert "headcount" in names, "domain business attribute must be inferred"
|
||||
for framed in ("properties", "relationships", "metadata", "merged_from", "merge_strategy"):
|
||||
assert framed not in names, f"framework field {framed!r} must not appear"
|
||||
@@ -638,89 +638,3 @@ Body paragraph under a distinct heading for separation checks.
|
||||
self.SAMPLE * 3, chunk_size=80, ner_method="pattern"
|
||||
)
|
||||
assert len(chunks) >= 1
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Mutable-default regression tests (fix: replace mutable default arguments)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestMutableDefaultRegression:
|
||||
"""Regression tests proving that mutable default arguments do not leak
|
||||
between calls. Each test mutates the list returned / stored by one call
|
||||
and verifies that a subsequent call still receives the *original* default
|
||||
value, not the mutated one.
|
||||
"""
|
||||
|
||||
# --- split_hierarchical -------------------------------------------------
|
||||
|
||||
def test_split_hierarchical_default_levels_are_independent_across_calls(self):
|
||||
"""Mutating the levels list from one call must not affect the next."""
|
||||
text = "Para one.\n\nPara two.\n\nPara three."
|
||||
|
||||
# First call – capture and mutate the levels list indirectly by
|
||||
# passing explicit levels and then appending to a reference.
|
||||
call1_levels: list = ["paragraph"]
|
||||
chunks1 = split_hierarchical(text, levels=call1_levels, chunk_sizes=[1000])
|
||||
# Mutate the list that was passed in.
|
||||
call1_levels.append("MUTATED")
|
||||
|
||||
# Second call with default levels=None must still use the canonical default.
|
||||
chunks2 = split_hierarchical(text)
|
||||
# The function must succeed and produce chunks (not raise because
|
||||
# "MUTATED" is not a valid level name).
|
||||
assert len(chunks2) >= 1
|
||||
|
||||
def test_split_hierarchical_none_default_creates_fresh_list_each_call(self):
|
||||
"""Two calls with levels=None must receive independent list objects."""
|
||||
text = "A sentence.\n\nAnother sentence."
|
||||
|
||||
# Patch the body assignment so we can capture it.
|
||||
captured: list = []
|
||||
original_fn = split_hierarchical.__wrapped__ if hasattr(split_hierarchical, "__wrapped__") else None
|
||||
|
||||
# Use a simpler black-box approach: call twice and verify behaviour.
|
||||
chunks_a = split_hierarchical(text)
|
||||
chunks_b = split_hierarchical(text)
|
||||
|
||||
# Both calls should produce identical results (same default).
|
||||
assert len(chunks_a) == len(chunks_b)
|
||||
assert [c.text for c in chunks_a] == [c.text for c in chunks_b]
|
||||
|
||||
def test_split_hierarchical_default_chunk_sizes_are_independent_across_calls(self):
|
||||
"""Mutating chunk_sizes in one call must not affect the next."""
|
||||
text = "Para A.\n\nPara B."
|
||||
mutable_sizes = [5000, 2000, 1000]
|
||||
split_hierarchical(text, chunk_sizes=mutable_sizes)
|
||||
# Mutate after first call.
|
||||
mutable_sizes[0] = 1 # Would produce very different chunking if leaked.
|
||||
|
||||
# Second call with default chunk_sizes=None must still use canonical defaults.
|
||||
chunks = split_hierarchical(text)
|
||||
assert len(chunks) >= 1
|
||||
|
||||
# --- HierarchicalChunker ------------------------------------------------
|
||||
|
||||
def test_hierarchical_chunker_default_levels_independent_across_instances(self):
|
||||
"""Mutating levels on one instance must not affect a second instance
|
||||
created with the default."""
|
||||
chunker_a = HierarchicalChunker()
|
||||
# Mutate the instance attribute that was built from the default.
|
||||
chunker_a.levels.append("MUTATED")
|
||||
|
||||
chunker_b = HierarchicalChunker()
|
||||
assert "MUTATED" not in chunker_b.levels, (
|
||||
"Mutation of chunker_a.levels leaked into chunker_b — "
|
||||
"mutable default not fixed properly"
|
||||
)
|
||||
|
||||
def test_hierarchical_chunker_default_levels_value(self):
|
||||
"""Default levels must equal the canonical list."""
|
||||
chunker = HierarchicalChunker()
|
||||
assert chunker.levels == ["section", "paragraph", "sentence"]
|
||||
|
||||
def test_hierarchical_chunker_explicit_levels_preserved(self):
|
||||
"""Explicitly passed levels must be stored as given."""
|
||||
custom = ["document", "paragraph"]
|
||||
chunker = HierarchicalChunker(levels=custom)
|
||||
assert chunker.levels == custom
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user