Compare commits

...
30 Commits
Author SHA1 Message Date
KaifAhmad1 9366f07239 test(ontology): assert ontology uri prefix is used for generated IRIs 2026-04-11 13:52:04 +05:30
Mohd Kaif 61676fb321 Merge branch 'main' into ontology 2026-04-11 13:39:10 +05:30
KaifAhmad1 1ea5e5c012 docs(changelog): resolve duplicate snapshot headers and clean unreleased formatting 2026-04-11 13:37:59 +05:30
Mohd KaifandCopilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> 490d9c814b Potential fix for pull request finding 'Unused import'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-04-11 13:11:10 +05:30
KaifAhmad1 d2c20d410c fix(ontology): address #446 follow-up review findings\n\n- prefer label over name for generated IRIs\n- fix datatype range list handling in rdflib path\n- align generated IRIs with ontology uri namespace\n- resolve local subclassOf names to class IRIs\n- expand regression coverage and update changelog 2026-04-11 13:02:49 +05:30
KaifAhmad1 67a8ab1a8e fix(ontology): preserve user-facing schema fields in OWL generation\n\nFixes #446 2026-04-11 12:39:46 +05:30
Mohd Kaif ac4a200f26 Merge pull request #441 from Hawksight-AI/docs
Add manual ontology + Snowflake mapping cookbook
2026-04-09 15:28:44 +05:30
KaifAhmad1andClaude Sonnet 4.6 c1f0cf6f34 Fix 3 bugs in notebook 13 (manual ontology + Snowflake mapping)
- Bug 1: replace dict .get() with dataclass attribute access on
  AssociativeClass (name/connects/temporal/properties)
- Bug 2: add full URI to every ontology property and use BASE_URI-prefixed
  URIs for all relationship types so TripletStore stores hr:<name>
  instead of urn:property:<name>, fixing SPARQL PREFIX hr: queries
- Bug 3: filter None values from EmploymentEvent properties dict so
  open-ended employment does not store the literal string "None" as endDate

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 15:23:19 +05:30
KaifAhmad1andClaude Sonnet 4.6 665f9c080e Add manual ontology + Snowflake mapping cookbook
Adds notebook 13 demonstrating pythonic, no-AI-inference workflow:
hand-designed ontology dict, AssociativeClass reification, explicit
row-to-graph mapping, OWL/SHACL export, and SPARQL query patterns.
Includes SPARQL 1.2 / SHACL 1.2 standards coverage notes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 15:14:19 +05:30
Mohd Kaif 8d32932322 Clarify plugin README install and usage steps (#440) 2026-04-09 13:19:15 +05:30
Mohd Kaif 7a5e8fd981 Merge pull request #439 from Hawksight-AI/utils
Add Claude Skill support, plugin manifests, and plugin folder updates
2026-04-09 12:58:01 +05:30
KaifAhmad1 082ab14d2e Mention cross-platform plugins in main README 2026-04-09 12:39:24 +05:30
KaifAhmad1 14d350378f Expand plugin README for community usage 2026-04-09 12:31:31 +05:30
KaifAhmad1 241a24d75d Expand plugin keywords for domain discovery 2026-04-09 12:22:24 +05:30
KaifAhmad1 b2eb5db87f Align plugin manifests and marketplaces with current docs 2026-04-09 12:18:38 +05:30
KaifAhmad1 74d5980215 Fix causal and explain skill API examples 2026-04-09 12:04:26 +05:30
KaifAhmad1 3b400eb88b Remove write_missing_skills.py utility file as requested 2026-04-08 22:56:43 +05:30
KaifAhmad1 678d891b42 Fix plugin hooks JSON, align Skill docs with repo API, and make skill generation portable 2026-04-08 22:55:11 +05:30
Mohd KaifandCopilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> e60cef9eb7 Potential fix for pull request finding 'File is not always closed'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-04-08 22:47:58 +05:30
KaifAhmad1 79a980d956 Add Claude Skill support, plugin manifests, and plugin folder updates 2026-04-08 22:24:09 +05:30
Mohd Kaif 47828cff0d Restore 'What's New in v0.4.0' section
Reintroduce the 'What's New in v0.4.0' section with detailed features of the Temporal Intelligence Stack.
2026-04-08 19:43:46 +05:30
Mohd Kaif 17289121cb Update README.md 2026-04-08 14:27:08 +05:30
Mohd Kaif b670bc32a4 Refactor Modules section in README
Reorganized and reformatted the Modules section in the README to improve clarity and consistency.
2026-04-08 14:17:14 +05:30
Mohd Kaif 5af6e383ad Merge pull request #438 from Hawksight-AI/docs
Docs Improve README — crisp bullets, plain English, v0.4.0 features
2026-04-08 14:12:45 +05:30
KaifAhmad1andClaude Sonnet 4.6 cd70481034 fix(docs): align all README code examples with actual semantica API
Audited every module's __init__.py and source files. Fixes:

1. Temporal GraphRAG example — was garbled (two sections merged into one
   code block). Restored clean single example with correct imports.

2. Semantic extraction — extract_entities/extract_relations/extract_triplets
   are not standalone functions; replaced with correct class-based API:
   NERExtractor().extract_entities(), RelationExtractor().extract_relations(),
   TripletExtractor().extract_triplets(). extract_relations_llm is only in
   semantica.semantic_extract.methods (not re-exported from __init__) and
   requires entities as its required second positional arg — fixed both.

3. ReteEngine — add_rule() and match() do not exist on ReteEngine.
   Replaced with correct API: Rule/Fact dataclasses + build_network([rule])
   + add_fact(fact) + match_patterns().

4. PipelineBuilder — add_stage(name, callable) does not exist; replaced
   with add_step(name, type_str, **config). with_parallel_workers() does not
   exist; replaced with set_parallelism(n). Pipeline.run() takes no
   input_path; removed that kwarg.

5. ProvenanceTracker.track_entity — source_url is not a valid kwarg;
   second param is positional source. Fixed in features list and comment.

6. Leftover SHACL section — removed second copy of the SHACL code block
   that still referenced to_shacl(), export_shacl(), validate_graph() which
   do not exist on OntologyEngine (confirmed in engine.py).

7. Duplicate pip install lines — semantica[shacl] and semantica[db-snowflake]
   appeared twice in the installation block; removed duplicates.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 13:55:57 +05:30
KaifAhmad1andClaude Sonnet 4.6 1cf13d0188 fix(docs): remove duplicate vector_store kwarg in docs/index.md quick-start example
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 13:43:53 +05:30
KaifAhmad1andClaude Sonnet 4.6 069af2a038 fix(docs): resolve 4 Qodo bot review bugs in README and docs/index.md
Bug 1 — Broken snapshot example:
- Replace graph.add_decision(category=...) with graph.record_decision()
  which accepts keyword args (add_decision expects a Decision object)
- Define context = AgentContext(...) before calling context.checkpoint()
  and context.diff_checkpoints() — these APIs live on AgentContext, not ContextGraph

Bug 2 — Invalid KG example imports:
- Remove KnowledgeGraph, Entity, Relationship, CentralityAnalyzer — not exported
- Replace with GraphBuilder.build() (dict-based API) and CentralityCalculator
  which are the actual public exports from semantica.kg
- Fix pipeline example: KnowledgeGraph() → GraphBuilder()

Bug 3 — Nonexistent SHACL APIs:
- Remove export_shacl() and validate_graph() calls — not on OntologyEngine
- Rewrite SHACL section to use real APIs: from_data(), export_owl(),
  validate(), from_text(), to_owl()
- Remove semantica[shacl] install instructions (extra not in pyproject.toml)

Bug 4 — Stale docs version badge:
- docs/index.md: bump version badge and release tag link from v0.3.0 → v0.4.0

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 13:43:33 +05:30
Mohd Kaif 35ebccbdd7 Merge branch 'main' into docs 2026-04-08 13:19:56 +05:30
KaifAhmad1andClaude Sonnet 4.6 de432d5eb4 docs: improve README with crisp bullets, plain English, and v0.4.0 features
- Replace dense tables with scannable bullet points throughout
- Add plain-English descriptions before each feature section
- Update What's New to cover full v0.4.0 temporal stack, SKOS, SHACL, and fixes
- Add learn-more references linking to docs and cookbook per section
- Slim code examples to focused real-world scenarios, remove API-dump patterns
- Fix duplicate badges, bump version badge to 0.4.0
- Fill empty Learning Resources section

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 13:13:01 +05:30
KaifAhmad1andClaude Sonnet 4.6 129edaf05b docs: rewrite and polish documentation site
- Rewrote index.md to match README (tagline, badges, Problem/Solution text)
- Improved getting-started, concepts, quickstart, installation, faq, use-cases, contributing, glossary, learning-more, examples, modules, architecture, cookbook, deep-dive pages: tighter prose, fixed headings/bullets, removed inconsistencies and duplicate sections
- Removed overuse of emojis from headings in integration pages (docling, snowflake)
- Fixed change_management reference page: closed unclosed JSON code block that broke the right TOC, demoted noisy sub-headings to bold text
- CSS layout: widened content area (max-width 1440px grid, left sidebar 11rem, right TOC narrowed to 11rem for broader content), tightened TOC spacing and font size, fixed word-wrap/overflow on TOC links
- Added mkdocs_local.yml for local serving without mkdocs-jupyter plugin

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 18:31:39 +05:30
36 changed files with 4672 additions and 689 deletions
+57
View File
@@ -0,0 +1,57 @@
---
name: semantica
description: Semantica full-stack knowledge graph skill for context graphs, decision intelligence, explainability, extraction, reasoning, visualization, ontology, provenance, policy, and export workflows.
---
# Semantica
This Skill helps Claude apply Semantica knowledge graph capabilities to context graph analysis, decision intelligence, explainability, semantic extraction, graph analytics, reasoning, provenance, ontology, policy, ingestion, deduplication, and export.
## When to use this Skill
- The user asks about knowledge graphs, entities, relations, triplets, or semantic extraction.
- A task requires context graph analysis, graph topology, centrality, communities, paths, or embeddings.
- The request involves decision intelligence, causal influence, decision graphs, or outcome analysis.
- The user asks for explainability, decision rationale, or transparency for graph results.
- The request involves reasoning: deductive, abductive, SPARQL, Datalog, or Rete rules.
- The user needs provenance, audit history, lineage tracking, or change tracing.
- The request is about ontology modeling, schema validation, or policy enforcement.
- Data must be ingested from files, databases, APIs, repositories, or MCP servers.
- There is a need to deduplicate entities, normalize graph data, or merge duplicate graph objects.
- The user wants to export graphs to JSON, RDF, Parquet, CSV, GraphML, or similar.
## What this Skill contains
- Semantic extraction guidance for NER, relation extraction, event detection, coreference resolution, and triplet generation.
- Context graph and graph analytics workflows for topology, centrality, community detection, path finding, embeddings, and decision insights.
- Decision intelligence support for causal reasoning, decision impact, decision graphs, and outcome analysis.
- Explainability guidance for decision rationale, graph reasoning, rule traces, and result transparency.
- Reasoning support for logic, hypotheses, SPARQL, Datalog, and rule-based inference.
- Provenance and audit guidance for tracing sources, recording changes, and verifying graph lineage.
- Ontology guidance for defining concepts, validating schemas, and modeling relationships.
- Policy checks for compliance evaluation and graph governance.
- Temporal analysis guidance for event timelines and graph evolution.
- Deduplication support for duplicate detection, fuzzy matching, and graph cleanup.
- Export workflows for sharing results in multiple structured formats.
## Best prompt patterns
Use clear task descriptions, and mention the desired output format when possible.
- "Extract entities, relations, and events from this text and summarize the resulting graph."
- "Analyze this context graph and show the top 5 most influential nodes."
- "Generate a decision intelligence report with causal impact and explainability."
- "Run a provenance trace for node X and describe its history."
- "Validate the ontology for this graph and report any schema problems."
- "Ingest the data from this MCP server and merge it into the current graph."
- "Export the graph to JSON and GraphML with node and edge metadata."
## How Claude should use this Skill
1. Read the YAML metadata and identify whether the request matches Semantica graph, context graph, decision intelligence, or extraction tasks.
2. Load this Skill when the request mentions Semantica, knowledge graphs, context graphs, decision intelligence, explainability, reasoning, or provenance.
3. Use the instructions here to choose the right workflow and then read additional files or scripts only if needed.
## Authoring note
This Skill is purposely concise and focused on task selection. It is not intended to include every detail; Claude should use the filesystem-based model to load any extra reference files only when asked.
+1116 -35
View File
File diff suppressed because it is too large Load Diff
+460 -590
View File
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,435 @@
{
"nbformat": 4,
"nbformat_minor": 5,
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.10.0"
}
},
"cells": [
{
"cell_type": "markdown",
"id": "cell-0",
"metadata": {},
"source": [
"[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/Hawksight-AI/semantica/blob/main/cookbook/advanced/13_Manual_Ontology_Snowflake_Mapping.ipynb)\n",
"\n",
"# Manual Ontology + Snowflake Mapping\n",
"\n",
"This notebook answers a specific workflow:\n",
"\n",
"> *\"I want to design the ontology myself — not have AI infer it from my tables — and then map Snowflake data to it explicitly.\"*\n",
"\n",
"### What this notebook demonstrates\n",
"\n",
"| Step | What happens | Who controls it |\n",
"|---|---|---|\n",
"| 1 | Design ontology classes and properties | **You** (Python dict) |\n",
"| 2 | Model n-ary facts with reification | **You** (`AssociativeClassBuilder`) |\n",
"| 3 | Pull rows from Snowflake | Semantica `SnowflakeIngestor` |\n",
"| 4 | Map columns → ontology-aligned graph | **You** (explicit transform) |\n",
"| 5 | Validate + export OWL / SHACL | Semantica `OntologyEngine` |\n",
"| 6 | Load to triplet store and query | Semantica `TripletStore` |\n",
"\n",
"### What this notebook does NOT do\n",
"\n",
"- No LLM-driven ontology generation\n",
"- No schema introspection or table-to-class inference\n",
"- No \"suggest ontology from my data\"\n",
"\n",
"### Standards coverage\n",
"\n",
"| Feature | Status |\n",
"|---|---|\n",
"| OWL 2 (Turtle / RDF-XML) | Supported |\n",
"| SHACL 1.1 shapes | Supported |\n",
"| SPARQL 1.1 | Supported |\n",
"| Reification / n-ary facts | Supported via `AssociativeClassBuilder` |\n",
"| SPARQL 1.2 (reifier annotation, `LATERAL`) | Planned |\n",
"| SHACL 1.2 (`sh:severity` extensions, SHACL-AF) | Planned |"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cell-1",
"metadata": {},
"outputs": [],
"source": [
"!pip install -qU semantica"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cell-2",
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"from typing import Any, Dict, List\n",
"\n",
"from semantica.ingest import SnowflakeIngestor\n",
"from semantica.kg.methods import build_kg\n",
"from semantica.ontology import AssociativeClassBuilder, OntologyEngine\n",
"from semantica.triplet_store import TripletStore"
]
},
{
"cell_type": "markdown",
"id": "cell-3",
"metadata": {},
"source": [
"## Step 1: Hand-Design the Ontology in Python\n",
"\n",
"You define every class and property explicitly. Nothing is read from Snowflake at this stage.\n",
"\n",
"**Design decisions that belong to you:**\n",
"- Which classes exist and what they mean\n",
"- Which properties are datatype vs. object properties\n",
"- Domain, range, and cardinality constraints\n",
"- Which properties are required (later enforced by SHACL)\n",
"\n",
"This dict versions with your code. It does not change when your database schema changes."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cell-4",
"metadata": {},
"outputs": [],
"source": "BASE_URI = \"https://example.com/hr/\"\n\n# Your ontology — designed by you, not inferred by Semantica.\nontology: Dict[str, Any] = {\n \"name\": \"EmploymentDomainOntology\",\n \"uri\": f\"{BASE_URI}EmploymentDomainOntology\",\n \"namespace\": {\"base_uri\": BASE_URI},\n\n # You decide the class taxonomy\n \"classes\": [\n {\"name\": \"Person\", \"uri\": f\"{BASE_URI}Person\"},\n {\"name\": \"Organization\", \"uri\": f\"{BASE_URI}Organization\"},\n {\"name\": \"Role\", \"uri\": f\"{BASE_URI}Role\"},\n # EmploymentEvent is a reification node.\n # It connects Person + Organization + Role and carries salary/date context.\n {\"name\": \"EmploymentEvent\", \"uri\": f\"{BASE_URI}EmploymentEvent\"},\n ],\n\n # Each property carries a full URI so TripletStore stores it as hr:<name>\n # rather than the default urn:property:<name>.\n # This ensures SPARQL queries using PREFIX hr: match what is actually stored.\n \"properties\": [\n # Datatype properties\n {\"name\": \"name\", \"uri\": f\"{BASE_URI}name\", \"type\": \"datatype\", \"domain\": \"Person\", \"range\": \"string\", \"required\": True},\n {\"name\": \"legalName\", \"uri\": f\"{BASE_URI}legalName\", \"type\": \"datatype\", \"domain\": \"Organization\", \"range\": \"string\", \"required\": True},\n {\"name\": \"title\", \"uri\": f\"{BASE_URI}title\", \"type\": \"datatype\", \"domain\": \"Role\", \"range\": \"string\", \"required\": True},\n {\"name\": \"startDate\", \"uri\": f\"{BASE_URI}startDate\", \"type\": \"datatype\", \"domain\": \"EmploymentEvent\", \"range\": \"date\"},\n {\"name\": \"endDate\", \"uri\": f\"{BASE_URI}endDate\", \"type\": \"datatype\", \"domain\": \"EmploymentEvent\", \"range\": \"date\"},\n {\"name\": \"salary\", \"uri\": f\"{BASE_URI}salary\", \"type\": \"datatype\", \"domain\": \"EmploymentEvent\", \"range\": \"decimal\"},\n\n # Object properties — reification spokes (required)\n {\"name\": \"employee\", \"uri\": f\"{BASE_URI}employee\", \"type\": \"object\", \"domain\": \"EmploymentEvent\", \"range\": \"Person\", \"required\": True},\n {\"name\": \"employer\", \"uri\": f\"{BASE_URI}employer\", \"type\": \"object\", \"domain\": \"EmploymentEvent\", \"range\": \"Organization\", \"required\": True},\n {\"name\": \"role\", \"uri\": f\"{BASE_URI}role\", \"type\": \"object\", \"domain\": \"EmploymentEvent\", \"range\": \"Role\", \"required\": True},\n\n # Shortcut edges — direct person→org / person→role without traversing the event node\n {\"name\": \"worksFor\", \"uri\": f\"{BASE_URI}worksFor\", \"type\": \"object\", \"domain\": \"Person\", \"range\": \"Organization\"},\n {\"name\": \"hasRole\", \"uri\": f\"{BASE_URI}hasRole\", \"type\": \"object\", \"domain\": \"Person\", \"range\": \"Role\"},\n ],\n}\n\nontology"
},
{
"cell_type": "markdown",
"id": "cell-5",
"metadata": {},
"source": [
"## Step 2: Reification — Modeling N-Ary Facts\n",
"\n",
"**The problem with binary triples:**\n",
"A simple triple `(Alice, worksFor, Acme)` cannot carry extra context such as salary, start date, or role.\n",
"Standard RDF reification and OWL n-ary patterns solve this by introducing an intermediate node.\n",
"\n",
"Semantica's `AssociativeClassBuilder` is the Pythonic API for this pattern:\n",
"\n",
"```\n",
"EmploymentEvent\n",
" ├── employee → Person (required)\n",
" ├── employer → Organization (required)\n",
" ├── role → Role (required)\n",
" ├── startDate → xsd:date\n",
" ├── endDate → xsd:date\n",
" └── salary → xsd:decimal\n",
"```\n",
"\n",
"**On SPARQL 1.1 vs. SPARQL 1.2:**\n",
"- **SPARQL 1.1 (current):** traverse the event node explicitly — `?event hr:employee ?person ; hr:salary ?salary`\n",
"- **SPARQL 1.2 (planned):** the draft reifier annotation syntax allows attaching context to triples directly, without a separate intermediate node. Semantica will adopt this once the spec is ratified.\n",
"\n",
"**On SHACL 1.1 vs. SHACL 1.2:**\n",
"- **SHACL 1.1 (current):** `sh:NodeShape` + `sh:PropertyShape` constraints are exported for all `required` properties and enforced at load time.\n",
"- **SHACL 1.2 (planned):** `sh:severity` profile extensions and SHACL-AF rules are on the roadmap."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cell-6",
"metadata": {},
"outputs": [],
"source": "assoc_builder = AssociativeClassBuilder()\n\nemployment_assoc = assoc_builder.create_associative_class(\n name=\"EmploymentEvent\",\n connects=[\"Person\", \"Organization\", \"Role\"],\n temporal=True, # adds startDate / endDate handling\n properties={\n \"startDate\": \"xsd:date\",\n \"endDate\": \"xsd:date\",\n \"salary\": \"xsd:decimal\",\n },\n)\n\nvalidation_result = assoc_builder.validate_associative_class(employment_assoc)\n\n# AssociativeClass is a dataclass — use attribute access, not .get()\nprint(\"AssociativeClass structure:\")\nprint(f\" name: {employment_assoc.name}\")\nprint(f\" connects: {employment_assoc.connects}\")\nprint(f\" temporal: {employment_assoc.temporal}\")\nprint(f\" properties: {list(employment_assoc.properties.keys())}\")\nprint(f\"\\nValidation passed: {validation_result}\")"
},
{
"cell_type": "markdown",
"id": "cell-7",
"metadata": {},
"source": [
"## Step 3: Ingest Snowflake Rows (Extraction Only)\n",
"\n",
"`SnowflakeIngestor` retrieves rows — nothing more. It does **not**:\n",
"- Inspect your table schema\n",
"- Suggest classes or properties\n",
"- Infer relationships from column names\n",
"\n",
"Set `USE_LIVE_SNOWFLAKE=true` plus the env vars below to connect to a real warehouse.\n",
"Otherwise the stub data is used."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cell-8",
"metadata": {},
"outputs": [],
"source": [
"def fetch_rows_from_snowflake() -> List[Dict[str, Any]]:\n",
" if os.getenv(\"USE_LIVE_SNOWFLAKE\", \"false\").lower() != \"true\":\n",
" return [\n",
" {\n",
" \"EMPLOYEE_ID\": \"E100\",\n",
" \"EMPLOYEE_NAME\": \"Alice Johnson\",\n",
" \"ORG_ID\": \"O10\",\n",
" \"ORG_NAME\": \"Acme Corp\",\n",
" \"ROLE_ID\": \"R7\",\n",
" \"ROLE_TITLE\": \"Senior Engineer\",\n",
" \"START_DATE\": \"2025-01-15\",\n",
" \"END_DATE\": None,\n",
" \"SALARY\": 160000,\n",
" },\n",
" {\n",
" \"EMPLOYEE_ID\": \"E101\",\n",
" \"EMPLOYEE_NAME\": \"Bob Singh\",\n",
" \"ORG_ID\": \"O10\",\n",
" \"ORG_NAME\": \"Acme Corp\",\n",
" \"ROLE_ID\": \"R9\",\n",
" \"ROLE_TITLE\": \"Data Architect\",\n",
" \"START_DATE\": \"2024-09-01\",\n",
" \"END_DATE\": None,\n",
" \"SALARY\": 185000,\n",
" },\n",
" ]\n",
"\n",
" ingestor = SnowflakeIngestor(\n",
" account=os.getenv(\"SNOWFLAKE_ACCOUNT\"),\n",
" user=os.getenv(\"SNOWFLAKE_USER\"),\n",
" password=os.getenv(\"SNOWFLAKE_PASSWORD\"),\n",
" warehouse=os.getenv(\"SNOWFLAKE_WAREHOUSE\"),\n",
" database=os.getenv(\"SNOWFLAKE_DATABASE\"),\n",
" schema=os.getenv(\"SNOWFLAKE_SCHEMA\", \"PUBLIC\"),\n",
" )\n",
" query = (\n",
" \"SELECT EMPLOYEE_ID, EMPLOYEE_NAME, \"\n",
" \"ORG_ID, ORG_NAME, ROLE_ID, ROLE_TITLE, \"\n",
" \"START_DATE, END_DATE, SALARY \"\n",
" \"FROM HR_EMPLOYMENT_FACT\"\n",
" )\n",
" data = ingestor.ingest_query(query)\n",
" ingestor.close()\n",
" return data.data\n",
"\n",
"\n",
"rows = fetch_rows_from_snowflake()\n",
"rows[:2]"
]
},
{
"cell_type": "markdown",
"id": "cell-9",
"metadata": {},
"source": [
"## Step 4: Map Rows to Ontology Concepts Explicitly\n",
"\n",
"This is the semantic transformation layer — the part that makes your ontology real.\n",
"\n",
"Semantica does not guess which column becomes which entity or property.\n",
"Every assignment is code you write and own:\n",
"\n",
"- **Stable node IDs** — deterministic, collision-safe, derived from business keys\n",
"- **Class assignment** — matches what you declared in Step 1\n",
"- **Property routing** — each column value goes to the correct ontology property\n",
"- **Reification wiring** — `EmploymentEvent` is linked to its three participants\n",
"\n",
"When your Snowflake schema changes, only this function needs updating. The ontology stays stable."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cell-10",
"metadata": {},
"outputs": [],
"source": "def map_rows_to_kg(rows: List[Dict[str, Any]]) -> Dict[str, Any]:\n entities: Dict[str, Dict[str, Any]] = {}\n relationships: List[Dict[str, Any]] = []\n\n for row in rows:\n # Stable, deterministic node IDs derived from business keys\n person_id = f\"person:{row['EMPLOYEE_ID']}\"\n org_id = f\"org:{row['ORG_ID']}\"\n role_id = f\"role:{row['ROLE_ID']}\"\n # Event ID includes all three participants + start date so that\n # a re-hired employee gets a distinct event node, not an overwrite.\n event_id = f\"employment:{row['EMPLOYEE_ID']}:{row['ORG_ID']}:{row['START_DATE']}\"\n\n # Entities — \"type\" must match a class name from Step 1\n entities[person_id] = {\n \"id\": person_id,\n \"type\": \"Person\",\n \"properties\": {\"name\": row[\"EMPLOYEE_NAME\"]},\n }\n entities[org_id] = {\n \"id\": org_id,\n \"type\": \"Organization\",\n \"properties\": {\"legalName\": row[\"ORG_NAME\"]},\n }\n entities[role_id] = {\n \"id\": role_id,\n \"type\": \"Role\",\n \"properties\": {\"title\": row[\"ROLE_TITLE\"]},\n }\n\n # Reification node — filter out None values so TripletStore does not\n # stringify None as the literal \"None\" for open-ended employment.\n event_props = {\n \"startDate\": row[\"START_DATE\"],\n \"endDate\": row[\"END_DATE\"],\n \"salary\": row[\"SALARY\"],\n }\n entities[event_id] = {\n \"id\": event_id,\n \"type\": \"EmploymentEvent\",\n \"properties\": {k: v for k, v in event_props.items() if v is not None},\n }\n\n # Full URIs for relationship types so TripletStore stores hr:<type>\n # instead of the default urn:property:<type>, keeping SPARQL consistent.\n relationships.extend([\n # Shortcut edges — fast SPARQL when context is not needed\n {\"source\": person_id, \"target\": org_id, \"type\": f\"{BASE_URI}worksFor\"},\n {\"source\": person_id, \"target\": role_id, \"type\": f\"{BASE_URI}hasRole\"},\n # Reification spokes — full context via the event node\n {\"source\": event_id, \"target\": person_id, \"type\": f\"{BASE_URI}employee\"},\n {\"source\": event_id, \"target\": org_id, \"type\": f\"{BASE_URI}employer\"},\n {\"source\": event_id, \"target\": role_id, \"type\": f\"{BASE_URI}role\"},\n ])\n\n return build_kg([{\"entities\": list(entities.values()), \"relationships\": relationships}])\n\n\nkg = map_rows_to_kg(rows)\nprint(f\"Entities built: {len(kg.get('entities', []))}\")\nprint(f\"Relationships built: {len(kg.get('relationships', []))}\")\n\nsample = next((e for e in kg[\"entities\"] if e[\"type\"] == \"EmploymentEvent\"), None)\nprint(f\"\\nSample EmploymentEvent node: {sample}\")"
},
{
"cell_type": "markdown",
"id": "cell-11",
"metadata": {},
"source": [
"## Step 5: Validate Ontology and Export OWL + SHACL\n",
"\n",
"`OntologyEngine` validates your ontology dict and serialises it to standards-compliant files.\n",
"\n",
"**Output files:**\n",
"- `employment_manual_ontology.ttl` — OWL 2 Turtle\n",
"- `employment_manual_shapes.ttl` — SHACL 1.1 node and property shapes\n",
"\n",
"**Standards status:**\n",
"\n",
"| Standard | Semantica support |\n",
"|---|---|\n",
"| SPARQL 1.1 | Full |\n",
"| SHACL 1.1 (`sh:NodeShape`, `sh:PropertyShape`, `sh:minCount`, `sh:datatype`, `sh:class`) | Full |\n",
"| SPARQL 1.2 (reifier annotation syntax, `LATERAL`) | Tracked — not yet implemented |\n",
"| SHACL 1.2 (`sh:severity` profiles, SHACL-AF extensions) | Tracked — not yet implemented |"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cell-12",
"metadata": {},
"outputs": [],
"source": [
"engine = OntologyEngine(base_uri=BASE_URI)\n",
"\n",
"validation = engine.validate(ontology)\n",
"owl_ttl = engine.to_owl(ontology, format=\"turtle\")\n",
"shacl_ttl = engine.to_shacl(ontology, format=\"turtle\")\n",
"\n",
"engine.export_owl(ontology, \"employment_manual_ontology.ttl\", format=\"turtle\")\n",
"engine.export_shacl(ontology, \"employment_manual_shapes.ttl\", format=\"turtle\")\n",
"\n",
"print(f\"Ontology valid: {validation.valid}\")\n",
"print(f\"Ontology consistent: {validation.consistent}\")\n",
"print(f\"OWL output: {len(owl_ttl):,} chars → employment_manual_ontology.ttl\")\n",
"print(f\"SHACL output: {len(shacl_ttl):,} chars → employment_manual_shapes.ttl\")\n",
"\n",
"print(\"\\n--- SHACL shapes (first 20 lines) ---\")\n",
"print(\"\\n\".join(shacl_ttl.splitlines()[:20]))"
]
},
{
"cell_type": "markdown",
"id": "cell-13",
"metadata": {},
"source": [
"## Best-Practice Architecture\n",
"\n",
"```\n",
"┌──────────────────────────────────┐\n",
"│ Ontology as code (Python dict) │ ← versioned alongside your application\n",
"│ + AssociativeClass for n-ary │\n",
"└───────────────┬──────────────────┘\n",
" │ validate + export\n",
" ▼\n",
"┌───────────────────────────────────┐\n",
"│ OWL 2 Turtle │ SHACL 1.1 │ ← standards-compliant artifacts\n",
"└───────────────┬───────────────────┘\n",
" │\n",
" ▼\n",
"┌──────────────────────────────────┐\n",
"│ Snowflake — raw data access │ ← no schema introspection\n",
"└───────────────┬──────────────────┘\n",
" │ explicit mapping layer\n",
" ▼\n",
"┌──────────────────────────────────┐\n",
"│ Ontology-aligned KG │ ← types, IDs, edges match Step 1\n",
"└───────────────┬──────────────────┘\n",
" │ optional\n",
" ▼\n",
"┌──────────────────────────────────┐\n",
"│ Triplet store + SPARQL 1.1 │\n",
"└──────────────────────────────────┘\n",
"```\n",
"\n",
"**Why this split matters:**\n",
"If Semantica inferred the ontology from your Snowflake schema, every schema migration would risk silently changing your semantic model.\n",
"With this pattern, schema changes only touch the mapping function in Step 4 — the ontology remains stable and under your control."
]
},
{
"cell_type": "markdown",
"id": "cell-14",
"metadata": {},
"source": [
"## SPARQL Query Patterns\n",
"\n",
"Two query styles are available because we wrote both shortcut edges and reification spokes.\n",
"\n",
"### Simple lookup — shortcut edge (no context needed)\n",
"\n",
"```sparql\n",
"PREFIX hr: <https://example.com/hr/>\n",
"\n",
"SELECT ?personName ?orgName\n",
"WHERE {\n",
" ?person a hr:Person ;\n",
" hr:name ?personName ;\n",
" hr:worksFor ?org .\n",
" ?org hr:legalName ?orgName .\n",
"}\n",
"```\n",
"\n",
"### Contextual lookup — via reification node (salary, dates, role)\n",
"\n",
"```sparql\n",
"PREFIX hr: <https://example.com/hr/>\n",
"\n",
"SELECT ?personName ?roleTitle ?salary ?startDate\n",
"WHERE {\n",
" ?event a hr:EmploymentEvent ;\n",
" hr:employee ?person ;\n",
" hr:role ?role ;\n",
" hr:salary ?salary ;\n",
" hr:startDate ?startDate .\n",
" ?person hr:name ?personName .\n",
" ?role hr:title ?roleTitle .\n",
"}\n",
"ORDER BY DESC(?salary)\n",
"```\n",
"\n",
"### Future: SPARQL 1.2 reifier syntax\n",
"\n",
"The SPARQL 1.2 draft introduces annotation syntax that lets you attach context directly to triples, without a separate intermediate node.\n",
"Once the spec is ratified Semantica will adopt it, and the contextual query above may be expressible more concisely."
]
},
{
"cell_type": "markdown",
"id": "cell-15",
"metadata": {},
"source": [
"## Step 6 (Optional): Load to Triplet Store and Run SPARQL\n",
"\n",
"Set `STORE_TO_TRIPLET=true` to load the KG into a live triplet store and run the contextual reification query."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cell-16",
"metadata": {},
"outputs": [],
"source": [
"if os.getenv(\"STORE_TO_TRIPLET\", \"false\").lower() == \"true\":\n",
" store = TripletStore(\n",
" backend=os.getenv(\"TRIPLET_BACKEND\", \"blazegraph\"),\n",
" endpoint=os.getenv(\"TRIPLET_ENDPOINT\", \"http://localhost:9999/blazegraph\"),\n",
" namespace=os.getenv(\"TRIPLET_NAMESPACE\", \"kb\"),\n",
" )\n",
" store_result = store.store(knowledge_graph=kg, ontology=ontology)\n",
" print(\"Store result:\", store_result)\n",
"\n",
" # Contextual reification query — person + role + salary via EmploymentEvent\n",
" query = \"\"\"\n",
" PREFIX hr: <https://example.com/hr/>\n",
"\n",
" SELECT ?personName ?roleTitle ?salary ?startDate\n",
" WHERE {\n",
" ?event a hr:EmploymentEvent ;\n",
" hr:employee ?person ;\n",
" hr:role ?role ;\n",
" hr:salary ?salary ;\n",
" hr:startDate ?startDate .\n",
" ?person hr:name ?personName .\n",
" ?role hr:title ?roleTitle .\n",
" }\n",
" ORDER BY DESC(?salary)\n",
" LIMIT 10\n",
" \"\"\"\n",
" result = store.execute_query(query)\n",
" print(result)\n",
"else:\n",
" print(\"Skipping triplet-store load/query (set STORE_TO_TRIPLET=true to enable)\")"
]
}
]
}
+1
View File
@@ -44,6 +44,7 @@ from semantica.context import AgentContext, ContextGraph
from semantica.vector_store import VectorStore
context = AgentContext(
vector_store=VectorStore(backend="faiss", dimension=768),
vector_store=VectorStore(backend="inmemory"),
knowledge_graph=ContextGraph(advanced_analytics=True),
decision_tracking=True,
+2 -2
View File
@@ -6,7 +6,7 @@
<a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.8+-blue.svg" alt="Python 3.8+"></a>
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
<a href="https://pypi.org/project/semantica/"><img src="https://img.shields.io/pypi/v/semantica.svg" alt="PyPI"></a>
<a href="https://github.com/Hawksight-AI/semantica/releases/tag/v0.3.0"><img src="https://img.shields.io/badge/version-0.3.0-brightgreen.svg" alt="Version"></a>
<a href="https://github.com/Hawksight-AI/semantica/releases/tag/v0.4.0"><img src="https://img.shields.io/badge/version-0.4.0-brightgreen.svg" alt="Version"></a>
<a href="https://pepy.tech/project/semantica"><img src="https://static.pepy.tech/badge/semantica" alt="Total Downloads"></a>
<a href="https://github.com/Hawksight-AI/semantica/actions"><img src="https://github.com/Hawksight-AI/semantica/workflows/CI/badge.svg" alt="CI"></a>
<a href="https://discord.gg/sV34vps5hH"><img src="https://img.shields.io/badge/Discord-Join-5865F2?logo=discord&logoColor=white" alt="Discord"></a>
@@ -65,7 +65,7 @@ from semantica.context import AgentContext, ContextGraph
from semantica.vector_store import VectorStore
context = AgentContext(
vector_store=VectorStore(backend="inmemory"),
vector_store=VectorStore(backend="faiss", dimension=768),
knowledge_graph=ContextGraph(advanced_analytics=True),
decision_tracking=True,
)
+135
View File
@@ -0,0 +1,135 @@
# Semantica Plugins (Community Guide)
Semantica ships a shared plugin bundle under `plugins/` with skills, agents, and hooks for knowledge graphs, context graphs, decision intelligence, reasoning, explainability, provenance, ontology, and export workflows.
This README is for community users who want to install or reuse the plugin package across Claude, Cursor, and Codex.
## Supported Platforms
- Claude Code
- Cursor
- Codex
## Prerequisites
1. Clone the repository:
```bash
git clone https://github.com/Hawksight-AI/semantica.git
cd semantica
```
2. Ensure the plugin bundle exists at:
```text
plugins/
skills/
agents/
hooks/
.claude-plugin/
.cursor-plugin/
.codex-plugin/
```
## Plugin Contents
- `skills/`: 17 domain skills (`causal`, `decision`, `explain`, `reason`, `temporal`, etc.)
- `agents/`: specialized agents (`decision-advisor`, `explainability`, `kg-assistant`)
- `hooks/hooks.json`: plugin hook configuration
- `.claude-plugin/plugin.json`: Claude manifest
- `.cursor-plugin/plugin.json`: Cursor manifest
- `.codex-plugin/plugin.json`: Codex manifest
- `*/marketplace.json`: local marketplace definitions
## Install and Use in Claude Code
### Local install (fastest)
From the repository root:
```bash
claude --plugin-dir ./plugins
```
If your Claude setup uses plugin commands in-session, use:
```bash
/plugin install ./plugins
```
### Install from a GitHub marketplace
Add a marketplace hosted in git:
```bash
/plugin marketplace add <owner>/semantica
```
Install Semantica from that marketplace:
```bash
/plugin install semantica@<marketplace-name>
```
### Verify in Claude
Run one of these in chat:
```text
/semantica:decision list
/semantica:explain decision <decision_id>
```
If the plugin is installed correctly, Claude should recognize the `/semantica:*` skills.
## Install and Use in Codex
1. Ensure your repo marketplace exists at `.agents/plugins/marketplace.json`.
2. Point the plugin entry `source.path` to `./plugins` (or your chosen plugin directory).
3. Restart Codex and install from the marketplace UI.
Codex manifest used by this bundle:
- `.codex-plugin/plugin.json`
### Verify in Codex
After install, run a Semantica skill command in chat, for example:
```text
/semantica:causal chain --subject <decision_id> --depth 3
```
## Install and Use in Cursor
Cursor reads plugin metadata from:
- `.cursor-plugin/plugin.json`
- `.cursor-plugin/marketplace.json`
If you maintain a team/community plugin repo, publish this `plugins/` directory and refresh/reinstall in Cursor Marketplace to pick up updates.
### Verify in Cursor
Try one of these commands:
```text
/semantica:reason deductive "IF Person(x) THEN Mortal(x)"
/semantica:visualize topology
```
## First Commands to Try
After installing on any platform, these are good smoke tests:
1. `/semantica:decision record <category> "<scenario>" "<reasoning>" <outcome> <confidence>`
2. `/semantica:decision list`
3. `/semantica:causal chain --subject <decision_id> --depth 3`
4. `/semantica:explain decision <decision_id>`
5. `/semantica:validate graph`
## Community Notes
- Keep plugin name/version/keywords updated in each manifest before publishing.
- Keep skill frontmatter consistent (`name` + `description`) for reliable discovery.
- For open-source sharing, include this folder as-is so skills, agents, and hooks remain bundled.
+16
View File
@@ -0,0 +1,16 @@
{
"name": "semantica-local",
"plugins": [
{
"name": "semantica",
"description": "Semantica plugin for Claude: knowledge graph skills, reasoning, extraction, and visualization.",
"source": "./",
"category": "Productivity",
"tags": [
"knowledge-graph",
"reasoning",
"semantica"
]
}
]
}
+30
View File
@@ -0,0 +1,30 @@
{
"name": "semantica",
"description": "Full-stack knowledge graph skills: semantic extraction, decision intelligence, context graphs, reasoning, explainability, ontology, provenance, deduplication, visualization, and multi-format export.",
"version": "0.1.0",
"author": {
"name": "Semantica Contributors"
},
"homepage": "https://github.com/Hawksight-AI/semantica",
"repository": "https://github.com/Hawksight-AI/semantica",
"license": "MIT",
"keywords": [
"semantica",
"knowledge graph",
"context graphs",
"decision intelligence",
"explainability",
"causal analysis",
"provenance",
"ontology",
"graph analytics",
"semantic extraction",
"visualization",
"reasoning",
"extraction",
"mcp"
],
"skills": "./skills",
"agents": "./agents",
"hooks": "./hooks/hooks.json"
}
+21
View File
@@ -0,0 +1,21 @@
{
"name": "semantica-local",
"interface": {
"displayName": "Semantica Local Plugins"
},
"plugins": [
{
"name": "semantica-codex",
"description": "Semantica plugin for Codex: knowledge graph commands and analytics.",
"source": {
"source": "local",
"path": "./"
},
"policy": {
"installation": "AVAILABLE",
"authentication": "ON_INSTALL"
},
"category": "Productivity"
}
]
}
+35
View File
@@ -0,0 +1,35 @@
{
"name": "semantica-codex",
"description": "Semantica plugin for Codex: knowledge graph commands, export capabilities, and reasoning workflows.",
"version": "0.1.0",
"author": {
"name": "Semantica Contributors"
},
"homepage": "https://github.com/Hawksight-AI/semantica",
"repository": "https://github.com/Hawksight-AI/semantica",
"license": "MIT",
"keywords": [
"semantica",
"knowledge graph",
"codex",
"context graphs",
"decision intelligence",
"explainability",
"causal analysis",
"provenance",
"ontology",
"graph analytics",
"semantic extraction",
"visualization",
"reasoning",
"extraction",
"mcp"
],
"skills": "./skills",
"interface": {
"displayName": "Semantica Codex Plugin",
"shortDescription": "Knowledge graph skills for Semantica workflows",
"category": "Productivity",
"developerName": "Semantica Contributors"
}
}
+18
View File
@@ -0,0 +1,18 @@
{
"name": "semantica-local",
"owner": {
"name": "Semantica Contributors"
},
"metadata": {
"description": "Semantica plugin marketplace for Cursor.",
"version": "0.1.0",
"pluginRoot": "."
},
"plugins": [
{
"name": "semantica-cursor",
"description": "Semantica plugin for Cursor: knowledge graph skills and analytics.",
"source": "."
}
]
}
+32
View File
@@ -0,0 +1,32 @@
{
"name": "semantica-cursor",
"displayName": "Semantica Cursor Plugin",
"description": "Semantica plugin for Cursor: knowledge graph skills, reasoning, extraction, and visualization.",
"version": "0.1.0",
"author": {
"name": "Semantica Contributors"
},
"homepage": "https://github.com/Hawksight-AI/semantica",
"repository": "https://github.com/Hawksight-AI/semantica",
"license": "MIT",
"keywords": [
"semantica",
"knowledge graph",
"cursor",
"context graphs",
"decision intelligence",
"explainability",
"causal analysis",
"provenance",
"ontology",
"graph analytics",
"semantic extraction",
"visualization",
"reasoning",
"extraction",
"mcp"
],
"skills": "./skills",
"agents": "./agents",
"hooks": "./hooks/hooks.json"
}
+126
View File
@@ -0,0 +1,126 @@
---
name: decision-advisor
description: Decision intelligence and causal reasoning specialist for Semantica. Proactively surfaces causal chains, precedent matches, policy violations, and influence scores when reviewing or recording decisions. Use for decision recording, precedent search, causal analysis, policy governance, and decision explainability workflows.
---
You are a **Decision Intelligence Specialist** for the Semantica library. You focus on the full decision lifecycle: recording, querying, precedent search, causal analysis, policy compliance, and explainability.
## Your Domain
### Recording Decisions
```python
from semantica.context import AgentContext
ctx = AgentContext(decision_tracking=True)
decision_id = ctx.record_decision(
category="loan_approval",
scenario="First-time homebuyer, income 80k",
reasoning="Good credit score, low DTI ratio",
outcome="approved",
confidence=0.95,
entities=["customer_123", "property_456"],
decision_maker="underwriting_agent",
valid_from="2025-01-01",
valid_until="2026-01-01",
)
```
### Querying and Precedent Search
```python
# Natural language query with multi-hop reasoning
decisions = ctx.query_decisions(query, max_hops=3, use_hybrid_search=True)
# Hybrid precedent search — semantic + structural + vector
precedents = ctx.find_precedents(scenario, category, limit=10, use_hybrid_search=True)
# Advanced KG-enhanced search
advanced = ctx.find_precedents_advanced(
scenario, use_kg_features=True,
similarity_weights={"semantic": 0.5, "structural": 0.3, "vector": 0.2}
)
# Category/entity/time filters via DecisionQuery
from semantica.context.decision_query import DecisionQuery
dq = DecisionQuery(graph_store=ctx.graph_store)
by_cat = dq.find_by_category(category, limit=100)
by_ent = dq.find_by_entity(entity_id, limit=100)
by_time = dq.find_by_time_range(start, end, limit=100)
multi_hop = dq.multi_hop_reasoning(start_entity, query_context, max_hops=3)
```
### Causal Analysis
```python
from semantica.context.causal_analyzer import CausalChainAnalyzer
analyzer = CausalChainAnalyzer(graph_store=ctx.graph_store)
# Upstream (what caused this?) or downstream (what did this cause?)
chain = analyzer.get_causal_chain(decision_id, direction="upstream", max_depth=10)
# Root causes
roots = analyzer.find_root_causes(decision_id)
# Downstream impact
influenced = analyzer.get_influenced_decisions(decision_id)
score = analyzer.get_causal_impact_score(decision_id)
# Full network analysis
network = analyzer.analyze_causal_network()
loops = analyzer.find_causal_loops()
# Historical chain at a specific time
historical = analyzer.trace_at_time(decision_id, at_time="2024-06-01", direction="upstream")
```
### Policy Compliance
```python
from semantica.context import AgentContext
engine = ctx.get_policy_engine()
# Check compliance
compliant = engine.check_compliance(decision, policy_id)
# Get all applicable policies
applicable = engine.get_applicable_policies(category, entities)
# Analyze impact of policy changes
impact = engine.analyze_policy_impact(policy_id, proposed_rules)
# Record exceptions
exception_id = engine.record_exception(decision_id, policy_id, reason, approver, justification)
```
### Explainability
```python
# Full explainability trace
explainability = ctx.trace_decision_explainability(decision_id)
# Influence analysis with KG algorithms
influence = ctx.analyze_decision_influence(decision_id, max_depth=3)
predictions = ctx.predict_decision_relationships(decision_id, top_k=5)
```
## Critical Invariants
- **Node type duality**: `record_decision()``"decision"` (lowercase); `add_decision()``"Decision"` (capitalized). Always search for both when querying.
- **No `DecisionQuery.query()`** — use `find_by_entity`, `find_by_category`, `find_by_time_range`, or `multi_hop_reasoning`.
- **`CausalChainAnalyzer` takes `graph_store=`** — no `trace_causes()`, use `get_causal_chain(direction="upstream")`.
- **`find_precedents(as_of=<date>)`** — supports temporal precedent search.
- **`graph_store` format** — both `DecisionQuery` and `CausalChainAnalyzer` need `{"records": [...]}` shape.
## Behavior
When a user shares a decision or asks about decision-making, **proactively**:
1. **Trace root causes** via `get_causal_chain(direction="upstream")`
2. **Check policy compliance** via `get_applicable_policies()` + `check_compliance()`
3. **Find precedents** via `find_precedents_advanced(use_kg_features=True)`
4. **Score influence** via `get_causal_impact_score()`
5. **Detect loops** — flag if this decision closes a causal loop
When reviewing Semantica decision code:
- Check method names against the list above
- Flag queries that only check one of `"decision"` / `"Decision"`
- Flag missing `entities=[]` arg (defaults to None, may miss entity-based precedent search)
Show causal chains as Mermaid `graph TD` blocks. Keep tables concise. Lead with decision status and compliance, then causal context, then influence score.
+129
View File
@@ -0,0 +1,129 @@
---
name: explainability
description: Reasoning transparency and auditability specialist for Semantica. Answers "why does the graph believe X?", "how was Y inferred?", and "is this decision explainable?" with full evidence chains. Produces audit-ready explanation reports using ExplanationGenerator, AgentContext.trace_decision_explainability, and ContextGraph.trace_decision_chain.
---
You are a **Reasoning Transparency and Explainability Specialist** for the Semantica library. You answer "why?" questions about graph facts, inferences, and decisions with complete, auditable evidence chains.
## Your Domain
### Explanation Generation
```python
from semantica.reasoning.explanation_generator import ExplanationGenerator
gen = ExplanationGenerator()
# generate_explanation(reasoning) → Explanation object
# reasoning can be any reasoning object, dict, or string context
explanation = gen.generate_explanation(reasoning=reasoning_input)
# explanation.summary, .confidence, .evidence
# show_reasoning_path(reasoning) → ReasoningPath object
path = gen.show_reasoning_path(reasoning=reasoning_input)
# path.steps: [Step(type, description, confidence)]
# path.conclusion
# justify_conclusion(conclusion, reasoning_path) → Justification object
justification = gen.justify_conclusion(
conclusion=conclusion,
reasoning_path=path,
)
# justification.is_justified, .confidence, .supporting_steps, .opposing_factors
```
### Decision Explainability
```python
from semantica.context import AgentContext, ContextGraph
ctx = AgentContext(decision_tracking=True, advanced_analytics=True)
# Full decision explainability trace
explainability = ctx.trace_decision_explainability(decision_id)
# Returns: reasoning_steps, evidence, causal_context, compliance_status
# Causal chain from ContextGraph
graph = ContextGraph(advanced_analytics=True)
chain = graph.trace_decision_chain(decision_id, max_steps=5)
causality = graph.trace_decision_causality(decision_id, max_depth=5)
# Influence analysis
influence = ctx.analyze_decision_influence(decision_id, max_depth=3)
```
### Provenance Tracing
```python
from semantica.kg.kg_provenance import GraphBuilderWithProvenance
from semantica.context.context_provenance import ContextManagerWithProvenance
from semantica.reasoning.reasoning_provenance import ReasoningEngineWithProvenance
from semantica.semantic_extract.semantic_extract_provenance import (
NERExtractorWithProvenance,
RelationExtractorWithProvenance,
EventDetectorWithProvenance,
)
```
Each provenance-enabled class wraps the base class and adds `.get_provenance_summary()` to retrieve lineage records.
### Reasoning Chains
```python
from semantica.reasoning.deductive_reasoner import DeductiveReasoner
reasoner = DeductiveReasoner()
proof = reasoner.prove_theorem(theorem)
# proof.steps, proof.is_valid, proof.confidence
validation = reasoner.validate_argument(argument)
```
## Explanation Types You Produce
**1. Decision explanations** — full trace: reasoning steps → causal antecedents → policy compliance → evidence
**2. Reasoning path explanations** — step-by-step rule chain with variable bindings
**3. Conclusion justifications** — why a conclusion follows from premises, with opposing factors noted
**4. Path explanations** — how two nodes are semantically connected via the graph
**5. Compliance explanations** — which rules passed/failed and why, with remediation advice
## Audit Report Format
When asked for an audit report:
```
Explainability Audit Report
════════════════════════════
Generated: <ISO timestamp>
Scope: <N decisions / K facts>
── Decision Explanations ─────────────────
Decision <id>: EXPLAINED ✓ (confidence: 0.91)
Steps: 3 | Evidence: 2 items | Provenance: complete
Causal antecedents: <n>
Policy compliance: 2/2 ✓
Decision <id>: PARTIALLY EXPLAINED ⚠
Missing: provenance gap on reasoning step 2
Low confidence: 0.43 on step 3
── Summary ──────────────────────────────
Total: N decisions analyzed
Fully explained: M (X%)
Partially explained: K (Y%)
Unexplained (gaps): J (Z%)
Provenance gaps: J nodes missing lineage
Low-confidence facts (<0.7): L
Circular reasoning detected: YES / NO
```
## Behavior
When asked "why does the graph believe X?":
1. Start with `ExplanationGenerator.generate_explanation()` for the natural-language summary
2. Supplement with `show_reasoning_path()` for the step trace
3. Cross-check with provenance wrappers for source lineage
4. Flag any provenance gaps
When a decision explanation is requested:
1. Always call `ctx.trace_decision_explainability(decision_id)` first
2. Then supplement with `trace_decision_chain()` and `trace_decision_causality()`
3. Check policy compliance via `get_applicable_policies()` + `check_compliance()`
Lead with the direct answer, then the evidence chain. Use Mermaid `sequenceDiagram` for multi-step reasoning chains. Use nested bullets for evidence items.
+73
View File
@@ -0,0 +1,73 @@
---
name: kg-assistant
description: General-purpose KG-aware assistant for any Semantica task. Knows all module APIs, exact method signatures, node-type conventions, and current graph schema. Use for broad questions, multi-module workflows, code review, or any task spanning multiple Semantica modules.
---
You are a knowledge graph expert assistant for the **Semantica** library — a full-stack Python library for knowledge graphs, semantic extraction, decision intelligence, reasoning, and context management.
## Module Overview
### Decision Intelligence (semantica.context)
- `AgentContext` — high-level interface: `store()`, `retrieve()`, `record_decision()`, `query_decisions()`, `find_precedents()`, `find_precedents_advanced()`, `analyze_decision_influence()`, `predict_decision_relationships()`, `trace_decision_explainability()`, `get_context_insights()`, `multi_hop_context_query()`, `expand_query()`, `query_with_reasoning()`, `get_causal_chain()`, `capture_cross_system_inputs()`, `get_policy_engine()`
- `ContextGraph` — in-memory graph: `add_node()`, `add_edge()`, `record_decision()`, `find_precedents_by_scenario()`, `find_similar_decisions()`, `analyze_decision_influence()`, `analyze_decision_impact()`, `get_causal_chain()`, `trace_decision_causality()`, `trace_decision_chain()`, `enforce_decision_policy()`, `check_decision_rules()`, `get_decision_insights()`, `get_decision_summary()`, `analyze_graph_with_kg()`, `get_node_centrality()`, `get_node_importance()`, `state_at()`, `query()`
- `DecisionQuery``find_by_category()`, `find_by_entity()`, `find_by_time_range()`, `find_precedents_hybrid()`, `find_similar_exceptions()`, `multi_hop_reasoning()`, `predict_decision_relationships()`, `analyze_decision_influence()`, `trace_decision_path()`
- `CausalChainAnalyzer``get_causal_chain(decision_id, direction, max_depth)`, `find_root_causes()`, `get_influenced_decisions()`, `get_causal_impact_score()`, `get_precedent_chain()`, `analyze_causal_network()`, `find_causal_loops()`, `trace_at_time(event_id, at_time, direction, max_depth)`
- `PolicyEngine``add_policy()`, `check_compliance()`, `get_applicable_policies()`, `update_policy()`, `record_exception()`, `analyze_policy_impact()`, `get_affected_decisions()`, `get_policy_history()`
- `DecisionRecorder``record_decision()`, `link_entities()`, `link_precedents()`, `apply_policies()`, `record_exception()`, `capture_cross_system_context()`, `record_approval_chain()`
### Knowledge Graph (semantica.kg)
- `GraphAnalyzer``analyze_graph()`, `calculate_centrality(graph, centrality_type)`, `detect_communities(graph, algorithm)`, `analyze_temporal_evolution()`, `compute_metrics()`, `analyze_connectivity()`
- `CentralityCalculator``calculate_degree_centrality()`, `calculate_betweenness_centrality()`, `calculate_closeness_centrality()`, `calculate_eigenvector_centrality()`, `calculate_pagerank()`, `calculate_all_centrality()`
- `CommunityDetector``detect_communities()`, `detect_communities_louvain()`, `detect_communities_leiden()`, `detect_communities_label_propagation()`, `detect_overlapping_communities()`, `analyze_community_structure()`, `calculate_community_metrics()`
- `NodeEmbedder``compute_embeddings(graph_store, node_labels, relationship_types)`, `find_similar_nodes(graph_store, node_id, top_k)`, `store_embeddings()`
- `SimilarityCalculator``cosine_similarity(vector1, vector2)`, `euclidean_distance()`, `manhattan_distance()`, `correlation_similarity()`, `find_most_similar()`, `batch_similarity()`, `pairwise_similarity()`
- `LinkPredictor``score_link(graph_store, node_id1, node_id2, method=)`, `predict_top_links()`, `predict_links()`, `batch_score_links()`
- `PathFinder``find_k_shortest_paths()`, `dijkstra_shortest_path()`, `bfs_shortest_path()`, `a_star_search()`, `all_shortest_paths()`, `path_length()`
### Reasoning (semantica.reasoning)
- `DeductiveReasoner``add_facts()`, `apply_logic(premises)`, `prove_theorem()`, `validate_argument()`
- `AbductiveReasoner``add_knowledge()`, `generate_hypotheses(observations)`, `find_explanations()`, `get_best_explanation()`, `rank_hypotheses()`
- `ExplanationGenerator``generate_explanation(reasoning)`, `show_reasoning_path(reasoning)`, `justify_conclusion(conclusion, reasoning_path)`
### Extraction (semantica.semantic_extract)
- `NamedEntityRecognizer`, `RelationExtractor`, `EventDetector`, `CoreferenceResolver`, `TripletExtractor`, `ExtractionValidator`
- **Always** call `_result_cache.clear()` before any extraction run
### Pipeline (semantica.pipeline)
- `PipelineBuilder``add_step()`, `connect_steps()`, `validate_pipeline()`, `build()`
- `PipelineValidator``validate(pipeline)``ValidationResult(valid, errors, warnings)`**does NOT raise**
- `FailureHandler``handle_failure(error, policy, retry_count)``RecoveryAction`
### Export (semantica.export)
- `RDFExporter.export_to_rdf(data, format='turtle')`**returns a string**, no `output_path`
- Format aliases: `"ttl"``"turtle"`, `"nt"`, `"xml"`, `"json-ld"`
- Other exporters: `OWLExporter`, `CSVExporter`, `JSONExporter`, `ParquetExporter`, `ArrowExporter`, `VectorExporter`, `YAMLSchemaExporter`, `ArangoAQLExporter`, `LPGExporter`, `ReportGenerator`
### Deduplication (semantica.deduplication)
- `DuplicateDetector.detect_duplicates(entities, threshold=)` — use **directly**, never via `methods.py` (infinite recursion bug)
## Critical API Invariants
| Area | Correct |
|------|---------|
| Decision node type | `record_decision()` → stored as `"decision"` (lowercase); `add_decision()``"Decision"` (capitalized). Query both. |
| `AgentContext.record_decision` | Returns a `decision_id: str`. Args: `category, scenario, reasoning, outcome, confidence, entities, decision_maker, valid_from, valid_until` |
| `CausalChainAnalyzer` | Takes `graph_store=` kwarg. No `trace_causes()` — use `get_causal_chain(direction="upstream")` |
| `ExplanationGenerator` | No `explain_decision/fact/inference` — use `generate_explanation(reasoning)`, `show_reasoning_path(reasoning)`, `justify_conclusion(conclusion, path)` |
| `DecisionQuery` | No `.query()` — use `find_by_entity`, `find_by_category`, `find_by_time_range`, `multi_hop_reasoning` |
| `SimilarityCalculator` | `cosine_similarity(vector1, vector2)` — two required positional args |
| `NodeEmbedder` | `compute_embeddings(graph_store, node_labels, relationship_types)` — all three positional, all required |
| `LinkPredictor` | `score_link(graph_store, node_id1, node_id2, method=)` |
| `PipelineValidator` | `validate(pipeline)` returns `ValidationResult` — never raises |
| `RDFExporter` | `export_to_rdf(data, format='turtle')` returns a string |
| Cache | `_result_cache.clear()` before every extraction |
| Graph store format | `DecisionQuery` and `CausalChainAnalyzer` need `{"records": [...]}` from graph store |
## How to Help
1. **Answer questions** with copy-paste-ready code that uses the correct method names
2. **Review Semantica code** — check against the invariants table above before suggesting anything
3. **Suggest the right skill** — map user intent to `/semantica:*` skills
4. **Debug errors** — common mistakes: wrong method name, wrong arg order, missing `_result_cache.clear()`, querying only one of `"decision"`/`"Decision"` types
Keep responses code-first. Show the full import path in every example.
+12
View File
@@ -0,0 +1,12 @@
{
"hooks": {
"PostToolUse": [
{"matcher": "Write|Edit", "hooks": [{"type": "command", "command": "FILE=$(jq -r .tool_input.file_path 2>/dev/null); if echo $FILE | grep -qE semantica/; then python -c 'import ast,sys; ast.parse(open(sys.argv[1]).read())' $FILE 2>&1; fi"}]},
{"matcher": "Write|Edit", "hooks": [{"type": "command", "command": "echo PostToolUse provenance check"}]}
],
"PreToolUse": [
{"matcher": "Bash", "hooks": [{"type": "command", "command": "CMD=$(jq -r .tool_input.command 2>/dev/null); if echo $CMD | grep -q deduplication/methods; then echo WARNING: use DuplicateDetector directly >&2; fi"}]}
]
}
}
+74
View File
@@ -0,0 +1,74 @@
---
name: causal
description: Analyze cause-and-effect relationships in the Semantica knowledge graph — causal chains, interventions, counterfactuals, and causal influence scores.
---
# /semantica:causal
Analyze causal relationships and infer impacts. Usage: `/semantica:causal <task> [args]`
`$ARGUMENTS` = task + optional target entity, filter, or intervention.
---
## `chain [--subject <node>] [--depth N]`
Build and inspect causal chains for a subject or category.
```python
from semantica.context.causal_analyzer import CausalChainAnalyzer
from semantica.context import AgentContext
# Option 1: Use an existing AgentContext decision backend
chain = ctx.get_causal_chain(
decision_id=decision_id,
direction="upstream",
max_depth=depth,
)
# Option 2: Use CausalChainAnalyzer directly
analyzer = CausalChainAnalyzer(graph_store=ctx.knowledge_graph)
downstream = analyzer.get_causal_chain(
decision_id=decision_id,
direction="downstream",
max_depth=depth,
)
```
Output: chain steps, cause strength, effect reach, and summary graph.
---
## `intervene <node> <action> [--scenario <json>]`
Analyze decision impact and influenced decisions (current causal API).
```python
analyzer = CausalChainAnalyzer(graph_store=ctx.knowledge_graph)
impact_score = analyzer.get_causal_impact_score(decision_id=decision_id)
influenced = analyzer.get_influenced_decisions(
decision_id=decision_id,
max_depth=depth,
)
```
Return: impact score, influenced decisions, and downstream scope.
---
## `counterfactual <fact> [--weight N]`
Trace root causes and temporal causal paths.
```python
analyzer = CausalChainAnalyzer(graph_store=ctx.knowledge_graph)
roots = analyzer.find_root_causes(decision_id=decision_id, max_depth=depth)
historical_chain = analyzer.trace_at_time(
event_id=decision_id,
at_time="2026-01-01T00:00:00Z",
direction="upstream",
max_depth=depth,
)
```
Output: root decision lineage and time-bounded causal context.
+38
View File
@@ -0,0 +1,38 @@
---
name: change
description: Track and inspect graph changes, diffs, temporal updates, and the impact of new data on Semantica knowledge graphs.
---
# /semantica:change
Inspect changes over time and evaluate updates. Usage: `/semantica:change <task> [args]`
`$ARGUMENTS` = task + optional node, time window, or filter.
---
## `diff [--from <ts>] [--to <ts>] [--node <id>]`
Compute graph diffs between two snapshots.
```python
from semantica.provenance.change_tracker import ChangeTracker
from semantica.context import ContextGraph
tracker = ChangeTracker()
diff = tracker.compute_diff(from_ts=from_ts, to_ts=to_ts, node_id=node_id)
```
Output: added/removed nodes and edges, attribute changes, and impact summary.
---
## `history <node_id> [--limit N]`
Show the change history for a node or relationship.
```python
history = tracker.get_node_history(node_id=node_id, limit=limit)
```
Return: revisions, timestamps, authors, and summary comments.
+197
View File
@@ -0,0 +1,197 @@
---
name: decision
description: Full decision lifecycle in Semantica — record, query, find precedents (hybrid/advanced), analyze influence, explain, insights dashboard, list, and record exceptions. Uses AgentContext, ContextGraph, DecisionQuery, CausalChainAnalyzer, DecisionRecorder.
---
# /semantica:decision
Full decision lifecycle management. Usage: `/semantica:decision <sub-command> [args]`
---
## `record <category> "<scenario>" "<reasoning>" <outcome> <confidence>`
Record a decision with full context.
```python
from semantica.context import AgentContext
ctx = AgentContext(decision_tracking=True)
decision_id = ctx.record_decision(
category=category, # "loan_approval", "deployment", "hiring"
scenario=scenario, # natural-language situation description
reasoning=reasoning, # why this decision was made
outcome=outcome, # "approved", "rejected", "deferred"
confidence=float(confidence),
entities=entities or [],
decision_maker="ai_agent",
valid_from=valid_from, # optional ISO date string
valid_until=valid_until,
)
```
Output: `Decision <decision_id> recorded | <category> | <outcome> (conf: 0.95)`
---
## `query "<question>" [--hops N] [--hybrid]`
Query decisions using natural language with multi-hop graph traversal.
```python
from semantica.context import AgentContext
ctx = AgentContext(decision_tracking=True, advanced_analytics=True)
results = ctx.query_decisions(
query=question,
max_hops=int(hops) if hops else 3,
include_context=True,
use_hybrid_search="--hybrid" in args,
)
```
For structured lookups use `DecisionQuery`:
```python
from semantica.context.decision_query import DecisionQuery
dq = DecisionQuery(graph_store=ctx.graph_store)
# dq.find_by_category(category, limit=100)
# dq.find_by_entity(entity_id, limit=100)
# dq.find_by_time_range(start, end, limit=100)
# dq.multi_hop_reasoning(start_entity, query_context, max_hops=3)
# dq.trace_decision_path(decision_id, relationship_types)
# dq.analyze_decision_influence(decision_id, max_depth=3)
```
Return: `| ID | Category | Scenario | Outcome | Confidence | Timestamp |`
---
## `precedents "<scenario>" [--category <cat>] [--advanced] [--hops N] [--as-of <date>]`
Find similar past decisions using hybrid semantic + structural + vector search.
```python
from semantica.context import AgentContext
ctx = AgentContext(decision_tracking=True, kg_algorithms=True, vector_store_features=True)
if "--advanced" in args:
precedents = ctx.find_precedents_advanced(
scenario=scenario, category=category, limit=10,
use_kg_features=True,
similarity_weights={"semantic": 0.5, "structural": 0.3, "vector": 0.2},
)
else:
precedents = ctx.find_precedents(
scenario=scenario, category=category, limit=10,
use_hybrid_search=True,
max_hops=int(hops) if hops else 3,
include_context=True,
include_superseded=False,
as_of=as_of_date or None, # temporal filter: only precedents that existed as_of this date
)
```
Return ranked: `| Rank | ID | Scenario | Outcome | Confidence | Similarity | Date |`
---
## `influence <decision_id> [--depth N]`
Analyze how a decision influences others across the graph.
```python
from semantica.context import AgentContext
ctx = AgentContext(decision_tracking=True, advanced_analytics=True, kg_algorithms=True)
influence = ctx.analyze_decision_influence(decision_id, max_depth=int(depth) if depth else 3)
predictions = ctx.predict_decision_relationships(decision_id, top_k=5)
```
Output: Influence score + influenced decisions table + predicted new relationships.
---
## `explain <decision_id>`
Full explainability trace — reasoning steps, causal antecedents, policy compliance.
```python
from semantica.context import AgentContext, ContextGraph
ctx = AgentContext(decision_tracking=True)
explainability = ctx.trace_decision_explainability(decision_id)
graph = ContextGraph(advanced_analytics=True)
chain = graph.trace_decision_chain(decision_id, max_steps=5)
causality = graph.trace_decision_causality(decision_id, max_depth=5)
```
Output: Reasoning steps, causal antecedents, evidence items, policy compliance status.
---
## `insights`
Comprehensive analytics across all tracked decisions.
```python
from semantica.context import ContextGraph, AgentContext
ctx = AgentContext(decision_tracking=True, advanced_analytics=True)
graph = ContextGraph(advanced_analytics=True)
insights = graph.get_decision_insights()
summary = graph.get_decision_summary()
context_insights = ctx.get_context_insights()
```
Output: Total count, category breakdown, outcome distribution, avg confidence, top influential.
---
## `list [--category <cat>] [--entity <id>] [--from <date>] [--to <date>]`
```python
from semantica.context.decision_query import DecisionQuery
from semantica.context import AgentContext
from datetime import datetime
ctx = AgentContext(decision_tracking=True)
dq = DecisionQuery(graph_store=ctx.graph_store)
if category: decisions = dq.find_by_category(category, limit=100)
elif entity: decisions = dq.find_by_entity(entity, limit=100)
elif from_date: decisions = dq.find_by_time_range(
start=datetime.fromisoformat(from_date),
end=datetime.fromisoformat(to_date or "2099-12-31"),
)
```
Return: `| ID | Category | Scenario | Outcome | Confidence | Maker | Timestamp |`
---
## `exception <decision_id> <policy_id> "<reason>" --approver <name>`
Record a formal policy exception.
```python
from semantica.context.decision_recorder import DecisionRecorder
from semantica.context import AgentContext
ctx = AgentContext(decision_tracking=True)
recorder = DecisionRecorder(graph_store=ctx.graph_store)
exception_id = recorder.record_exception(
decision_id=decision_id, policy_id=policy_id,
reason=reason, approver=approver,
approval_method="manual_override", justification=reason,
)
from semantica.context.decision_query import DecisionQuery
dq = DecisionQuery(graph_store=ctx.graph_store)
similar = dq.find_similar_exceptions(exception_reason=reason, limit=5)
```
Output: `Exception recorded: <exception_id>` + similar past exceptions for audit context.
+41
View File
@@ -0,0 +1,41 @@
---
name: deduplicate
description: Detect duplicate entities, duplicate groups, and relationship duplicates in Semantica using fuzzy matching, schema heuristics, and graph similarity.
---
# /semantica:deduplicate
Remove duplicates from the knowledge graph. Usage: `/semantica:deduplicate <strategy> [args]`
`$ARGUMENTS` = deduplication strategy + optional entity or threshold.
---
## `entities [--threshold <score>] [--field <name>]`
Detect duplicate entities and group them by similarity.
```python
from semantica.deduplication import DuplicateDetector
finder = DuplicateDetector()
candidates = finder.detect_duplicates(entities, threshold=threshold)
groups = finder.detect_duplicate_groups(entities, threshold=threshold)
```
Output: duplicate candidate list, duplicate groups, and representative merge recommendations.
---
## `relations [--similarity <score>]`
Detect duplicate relationships and normalize edge representations.
```python
from semantica.deduplication import DuplicateDetector
finder = DuplicateDetector()
relations = finder.detect_duplicates(relation_list, threshold=similarity)
```
Result: duplicate relation candidates, normalized relationship groups, and cleanup summary.
+230
View File
@@ -0,0 +1,230 @@
---
name: embed
description: Generate, inspect, and use node/text embeddings in Semantica — compute Node2Vec embeddings, find similar nodes, score link predictions, batch similarity, and pairwise similarity. Uses NodeEmbedder, SimilarityCalculator, LinkPredictor, and AgentContext. Sub-commands: compute, similar, similarity, predict-link, top-links, batch, pairwise.
---
# /semantica:embed
Generate and inspect graph embeddings. Usage: `/semantica:embed <sub-command> [args]`
`$ARGUMENTS` = sub-command + arguments.
---
## `compute [--labels <t1,t2>] [--rels <r1,r2>] [--dim N] [--walks N]`
Generate Node2Vec embeddings for graph nodes.
```python
from semantica.kg.node_embeddings import NodeEmbedder
from semantica.context import ContextGraph
graph = ContextGraph()
embedder = NodeEmbedder()
node_labels = labels_arg.split(",") if labels_arg else graph.get_all_node_types()
rel_types = rels_arg.split(",") if rels_arg else []
# All positional args required: graph_store, node_labels, relationship_types
embeddings = embedder.compute_embeddings(
graph_store=graph,
node_labels=node_labels,
relationship_types=rel_types,
embedding_dimension=int(dim_arg) if dim_arg else None,
num_walks=int(walks_arg) if walks_arg else None,
)
# Store embeddings back on nodes
embedder.store_embeddings(
graph_store=graph,
embeddings=embeddings,
property_name="node2vec_embedding",
)
```
Output:
```
Embeddings computed and stored.
Nodes embedded: N
Embedding dim: 128
Node types covered: [type1, type2, ...]
Sample (first 5 nodes):
| Node | Type | Embedding dim | Stored |
```
---
## `similar <node_id> [--top N]`
Find the most similar nodes to a given node in embedding space.
```python
from semantica.kg.node_embeddings import NodeEmbedder
from semantica.context import ContextGraph, AgentContext
graph = ContextGraph()
embedder = NodeEmbedder()
# NodeEmbedder.find_similar_nodes uses the stored node2vec_embedding property
neighbors = embedder.find_similar_nodes(
graph_store=graph,
node_id=node_id,
top_k=int(top_n) if top_n else 10,
embedding_property="node2vec_embedding",
)
# Also use AgentContext for richer similarity with metadata
ctx = AgentContext(kg_algorithms=True)
entity_similar = ctx.find_similar_entities(
entity_id=node_id,
similarity_type="content", # or "structural", "hybrid"
top_k=int(top_n) if top_n else 10,
)
```
Return: `| Rank | Node ID | Type | Cosine Similarity | Shared Properties |`
---
## `similarity <n1> <n2> [--method cosine|euclidean|manhattan|correlation]`
Compute pairwise similarity between two nodes.
```python
from semantica.kg.similarity_calculator import SimilarityCalculator
from semantica.kg.node_embeddings import NodeEmbedder
from semantica.context import ContextGraph
graph = ContextGraph()
embedder = NodeEmbedder()
calc = SimilarityCalculator()
# Get embeddings for both nodes
v1 = embedder.find_similar_nodes(graph, n1, top_k=1) # placeholder — use stored embedding
v2 = embedder.find_similar_nodes(graph, n2, top_k=1)
method = method_arg or "cosine"
if method == "cosine":
score = calc.cosine_similarity(vector1=v1, vector2=v2)
elif method == "euclidean":
score = calc.euclidean_distance(v1, v2)
elif method == "manhattan":
score = calc.manhattan_distance(v1, v2)
elif method == "correlation":
score = calc.correlation_similarity(v1, v2)
```
Output:
```
Similarity: "<n1>" ↔ "<n2>"
Method: cosine
Score: 0.847
Interpretation: HIGH similarity (>0.8)
Shared neighbors: K
Common node types: [types]
```
---
## `predict-link <n1> <n2> [--method cosine|jaccard|adamic-adar|common-neighbors]`
Score the likelihood of a relationship between two nodes.
```python
from semantica.kg.link_predictor import LinkPredictor
from semantica.context import ContextGraph
graph = ContextGraph()
predictor = LinkPredictor()
# score_link(graph_store, node_id1, node_id2, method=)
score = predictor.score_link(
graph_store=graph,
node_id1=n1,
node_id2=n2,
method=method_arg or None,
)
```
Output:
```
Link Prediction: "<n1>" → "<n2>"
Method: cosine
Score: 0.723 (threshold: 0.5 → LIKELY)
Recommendation: This link is LIKELY to be meaningful.
```
---
## `top-links <node_id> [--top N] [--method <method>]`
Find the top-N most likely new connections for a node.
```python
from semantica.kg.link_predictor import LinkPredictor
from semantica.context import ContextGraph
graph = ContextGraph()
predictor = LinkPredictor()
top = predictor.predict_top_links(
graph_store=graph,
node_id=node_id,
top_k=int(top_n) if top_n else 10,
method=method_arg or None,
)
```
Return: `| Rank | Target Node | Type | Score | Existing Link? |`
---
## `batch <query_node> [--against <n1,n2,...>] [--top N]`
Score similarity between a query node and a set of target nodes (or all nodes).
```python
from semantica.kg.similarity_calculator import SimilarityCalculator
from semantica.kg.node_embeddings import NodeEmbedder
from semantica.context import ContextGraph
graph = ContextGraph()
embedder = NodeEmbedder()
calc = SimilarityCalculator()
# Get query embedding and all target embeddings
query_vec = ... # from stored node2vec_embedding
target_embeddings = {n: embedder.get_embedding(n) for n in targets}
scores = calc.batch_similarity(
embeddings=target_embeddings,
query_embedding=query_vec,
top_k=int(top_n) if top_n else 20,
)
```
Return: `| Node | Type | Score |` sorted descending.
---
## `pairwise [--labels <t1,t2>] [--method cosine|euclidean]`
Compute all pairwise similarities among a set of nodes.
```python
from semantica.kg.similarity_calculator import SimilarityCalculator
calc = SimilarityCalculator()
pairwise = calc.pairwise_similarity(
embeddings=embeddings_dict,
method=method_arg or None,
)
```
Show as a heatmap summary — top-5 most similar pairs and bottom-5 most dissimilar pairs. Full matrix on request.
Also use `AgentContext.predict_decision_relationships(decision_id, top_k)` when working within decision graphs for relationship prediction enriched with KG algorithms.
+44
View File
@@ -0,0 +1,44 @@
---
name: explain
description: Explain Semantica reasoning, decision logic, and graph results with traceability, causal context, and human-readable rationale.
---
# /semantica:explain
Produce explanations for decisions, rules, and graph analytics. Usage: `/semantica:explain <target> [args]`
`$ARGUMENTS` = explanation target + optional detail level.
---
## `decision <decision_id> [--detail <level>]`
Explain why a decision was reached.
```python
from semantica.reasoning.explanation_generator import ExplanationGenerator
# For decision explainability in Semantica contexts:
decision_trace = ctx.trace_decision_explainability(decision_id=decision_id)
# For reasoning/proof explanations:
generator = ExplanationGenerator(detail_level=detail)
explanation = generator.generate_explanation(reasoning_result)
```
Output: decision factors, rule traces, confidence, and suggested next steps.
---
## `graph <node_id> [--path N]`
Explain graph relationships and why a node is connected.
```python
# Use AgentContext explainability + causal tracing for graph-connected decisions
graph_explanation = ctx.trace_decision_explainability(decision_id=node_id)
upstream = ctx.get_causal_chain(decision_id=node_id, direction="upstream", max_depth=depth)
downstream = ctx.get_causal_chain(decision_id=node_id, direction="downstream", max_depth=depth)
```
Return: cause/effect chains, supporting evidence, and relevant metadata.
+67
View File
@@ -0,0 +1,67 @@
---
name: export
description: Export Semantica graphs, results, and provenance to JSON, RDF, Parquet, CSV, GraphML, and other formats.
---
# /semantica:export
Export knowledge graph data. Usage: `/semantica:export <format> [args]`
`$ARGUMENTS` = format + optional target or destination.
---
## `json [--output <path>] [--filter <query>]`
Export graph data as JSON.
```python
from semantica.export.methods import export_json
export_json(data=graph_data, file_path=output, format='json')
```
Output: JSON file or inline JSON payload.
---
## `rdf [--format turtle|rdfxml|jsonld|ntriples|n3] [--output <path>]`
Export the graph in RDF serialization.
```python
from semantica.export.methods import export_rdf
export_rdf(data=graph_data, file_path=output, format='turtle')
```
Return: RDF text or file path.
---
## `parquet [--output <path>]`
Export nodes and edges to Parquet for analytics.
```python
from semantica.export.methods import export_parquet
export_parquet(data=graph_data, file_path=output, compression='snappy')
```
Output: Parquet dataset ready for downstream processing.
---
## `graphml|gexf|dot [--output <path>]`
Export the graph to a supported graph format.
```python
from semantica.export import GraphExporter
exporter = GraphExporter(format='graphml', include_attributes=True)
exporter.export(graph_data, output)
```
Output: Graph format file suitable for visualization tools.
+93
View File
@@ -0,0 +1,93 @@
---
name: extract
description: Run the full Semantica semantic extraction pipeline on a file or selected text — NER, relations, events, coreference resolution, triplets, and validation. Clears result cache before each run. Returns Markdown tables with entity/relation/event/triplet results and inline validator warnings.
---
# /semantica:extract
Run the full extraction pipeline. Usage: `/semantica:extract [file_path | "inline text"]`
`$ARGUMENTS` = file path, inline text in quotes, or blank (uses active editor file).
---
## Steps
**1. Resolve the source.**
- If `$ARGUMENTS` is a readable file path → `text = open(path).read()`
- If it's quoted inline text → use directly
- If blank → use the active editor file
**2. Clear the result cache** to prevent cross-invocation pollution:
```python
from semantica.semantic_extract.cache import _result_cache
_result_cache.clear()
```
**3. Run the full pipeline:**
```python
from semantica.semantic_extract import (
NamedEntityRecognizer,
RelationExtractor,
EventDetector,
CoreferenceResolver,
TripletExtractor,
ExtractionValidator,
)
# Named Entity Recognition
ner = NamedEntityRecognizer()
entities = ner.extract(text)
# Relation Extraction
rel = RelationExtractor()
relations = rel.extract(text)
# Event Detection
evt = EventDetector()
events = evt.extract(text)
# Coreference Resolution — resolve pronouns/aliases before extraction
coref = CoreferenceResolver()
resolved_text = coref.resolve(text)
# Triplet Extraction (subjectpredicateobject)
triplet = TripletExtractor()
triplets = triplet.extract(resolved_text)
# Validate quality
validator = ExtractionValidator()
issues = validator.validate(entities, relations)
```
**4. Report validator warnings** above results:
```
⚠ ExtractionValidator: <warning message>
```
**5. Return results as Markdown tables:**
**Entities** (N total)
| Label | Type | Confidence | Span |
|-------|------|------------|------|
**Relations** (M total)
| Source | Relation Type | Target | Confidence |
|--------|---------------|--------|------------|
**Events** (K total)
| Label | Type | Participants | Confidence |
|-------|------|--------------|------------|
**Triplets** (J total)
| Subject | Predicate | Object | Confidence |
|---------|-----------|--------|------------|
**6. Summary line:**
```
Extracted: N entities, M relations, K events, J triplets — from <source>
```
For large files (>50KB), process in chunks and show a progress indicator. Highlight any entities appearing in the context graph already (`ContextGraph.has_node(label)`) with `[in graph]` tag.
+38
View File
@@ -0,0 +1,38 @@
---
name: ingest
description: Ingest data from files, databases, APIs, or streams into Semantica knowledge graphs with schema mapping and entity linking.
---
# /semantica:ingest
Ingest new data into the knowledge graph. Usage: `/semantica:ingest <source> [args]`
`$ARGUMENTS` = source type + optional file path, connection string, or dataset identifier.
---
## `file <path> [--format json|csv|yaml|xml]`
Ingest structured data from a local file.
```python
from semantica.ingest import ingest_file
data = ingest_file(file_path=path, method='file', file_format=file_format)
```
Output: imported node/edge count and ingestion summary.
---
## `db <connection> [--query <sql>]`
Ingest data from a database source.
```python
from semantica.ingest import ingest_database
result = ingest_database(connection_string=conn, query=query)
```
Return: rows ingested, mapped entities, and warnings.
+37
View File
@@ -0,0 +1,37 @@
---
name: ontology
description: Manage ontology schemas, concepts, relationships, and alignments for Semantica knowledge graphs.
---
# /semantica:ontology
Manage ontology definitions and validation. Usage: `/semantica:ontology <task> [args]`
`$ARGUMENTS` = task + optional ontology item or schema file.
---
## `describe <concept>`
Show ontology concept details.
```python
from semantica.ontology import OntologyManager
manager = OntologyManager()
concept = manager.get_concept(concept_name)
```
Output: properties, relationships, inherited types, and examples.
---
## `validate [--schema <file>]`
Validate the graph or schema against the ontology.
```python
result = manager.validate_graph(graph=graph, schema_file=schema_file)
```
Return: validation status, errors, and correction suggestions.
+37
View File
@@ -0,0 +1,37 @@
---
name: policy
description: Define and enforce policies, access controls, and compliance rules over Semantica knowledge graphs.
---
# /semantica:policy
Apply policy rules and checks. Usage: `/semantica:policy <task> [args]`
`$ARGUMENTS` = task + optional policy name, rule set, or target entity.
---
## `check [--rule <name>] [--target <id>]`
Run policy checks against the graph.
```python
from semantica.policy import PolicyEngine
engine = PolicyEngine()
result = engine.check(rule_name=rule_name, target=target)
```
Output: compliance status, failing rules, and remediation guidance.
---
## `list`
List available policy rules and categories.
```python
rules = engine.list_rules()
```
Return: rule name, description, severity, and category.
+37
View File
@@ -0,0 +1,37 @@
---
name: provenance
description: Trace data lineage, source attribution, audit trails, and provenance assertions in Semantica graphs.
---
# /semantica:provenance
Inspect provenance metadata. Usage: `/semantica:provenance <task> [args]`
`$ARGUMENTS` = task + optional node, edge, or time range.
---
## `trace <node_id> [--depth N]`
Trace the provenance of a node or fact.
```python
from semantica.provenance import ProvenanceTracer
tracer = ProvenanceTracer()
trace = tracer.trace_node(node_id=node_id, depth=depth)
```
Output: source chain, authors, timestamps, and validation status.
---
## `audit [--since <ts>] [--actor <id>]`
View audit logs for graph changes.
```python
audit_log = tracer.get_audit_log(since=since, actor=actor)
```
Return: change events, actor, affected objects, and action details.
+49
View File
@@ -0,0 +1,49 @@
---
name: query
description: Query the Semantica knowledge graph using SPARQL, Cypher, keyword search, and structured graph query patterns.
---
# /semantica:query
Run graph queries and search. Usage: `/semantica:query <mode> [args]`
`$ARGUMENTS` = query mode + query string or filter.
---
## `sparql <query>`
Execute a SPARQL query against the graph.
```python
from semantica.query import QueryEngine
engine = QueryEngine()
results = engine.query_sparql(query)
```
Return: query bindings as a Markdown table.
---
## `cypher <query>`
Execute a Cypher-like query.
```python
results = engine.query_cypher(query)
```
Output: node/relationship results and path summaries.
---
## `search <keywords> [--filter <type>]`
Search graph entities by keyword.
```python
results = engine.search(keywords=keywords, filter_type=filter_type)
```
Return: ranked matches with entity types and relevance scores.
+201
View File
@@ -0,0 +1,201 @@
---
name: reason
description: Run reasoning over the Semantica knowledge graph — deductive logic, abductive hypothesis generation, Datalog programs, SPARQL queries, Rete network evaluation. Uses DeductiveReasoner, AbductiveReasoner, DatalogReasoner, SPARQLReasoner, ReteEngine. Sub-commands: deductive, abductive, datalog, sparql, rete, prove, hypotheses.
---
# /semantica:reason
Apply reasoning over the knowledge graph. Usage: `/semantica:reason <mode> [args]`
`$ARGUMENTS` = reasoning mode + rules/observations/query.
---
## `deductive [--facts '<json-list>'] [--rules '<rule1>|<rule2>']`
Apply deductive rules to known facts to derive new conclusions.
```python
from semantica.reasoning.deductive_reasoner import DeductiveReasoner, Premise
reasoner = DeductiveReasoner()
# Add base facts to working memory
# Facts can be strings like "Person(John)" or structured dicts
import json
facts = json.loads(facts_json) if facts_json else []
reasoner.add_facts(facts)
# Apply logic with explicit premises
# Premise objects have: statement, confidence, source
premises = [
Premise(statement=fact, confidence=1.0)
for fact in facts
]
conclusions = reasoner.apply_logic(premises=premises)
```
Return: `| Conclusion | Triggering Premises | Confidence | Rule Applied |`
If zero rules given, run `reasoner.prove_theorem()` on any provided theorem:
```python
proof = reasoner.prove_theorem(theorem=theorem_text)
```
Output: `Proof: <proof.steps> | Valid: YES / NO`
---
## `prove <theorem> [--facts '<json-list>']`
Prove or disprove a theorem against known facts.
```python
from semantica.reasoning.deductive_reasoner import DeductiveReasoner
reasoner = DeductiveReasoner()
import json
reasoner.add_facts(json.loads(facts_json) if facts_json else [])
proof = reasoner.prove_theorem(theorem=theorem)
```
Output:
```
Theorem: "<theorem>"
Result: PROVED ✓ | DISPROVED ✗ | UNDECIDABLE ⚠
Proof steps:
1. <premise> — <justification>
2. ...
→ QED: <theorem>
Confidence: <proof.confidence>
```
---
## `abductive <observation> [--knowledge '<json-list>'] [--top N]`
Generate and rank hypotheses that explain an observation.
```python
from semantica.reasoning.abductive_reasoner import (
AbductiveReasoner, Observation
)
reasoner = AbductiveReasoner()
import json
if knowledge_json:
reasoner.add_knowledge(json.loads(knowledge_json))
obs = Observation(description=observation)
# Generate all hypotheses then rank them
hypotheses = reasoner.generate_hypotheses(observations=[obs])
ranked = reasoner.rank_hypotheses(hypotheses)
best = reasoner.get_best_explanation(obs)
# Also get full explanations with evidence
explanations = reasoner.find_explanations(observations=[obs])
```
Output:
```
Abductive Reasoning for: "<observation>"
Best explanation:
<best.description> (confidence: 0.87)
All hypotheses (ranked):
| Rank | Hypothesis | Confidence | Supporting Evidence |
| 1 | <hyp> | 0.87 | <evidence> |
| 2 | ...
Full explanations:
Explanation 1: <explanation.summary>
Evidence: <evidence items>
```
---
## `datalog <program>`
Evaluate a Datalog program over graph facts.
```python
from semantica.reasoning.datalog_reasoner import DatalogReasoner
from semantica.context import ContextGraph
graph = ContextGraph()
reasoner = DatalogReasoner()
# program is a string of Datalog rules and queries
results = reasoner.evaluate(program=program, graph=graph)
```
Return derived tuples as a relation table. Show rule derivation counts.
---
## `sparql <query>`
Run a SPARQL query over the knowledge graph and return results.
```python
from semantica.reasoning.sparql_reasoner import SPARQLReasoner
from semantica.context import ContextGraph
graph = ContextGraph()
reasoner = SPARQLReasoner()
results = reasoner.query(sparql_query=query, graph=graph)
```
Return as a Markdown table with bound variable columns matching the SELECT clause.
---
## `rete [--rules '<rule1>|<rule2>'] [--facts '<json-list>']`
Incremental rule evaluation using the Rete network with working memory.
```python
from semantica.reasoning.rete_engine import ReteEngine
import json
engine = ReteEngine()
rules = rules_str.split("|") if rules_str else []
facts = json.loads(facts_json) if facts_json else []
engine.load_rules(rules)
engine.process_facts(facts)
activations = engine.get_activations()
```
Return: `| Rule Fired | Variable Bindings | Working Memory Delta | Activation Order |`
---
## `hypotheses "<scenario>" [--knowledge '<json-list>'] [--top N]`
Generate the top-N most probable explanations for a complex scenario.
```python
from semantica.reasoning.abductive_reasoner import AbductiveReasoner, Observation
import json
reasoner = AbductiveReasoner()
if knowledge_json:
reasoner.add_knowledge(json.loads(knowledge_json))
obs = Observation(description=scenario)
hypotheses = reasoner.generate_hypotheses(observations=[obs])
ranked = reasoner.rank_hypotheses(hypotheses)
top_n = ranked[:int(n) if n else 5]
```
For each hypothesis also show: what evidence supports it, what would falsify it, and which is the most parsimonious (fewest assumptions).
+164
View File
@@ -0,0 +1,164 @@
---
name: temporal
description: Temporal graph operations on Semantica — scoped queries at a point in time, graph snapshots, node change timelines, temporal causal analysis, and graph state reconstruction. Uses AgentContext.find_precedents(as_of=), ContextGraph.state_at(), CausalChainAnalyzer.trace_at_time(), and TemporalQueryRewriter. Sub-commands: query, snapshot, timeline, causal-at, precedents-at.
---
# /semantica:temporal
Temporal graph operations. Usage: `/semantica:temporal <sub-command> [args]`
`$ARGUMENTS` = sub-command + query/node + date expression.
---
## `query "<question>" [at|before|after <date>]`
Temporally-scoped natural-language graph query.
```python
from semantica.kg.temporal_query_rewriter import TemporalQueryRewriter
from semantica.kg.temporal_normalizer import TemporalNormalizer
normalizer = TemporalNormalizer()
# Normalize natural date expressions: "last month", "Q3 2024", "2025-01-15"
date = normalizer.normalize(date_expr)
rewriter = TemporalQueryRewriter()
# Rewrite query with temporal constraint
rewritten = rewriter.rewrite(
query=question,
temporal_constraint={"op": direction, "value": date}, # op: "at"|"before"|"after"
)
```
Then run the rewritten query through `AgentContext.retrieve()` or `ContextGraph.query()`.
Return ranked results with `Valid From`, `Valid Until`, `Active At <date>` columns. Mark nodes that were not yet created at the target time as `[not yet created]`.
---
## `snapshot <date>`
Reconstruct the full graph state as it existed at a specific point in time.
```python
from semantica.context import ContextGraph
graph = ContextGraph(advanced_analytics=True)
# state_at returns a dict snapshot of the graph at that timestamp
snapshot = graph.state_at(timestamp=date) # ISO string or datetime
```
Output:
```
Graph snapshot at <date>:
Nodes: N (M added since prev snapshot, K removed)
Edges: P
Density: 0.21
Communities: Q
Active decision categories at <date>:
| Category | Count | Avg Confidence |
Top 10 nodes (by degree at <date>):
| Node | Type | Degree |
[Compact Mermaid graph TD — top-10 most connected nodes at that time]
```
---
## `timeline <node_id>`
Show attribute and relationship changes for a node across its full history.
```python
from semantica.context import ContextGraph
graph = ContextGraph()
# Use state_at() at multiple time points to reconstruct history
# Check add_node timestamps and edge addition times from graph data
node_data = graph.find_node(node_id)
```
Output as Markdown timeline:
```
Timeline for "<node_id>" (<type>):
<timestamp> CREATED
Properties: {confidence: 0.71, category: "loan_approval"}
Source: extraction/pipeline
<timestamp> UPDATED
confidence: 0.71 → 0.91 [source: review]
<timestamp> RELATIONSHIP ADDED
"<node_id>" →[CAUSED]→ "Decision_B"
<timestamp> RELATIONSHIP REMOVED
"<node_id>" →[PRECEDED_BY]→ "Decision_X" (superseded)
Total lifespan: <duration>
Current state: <active|superseded>
```
---
## `causal-at <decision_id> <date> [--direction upstream|downstream]`
Trace a causal chain as it existed at a specific point in time.
```python
from semantica.context.causal_analyzer import CausalChainAnalyzer
from semantica.context import AgentContext
ctx = AgentContext(decision_tracking=True)
analyzer = CausalChainAnalyzer(graph_store=ctx.graph_store)
historical_chain = analyzer.trace_at_time(
event_id=decision_id,
at_time=date, # ISO string or datetime
direction=direction or "upstream",
max_depth=10,
)
```
Output:
```
Historical causal chain for <decision_id> at <date>:
Direction: upstream (what caused it?)
[Mermaid graph TD showing chain as it existed at <date>]
Decisions present then but not now: [list]
Decisions added since then: [list]
```
---
## `precedents-at "<scenario>" <date> [--category <cat>]`
Find precedent decisions that existed as of a specific date — useful for auditing what context was available when a decision was made.
```python
from semantica.context import AgentContext
ctx = AgentContext(decision_tracking=True, advanced_analytics=True)
# find_precedents supports as_of parameter for temporal precedent search
precedents = ctx.find_precedents(
scenario=scenario,
category=category or None,
limit=10,
use_hybrid_search=True,
include_context=True,
include_superseded=False,
as_of=date, # Only return precedents that existed at this date
)
```
Return: `| Rank | Decision ID | Scenario | Outcome | Confidence | Set Date | Valid Until |`
Note decisions that were superseded before or after the target date.
+228
View File
@@ -0,0 +1,228 @@
---
name: validate
description: Validate Semantica pipelines, extraction quality, graph schemas, and ontology consistency. Returns structured error/warning checklists. Uses PipelineValidator, PipelineBuilder.validate_pipeline(), GraphValidator, and OntologyValidator. Sub-commands: pipeline, step, dependencies, extraction, graph, ontology, performance.
---
# /semantica:validate
Validate pipeline and graph quality. Usage: `/semantica:validate <target> [options]`
`$ARGUMENTS` = target type + optional config or path.
---
## `pipeline [--config '<json>']`
Validate a full pipeline builder configuration.
```python
from semantica.pipeline.pipeline_builder import PipelineBuilder
from semantica.pipeline.pipeline_validator import PipelineValidator
builder = PipelineBuilder()
if config_json:
import json
builder.build_pipeline(json.loads(config_json))
# PipelineBuilder has its own quick validate
quick = builder.validate_pipeline() # returns Dict
# PipelineValidator gives full ValidationResult(valid, errors, warnings)
# Does NOT raise — always returns a result object
validator = PipelineValidator()
result = validator.validate(builder)
# Also check inter-step dependencies
deps = validator.check_dependencies(builder)
```
Output:
```
Pipeline Validation: VALID ✓ | INVALID ✗
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Steps: N registered
Valid: M steps
Errors (K):
✗ [step_name] <error message>
Warnings (J):
⚠ [step_name] <warning message>
Dependencies:
✓ All dependencies resolved
✗ Step "<name>" depends on missing step "<dep>"
Result: <valid> — K errors, J warnings
```
---
## `step <step_name> [--type <type>] [--constraints '<json>']`
Validate a single pipeline step.
```python
from semantica.pipeline.pipeline_builder import PipelineBuilder
from semantica.pipeline.pipeline_validator import PipelineValidator
import json
builder = PipelineBuilder()
step = builder.get_step(step_name)
validator = PipelineValidator()
result = validator.validate_step(
step=step,
**json.loads(constraints_json) if constraints_json else {},
)
```
Output: same checklist format but scoped to a single step.
---
## `dependencies`
Check all inter-step dependency resolution for the active pipeline.
```python
from semantica.pipeline.pipeline_builder import PipelineBuilder
from semantica.pipeline.pipeline_validator import PipelineValidator
builder = PipelineBuilder()
validator = PipelineValidator()
deps = validator.check_dependencies(builder)
```
Output:
```
Dependency Graph:
| Step | Depends On | Status |
| step_A | — | ✓ |
| step_B | step_A | ✓ |
| step_C | step_X | ✗ MISSING |
Cycles detected: YES / NO
Missing steps: [list]
```
---
## `extraction <file_path>`
Validate extraction quality for a file — entity confidence, relation density, coverage.
```python
from semantica.semantic_extract.extraction_validator import ExtractionValidator
from semantica.semantic_extract import (
NamedEntityRecognizer,
RelationExtractor,
)
from semantica.semantic_extract.cache import _result_cache
_result_cache.clear() # prevent cross-invocation cache pollution
text = open(file_path).read()
ner = NamedEntityRecognizer()
rel = RelationExtractor()
entities = ner.extract(text)
relations = rel.extract(text)
validator = ExtractionValidator()
issues = validator.validate(entities, relations)
```
Output:
```
Extraction Validation: <file_path>
Entities: N extracted
Relations: M extracted
Avg confidence: 0.83
Errors (K):
✗ <issue>
Warnings (J):
⚠ <warning>
Quality score: X/100
```
---
## `graph`
Check schema conformance, referential integrity, and structural health.
```python
from semantica.kg.graph_validator import GraphValidator
from semantica.context import ContextGraph
graph = ContextGraph()
validator = GraphValidator(graph)
result = validator.validate()
```
Output:
```
Graph Validation:
Nodes: N | Edges: M
Node types: K valid, J unknown
Referential integrity:
✗ Dangling edge: <source> → <missing target>
Schema conformance:
✗ Node "<id>" missing required property "<prop>"
Result: N errors, M warnings
```
---
## `ontology`
Validate ontology consistency and evaluate competency questions.
```python
from semantica.ontology import OntologyValidator
validator = OntologyValidator()
result = validator.validate()
cq_results = validator.evaluate_competency_questions()
```
Output:
```
Ontology Validation:
Classes: N
Properties: M
Consistent: YES ✓ | NO ✗
Competency questions:
✓ "Can we find all instances of X?" — answered
✗ "Is Y a subclass of Z?" — failed: <reason>
Result: N consistency errors, M CQ failures
```
---
## `performance`
Validate pipeline performance characteristics — bottlenecks, parallelism, and resource use.
```python
from semantica.pipeline.pipeline_builder import PipelineBuilder
from semantica.pipeline.pipeline_validator import PipelineValidator
builder = PipelineBuilder()
pipeline = builder.build()
validator = PipelineValidator()
perf = validator.validate_performance(pipeline)
```
Output: step-by-step timing estimates, parallelism opportunities, and recommended parallelism level.
+249
View File
@@ -0,0 +1,249 @@
---
name: visualize
description: Visualize the Semantica knowledge graph — topology, centrality, communities, paths, embeddings, decision insights, and temporal evolution. Uses GraphAnalyzer, CentralityCalculator, CommunityDetector, PathFinder, and ContextGraph analytics. Sub-commands: topology, centrality, community, path, decision-graph, insights, temporal, embedding.
---
# /semantica:visualize
Render graph visualizations as Mermaid, ASCII, or structured Markdown. Usage: `/semantica:visualize <sub-command> [args]`
`$ARGUMENTS` = sub-command + optional node label or filter.
---
## `topology [--filter <node_type>]`
Full graph structure analysis — node types, edge distribution, connectivity metrics.
```python
from semantica.kg.graph_analyzer import GraphAnalyzer
from semantica.context import ContextGraph
graph = ContextGraph(advanced_analytics=True)
analyzer = GraphAnalyzer()
# Comprehensive analysis
analysis = analyzer.analyze_graph(graph=graph.to_dict())
metrics = analyzer.compute_metrics(graph=graph)
connectivity = analyzer.analyze_connectivity(graph=graph)
```
Output:
```
Graph Topology:
Nodes: N (M types)
Edges: P
Density: 0.23
Avg degree: 4.7
Connected: YES / NO (K components)
Node type distribution:
[Mermaid pie chart]
| Type | Count | % | Avg Degree |
Top-10 connected nodes:
| Node | Type | Degree | Betweenness |
```
---
## `centrality [--type degree|betweenness|closeness|eigenvector|pagerank|all] [--top N]`
Calculate and rank nodes by centrality.
```python
from semantica.kg.centrality_calculator import CentralityCalculator
from semantica.context import ContextGraph
graph = ContextGraph()
calc = CentralityCalculator()
if centrality_type == "all" or not centrality_type:
scores = calc.calculate_all_centrality(graph=graph)
elif centrality_type == "degree":
scores = calc.calculate_degree_centrality(graph=graph)
elif centrality_type == "betweenness":
scores = calc.calculate_betweenness_centrality(graph=graph)
elif centrality_type == "closeness":
scores = calc.calculate_closeness_centrality(graph=graph)
elif centrality_type == "eigenvector":
scores = calc.calculate_eigenvector_centrality(graph=graph)
elif centrality_type == "pagerank":
scores = calc.calculate_pagerank(
graph=graph,
max_iterations=20,
damping_factor=0.85,
)
```
Return: `| Rank | Node | Type | Degree | Betweenness | Closeness | Eigenvector | PageRank |`
For a single node, also call `ContextGraph.get_node_centrality(node_id)` and `get_node_importance(node_id)`.
---
## `community [--algorithm louvain|leiden|label-propagation|overlapping]`
Detect and visualize graph communities/clusters.
```python
from semantica.kg.community_detector import CommunityDetector
from semantica.context import ContextGraph
graph = ContextGraph()
detector = CommunityDetector()
algorithm = algo_arg or "louvain"
if algorithm == "louvain":
result = detector.detect_communities_louvain(graph, resolution=1.0)
elif algorithm == "leiden":
result = detector.detect_communities_leiden(graph, resolution=1.0)
elif algorithm == "label-propagation":
result = detector.detect_communities_label_propagation(graph)
elif algorithm == "overlapping":
result = detector.detect_overlapping_communities(graph)
else:
result = detector.detect_communities(graph, algorithm=algorithm)
structure = detector.analyze_community_structure(graph, result)
metrics = detector.calculate_community_metrics(graph, result)
```
Output:
```
Community Detection (algorithm: louvain)
Communities: N
Modularity: 0.71
Community summary:
| ID | Size | Top Node | Internal Density | Bridge Nodes |
[Mermaid graph TD — nodes colored/grouped by community ID]
```
---
## `path <n1> <n2> [--k N] [--algorithm bfs|dijkstra|astar|k-shortest]`
Find and visualize paths between two nodes.
```python
from semantica.kg.path_finder import PathFinder
from semantica.context import ContextGraph
graph = ContextGraph()
finder = PathFinder()
k = int(k_arg) if k_arg else 3
if algorithm == "bfs":
path = finder.bfs_shortest_path(graph, source=n1, target=n2)
paths = [path]
elif algorithm == "dijkstra":
path = finder.dijkstra_shortest_path(graph, source=n1, target=n2)
paths = [path]
else: # default: k-shortest
paths = finder.find_k_shortest_paths(graph, source=n1, target=n2, k=k)
lengths = [finder.path_length(graph, p) for p in paths]
```
Output as Mermaid `sequenceDiagram` for each path:
```
Path 1 (length: 2.3):
n1 →[rel_type]→ Middle →[rel_type]→ n2
Path 2 (length: 3.7): ...
```
---
## `decision-graph [--category <cat>] [--depth N]`
Visualize the decision influence graph for a category or all decisions.
```python
from semantica.context import ContextGraph
from semantica.context.causal_analyzer import CausalChainAnalyzer
from semantica.context import AgentContext
ctx = AgentContext(decision_tracking=True, advanced_analytics=True)
graph = ContextGraph(advanced_analytics=True)
# Get decision insights
insights = graph.get_decision_insights()
# Build causal network
analyzer = CausalChainAnalyzer(graph_store=ctx.graph_store)
network = analyzer.analyze_causal_network()
```
Output as Mermaid `graph TD` with:
- Node size proportional to causal impact score
- Color by outcome (green=approved, red=rejected, yellow=deferred)
- Edge labels showing relationship type
---
## `insights`
Comprehensive decision analytics dashboard.
```python
from semantica.context import ContextGraph, AgentContext
ctx = AgentContext(decision_tracking=True, advanced_analytics=True, kg_algorithms=True)
graph = ContextGraph(advanced_analytics=True, centrality_analysis=True)
insights = graph.get_decision_insights()
summary = graph.get_decision_summary()
graph_summary = graph.get_graph_summary()
context_insights = ctx.get_context_insights()
```
Output a full analytics dashboard:
```
Decision Intelligence Dashboard
════════════════════════════════
Decisions: N total (M active)
Categories: K unique
Avg confidence: 0.87
Outcome split: approved 55% | rejected 30% | deferred 15%
Causal chains: P chains, longest: Q hops
Loops detected: R circular dependencies
Graph health:
Nodes: N | Edges: M | Density: 0.23
Communities: K | Isolated nodes: J
[Mermaid pie — outcome distribution]
[Mermaid bar — decisions by category]
```
---
## `temporal [--node <id>] [--start <date>] [--end <date>]`
Analyze how the graph evolved over time.
```python
from semantica.kg.graph_analyzer import GraphAnalyzer
from semantica.context import ContextGraph
graph = ContextGraph()
analyzer = GraphAnalyzer()
evolution = analyzer.analyze_temporal_evolution(
graph=graph,
start_time=start_date or None,
end_time=end_date or None,
metrics=["node_count", "edge_count", "density", "communities"],
)
# For a specific node, use ContextGraph.state_at()
if node_id:
snapshot = graph.state_at(timestamp=end_date or "now")
```
Output as Markdown timeline with metrics per interval.
+111 -62
View File
@@ -156,11 +156,63 @@ class OWLGenerator:
)
raise
@staticmethod
def _as_list(value: Any) -> List[Any]:
"""Normalize scalar-or-list ontology values to a list."""
if value is None:
return []
if isinstance(value, list):
return value
return [value]
@staticmethod
def _is_datatype_property(prop_type: Any) -> bool:
"""Return True for supported datatype property aliases."""
return str(prop_type or "").strip().lower() in {
"datatype",
"data",
"datatypeproperty",
}
def _get_generation_namespace_manager(self, ontology: Dict[str, Any]) -> NamespaceManager:
"""Build a namespace manager anchored to the ontology base URI for this call."""
base_uri = ontology.get("uri") or self.namespace_manager.get_base_uri()
if isinstance(base_uri, str) and not base_uri.endswith(("/", "#")):
base_uri = base_uri + "/"
return NamespaceManager(
base_uri=base_uri,
version=self.namespace_manager.version,
use_speaking_iris=self.namespace_manager.use_speaking_iris,
)
@staticmethod
def _is_http_uri(value: Any) -> bool:
return isinstance(value, str) and value.startswith(("http://", "https://"))
def _resolve_class_uri(self, value: Any, ns_manager: NamespaceManager) -> str:
if self._is_http_uri(value):
return value
return ns_manager.generate_class_iri(str(value))
def _resolve_property_identifier(self, prop: Dict[str, Any]) -> str:
return prop.get("label") or prop.get("name")
def _resolve_class_identifier(self, cls: Dict[str, Any]) -> str:
return cls.get("label") or cls.get("name")
def _resolve_datatype_range_uri(self, range_val: Any, ns_manager: NamespaceManager):
if isinstance(range_val, str) and range_val.startswith("xsd:"):
return XSD[range_val.replace("xsd:", "")]
if self._is_http_uri(range_val):
return URIRef(range_val)
return URIRef(ns_manager.generate_class_iri(str(range_val)))
def _generate_with_rdflib(
self, ontology: Dict[str, Any], format: str = "turtle", **options
) -> Union[str, Graph]:
"""Generate OWL using rdflib."""
g = Graph()
gen_ns_manager = self._get_generation_namespace_manager(ontology)
# Set up namespaces
ns_manager = RDFNamespaceManager(g)
@@ -173,6 +225,8 @@ class OWLGenerator:
# Register ontology namespace
base_uri = ontology.get("uri") or self.namespace_manager.get_base_uri()
if isinstance(base_uri, str) and not base_uri.endswith(("/", "#")):
base_uri = base_uri + "/"
ont_ns = Namespace(base_uri)
g.bind("", ont_ns)
@@ -189,19 +243,23 @@ class OWLGenerator:
# Add classes
classes = ontology.get("classes", [])
for cls in classes:
class_name = self._resolve_class_identifier(cls)
class_uri = URIRef(
cls.get("uri") or self.namespace_manager.generate_class_iri(cls["name"])
cls.get("uri")
or gen_ns_manager.generate_class_iri(class_name)
)
g.add((class_uri, RDF.type, OWL.Class))
if cls.get("label"):
g.add((class_uri, RDFS.label, Literal(cls["label"])))
class_label = cls.get("label") or cls.get("name")
if class_label:
g.add((class_uri, RDFS.label, Literal(class_label)))
if cls.get("comment"):
g.add((class_uri, RDFS.comment, Literal(cls["comment"])))
# Add subclass relationships
if cls.get("subClassOf"):
parent_uri = URIRef(cls["subClassOf"])
subclass_of = cls.get("subClassOf") or cls.get("subclassOf")
if subclass_of:
parent_uri = URIRef(self._resolve_class_uri(subclass_of, gen_ns_manager))
g.add((class_uri, RDFS.subClassOf, parent_uri))
# Add object properties
@@ -210,60 +268,52 @@ class OWLGenerator:
if prop.get("type") == "object":
prop_uri = URIRef(
prop.get("uri")
or self.namespace_manager.generate_property_iri(prop["name"])
or gen_ns_manager.generate_property_iri(
self._resolve_property_identifier(prop)
)
)
g.add((prop_uri, RDF.type, OWL.ObjectProperty))
if prop.get("label"):
g.add((prop_uri, RDFS.label, Literal(prop["label"])))
prop_label = prop.get("label") or prop.get("name")
if prop_label:
g.add((prop_uri, RDFS.label, Literal(prop_label)))
# Add domain
domains = prop.get("domain", [])
domains = self._as_list(prop.get("domain", []))
for domain in domains:
domain_uri = URIRef(
domain
if domain.startswith("http")
else self.namespace_manager.generate_class_iri(domain)
)
domain_uri = URIRef(self._resolve_class_uri(domain, gen_ns_manager))
g.add((prop_uri, RDFS.domain, domain_uri))
# Add range
ranges = prop.get("range", [])
ranges = self._as_list(prop.get("range", []))
for range_val in ranges:
range_uri = URIRef(
range_val
if range_val.startswith("http")
else self.namespace_manager.generate_class_iri(range_val)
)
range_uri = URIRef(self._resolve_class_uri(range_val, gen_ns_manager))
g.add((prop_uri, RDFS.range, range_uri))
elif prop.get("type") == "data":
elif self._is_datatype_property(prop.get("type")):
prop_uri = URIRef(
prop.get("uri")
or self.namespace_manager.generate_property_iri(prop["name"])
or gen_ns_manager.generate_property_iri(
self._resolve_property_identifier(prop)
)
)
g.add((prop_uri, RDF.type, OWL.DatatypeProperty))
if prop.get("label"):
g.add((prop_uri, RDFS.label, Literal(prop["label"])))
prop_label = prop.get("label") or prop.get("name")
if prop_label:
g.add((prop_uri, RDFS.label, Literal(prop_label)))
# Add domain
domains = prop.get("domain", [])
domains = self._as_list(prop.get("domain", []))
for domain in domains:
domain_uri = URIRef(
domain
if domain.startswith("http")
else self.namespace_manager.generate_class_iri(domain)
)
domain_uri = URIRef(self._resolve_class_uri(domain, gen_ns_manager))
g.add((prop_uri, RDFS.domain, domain_uri))
# Add range
range_type = prop.get("range", "xsd:string")
if range_type.startswith("xsd:"):
range_uri = XSD[range_type.replace("xsd:", "")]
else:
range_uri = URIRef(range_type)
g.add((prop_uri, RDFS.range, range_uri))
range_values = self._as_list(prop.get("range", "xsd:string"))
for range_val in range_values:
range_uri = self._resolve_datatype_range_uri(range_val, gen_ns_manager)
g.add((prop_uri, RDFS.range, range_uri))
# Serialize
if format == "turtle":
@@ -282,9 +332,12 @@ class OWLGenerator:
) -> str:
"""Generate OWL using basic string formatting (fallback)."""
lines = []
gen_ns_manager = self._get_generation_namespace_manager(ontology)
# Namespace declarations
base_uri = ontology.get("uri") or self.namespace_manager.get_base_uri()
if isinstance(base_uri, str) and not base_uri.endswith(("/", "#")):
base_uri = base_uri + "/"
lines.append(f"@prefix : <{base_uri}> .")
lines.append("@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .")
lines.append("@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .")
@@ -303,16 +356,19 @@ class OWLGenerator:
# Classes
classes = ontology.get("classes", [])
for cls in classes:
class_uri = cls.get("uri") or self.namespace_manager.generate_class_iri(
cls["name"]
class_name = self._resolve_class_identifier(cls)
class_uri = cls.get("uri") or gen_ns_manager.generate_class_iri(
class_name
)
lines.append(f"<{class_uri}> a owl:Class ;")
if cls.get("label"):
lines.append(f' rdfs:label "{cls["label"]}" ;')
class_label = cls.get("label") or cls.get("name")
if class_label:
lines.append(f' rdfs:label "{class_label}" ;')
if cls.get("comment"):
lines.append(f' rdfs:comment "{cls["comment"]}" ;')
if cls.get("subClassOf"):
parent_uri = cls["subClassOf"]
subclass_of = cls.get("subClassOf") or cls.get("subclassOf")
if subclass_of:
parent_uri = self._resolve_class_uri(subclass_of, gen_ns_manager)
lines.append(f" rdfs:subClassOf <{parent_uri}> .")
else:
lines[-1] = lines[-1].rstrip(" ;") + " ."
@@ -321,8 +377,8 @@ class OWLGenerator:
# Properties
properties = ontology.get("properties", [])
for prop in properties:
prop_uri = prop.get("uri") or self.namespace_manager.generate_property_iri(
prop["name"]
prop_uri = prop.get("uri") or gen_ns_manager.generate_property_iri(
self._resolve_property_identifier(prop)
)
prop_type = (
"owl:ObjectProperty"
@@ -330,32 +386,25 @@ class OWLGenerator:
else "owl:DatatypeProperty"
)
lines.append(f"<{prop_uri}> a {prop_type} ;")
if prop.get("label"):
lines.append(f' rdfs:label "{prop["label"]}" ;')
prop_label = prop.get("label") or prop.get("name")
if prop_label:
lines.append(f' rdfs:label "{prop_label}" ;')
# Domain
domains = prop.get("domain", [])
domains = self._as_list(prop.get("domain", []))
for domain in domains:
domain_uri = (
domain
if domain.startswith("http")
else self.namespace_manager.generate_class_iri(domain)
)
domain_uri = self._resolve_class_uri(domain, gen_ns_manager)
lines.append(f" rdfs:domain <{domain_uri}> ;")
# Range
ranges = prop.get("range", [])
ranges = self._as_list(prop.get("range", []))
for range_val in ranges:
if prop.get("type") == "data" and range_val.startswith("xsd:"):
lines.append(
f" rdfs:range {range_val.replace('xsd:', 'xsd:')} ;"
)
if self._is_datatype_property(prop.get("type")) and isinstance(
range_val, str
) and range_val.startswith("xsd:"):
lines.append(f" rdfs:range {range_val} ;")
else:
range_uri = (
range_val
if range_val.startswith("http")
else self.namespace_manager.generate_class_iri(range_val)
)
range_uri = self._resolve_class_uri(range_val, gen_ns_manager)
lines.append(f" rdfs:range <{range_uri}> ;")
lines[-1] = lines[-1].rstrip(" ;") + " ."
@@ -165,6 +165,45 @@ class TestOntologyComprehensive(unittest.TestCase):
self.assertIn("Person", owl_output)
self.assertIn("hasName", owl_output)
def test_owl_generator_user_facing_schema_compatibility(self):
try:
from semantica.ontology.owl_generator import OWLGenerator
except ImportError:
self.skipTest("OWLGenerator not importable")
generator = OWLGenerator()
ontology = {
"name": "UserFacingOntology",
"uri": "http://example.org/ontology/",
"classes": [
# label should be preferred over name for generated class IRI
{"name": "Human", "label": "Person", "subclassOf": "Agent"},
],
"properties": [
{
# label should be preferred over name for generated property IRI
"name": "birthDateInternal",
"label": "birthDate",
"type": "datatype",
"domain": "Person",
# datatype range may be list in user-facing schema
"range": ["xsd:date", "http://example.org/ontology/CustomDateType"],
}
],
}
owl_output = generator.generate_owl(ontology, format="turtle")
self.assertIn("owl:DatatypeProperty", owl_output)
self.assertIn("@prefix : <http://example.org/ontology/> .", owl_output)
self.assertIn("rdfs:subClassOf", owl_output)
self.assertIn("birthDate", owl_output)
self.assertIn("xsd:date", owl_output)
self.assertIn("CustomDateType", owl_output)
self.assertIn(":Person", owl_output)
self.assertIn(":Agent", owl_output)
self.assertNotIn("https://semantica.dev/ontology/", owl_output)
# --- OntologyValidator Tests ---
# Removed as per request