Merge pull request #117 from Hawksight-AI/llms

Add LLM Providers Module and GraphRAG Reasoning Features
This commit is contained in:
Mohd Kaif
2025-12-27 23:27:49 +05:30
committed by GitHub
12 changed files with 1885 additions and 129 deletions
+91 -11
View File
@@ -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
@@ -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": [
"<div style='font-family: monospace;'><h4>🧠 Semantica - 📊 Current Progress</h4><table style='width: 100%; border-collapse: collapse;'><tr><th>Status</th><th>Action</th><th>Module</th><th>Submodule</th><th>File</th><th>Time</th></tr><tr><td>✅</td><td>Semantica is extracting</td><td>🎯 semantic_extract</td><td>NERExtractor</td><td>-</td><td>0.55s</td></tr><tr><td>✅</td><td>Semantica is extracting</td><td>🎯 semantic_extract</td><td>RelationExtractor</td><td>-</td><td>0.05s</td></tr><tr><td>✅</td><td>Semantica is resolving</td><td>⚠️ conflicts</td><td>ConflictDetector</td><td>-</td><td>0.00s</td></tr><tr><td>✅</td><td>Semantica is building</td><td>🧠 kg</td><td>GraphBuilder</td><td>-</td><td>350.71s</td></tr><tr><td>🔄</td><td>Semantica is building</td><td>🧠 kg</td><td>EntityResolver</td><td>-</td><td>84.09s</td></tr><tr><td>✅</td><td>Semantica is deduplicating</td><td>🔄 deduplication</td><td>DuplicateDetector</td><td>-</td><td>0.08s</td></tr><tr><td>✅</td><td>Semantica is deduplicating</td><td>🔄 deduplication</td><td>SimilarityCalculator</td><td>-</td><td>0.02s</td></tr><tr><td>✅</td><td>Semantica is deduplicating</td><td>🔄 deduplication</td><td>EntityMerger</td><td>-</td><td>0.13s</td></tr><tr><td>✅</td><td>Semantica is deduplicating</td><td>🔄 deduplication</td><td>MergeStrategyManager</td><td>-</td><td>0.03s</td></tr><tr><td>✅</td><td>Semantica is indexing</td><td>📊 vector_store</td><td>VectorStore</td><td>-</td><td>0.01s</td></tr></table></div>"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"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,
+104 -12
View File
@@ -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 |
+134
View File
@@ -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.
+277
View File
@@ -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
+58
View File
@@ -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,
+348
View File
@@ -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
+46
View File
@@ -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"]
+89
View File
@@ -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)
+100
View File
@@ -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)
+189
View File
@@ -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}")
+89
View File
@@ -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)