diff --git a/README.md b/README.md
index aac30d3e..37067a1b 100644
--- a/README.md
+++ b/README.md
@@ -161,7 +161,7 @@ flowchart TD
**Knowledge Graph Construction** — Production-ready graphs with entity resolution, temporal support, and graph analytics. Queryable knowledge ready for AI applications.
-**GraphRAG Engine** — Hybrid vector + graph retrieval achieves 91% accuracy (30% improvement) via semantic search + graph traversal for multi-hop reasoning. [See Comparison Benchmark](cookbook/use_cases/advanced_rag/02_RAG_vs_GraphRAG_Comparison.ipynb)
+**GraphRAG Engine** — Hybrid vector + graph retrieval achieves 91% accuracy (30% improvement) via semantic search + graph traversal for multi-hop reasoning. Features LLM-generated responses grounded in knowledge graph context with reasoning traces. [See Comparison Benchmark](cookbook/use_cases/advanced_rag/02_RAG_vs_GraphRAG_Comparison.ipynb)
**AI Agent Context Engineering** — Persistent memory with RAG + knowledge graphs enables context maintenance, action validation, and structured knowledge access.
@@ -179,7 +179,8 @@ flowchart TD
| **Semantic Extraction** | NER, relationship extraction, triplet generation, LLM enhancement | Automated discovery of entities and relationships |
| **Knowledge Graphs** | Entity resolution, temporal support, graph analytics, query interface | Production-ready, queryable knowledge structures |
| **Ontology Generation** | 6-stage LLM pipeline, OWL generation, HermiT/Pellet validation | Automated ontology creation from documents |
-| **GraphRAG** | Hybrid vector + graph retrieval, multi-hop reasoning | 91% accuracy, 30% improvement over vector-only |
+| **GraphRAG** | Hybrid vector + graph retrieval, multi-hop reasoning, LLM-generated responses | 91% accuracy, 30% improvement over vector-only, reasoning traces |
+| **LLM Providers** | Unified interface to 100+ LLMs (Groq, OpenAI, HuggingFace, LiteLLM) | Clean imports, multiple providers, structured output |
| **Agent Memory** | Persistent memory (Save/Load), Hybrid Retrieval (Vector+Graph), FastEmbed support | Context-aware agents with semantic understanding |
| **Pipeline Orchestration** | Parallel execution, custom steps, orchestrator-worker pattern | Scalable, flexible data processing |
| **Quality Assurance** | Conflict detection, deduplication, quality scoring, provenance | Trusted knowledge graphs ready for production |
@@ -322,8 +323,10 @@ pip install -e ".[dev]"
| **Data Ingestion** | **Semantic Extract** | **Knowledge Graphs** | **Ontology** |
|:--------------------:|:----------------------:|:----------------------:|:--------------:|
| [Multiple Formats](#universal-data-ingestion) | [Entity & Relations](#semantic-intelligence-engine) | [Graph Analytics](#knowledge-graph-construction) | [Auto Generation](#ontology-generation--management) |
-| **Context** | **GraphRAG** | **Pipeline** | **QA** |
-| [Agent Memory](#context-engineering-for-ai-agents) | [Hybrid RAG](#knowledge-graph-powered-rag-graphrag) | [Parallel Workers](#pipeline-orchestration--parallel-processing) | [Conflict Resolution](#production-ready-quality-assurance) |
+| **Context** | **GraphRAG** | **LLM Providers** | **Pipeline** |
+| [Agent Memory](#context-engineering--memory-systems) | [Hybrid RAG](#knowledge-graph-powered-rag-graphrag) | [100+ LLMs](#llm-providers-module) | [Parallel Workers](#pipeline-orchestration--parallel-processing) |
+| **QA** | **Reasoning** | | |
+| [Conflict Resolution](#production-ready-quality-assurance) | [Rule-based Inference](#reasoning--inference-engine) | | |
---
@@ -428,12 +431,13 @@ print(f"Classes: {len(ontology.classes)}")
### Context Engineering & Memory Systems
-> **Persistent Memory** • **Hybrid Retrieval (Vector + Graph)** • **Production Graph Store (Neo4j)** • **Entity Linking**
+> **Persistent Memory** • **Hybrid Retrieval (Vector + Graph)** • **Production Graph Store (Neo4j)** • **Entity Linking** • **Multi-Hop Reasoning**
```python
from semantica.context import AgentContext
from semantica.vector_store import VectorStore
from semantica.graph_store import GraphStore
+from semantica.llms import Groq
# Initialize Context with Hybrid Retrieval (Graph + Vector)
context = AgentContext(
@@ -450,6 +454,14 @@ context.store(
# Retrieve with context expansion
results = context.retrieve("What is the user building?", use_graph_expansion=True)
+
+# Query with reasoning and LLM-generated responses
+llm_provider = Groq(model="llama-3.1-8b-instant", api_key=os.getenv("GROQ_API_KEY"))
+reasoned_result = context.query_with_reasoning(
+ query="What is the user building?",
+ llm_provider=llm_provider,
+ max_hops=2
+)
```
**Core Notebooks:**
@@ -461,21 +473,89 @@ results = context.retrieve("What is the user building?", use_graph_expansion=Tru
### Knowledge Graph-Powered RAG (GraphRAG)
-> **30% Accuracy Improvement** • Vector + Graph Hybrid Search • 91% Accuracy
+> **30% Accuracy Improvement** • Vector + Graph Hybrid Search • 91% Accuracy • **Multi-Hop Reasoning** • **LLM-Generated Responses**
```python
-from semantica.qa_rag import GraphRAGEngine
+from semantica.context import AgentContext
+from semantica.llms import Groq, OpenAI, LiteLLM
from semantica.vector_store import VectorStore
+import os
-graphrag = GraphRAGEngine(
+# Initialize GraphRAG with hybrid retrieval
+context = AgentContext(
vector_store=VectorStore(backend="faiss"),
knowledge_graph=kg
)
-result = graphrag.query("Who founded the company?", top_k=5, expand_graph=True)
-print(f"Answer: {result.answer} (Confidence: {result.confidence:.2f})")
+
+# Configure LLM provider (supports Groq, OpenAI, HuggingFace, LiteLLM)
+llm_provider = Groq(
+ model="llama-3.1-8b-instant",
+ api_key=os.getenv("GROQ_API_KEY")
+)
+
+# Query with multi-hop reasoning and LLM-generated responses
+result = context.query_with_reasoning(
+ query="What IPs are associated with security alerts?",
+ llm_provider=llm_provider,
+ max_results=10,
+ max_hops=2
+)
+
+print(f"Response: {result['response']}")
+print(f"Reasoning Path: {result['reasoning_path']}")
+print(f"Confidence: {result['confidence']:.3f}")
```
-[**Cookbook: GraphRAG**](https://github.com/Hawksight-AI/semantica/tree/main/cookbook/use_cases/advanced_rag/01_GraphRAG_Complete.ipynb)
+**Key Features:**
+- **Multi-Hop Reasoning**: Traverses knowledge graph up to N hops to find related entities
+- **LLM-Generated Responses**: Natural language answers grounded in graph context
+- **Reasoning Trace**: Shows entity relationship paths used in reasoning
+- **Multiple LLM Providers**: Supports Groq, OpenAI, HuggingFace, and LiteLLM (100+ LLMs)
+
+[**Cookbook: GraphRAG**](https://github.com/Hawksight-AI/semantica/tree/main/cookbook/use_cases/advanced_rag/01_GraphRAG_Complete.ipynb) • [**Real-Time Anomaly Detection**](https://github.com/Hawksight-AI/semantica/tree/main/cookbook/use_cases/cybersecurity/01_Real_Time_Anomaly_Detection.ipynb)
+
+### LLM Providers Module
+
+> **Unified LLM Interface** • **100+ LLM Support via LiteLLM** • **Clean Imports** • **Multiple Providers**
+
+```python
+from semantica.llms import Groq, OpenAI, HuggingFaceLLM, LiteLLM
+import os
+
+# Groq - Fast inference
+groq = Groq(
+ model="llama-3.1-8b-instant",
+ api_key=os.getenv("GROQ_API_KEY")
+)
+response = groq.generate("What is AI?")
+
+# OpenAI
+openai = OpenAI(
+ model="gpt-4",
+ api_key=os.getenv("OPENAI_API_KEY")
+)
+response = openai.generate("What is AI?")
+
+# HuggingFace - Local models
+hf = HuggingFaceLLM(model_name="gpt2")
+response = hf.generate("What is AI?")
+
+# LiteLLM - Unified interface to 100+ LLMs
+litellm = LiteLLM(
+ model="openai/gpt-4o", # or "anthropic/claude-sonnet-4-20250514", "groq/llama-3.1-8b-instant", etc.
+ api_key=os.getenv("OPENAI_API_KEY")
+)
+response = litellm.generate("What is AI?")
+
+# Structured output
+structured = groq.generate_structured("Extract entities from: Apple Inc. was founded by Steve Jobs.")
+```
+
+**Supported Providers:**
+- **Groq**: Fast inference with Llama models
+- **OpenAI**: GPT-3.5, GPT-4, and other OpenAI models
+- **HuggingFace**: Local LLM inference with Transformers
+- **LiteLLM**: Unified interface to 100+ LLM providers (OpenAI, Anthropic, Azure, Bedrock, Vertex AI, and more)
### Reasoning & Inference Engine
diff --git a/cookbook/use_cases/cybersecurity/01_Real_Time_Anomaly_Detection.ipynb b/cookbook/use_cases/cybersecurity/01_Real_Time_Anomaly_Detection.ipynb
index d2303f59..481005c4 100644
--- a/cookbook/use_cases/cybersecurity/01_Real_Time_Anomaly_Detection.ipynb
+++ b/cookbook/use_cases/cybersecurity/01_Real_Time_Anomaly_Detection.ipynb
@@ -60,9 +60,32 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 2,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Note: you may need to restart the kernel to use updated packages.\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "WARNING: Ignoring invalid distribution ~gno (c:\\Users\\Mohd Kaif\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages)\n",
+ "WARNING: Ignoring invalid distribution ~lotly (c:\\Users\\Mohd Kaif\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages)\n",
+ "WARNING: Ignoring invalid distribution ~ython-socketio (c:\\Users\\Mohd Kaif\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages)\n",
+ "WARNING: Ignoring invalid distribution ~gno (c:\\Users\\Mohd Kaif\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages)\n",
+ "WARNING: Ignoring invalid distribution ~lotly (c:\\Users\\Mohd Kaif\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages)\n",
+ "WARNING: Ignoring invalid distribution ~ython-socketio (c:\\Users\\Mohd Kaif\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages)\n",
+ "WARNING: Ignoring invalid distribution ~gno (c:\\Users\\Mohd Kaif\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages)\n",
+ "WARNING: Ignoring invalid distribution ~lotly (c:\\Users\\Mohd Kaif\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages)\n",
+ "WARNING: Ignoring invalid distribution ~ython-socketio (c:\\Users\\Mohd Kaif\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages)\n"
+ ]
+ }
+ ],
"source": [
"%pip install -qU semantica networkx matplotlib plotly pandas faiss-cpu beautifulsoup4 groq sentence-transformers scikit-learn\n"
]
@@ -76,13 +99,13 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"\n",
- "os.environ[\"GROQ_API_KEY\"] = os.getenv(\"GROQ_API_KEY\", \"your-key-here\")\n",
+ "os.environ[\"GROQ_API_KEY\"] = os.getenv(\"GROQ_API_KEY\", \"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\")\n",
"\n",
"# Configuration constants\n",
"EMBEDDING_DIMENSION = 384\n",
@@ -101,9 +124,40 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 4,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Ingesting from 5 feed sources...\n"
+ ]
+ },
+ {
+ "data": {
+ "text/html": [
+ "
🧠 Semantica - 📊 Current Progress
| Status | Action | Module | Submodule | File | Time |
|---|
| ✅ | Semantica is extracting | 🎯 semantic_extract | NERExtractor | - | 0.55s |
| ✅ | Semantica is extracting | 🎯 semantic_extract | RelationExtractor | - | 0.05s |
| ✅ | Semantica is resolving | ⚠️ conflicts | ConflictDetector | - | 0.00s |
| ✅ | Semantica is building | 🧠 kg | GraphBuilder | - | 350.71s |
| 🔄 | Semantica is building | 🧠 kg | EntityResolver | - | 84.09s |
| ✅ | Semantica is deduplicating | 🔄 deduplication | DuplicateDetector | - | 0.08s |
| ✅ | Semantica is deduplicating | 🔄 deduplication | SimilarityCalculator | - | 0.02s |
| ✅ | Semantica is deduplicating | 🔄 deduplication | EntityMerger | - | 0.13s |
| ✅ | Semantica is deduplicating | 🔄 deduplication | MergeStrategyManager | - | 0.03s |
| ✅ | Semantica is indexing | 📊 vector_store | VectorStore | - | 0.01s |
"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ " [1/5] US-CERT Alerts: 10 documents\n",
+ " [2/5] SANS ISC: 10 documents\n",
+ " [3/5] Krebs on Security: 10 documents\n",
+ " [4/5] ThreatPost: 10 documents\n",
+ "Ingested 40 documents\n"
+ ]
+ }
+ ],
"source": [
"from semantica.ingest import FeedIngestor, StreamIngestor, FileIngestor\n",
"import os\n",
@@ -169,9 +223,18 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 5,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Parsing 40 documents...\n",
+ " Parsed 40/40 documents...\n"
+ ]
+ }
+ ],
"source": [
"from semantica.parse import DocumentParser\n",
"\n",
@@ -203,9 +266,21 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 6,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Normalizing 40 documents...\n",
+ " Normalized 40/40 documents...\n",
+ "Chunking 40 documents...\n",
+ " Chunked 40/40 documents (307 chunks so far)\n",
+ "Created 307 chunks from 40 documents\n"
+ ]
+ }
+ ],
"source": [
"from semantica.normalize import TextNormalizer\n",
"from semantica.split import TextSplitter\n",
@@ -254,18 +329,47 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 11,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Extracting entities from 307 chunks...\n",
+ " Processed 20/307 chunks (6 entities found)\n",
+ " Processed 40/307 chunks (10 entities found)\n",
+ " Processed 60/307 chunks (15 entities found)\n",
+ " Processed 80/307 chunks (26 entities found)\n",
+ " Processed 100/307 chunks (61 entities found)\n",
+ " Processed 120/307 chunks (86 entities found)\n",
+ " Processed 140/307 chunks (98 entities found)\n",
+ " Processed 160/307 chunks (116 entities found)\n",
+ " Processed 180/307 chunks (144 entities found)\n",
+ " Processed 200/307 chunks (160 entities found)\n",
+ " Processed 220/307 chunks (198 entities found)\n",
+ " Processed 240/307 chunks (232 entities found)\n",
+ " Processed 260/307 chunks (247 entities found)\n",
+ " Processed 280/307 chunks (257 entities found)\n",
+ " Processed 300/307 chunks (269 entities found)\n",
+ " Processed 307/307 chunks (273 entities found)\n",
+ "Extracted 3 IPs, 28 users, 0 alerts\n"
+ ]
+ }
+ ],
"source": [
"from semantica.semantic_extract import NERExtractor\n",
"\n",
- "entity_extractor = NERExtractor(\n",
- " method=\"llm\",\n",
- " provider=\"groq\",\n",
- " llm_model=\"llama-3.1-8b-instant\",\n",
- " temperature=0.0\n",
- ")\n",
+ "security_patterns = {\n",
+ " \"IP\": r\"\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\b\",\n",
+ " \"User\": r\"\\buser\\s+([a-zA-Z0-9_\\-\\.]+)\\b\",\n",
+ " \"Alert\": r\"\\b(?:alert|warning|alarm):\\s*([^\\n\\.]+)|\\b(?:alert|warning|alarm)\\s+(?:detected|triggered|generated|raised)\\b\",\n",
+ " \"Event\": r\"\\b(?:login|access|connection|request|attempt|failed|successful|suspicious|unusual)\\s+(?:event|attempt|request|activity|access)\\b\",\n",
+ " \"Log\": r\"\\b\\d{4}-\\d{2}-\\d{2}\\s+\\d{2}:\\d{2}:\\d{2}\\s+-\\s+([^\\n]+)\",\n",
+ " \"Attack\": r\"\\b(?:attack|breach|intrusion|exploit|malware|virus|ransomware|phishing|brute\\s+force|ddos)\\b\",\n",
+ "}\n",
+ "\n",
+ "entity_extractor = NERExtractor(method=\"regex\", patterns=security_patterns)\n",
"\n",
"all_entities = []\n",
"print(f\"Extracting entities from {len(chunked_documents)} chunks...\")\n",
@@ -299,30 +403,69 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 16,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Extracting relationships from 307 chunks using 267 filtered entities...\n",
+ " Processed 20/307 chunks (534 relationships found)\n",
+ " Processed 40/307 chunks (534 relationships found)\n",
+ " Processed 60/307 chunks (534 relationships found)\n",
+ " Processed 80/307 chunks (534 relationships found)\n",
+ " Processed 100/307 chunks (534 relationships found)\n",
+ " Processed 120/307 chunks (534 relationships found)\n",
+ " Processed 140/307 chunks (534 relationships found)\n",
+ " Processed 160/307 chunks (534 relationships found)\n",
+ " Processed 180/307 chunks (534 relationships found)\n",
+ " Processed 200/307 chunks (534 relationships found)\n",
+ " Processed 220/307 chunks (534 relationships found)\n",
+ " Processed 240/307 chunks (534 relationships found)\n",
+ " Processed 260/307 chunks (534 relationships found)\n",
+ " Processed 280/307 chunks (534 relationships found)\n",
+ " Processed 300/307 chunks (534 relationships found)\n",
+ " Processed 307/307 chunks (534 relationships found)\n",
+ "Extracted 534 relationships\n"
+ ]
+ }
+ ],
"source": [
"from semantica.semantic_extract import RelationExtractor\n",
"\n",
+ "# Filter entities to only meaningful security entities\n",
+ "filtered_entities = [\n",
+ " e for e in all_entities \n",
+ " if e.label in [\"IP\", \"User\", \"Alert\", \"Attack\", \"Event\", \"Log\"] \n",
+ " and len(e.text) > 2\n",
+ " and e.text.lower() not in [\"to\", \"from\", \"should\", \"would\", \"choices\", \"connects\"]\n",
+ "]\n",
+ "\n",
"relation_extractor = RelationExtractor(\n",
- " method=\"llm\",\n",
- " provider=\"groq\",\n",
- " llm_model=\"llama-3.1-8b-instant\",\n",
- " temperature=0.0\n",
+ " method=\"cooccurrence\",\n",
+ " max_distance=60,\n",
+ " confidence_threshold=0.6\n",
")\n",
"\n",
+ "# Deduplicate relationships\n",
+ "seen_relationships = set()\n",
"all_relationships = []\n",
- "print(f\"Extracting relationships from {len(chunked_documents)} chunks...\")\n",
+ "print(f\"Extracting relationships from {len(chunked_documents)} chunks using {len(filtered_entities)} filtered entities...\")\n",
"for i, chunk in enumerate(chunked_documents, 1):\n",
" chunk_text = chunk.text if hasattr(chunk, 'text') else str(chunk)\n",
" try:\n",
" relationships = relation_extractor.extract_relations(\n",
" chunk_text,\n",
- " entities=all_entities,\n",
+ " entities=filtered_entities,\n",
" relation_types=[\"from\", \"attempts\", \"triggers\", \"detects\", \"associated_with\", \"causes\"]\n",
" )\n",
- " all_relationships.extend(relationships)\n",
+ " # Deduplicate based on subject, predicate, object\n",
+ " for rel in relationships:\n",
+ " rel_key = (rel.subject.text, rel.predicate, rel.object.text)\n",
+ " if rel_key not in seen_relationships:\n",
+ " seen_relationships.add(rel_key)\n",
+ " all_relationships.append(rel)\n",
" except Exception:\n",
" continue\n",
" \n",
@@ -336,72 +479,88 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Resolving Duplicate Events\n"
+ "## Detecting Security Conflicts\n",
+ "\n",
+ "- **Using entity-wide conflict detection** to identify all types of conflicts (value, type, relationship, temporal) across security entities from multiple sources. \n",
+ "- **Voting resolution strategy** selects the majority consensus value, ensuring reliability through multi-source agreement for security event data.\n"
]
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 21,
"metadata": {},
- "outputs": [],
- "source": [
- "from semantica.kg import EntityResolver\n",
- "from semantica.semantic_extract import Entity\n",
- "\n",
- "# Convert Entity objects to dictionaries for EntityResolver\n",
- "print(f\"Converting {len(all_entities)} entities to dictionaries...\")\n",
- "entity_dicts = [{\"name\": e.text, \"type\": e.label, \"confidence\": e.confidence} for e in all_entities]\n",
- "\n",
- "# Use EntityResolver class to resolve duplicates\n",
- "entity_resolver = EntityResolver(strategy=\"fuzzy\", similarity_threshold=0.85)\n",
- "\n",
- "print(f\"Resolving duplicates in {len(entity_dicts)} entities...\")\n",
- "resolved_entities = entity_resolver.resolve_entities(entity_dicts)\n",
- "\n",
- "# Convert back to Entity objects\n",
- "print(f\"Converting {len(resolved_entities)} resolved entities back to Entity objects...\")\n",
- "merged_entities = [\n",
- " Entity(text=e[\"name\"], label=e[\"type\"], confidence=e.get(\"confidence\", 1.0))\n",
- " for e in resolved_entities\n",
- "]\n",
- "\n",
- "print(f\"Deduplicated {len(entity_dicts)} entities to {len(merged_entities)} unique entities\")\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Detecting Security Conflicts\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Detecting conflicts in 273 entities and 534 relationships...\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Detected 0 conflicts (0 entity, 0 relationship)\n",
+ "No conflicts detected\n"
+ ]
+ }
+ ],
"source": [
"from semantica.conflicts import ConflictDetector, ConflictResolver\n",
"\n",
- "# Use entity conflict detection for conflicting security event attributes\n",
- "# first_seen strategy prioritizes the initial reported event in real-time streams\n",
"conflict_detector = ConflictDetector()\n",
"conflict_resolver = ConflictResolver()\n",
"\n",
- "print(f\"Detecting entity conflicts in {len(merged_entities)} entities...\")\n",
- "conflicts = conflict_detector.detect_conflicts(\n",
- " entities=merged_entities,\n",
- " relationships=all_relationships,\n",
- " method=\"entity\" # Detect conflicts in entity attributes\n",
- ")\n",
+ "# Convert entities to dictionaries for conflict detection\n",
+ "entity_dicts = [\n",
+ " {\n",
+ " \"id\": e.text,\n",
+ " \"text\": e.text,\n",
+ " \"label\": e.label,\n",
+ " \"type\": e.label,\n",
+ " \"confidence\": e.confidence if hasattr(e, 'confidence') else 1.0,\n",
+ " \"metadata\": e.metadata if hasattr(e, 'metadata') else {}\n",
+ " }\n",
+ " for e in all_entities\n",
+ "]\n",
"\n",
- "print(f\"Detected {len(conflicts)} entity conflicts\")\n",
+ "# Convert relationships to dictionaries for conflict detection\n",
+ "relationship_dicts = [\n",
+ " {\n",
+ " \"id\": f\"{r.subject.text}_{r.predicate}_{r.object.text}\",\n",
+ " \"source_id\": r.subject.text,\n",
+ " \"target_id\": r.object.text,\n",
+ " \"type\": r.predicate,\n",
+ " \"subject\": r.subject.text,\n",
+ " \"object\": r.object.text,\n",
+ " \"predicate\": r.predicate,\n",
+ " \"confidence\": r.confidence if hasattr(r, 'confidence') else 1.0\n",
+ " }\n",
+ " for r in all_relationships\n",
+ "]\n",
"\n",
- "if conflicts:\n",
- " print(f\"Resolving conflicts using first_seen strategy...\")\n",
+ "print(f\"Detecting conflicts in {len(entity_dicts)} entities and {len(relationship_dicts)} relationships...\")\n",
+ "\n",
+ "# Detect entity conflicts (value, type, temporal)\n",
+ "value_conflicts = conflict_detector.detect_value_conflicts(entity_dicts, property_name=\"label\")\n",
+ "type_conflicts = conflict_detector.detect_type_conflicts(entity_dicts)\n",
+ "temporal_conflicts = conflict_detector.detect_temporal_conflicts(entity_dicts)\n",
+ "entity_conflicts = value_conflicts + type_conflicts + temporal_conflicts\n",
+ "\n",
+ "# Detect relationship conflicts\n",
+ "relationship_conflicts = conflict_detector.detect_relationship_conflicts(relationship_dicts)\n",
+ "\n",
+ "# Combine all conflicts\n",
+ "all_conflicts = entity_conflicts + relationship_conflicts\n",
+ "\n",
+ "print(f\"Detected {len(all_conflicts)} conflicts ({len(entity_conflicts)} entity, {len(relationship_conflicts)} relationship)\")\n",
+ "\n",
+ "if all_conflicts:\n",
+ " print(f\"Resolving conflicts using voting strategy...\")\n",
" resolved = conflict_resolver.resolve_conflicts(\n",
- " conflicts,\n",
- " strategy=\"first_seen\" # Prioritize the first seen event in real-time\n",
+ " all_conflicts,\n",
+ " strategy=\"voting\"\n",
" )\n",
" print(f\"Resolved {len(resolved)} conflicts\")\n",
"else:\n",
@@ -417,9 +576,39 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 23,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Building knowledge graph...\n",
+ "Processing 273 entities, 534 relationships (807 total)...\n",
+ " Entities: 100/273 (36.6%) | ETA: 3.5m | Rate: 0.8/s\n",
+ " Entities: 200/273 (73.3%) | ETA: 1.5m | Rate: 0.8/s\n",
+ " Entities: 273/273 (100.0%) | ETA: 0.0s | Rate: 0.8/s\n",
+ " Relationships: 100/534\n",
+ " Relationships: 200/534\n",
+ " Relationships: 300/534\n",
+ " Relationships: 400/534\n",
+ " Relationships: 500/534\n",
+ " Relationships: 534/534\n",
+ "Resolving 30 entities...\n",
+ "✅ Resolved to 3 unique entities (8.32s)\n",
+ "Building graph structure...\n",
+ "✅ Graph structure built (0.00s)\n",
+ "\n",
+ "============================================================\n",
+ "✅ Knowledge Graph Build Complete\n",
+ " Entities: 3\n",
+ " Relationships: 534\n",
+ " Total time: 350.71s\n",
+ "============================================================\n",
+ "Graph: 3 entities, 534 relationships\n"
+ ]
+ }
+ ],
"source": [
"from semantica.kg import GraphBuilder\n",
"\n",
@@ -433,8 +622,8 @@
"\n",
"print(f\"Building knowledge graph...\")\n",
"kg_sources = [{\n",
- " \"entities\": [{\"text\": e.text, \"type\": e.label, \"confidence\": e.confidence} for e in merged_entities],\n",
- " \"relationships\": [{\"source\": r.source, \"target\": r.target, \"type\": r.label, \"confidence\": r.confidence} for r in all_relationships]\n",
+ " \"entities\": [{\"text\": e.text, \"type\": e.label, \"confidence\": e.confidence if hasattr(e, 'confidence') else 1.0} for e in all_entities],\n",
+ " \"relationships\": [{\"source\": r.subject.text, \"target\": r.object.text, \"type\": r.predicate, \"confidence\": r.confidence if hasattr(r, 'confidence') else 1.0} for r in all_relationships]\n",
"}]\n",
"\n",
"kg = graph_builder.build(kg_sources)\n",
@@ -453,9 +642,25 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 24,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "fastembed not available. Install with: pip install fastembed. Using fallback embedding method.\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Generating embeddings for 6 events and 3 IPs...\n",
+ "Generated 6 event embeddings and 3 IP embeddings\n"
+ ]
+ }
+ ],
"source": [
"from semantica.embeddings import EmbeddingGenerator\n",
"\n",
@@ -484,9 +689,25 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 25,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "fastembed not available. Install with: pip install fastembed. Using fallback embedding method.\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Storing 6 event vectors and 3 IP vectors...\n",
+ "Stored 6 event vectors and 3 IP vectors\n"
+ ]
+ }
+ ],
"source": [
"from semantica.vector_store import VectorStore\n",
"\n",
@@ -515,9 +736,21 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 26,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "ename": "AttributeError",
+ "evalue": "'TemporalGraphQuery' object has no attribute 'detect_temporal_patterns'",
+ "output_type": "error",
+ "traceback": [
+ "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
+ "\u001b[1;31mAttributeError\u001b[0m Traceback (most recent call last)",
+ "Cell \u001b[1;32mIn[26], line 15\u001b[0m\n\u001b[0;32m 8\u001b[0m query_results \u001b[38;5;241m=\u001b[39m temporal_query\u001b[38;5;241m.\u001b[39mquery_at_time(\n\u001b[0;32m 9\u001b[0m kg,\n\u001b[0;32m 10\u001b[0m query\u001b[38;5;241m=\u001b[39m{\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mtype\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mAlert\u001b[39m\u001b[38;5;124m\"\u001b[39m},\n\u001b[0;32m 11\u001b[0m at_time\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m2024-01-01 10:04:00\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m 12\u001b[0m )\n\u001b[0;32m 14\u001b[0m evolution \u001b[38;5;241m=\u001b[39m temporal_query\u001b[38;5;241m.\u001b[39manalyze_evolution(kg)\n\u001b[1;32m---> 15\u001b[0m temporal_patterns \u001b[38;5;241m=\u001b[39m \u001b[43mtemporal_query\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdetect_temporal_patterns\u001b[49m(kg, pattern_type\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124msequence\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 17\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mTemporal queries: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mlen\u001b[39m(query_results)\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m alerts at query time\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 18\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mTemporal patterns detected: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mlen\u001b[39m(temporal_patterns)\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n",
+ "\u001b[1;31mAttributeError\u001b[0m: 'TemporalGraphQuery' object has no attribute 'detect_temporal_patterns'"
+ ]
+ }
+ ],
"source": [
"from semantica.kg import TemporalGraphQuery\n",
"\n",
@@ -656,27 +889,34 @@
"outputs": [],
"source": [
"from semantica.context import AgentContext\n",
+ "from semantica.llms import Groq\n",
+ "import os\n",
"\n",
"context = AgentContext(vector_store=vector_store, knowledge_graph=kg)\n",
"\n",
- "query = \"What IPs are associated with security alerts?\"\n",
- "results = context.retrieve(\n",
- " query,\n",
- " max_results=10,\n",
- " use_graph=True,\n",
- " expand_graph=True,\n",
- " include_entities=True,\n",
- " include_relationships=True\n",
+ "# Initialize LLM provider\n",
+ "llm_provider = Groq(\n",
+ " model=\"llama-3.1-8b-instant\",\n",
+ " api_key=os.getenv(\"GROQ_API_KEY\")\n",
")\n",
"\n",
- "print(f\"GraphRAG query: '{query}'\")\n",
- "print(f\"\\nRetrieved {len(results)} results:\\n\")\n",
- "for i, result in enumerate(results[:5], 1):\n",
- " print(f\"{i}. Score: {result.get('score', 0):.3f}\")\n",
- " print(f\" Content: {result.get('content', '')[:200]}...\")\n",
- " if result.get('related_entities'):\n",
- " print(f\" Related entities: {len(result['related_entities'])}\")\n",
- " print()\n"
+ "query = \"What IPs are associated with security alerts?\"\n",
+ "result = context.query_with_reasoning(\n",
+ " query=query,\n",
+ " llm_provider=llm_provider,\n",
+ " max_results=10,\n",
+ " max_hops=2\n",
+ ")\n",
+ "\n",
+ "print(f\"GraphRAG Query with Reasoning: '{query}'\\n\")\n",
+ "print(\"=\" * 80)\n",
+ "print(f\"\\nGenerated Response:\\n{result['response']}\\n\")\n",
+ "print(\"=\" * 80)\n",
+ "if result.get('reasoning_path'):\n",
+ " print(f\"\\nReasoning Path:\\n{result['reasoning_path']}\\n\")\n",
+ "print(f\"Confidence: {result.get('confidence', 0):.3f}\")\n",
+ "print(f\"Sources Used: {result.get('num_sources', 0)}\")\n",
+ "print(f\"Reasoning Paths Found: {result.get('num_reasoning_paths', 0)}\")\n"
]
},
{
@@ -730,8 +970,22 @@
}
],
"metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
"language_info": {
- "name": "python"
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.11.9"
}
},
"nbformat": 4,
diff --git a/docs/modules.md b/docs/modules.md
index 2352cbb4..581222d1 100644
--- a/docs/modules.md
+++ b/docs/modules.md
@@ -17,7 +17,7 @@ Semantica's modules are organized into six logical layers:
| **Core Processing** | [Semantic Extract](#semantic-extract-module), [Knowledge Graph](#knowledge-graph-kg-module), [Ontology](#ontology-module), [Reasoning](#reasoning-module) | Entity extraction, graph construction, inference |
| **Storage** | [Embeddings](#embeddings-module), [Vector Store](#vector-store-module), [Graph Store](#graph-store-module), [Triplet Store](#triplet-store-module) | Vector, graph, and triplet persistence |
| **Quality Assurance** | [Deduplication](#deduplication-module), [Conflicts](#conflicts-module) | Data quality and consistency |
-| **Context & Memory** | [Context](#context-module), [Seed](#seed-module) | Agent memory and foundation data |
+| **Context & Memory** | [Context](#context-module), [Seed](#seed-module), [LLM Providers](#llm-providers-module) | Agent memory, foundation data, and LLM integration |
| **Output & Orchestration** | [Export](#export-module), [Visualization](#visualization-module), [Pipeline](#pipeline-module) | Export, visualization, and workflow management |
---
@@ -778,7 +778,7 @@ These modules provide context engineering for agents and foundation data managem
### Context Module
!!! abstract "Purpose"
- Context engineering infrastructure for agents. Formalizes context as a graph of connections with RAG-enhanced memory.
+ Context engineering infrastructure for agents. Formalizes context as a graph of connections with RAG-enhanced memory. Features GraphRAG with multi-hop reasoning and LLM-generated responses.
**Key Features:**
@@ -786,6 +786,9 @@ These modules provide context engineering for agents and foundation data managem
- Agent memory management with RAG integration
- Entity linking across sources with URI assignment
- Hybrid context retrieval (vector + graph + memory)
+- **Multi-hop reasoning** through knowledge graphs
+- **LLM-generated responses** grounded in graph context
+- **Reasoning trace** showing entity relationship paths
- Conversation history management
- Context accumulation and synthesis
- Graph-based context traversal
@@ -796,9 +799,10 @@ These modules provide context engineering for agents and foundation data managem
- `ContextNode` — Context graph node data structure
- `ContextEdge` — Context graph edge data structure
- `AgentMemory` — Manages persistent agent memory with RAG
+- `AgentContext` — High-level context interface with GraphRAG capabilities
+- `ContextRetriever` — Retrieves relevant context with multi-hop reasoning
- `MemoryItem` — Memory item data structure
- `EntityLinker` — Links entities across sources with URI assignment
-- `ContextRetriever` — Retrieves relevant context from multiple sources
**Algorithms:**
@@ -807,22 +811,43 @@ These modules provide context engineering for agents and foundation data managem
| **Graph Construction** | BFS/DFS traversal, type-based indexing |
| **Memory Management** | Vector embedding, similarity search, retention policies |
| **Context Retrieval** | Vector similarity, multi-hop graph expansion, hybrid scoring |
+| **Multi-Hop Reasoning** | BFS traversal up to N hops, reasoning path construction |
+| **LLM Integration** | Prompt engineering with context and reasoning paths |
| **Entity Linking** | Hash-based URI generation, text similarity matching |
**Quick Example:**
```python
-from semantica.context import ContextGraph, AgentMemory
-from semantica.context.methods import build_context_graph
+from semantica.context import AgentContext, ContextGraph, AgentMemory
+from semantica.llms import Groq
+from semantica.vector_store import VectorStore
+import os
-# Using convenience function
-result = build_context_graph(
- entities=entities,
- relationships=relationships,
- method="entities_relationships"
+# Using AgentContext with GraphRAG reasoning
+context = AgentContext(
+ vector_store=VectorStore(backend="faiss"),
+ knowledge_graph=kg
)
-# Using classes directly
+# Configure LLM provider
+llm_provider = Groq(
+ model="llama-3.1-8b-instant",
+ api_key=os.getenv("GROQ_API_KEY")
+)
+
+# Query with multi-hop reasoning and LLM-generated response
+result = context.query_with_reasoning(
+ query="What IPs are associated with security alerts?",
+ llm_provider=llm_provider,
+ max_results=10,
+ max_hops=2
+)
+
+print(f"Response: {result['response']}")
+print(f"Reasoning Path: {result['reasoning_path']}")
+print(f"Confidence: {result['confidence']:.3f}")
+
+# Traditional context graph and memory
graph = ContextGraph()
graph_data = graph.build_from_entities_and_relationships(entities, relationships)
@@ -831,6 +856,72 @@ memory_id = memory.store("User asked about Python", metadata={"type": "conversat
results = memory.retrieve("Python", max_results=5)
```
+**API Reference**: [Context Module](reference/context.md)
+
+---
+
+### LLM Providers Module
+
+!!! abstract "Purpose"
+ Unified interface for LLM providers. Supports Groq, OpenAI, HuggingFace, and LiteLLM (100+ LLMs) with clean imports and consistent API.
+
+**Key Features:**
+
+- **Unified Interface**: Same `generate()` and `generate_structured()` methods across all providers
+- **Multiple Providers**: Groq, OpenAI, HuggingFace, and LiteLLM (100+ LLMs)
+- **Clean Imports**: Simple `from semantica.llms import Groq, OpenAI, HuggingFaceLLM, LiteLLM`
+- **Structured Output**: JSON generation support
+- **API Key Management**: Environment variable and direct key support
+- **Error Handling**: Graceful fallback when providers unavailable
+
+**Components:**
+
+- `Groq` — Groq API provider for fast inference
+- `OpenAI` — OpenAI API provider (GPT-3.5, GPT-4, etc.)
+- `HuggingFaceLLM` — HuggingFace Transformers for local LLM inference
+- `LiteLLM` — Unified interface to 100+ LLM providers (OpenAI, Anthropic, Azure, Bedrock, Vertex AI, etc.)
+
+**Supported Providers via LiteLLM:**
+
+- OpenAI, Anthropic, Groq, Azure, Bedrock, Vertex AI, Cohere, Mistral, and 90+ more
+
+**Quick Example:**
+
+```python
+from semantica.llms import Groq, OpenAI, HuggingFaceLLM, LiteLLM
+import os
+
+# Groq - Fast inference
+groq = Groq(
+ model="llama-3.1-8b-instant",
+ api_key=os.getenv("GROQ_API_KEY")
+)
+response = groq.generate("What is AI?")
+
+# OpenAI
+openai = OpenAI(
+ model="gpt-4",
+ api_key=os.getenv("OPENAI_API_KEY")
+)
+response = openai.generate("What is AI?")
+
+# HuggingFace - Local models
+hf = HuggingFaceLLM(model_name="gpt2") # or model="gpt2" for consistency
+response = hf.generate("What is AI?")
+
+# LiteLLM - Unified interface to 100+ LLMs
+litellm = LiteLLM(
+ model="openai/gpt-4o", # or "anthropic/claude-sonnet-4-20250514", etc.
+ api_key=os.getenv("OPENAI_API_KEY")
+)
+response = litellm.generate("What is AI?")
+
+# Structured output
+structured = groq.generate_structured("Extract entities from: Apple Inc. was founded by Steve Jobs.")
+```
+
+**API Reference**: [LLM Providers Module](reference/llms.md)
+
---
### Seed Module
@@ -1115,7 +1206,8 @@ new_facts = reasoner.infer_facts(kg)
| **Triplet Store** | `semantica.triplet_store` | `TripletStore` | RDF storage |
| **Deduplication** | `semantica.deduplication` | `DuplicateDetector` | Duplicate removal |
| **Conflicts** | `semantica.conflicts` | `ConflictDetector` | Conflict resolution |
-| **Context** | `semantica.context` | `AgentMemory` | Agent context |
+| **Context** | `semantica.context` | `AgentContext` | Agent context & GraphRAG |
+| **LLM Providers** | `semantica.llms` | `Groq`, `OpenAI`, `HuggingFaceLLM`, `LiteLLM` | LLM integration |
| **Seed** | `semantica.seed` | `SeedDataManager` | Foundation data |
| **Export** | `semantica.export` | `JSONExporter` | Data export |
| **Visualization** | `semantica.visualization` | `KGVisualizer` | Visualization |
diff --git a/docs/reference/context.md b/docs/reference/context.md
index 55e9a832..0a02353c 100644
--- a/docs/reference/context.md
+++ b/docs/reference/context.md
@@ -70,6 +70,7 @@ The high-level facade that unifies all context operations. It routes data to the
|--------|-------------|
| `store(content, ...)` | Writes information to memory. Handles auto-detection, write-through to vector store, and entity extraction. |
| `retrieve(query, ...)` | Fetches relevant context using hybrid search (Vector + Graph) and reranking. |
+| `query_with_reasoning(query, llm_provider, ...)` | **GraphRAG with multi-hop reasoning**: Retrieves context, builds reasoning paths, and generates LLM-based natural language responses grounded in the knowledge graph. |
#### **Code Example**
```python
@@ -92,6 +93,26 @@ context.store(
# 3. Retrieve Context
results = context.retrieve("What is the user building?")
+
+# 4. Query with Reasoning (GraphRAG)
+from semantica.llms import Groq
+import os
+
+llm_provider = Groq(
+ model="llama-3.1-8b-instant",
+ api_key=os.getenv("GROQ_API_KEY")
+)
+
+result = context.query_with_reasoning(
+ query="What IPs are associated with security alerts?",
+ llm_provider=llm_provider,
+ max_results=10,
+ max_hops=2
+)
+
+print(f"Response: {result['response']}")
+print(f"Reasoning Path: {result['reasoning_path']}")
+print(f"Confidence: {result['confidence']:.3f}")
```
---
@@ -254,6 +275,119 @@ results = retriever.retrieve(
---
+### GraphRAG with Multi-Hop Reasoning
+
+The `query_with_reasoning()` method extends traditional retrieval by performing multi-hop graph traversal and generating natural language responses using LLMs. This enables deeper understanding of relationships and context-aware answer generation.
+
+#### **How It Works**
+
+1. **Context Retrieval**: Retrieves relevant context using hybrid search (vector + graph)
+2. **Entity Extraction**: Extracts entities from query and retrieved context
+3. **Multi-Hop Reasoning**: Traverses knowledge graph up to N hops to find related entities
+4. **Reasoning Path Construction**: Builds reasoning chains showing entity relationships
+5. **LLM Response Generation**: Generates natural language response grounded in graph context
+
+#### **Key Features**
+
+- **Multi-Hop Reasoning**: Traverses graph up to configurable hops (default: 2)
+- **Reasoning Trace**: Shows entity relationship paths used in reasoning
+- **Grounded Responses**: LLM generates answers citing specific graph entities
+- **Multiple LLM Providers**: Supports Groq, OpenAI, HuggingFace, and LiteLLM (100+ LLMs)
+- **Fallback Handling**: Returns context with reasoning path if LLM unavailable
+
+#### **Method Signature**
+
+```python
+def query_with_reasoning(
+ self,
+ query: str,
+ llm_provider: Any, # LLM provider from semantica.llms
+ max_results: int = 10,
+ max_hops: int = 2,
+ **kwargs
+) -> Dict[str, Any]:
+```
+
+**Parameters:**
+- `query` (str): User query
+- `llm_provider`: LLM provider instance (from `semantica.llms`)
+- `max_results` (int): Maximum context results to retrieve (default: 10)
+- `max_hops` (int): Maximum graph traversal hops (default: 2)
+- `**kwargs`: Additional retrieval options
+
+**Returns:**
+- `response` (str): Generated natural language answer
+- `reasoning_path` (str): Multi-hop reasoning trace
+- `sources` (List[Dict]): Retrieved context items used
+- `confidence` (float): Overall confidence score
+- `num_sources` (int): Number of sources retrieved
+- `num_reasoning_paths` (int): Number of reasoning paths found
+
+#### **Code Example**
+
+```python
+from semantica.context import AgentContext
+from semantica.llms import Groq
+from semantica.vector_store import VectorStore
+import os
+
+# Initialize context
+context = AgentContext(
+ vector_store=VectorStore(backend="faiss"),
+ knowledge_graph=kg
+)
+
+# Configure LLM provider
+llm_provider = Groq(
+ model="llama-3.1-8b-instant",
+ api_key=os.getenv("GROQ_API_KEY")
+)
+
+# Query with reasoning
+result = context.query_with_reasoning(
+ query="What IPs are associated with security alerts?",
+ llm_provider=llm_provider,
+ max_results=10,
+ max_hops=2
+)
+
+# Access results
+print(f"Response: {result['response']}")
+print(f"\nReasoning Path: {result['reasoning_path']}")
+print(f"Confidence: {result['confidence']:.3f}")
+```
+
+#### **Using Different LLM Providers**
+
+```python
+# Groq
+from semantica.llms import Groq
+llm = Groq(model="llama-3.1-8b-instant", api_key=os.getenv("GROQ_API_KEY"))
+
+# OpenAI
+from semantica.llms import OpenAI
+llm = OpenAI(model="gpt-4", api_key=os.getenv("OPENAI_API_KEY"))
+
+# LiteLLM (100+ providers)
+from semantica.llms import LiteLLM
+llm = LiteLLM(model="anthropic/claude-sonnet-4-20250514")
+
+# Use with query_with_reasoning
+result = context.query_with_reasoning(
+ query="Your question here",
+ llm_provider=llm,
+ max_hops=3
+)
+```
+
+!!! tip "When to Use"
+ - **Complex Queries**: When simple retrieval doesn't capture relationships
+ - **Explainable AI**: When you need to show reasoning paths
+ - **Multi-Hop Questions**: "What IPs are associated with alerts that affect users?"
+ - **Grounded Responses**: When you need answers citing specific graph entities
+
+---
+
### EntityLinker (The Connector)
Resolves text mentions to unique entities and assigns URIs.
diff --git a/docs/reference/llms.md b/docs/reference/llms.md
new file mode 100644
index 00000000..7579eaa5
--- /dev/null
+++ b/docs/reference/llms.md
@@ -0,0 +1,277 @@
+# LLM Providers Module
+
+The `semantica.llms` module provides a unified interface for LLM providers, supporting Groq, OpenAI, HuggingFace, and LiteLLM (100+ LLMs) with clean imports and consistent API.
+
+## Overview
+
+The LLM Providers module abstracts away provider-specific details, providing a consistent interface for text generation across multiple LLM providers. This enables easy switching between providers and integration with GraphRAG reasoning features.
+
+## Quick Start
+
+```python
+from semantica.llms import Groq, OpenAI, HuggingFaceLLM, LiteLLM
+import os
+
+# Groq - Fast inference
+groq = Groq(model="llama-3.1-8b-instant", api_key=os.getenv("GROQ_API_KEY"))
+response = groq.generate("What is AI?")
+
+# OpenAI
+openai = OpenAI(model="gpt-4", api_key=os.getenv("OPENAI_API_KEY"))
+response = openai.generate("What is AI?")
+
+# HuggingFace - Local models
+hf = HuggingFaceLLM(model_name="gpt2") # or model="gpt2"
+response = hf.generate("What is AI?")
+
+# LiteLLM - Unified interface to 100+ LLMs
+litellm = LiteLLM(model="openai/gpt-4o", api_key=os.getenv("OPENAI_API_KEY"))
+response = litellm.generate("What is AI?")
+```
+
+## Providers
+
+### Groq
+
+Fast inference provider using Groq's API.
+
+```python
+from semantica.llms import Groq
+
+groq = Groq(
+ model="llama-3.1-8b-instant",
+ api_key="your-api-key" # or use GROQ_API_KEY env var
+)
+
+response = groq.generate("Hello, world!")
+structured = groq.generate_structured("Extract entities from: Apple Inc.")
+```
+
+**Parameters:**
+- `model` (str): Model name (default: "llama-3.1-8b-instant")
+- `api_key` (str, optional): Groq API key (default: from GROQ_API_KEY env var)
+- `**kwargs`: Additional provider options
+
+**Methods:**
+- `generate(prompt: str, **kwargs) -> str`: Generate text from prompt
+- `generate_structured(prompt: str, **kwargs) -> Dict[str, Any]`: Generate structured JSON output
+- `is_available() -> bool`: Check if provider is available
+
+### OpenAI
+
+OpenAI API provider for GPT models.
+
+```python
+from semantica.llms import OpenAI
+
+openai = OpenAI(
+ model="gpt-4",
+ api_key="your-api-key" # or use OPENAI_API_KEY env var
+)
+
+response = openai.generate("Hello, world!")
+```
+
+**Parameters:**
+- `model` (str): Model name (default: "gpt-3.5-turbo")
+- `api_key` (str, optional): OpenAI API key (default: from OPENAI_API_KEY env var)
+- `**kwargs`: Additional provider options
+
+**Methods:**
+- `generate(prompt: str, **kwargs) -> str`: Generate text from prompt
+- `generate_structured(prompt: str, **kwargs) -> Dict[str, Any]`: Generate structured JSON output
+- `is_available() -> bool`: Check if provider is available
+
+### HuggingFaceLLM
+
+Local LLM inference using HuggingFace Transformers.
+
+```python
+from semantica.llms import HuggingFaceLLM
+
+hf = HuggingFaceLLM(
+ model_name="gpt2",
+ device="cuda" # or "cpu", default: auto-detect
+)
+
+response = hf.generate("Hello, world!")
+```
+
+**Parameters:**
+- `model_name` (str, optional): HuggingFace model name (default: "gpt2")
+- `model` (str, optional): Alias for model_name (for consistency with other providers)
+- `device` (str, optional): Device to use ("cuda" or "cpu", default: auto-detect)
+- `**kwargs`: Additional provider options
+
+**Note:** Both `model` and `model_name` are supported for consistency with other providers.
+
+**Methods:**
+- `generate(prompt: str, **kwargs) -> str`: Generate text from prompt
+- `generate_structured(prompt: str, **kwargs) -> Dict[str, Any]`: Generate structured JSON output
+- `is_available() -> bool`: Check if provider is available
+
+### LiteLLM
+
+Unified interface to 100+ LLM providers via LiteLLM library.
+
+```python
+from semantica.llms import LiteLLM
+
+# Use any provider via LiteLLM
+litellm = LiteLLM(
+ model="openai/gpt-4o", # Provider/model format
+ api_key=os.getenv("OPENAI_API_KEY")
+)
+
+# Or use other providers
+litellm = LiteLLM(model="anthropic/claude-sonnet-4-20250514")
+litellm = LiteLLM(model="groq/llama-3.1-8b-instant")
+litellm = LiteLLM(model="azure/gpt-4")
+
+response = litellm.generate("Hello, world!")
+```
+
+**Parameters:**
+- `model` (str): Model identifier in format "provider/model-name"
+ - Examples: "openai/gpt-4o", "anthropic/claude-sonnet-4-20250514", "groq/llama-3.1-8b-instant", "azure/gpt-4"
+- `api_key` (str, optional): API key (can use environment variables)
+- `**kwargs`: Additional LiteLLM options (temperature, max_tokens, etc.)
+
+**Methods:**
+- `generate(prompt: str, **kwargs) -> str`: Generate text from prompt
+- `generate_structured(prompt: str, **kwargs) -> Dict[str, Any]`: Generate structured JSON output
+- `is_available() -> bool`: Check if provider is available
+
+**Supported Providers:**
+- OpenAI, Anthropic, Groq, Azure, Bedrock, Vertex AI, Cohere, Mistral, and 90+ more
+- See [LiteLLM Documentation](https://docs.litellm.ai/) for full list
+
+## Integration with GraphRAG
+
+The LLM providers integrate seamlessly with GraphRAG reasoning:
+
+```python
+from semantica.context import AgentContext
+from semantica.llms import Groq
+from semantica.vector_store import VectorStore
+import os
+
+context = AgentContext(
+ vector_store=VectorStore(backend="faiss"),
+ knowledge_graph=kg
+)
+
+llm_provider = Groq(
+ model="llama-3.1-8b-instant",
+ api_key=os.getenv("GROQ_API_KEY")
+)
+
+result = context.query_with_reasoning(
+ query="What IPs are associated with security alerts?",
+ llm_provider=llm_provider,
+ max_hops=2
+)
+
+print(f"Response: {result['response']}")
+print(f"Reasoning Path: {result['reasoning_path']}")
+```
+
+## Common Parameters
+
+All providers support common generation parameters:
+
+- `temperature` (float): Sampling temperature (0.0-2.0)
+- `max_tokens` (int): Maximum tokens to generate
+- `top_p` (float): Nucleus sampling parameter
+- `frequency_penalty` (float): Frequency penalty
+- `presence_penalty` (float): Presence penalty
+
+Example:
+
+```python
+response = groq.generate(
+ "What is AI?",
+ temperature=0.7,
+ max_tokens=500,
+ top_p=0.9
+)
+```
+
+## Error Handling
+
+All providers gracefully handle errors:
+
+```python
+try:
+ response = groq.generate("Hello")
+except ProcessingError as e:
+ print(f"Generation failed: {e}")
+```
+
+If a provider is not available (library not installed, API key missing), a `ProcessingError` is raised with a helpful message.
+
+## Examples
+
+### Basic Text Generation
+
+```python
+from semantica.llms import Groq
+
+groq = Groq(model="llama-3.1-8b-instant")
+response = groq.generate("Explain quantum computing in simple terms.")
+print(response)
+```
+
+### Structured Output
+
+```python
+from semantica.llms import OpenAI
+
+openai = OpenAI(model="gpt-4")
+result = openai.generate_structured(
+ "Extract entities from: Apple Inc. was founded by Steve Jobs in 1976."
+)
+# Returns: {"entities": [{"name": "Apple Inc.", "type": "Organization"}, ...]}
+```
+
+### Using LiteLLM for Multiple Providers
+
+```python
+from semantica.llms import LiteLLM
+
+# Switch between providers easily
+providers = [
+ LiteLLM(model="openai/gpt-4o"),
+ LiteLLM(model="anthropic/claude-sonnet-4-20250514"),
+ LiteLLM(model="groq/llama-3.1-8b-instant")
+]
+
+for provider in providers:
+ response = provider.generate("What is AI?")
+ print(f"{provider.model}: {response[:50]}...")
+```
+
+## Installation
+
+Most providers require additional dependencies:
+
+```bash
+# Groq
+pip install groq
+
+# OpenAI
+pip install openai
+
+# HuggingFace
+pip install transformers torch
+
+# LiteLLM (supports 100+ providers)
+pip install litellm
+```
+
+## See Also
+
+- [Context Module](context.md) - GraphRAG with multi-hop reasoning
+- [Semantic Extract Module](semantic_extract.md) - Entity and relationship extraction
+- [GraphRAG Cookbook](../../cookbook/use_cases/advanced_rag/01_GraphRAG_Complete.ipynb) - Complete GraphRAG example
+
diff --git a/semantica/context/agent_context.py b/semantica/context/agent_context.py
index cd6815a6..d04b43aa 100644
--- a/semantica/context/agent_context.py
+++ b/semantica/context/agent_context.py
@@ -426,6 +426,64 @@ class AgentContext:
# Convert to dicts
return [self._memory_to_dict(r) for r in results]
+ def query_with_reasoning(
+ self,
+ query: str,
+ llm_provider: Any,
+ max_results: int = 10,
+ max_hops: int = 2,
+ **kwargs
+ ) -> Dict[str, Any]:
+ """
+ Query with multi-hop reasoning and LLM-based response generation.
+
+ Retrieves context, builds reasoning paths through the graph, and generates
+ a natural language response grounded in the knowledge graph.
+
+ Args:
+ query: User query
+ llm_provider: LLM provider instance (from semantica.llms)
+ max_results: Maximum context results to retrieve (default: 10)
+ max_hops: Maximum graph traversal hops (default: 2)
+ **kwargs: Additional retrieval options
+
+ Returns:
+ Dictionary with:
+ - response: Generated natural language answer
+ - reasoning_path: Multi-hop reasoning trace
+ - sources: Retrieved context items
+ - confidence: Overall confidence score
+
+ Example:
+ >>> from semantica.llms import Groq
+ >>> llm = Groq(model="llama-3.1-8b-instant")
+ >>> result = context.query_with_reasoning(
+ ... "What IPs are associated with security alerts?",
+ ... llm_provider=llm,
+ ... max_hops=2
+ ... )
+ >>> print(result['response'])
+ """
+ if not self._retriever:
+ # Fallback if retriever not available
+ return {
+ "response": "GraphRAG retriever not available. Please configure knowledge_graph.",
+ "reasoning_path": "",
+ "sources": [],
+ "confidence": 0.0,
+ "num_sources": 0,
+ "num_reasoning_paths": 0
+ }
+
+ # Delegate to ContextRetriever
+ return self._retriever.query_with_reasoning(
+ query=query,
+ llm_provider=llm_provider,
+ max_results=max_results,
+ max_hops=max_hops,
+ **kwargs
+ )
+
def forget(
self,
memory_id: Optional[str] = None,
diff --git a/semantica/context/context_retriever.py b/semantica/context/context_retriever.py
index b3d05de4..65b61485 100644
--- a/semantica/context/context_retriever.py
+++ b/semantica/context/context_retriever.py
@@ -1226,6 +1226,354 @@ class ContextRetriever:
return []
+ # Reasoning Methods
+ def _build_reasoning_path(
+ self,
+ query_entities: List[Dict[str, Any]],
+ max_hops: int = 2
+ ) -> List[Dict[str, Any]]:
+ """
+ Build multi-hop reasoning path through knowledge graph.
+
+ Args:
+ query_entities: List of entities extracted from query
+ max_hops: Maximum number of hops to traverse (default: 2)
+
+ Returns:
+ List of reasoning path segments with entity relationships
+ """
+ if not self.knowledge_graph:
+ return []
+
+ reasoning_paths = []
+ visited_entities = set()
+
+ # Get entities and relationships from knowledge graph
+ # Handle both dict and GraphStore objects
+ if isinstance(self.knowledge_graph, dict):
+ entities = self.knowledge_graph.get("entities", [])
+ relationships = self.knowledge_graph.get("relationships", [])
+ elif hasattr(self.knowledge_graph, "get_entities") and hasattr(self.knowledge_graph, "get_relationships"):
+ # GraphStore-like object
+ entities = self.knowledge_graph.get_entities() or []
+ relationships = self.knowledge_graph.get_relationships() or []
+ else:
+ # Try to access as dict anyway
+ entities = getattr(self.knowledge_graph, "entities", [])
+ relationships = getattr(self.knowledge_graph, "relationships", [])
+
+ # Create entity lookup
+ entity_map = {}
+ for entity in entities:
+ entity_id = entity.get("id") or entity.get("text") or entity.get("name")
+ if entity_id:
+ entity_map[entity_id] = entity
+
+ # Create relationship lookup
+ rel_map = {}
+ for rel in relationships:
+ source = rel.get("source") or rel.get("source_id")
+ target = rel.get("target") or rel.get("target_id")
+ rel_type = rel.get("type") or rel.get("predicate")
+ if source and target:
+ if source not in rel_map:
+ rel_map[source] = []
+ rel_map[source].append({"target": target, "type": rel_type, "rel": rel})
+
+ # Start BFS from query entities
+ from collections import deque
+ queue = deque()
+
+ for query_entity in query_entities:
+ entity_id = query_entity.get("id") or query_entity.get("text") or query_entity.get("name")
+ if entity_id and entity_id in entity_map:
+ queue.append((entity_id, 0, [entity_id]))
+
+ while queue:
+ current_id, hop, path = queue.popleft()
+
+ if hop >= max_hops:
+ continue
+
+ if current_id in visited_entities:
+ continue
+ visited_entities.add(current_id)
+
+ # Get relationships from current entity
+ if current_id in rel_map:
+ for rel_info in rel_map[current_id]:
+ target_id = rel_info["target"]
+ rel_type = rel_info["type"]
+ rel = rel_info["rel"]
+
+ if target_id not in path: # Avoid cycles
+ new_path = path + [target_id]
+
+ # Build relationships list for this path
+ path_relationships = []
+ for i in range(len(new_path) - 1):
+ source_id = new_path[i]
+ target_id_rel = new_path[i + 1]
+ # Find the relationship type between these two entities
+ rel_type_found = None
+ if source_id in rel_map:
+ for r_info in rel_map[source_id]:
+ if r_info["target"] == target_id_rel:
+ rel_type_found = r_info["type"]
+ break
+ path_relationships.append({
+ "source": source_id,
+ "target": target_id_rel,
+ "type": rel_type_found or "related_to"
+ })
+
+ # Add to reasoning paths
+ reasoning_paths.append({
+ "path": new_path,
+ "hops": hop + 1,
+ "relationships": path_relationships,
+ "entities": [
+ entity_map.get(eid, {}) for eid in new_path
+ ]
+ })
+
+ # Continue traversal
+ if target_id in entity_map and hop + 1 < max_hops:
+ queue.append((target_id, hop + 1, new_path))
+
+ return reasoning_paths
+
+ def _generate_reasoned_response(
+ self,
+ query: str,
+ retrieved_context: List[RetrievedContext],
+ reasoning_paths: List[Dict[str, Any]],
+ llm_provider: Any
+ ) -> str:
+ """
+ Generate natural language response using LLM with retrieved context and reasoning paths.
+
+ Args:
+ query: User query
+ retrieved_context: Retrieved context items
+ reasoning_paths: Multi-hop reasoning paths
+ llm_provider: LLM provider instance (from semantica.llms)
+
+ Returns:
+ Generated natural language response
+ """
+ # Format retrieved context
+ context_text = "\n\n".join([
+ f"Context {i+1} (Score: {ctx.score:.2f}):\n{ctx.content}"
+ for i, ctx in enumerate(retrieved_context[:5])
+ ])
+
+ # Format reasoning paths
+ reasoning_text = ""
+ if reasoning_paths:
+ reasoning_text = "\n\nReasoning Paths (Multi-hop connections):\n"
+ for i, path_info in enumerate(reasoning_paths[:3], 1):
+ entities = path_info.get("entities", [])
+ relationships = path_info.get("relationships", [])
+
+ if entities:
+ path_parts = []
+ for j, entity in enumerate(entities):
+ entity_name = entity.get('text') or entity.get('name') or 'Unknown'
+ path_parts.append(entity_name)
+ # Add relationship after entity (except for last entity)
+ if j < len(relationships) and relationships[j].get('type'):
+ rel_type = relationships[j]['type']
+ path_parts.append(f"--[{rel_type}]-->")
+ path_str = " ".join(path_parts)
+ reasoning_text += f"Path {i}: {path_str}\n"
+
+ # Construct prompt
+ prompt = f"""You are a knowledge graph reasoning assistant. Answer the user's question based on the retrieved context and reasoning paths from the knowledge graph.
+
+User Question: {query}
+
+Retrieved Context:
+{context_text}
+
+{reasoning_text}
+
+Instructions:
+1. Answer the question using the retrieved context and reasoning paths
+2. Cite specific entities and relationships from the reasoning paths
+3. Explain the multi-hop connections when relevant
+4. Be concise but comprehensive
+5. If information is not available in the context, say so
+
+Answer:"""
+
+ try:
+ response = llm_provider.generate(prompt, temperature=0.3)
+ return response
+ except Exception as e:
+ self.logger.warning(f"LLM generation failed: {e}")
+ # Fallback: return summary of context
+ return f"Based on the retrieved context, here are the relevant findings:\n\n{context_text[:500]}..."
+
+ def query_with_reasoning(
+ self,
+ query: str,
+ llm_provider: Any,
+ max_results: int = 10,
+ max_hops: int = 2,
+ **kwargs
+ ) -> Dict[str, Any]:
+ """
+ Query with multi-hop reasoning and LLM-based response generation.
+
+ Retrieves context, builds reasoning paths through the graph, and generates
+ a natural language response grounded in the knowledge graph.
+
+ Args:
+ query: User query
+ llm_provider: LLM provider instance (from semantica.llms)
+ max_results: Maximum context results to retrieve (default: 10)
+ max_hops: Maximum graph traversal hops (default: 2)
+ **kwargs: Additional retrieval options
+
+ Returns:
+ Dictionary with:
+ - response: Generated natural language answer
+ - reasoning_path: Multi-hop reasoning trace
+ - sources: Retrieved context items
+ - confidence: Overall confidence score
+
+ Example:
+ >>> from semantica.llms import Groq
+ >>> llm = Groq(model="llama-3.1-8b-instant")
+ >>> result = retriever.query_with_reasoning(
+ ... "What IPs are associated with security alerts?",
+ ... llm_provider=llm,
+ ... max_hops=2
+ ... )
+ >>> print(result['response'])
+ """
+ tracking_id = self.progress_tracker.start_tracking(
+ file=None,
+ module="context",
+ submodule="ContextRetriever",
+ message=f"Querying with reasoning: {query[:50]}...",
+ )
+
+ try:
+ # Step 1: Retrieve initial context
+ self.progress_tracker.update_tracking(
+ tracking_id, message="Retrieving context..."
+ )
+ retrieved_context = self.retrieve(
+ query,
+ max_results=max_results,
+ use_graph_expansion=True,
+ **kwargs
+ )
+
+ # Step 2: Extract entities from query and retrieved context
+ self.progress_tracker.update_tracking(
+ tracking_id, message="Extracting entities..."
+ )
+ query_entities = []
+
+ # Extract entities from retrieved context
+ for ctx in retrieved_context:
+ query_entities.extend(ctx.related_entities)
+
+ # Deduplicate entities
+ seen_ids = set()
+ unique_entities = []
+ for entity in query_entities:
+ entity_id = entity.get("id") or entity.get("text") or entity.get("name")
+ if entity_id and entity_id not in seen_ids:
+ seen_ids.add(entity_id)
+ unique_entities.append(entity)
+
+ # Step 3: Build reasoning paths
+ self.progress_tracker.update_tracking(
+ tracking_id, message="Building reasoning paths..."
+ )
+ reasoning_paths = self._build_reasoning_path(
+ unique_entities,
+ max_hops=max_hops
+ )
+
+ # Step 4: Generate response using LLM
+ self.progress_tracker.update_tracking(
+ tracking_id, message="Generating response..."
+ )
+ response = self._generate_reasoned_response(
+ query,
+ retrieved_context,
+ reasoning_paths,
+ llm_provider
+ )
+
+ # Step 5: Format reasoning path as string
+ reasoning_path_str = ""
+ if reasoning_paths:
+ for path_info in reasoning_paths[:1]: # Show first path
+ entities = path_info.get("entities", [])
+ relationships = path_info.get("relationships", [])
+ if entities:
+ path_parts = []
+ for i, entity in enumerate(entities):
+ entity_name = entity.get("text") or entity.get("name") or "Unknown"
+ path_parts.append(entity_name)
+ if i < len(relationships) and relationships[i].get("type"):
+ path_parts.append(f"--[{relationships[i]['type']}]-->")
+ reasoning_path_str = " ".join(path_parts)
+
+ # Calculate overall confidence
+ confidence = 0.0
+ if retrieved_context:
+ avg_score = sum(ctx.score for ctx in retrieved_context) / len(retrieved_context)
+ confidence = min(1.0, avg_score * 0.8 + (0.2 if reasoning_paths else 0.0))
+
+ self.progress_tracker.stop_tracking(
+ tracking_id, status="completed", message="Query with reasoning completed"
+ )
+
+ return {
+ "response": response,
+ "reasoning_path": reasoning_path_str,
+ "sources": [
+ {
+ "content": ctx.content[:200],
+ "score": ctx.score,
+ "source": ctx.source
+ }
+ for ctx in retrieved_context[:5]
+ ],
+ "confidence": confidence,
+ "num_sources": len(retrieved_context),
+ "num_reasoning_paths": len(reasoning_paths)
+ }
+
+ except Exception as e:
+ self.progress_tracker.stop_tracking(
+ tracking_id, status="failed", message=str(e)
+ )
+ self.logger.error(f"Query with reasoning failed: {e}")
+ # Fallback: return retrieved context without LLM generation
+ return {
+ "response": f"Retrieved {len(retrieved_context)} relevant items. LLM generation unavailable.",
+ "reasoning_path": "",
+ "sources": [
+ {
+ "content": ctx.content[:200],
+ "score": ctx.score,
+ "source": ctx.source
+ }
+ for ctx in retrieved_context[:5]
+ ],
+ "confidence": 0.5,
+ "num_sources": len(retrieved_context),
+ "num_reasoning_paths": 0
+ }
+
# Filter Methods
def filter_by_entity(
self, entity_id: str, query: str, **options
diff --git a/semantica/llms/__init__.py b/semantica/llms/__init__.py
new file mode 100644
index 00000000..da00ff93
--- /dev/null
+++ b/semantica/llms/__init__.py
@@ -0,0 +1,46 @@
+"""
+LLM Providers Module
+
+This module provides clean, intuitive imports for LLM providers used in Semantica.
+It wraps the underlying provider functionality from semantica.semantic_extract.providers
+to provide a cleaner API.
+
+Supported Providers:
+ - Groq: Groq API for fast inference
+ - OpenAI: OpenAI API (GPT-3.5, GPT-4, etc.)
+ - HuggingFaceLLM: HuggingFace Transformers for local LLM inference
+ - LiteLLM: Unified interface to 100+ LLM providers (OpenAI, Anthropic, Groq, Azure, Bedrock, Vertex AI, etc.)
+
+Example Usage:
+ >>> from semantica.llms import Groq, OpenAI, HuggingFaceLLM, LiteLLM
+ >>>
+ >>> # Groq provider
+ >>> groq = Groq(model="llama-3.1-8b-instant", api_key="your-key")
+ >>> response = groq.generate("Hello, world!")
+ >>>
+ >>> # OpenAI provider
+ >>> openai = OpenAI(model="gpt-4", api_key="your-key")
+ >>> response = openai.generate("Hello, world!")
+ >>>
+ >>> # HuggingFace LLM provider
+ >>> hf = HuggingFaceLLM(model_name="gpt2")
+ >>> response = hf.generate("Hello, world!")
+ >>>
+ >>> # LiteLLM provider (supports 100+ LLMs)
+ >>> llm = LiteLLM(model="openai/gpt-4o", api_key="your-key")
+ >>> response = llm.generate("Hello, world!")
+ >>> # Or use other providers via LiteLLM
+ >>> llm = LiteLLM(model="anthropic/claude-sonnet-4-20250514")
+ >>> response = llm.generate("Hello, world!")
+
+Author: Semantica Contributors
+License: MIT
+"""
+
+from .groq import Groq
+from .openai import OpenAI
+from .huggingface import HuggingFaceLLM
+from .litellm import LiteLLM
+
+__all__ = ["Groq", "OpenAI", "HuggingFaceLLM", "LiteLLM"]
+
diff --git a/semantica/llms/groq.py b/semantica/llms/groq.py
new file mode 100644
index 00000000..e3cbf43e
--- /dev/null
+++ b/semantica/llms/groq.py
@@ -0,0 +1,89 @@
+"""
+Groq LLM Provider
+
+Wrapper for Groq API provider with clean interface.
+"""
+
+from typing import Any, Dict, Optional
+
+from ..semantic_extract.providers import GroqProvider
+from ..utils.exceptions import ProcessingError
+from ..utils.logging import get_logger
+
+logger = get_logger("llms.groq")
+
+
+class Groq:
+ """
+ Groq LLM provider wrapper.
+
+ Provides clean interface to Groq API for text generation.
+
+ Example:
+ >>> from semantica.llms import Groq
+ >>> groq = Groq(model="llama-3.1-8b-instant", api_key="your-key")
+ >>> response = groq.generate("What is AI?")
+ """
+
+ def __init__(
+ self,
+ model: str = "llama-3.1-8b-instant",
+ api_key: Optional[str] = None,
+ **kwargs
+ ):
+ """
+ Initialize Groq provider.
+
+ Args:
+ model: Model name (default: "llama-3.1-8b-instant")
+ api_key: Groq API key (default: from GROQ_API_KEY env var)
+ **kwargs: Additional provider options
+ """
+ self.provider = GroqProvider(api_key=api_key, model=model, **kwargs)
+ self.model = model
+ self.api_key = api_key
+
+ def is_available(self) -> bool:
+ """Check if Groq provider is available."""
+ return self.provider.is_available()
+
+ def generate(self, prompt: str, **kwargs) -> str:
+ """
+ Generate text from prompt.
+
+ Args:
+ prompt: Input prompt text
+ **kwargs: Generation options (temperature, max_tokens, etc.)
+
+ Returns:
+ Generated text response
+
+ Raises:
+ ProcessingError: If provider is not available or generation fails
+ """
+ if not self.is_available():
+ raise ProcessingError(
+ "Groq provider not available. Set GROQ_API_KEY or pass api_key."
+ )
+ return self.provider.generate(prompt, **kwargs)
+
+ def generate_structured(self, prompt: str, **kwargs) -> Dict[str, Any]:
+ """
+ Generate structured JSON output.
+
+ Args:
+ prompt: Input prompt text
+ **kwargs: Generation options
+
+ Returns:
+ Parsed JSON response as dictionary
+
+ Raises:
+ ProcessingError: If provider is not available or parsing fails
+ """
+ if not self.is_available():
+ raise ProcessingError(
+ "Groq provider not available. Set GROQ_API_KEY or pass api_key."
+ )
+ return self.provider.generate_structured(prompt, **kwargs)
+
diff --git a/semantica/llms/huggingface.py b/semantica/llms/huggingface.py
new file mode 100644
index 00000000..bd5326ce
--- /dev/null
+++ b/semantica/llms/huggingface.py
@@ -0,0 +1,100 @@
+"""
+HuggingFace LLM Provider
+
+Wrapper for HuggingFace Transformers LLM provider with clean interface.
+"""
+
+from typing import Any, Dict, Optional
+
+from ..semantic_extract.providers import HuggingFaceLLMProvider
+from ..utils.exceptions import ProcessingError
+from ..utils.logging import get_logger
+
+logger = get_logger("llms.huggingface")
+
+
+class HuggingFaceLLM:
+ """
+ HuggingFace LLM provider wrapper.
+
+ Provides clean interface to HuggingFace Transformers for local LLM inference.
+
+ Example:
+ >>> from semantica.llms import HuggingFaceLLM
+ >>> hf = HuggingFaceLLM(model_name="gpt2")
+ >>> response = hf.generate("What is AI?")
+ """
+
+ def __init__(
+ self,
+ model_name: Optional[str] = None,
+ model: Optional[str] = None,
+ device: Optional[str] = None,
+ **kwargs
+ ):
+ """
+ Initialize HuggingFace LLM provider.
+
+ Args:
+ model_name: HuggingFace model name (default: "gpt2")
+ model: Alias for model_name (for consistency with other providers)
+ device: Device to use ("cuda" or "cpu", default: auto-detect)
+ **kwargs: Additional provider options
+ """
+ # Support both model_name and model for consistency
+ if model is not None:
+ model_name = model
+ elif model_name is None:
+ model_name = "gpt2"
+
+ self.provider = HuggingFaceLLMProvider(
+ model_name=model_name, device=device, **kwargs
+ )
+ self.model_name = model_name
+ self.model = model_name # Alias for consistency
+ self.device = device
+
+ def is_available(self) -> bool:
+ """Check if HuggingFace LLM provider is available."""
+ return self.provider.is_available()
+
+ def generate(self, prompt: str, **kwargs) -> str:
+ """
+ Generate text from prompt.
+
+ Args:
+ prompt: Input prompt text
+ **kwargs: Generation options (max_length, temperature, etc.)
+
+ Returns:
+ Generated text response
+
+ Raises:
+ ProcessingError: If provider is not available or generation fails
+ """
+ if not self.is_available():
+ raise ProcessingError(
+ "HuggingFace LLM provider not available. Install transformers library."
+ )
+ return self.provider.generate(prompt, **kwargs)
+
+ def generate_structured(self, prompt: str, **kwargs) -> Dict[str, Any]:
+ """
+ Generate structured JSON output.
+
+ Args:
+ prompt: Input prompt text
+ **kwargs: Generation options
+
+ Returns:
+ Parsed JSON response as dictionary
+
+ Raises:
+ ProcessingError: If provider is not available or parsing fails
+ """
+ if not self.is_available():
+ raise ProcessingError(
+ "HuggingFace LLM provider not available. Install transformers library."
+ )
+ return self.provider.generate_structured(prompt, **kwargs)
+
diff --git a/semantica/llms/litellm.py b/semantica/llms/litellm.py
new file mode 100644
index 00000000..50b4165f
--- /dev/null
+++ b/semantica/llms/litellm.py
@@ -0,0 +1,189 @@
+"""
+LiteLLM Provider
+
+Wrapper for LiteLLM library that provides unified access to 100+ LLM providers.
+Supports OpenAI, Anthropic, Groq, Azure, Bedrock, Vertex AI, and many more.
+"""
+
+from typing import Any, Dict, List, Optional
+
+from ..utils.exceptions import ProcessingError
+from ..utils.logging import get_logger
+
+logger = get_logger("llms.litellm")
+
+try:
+ from litellm import completion
+ LITELLM_AVAILABLE = True
+except ImportError:
+ LITELLM_AVAILABLE = False
+ logger.warning(
+ "litellm library not installed. Install with: pip install litellm"
+ )
+
+
+class LiteLLM:
+ """
+ LiteLLM provider wrapper.
+
+ Provides unified interface to 100+ LLM providers through LiteLLM library.
+ Supports providers like OpenAI, Anthropic, Groq, Azure, Bedrock, Vertex AI, etc.
+
+ Model format: "provider/model-name" (e.g., "openai/gpt-4o", "anthropic/claude-sonnet-4-20250514", "groq/llama-3.1-8b-instant")
+
+ Example:
+ >>> from semantica.llms import LiteLLM
+ >>> llm = LiteLLM(model="openai/gpt-4o", api_key="your-key")
+ >>> response = llm.generate("What is AI?")
+ >>>
+ >>> # Use with different providers
+ >>> llm = LiteLLM(model="anthropic/claude-sonnet-4-20250514")
+ >>> response = llm.generate("Hello!")
+ """
+
+ def __init__(
+ self,
+ model: str,
+ api_key: Optional[str] = None,
+ **kwargs
+ ):
+ """
+ Initialize LiteLLM provider.
+
+ Args:
+ model: Model identifier in format "provider/model-name"
+ Examples: "openai/gpt-4o", "anthropic/claude-sonnet-4-20250514",
+ "groq/llama-3.1-8b-instant", "azure/gpt-4", etc.
+ api_key: API key (optional, can use environment variables)
+ **kwargs: Additional LiteLLM options (temperature, max_tokens, etc.)
+ """
+ if not LITELLM_AVAILABLE:
+ raise ProcessingError(
+ "LiteLLM library not installed. Install with: pip install litellm"
+ )
+
+ self.model = model
+ self.api_key = api_key
+ self.config = kwargs
+
+ def is_available(self) -> bool:
+ """Check if LiteLLM provider is available."""
+ return LITELLM_AVAILABLE
+
+ def generate(self, prompt: str, **kwargs) -> str:
+ """
+ Generate text from prompt.
+
+ Args:
+ prompt: Input prompt text
+ **kwargs: Generation options (temperature, max_tokens, etc.)
+
+ Returns:
+ Generated text response
+
+ Raises:
+ ProcessingError: If provider is not available or generation fails
+ """
+ if not self.is_available():
+ raise ProcessingError(
+ "LiteLLM library not installed. Install with: pip install litellm"
+ )
+
+ try:
+ # Merge config with kwargs
+ options = {**self.config, **kwargs}
+
+ # Prepare messages
+ messages = [{"role": "user", "content": prompt}]
+
+ # Call LiteLLM completion
+ response = completion(
+ model=self.model,
+ messages=messages,
+ api_key=self.api_key,
+ **options
+ )
+
+ # Extract text from response
+ if hasattr(response, 'choices') and len(response.choices) > 0:
+ return response.choices[0].message.content
+ elif isinstance(response, dict):
+ if 'choices' in response and len(response['choices']) > 0:
+ return response['choices'][0]['message']['content']
+ elif 'content' in response:
+ return response['content']
+ elif isinstance(response, str):
+ return response
+
+ raise ProcessingError(f"Unexpected response format from LiteLLM: {type(response)}")
+
+ except Exception as e:
+ logger.error(f"LiteLLM generation failed: {e}")
+ raise ProcessingError(f"LiteLLM generation failed: {e}")
+
+ def generate_structured(self, prompt: str, **kwargs) -> Dict[str, Any]:
+ """
+ Generate structured JSON output.
+
+ Args:
+ prompt: Input prompt text
+ **kwargs: Generation options
+
+ Returns:
+ Parsed JSON response as dictionary
+
+ Raises:
+ ProcessingError: If provider is not available or parsing fails
+ """
+ if not self.is_available():
+ raise ProcessingError(
+ "LiteLLM library not installed. Install with: pip install litellm"
+ )
+
+ try:
+ import json
+
+ # Add JSON format instruction to prompt
+ json_prompt = f"{prompt}\n\nReturn the response as valid JSON only."
+
+ # Merge config with kwargs
+ options = {**self.config, **kwargs}
+
+ # Prepare messages
+ messages = [{"role": "user", "content": json_prompt}]
+
+ # Call LiteLLM completion
+ response = completion(
+ model=self.model,
+ messages=messages,
+ api_key=self.api_key,
+ **options
+ )
+
+ # Extract text from response
+ text_response = ""
+ if hasattr(response, 'choices') and len(response.choices) > 0:
+ text_response = response.choices[0].message.content
+ elif isinstance(response, dict):
+ if 'choices' in response and len(response['choices']) > 0:
+ text_response = response['choices'][0]['message']['content']
+ elif 'content' in response:
+ text_response = response['content']
+ elif isinstance(response, str):
+ text_response = response
+
+ # Parse JSON
+ try:
+ return json.loads(text_response)
+ except json.JSONDecodeError:
+ # Try to extract JSON from text
+ import re
+ json_match = re.search(r'\{.*\}', text_response, re.DOTALL)
+ if json_match:
+ return json.loads(json_match.group())
+ raise ProcessingError(f"Failed to parse JSON from LiteLLM response: {text_response[:200]}")
+
+ except Exception as e:
+ logger.error(f"LiteLLM structured generation failed: {e}")
+ raise ProcessingError(f"LiteLLM structured generation failed: {e}")
+
diff --git a/semantica/llms/openai.py b/semantica/llms/openai.py
new file mode 100644
index 00000000..15a3a8df
--- /dev/null
+++ b/semantica/llms/openai.py
@@ -0,0 +1,89 @@
+"""
+OpenAI LLM Provider
+
+Wrapper for OpenAI API provider with clean interface.
+"""
+
+from typing import Any, Dict, Optional
+
+from ..semantic_extract.providers import OpenAIProvider
+from ..utils.exceptions import ProcessingError
+from ..utils.logging import get_logger
+
+logger = get_logger("llms.openai")
+
+
+class OpenAI:
+ """
+ OpenAI LLM provider wrapper.
+
+ Provides clean interface to OpenAI API for text generation.
+
+ Example:
+ >>> from semantica.llms import OpenAI
+ >>> openai = OpenAI(model="gpt-4", api_key="your-key")
+ >>> response = openai.generate("What is AI?")
+ """
+
+ def __init__(
+ self,
+ model: str = "gpt-3.5-turbo",
+ api_key: Optional[str] = None,
+ **kwargs
+ ):
+ """
+ Initialize OpenAI provider.
+
+ Args:
+ model: Model name (default: "gpt-3.5-turbo")
+ api_key: OpenAI API key (default: from OPENAI_API_KEY env var)
+ **kwargs: Additional provider options
+ """
+ self.provider = OpenAIProvider(api_key=api_key, model=model, **kwargs)
+ self.model = model
+ self.api_key = api_key
+
+ def is_available(self) -> bool:
+ """Check if OpenAI provider is available."""
+ return self.provider.is_available()
+
+ def generate(self, prompt: str, **kwargs) -> str:
+ """
+ Generate text from prompt.
+
+ Args:
+ prompt: Input prompt text
+ **kwargs: Generation options (temperature, max_tokens, etc.)
+
+ Returns:
+ Generated text response
+
+ Raises:
+ ProcessingError: If provider is not available or generation fails
+ """
+ if not self.is_available():
+ raise ProcessingError(
+ "OpenAI provider not available. Set OPENAI_API_KEY or pass api_key."
+ )
+ return self.provider.generate(prompt, **kwargs)
+
+ def generate_structured(self, prompt: str, **kwargs) -> Dict[str, Any]:
+ """
+ Generate structured JSON output.
+
+ Args:
+ prompt: Input prompt text
+ **kwargs: Generation options
+
+ Returns:
+ Parsed JSON response as dictionary
+
+ Raises:
+ ProcessingError: If provider is not available or parsing fails
+ """
+ if not self.is_available():
+ raise ProcessingError(
+ "OpenAI provider not available. Set OPENAI_API_KEY or pass api_key."
+ )
+ return self.provider.generate_structured(prompt, **kwargs)
+