mirror of
https://github.com/semantica-agi/semantica.git
synced 2026-08-29 04:26:20 +00:00
- Update notebook to use corrected RelationExtractor API - Move provider/model parameters to initialization - Add verbose logging for debugging - Include working relation extraction examples
44 KiB
44 KiB
In [1]:
!pip install -qU semantica docling pdfplumber groq
WARNING: Ignoring invalid distribution ~gno (C:\Users\Mohd Kaif\AppData\Local\Programs\Python\Python311\Lib\site-packages) WARNING: Ignoring invalid distribution ~lotly (C:\Users\Mohd Kaif\AppData\Local\Programs\Python\Python311\Lib\site-packages) WARNING: Ignoring invalid distribution ~ython-socketio (C:\Users\Mohd Kaif\AppData\Local\Programs\Python\Python311\Lib\site-packages) WARNING: Ignoring invalid distribution ~gno (C:\Users\Mohd Kaif\AppData\Local\Programs\Python\Python311\Lib\site-packages) WARNING: Ignoring invalid distribution ~lotly (C:\Users\Mohd Kaif\AppData\Local\Programs\Python\Python311\Lib\site-packages) WARNING: Ignoring invalid distribution ~ython-socketio (C:\Users\Mohd Kaif\AppData\Local\Programs\Python\Python311\Lib\site-packages) WARNING: Ignoring invalid distribution ~gno (C:\Users\Mohd Kaif\AppData\Local\Programs\Python\Python311\Lib\site-packages) WARNING: Ignoring invalid distribution ~lotly (C:\Users\Mohd Kaif\AppData\Local\Programs\Python\Python311\Lib\site-packages) WARNING: Ignoring invalid distribution ~ython-socketio (C:\Users\Mohd Kaif\AppData\Local\Programs\Python\Python311\Lib\site-packages)
In [2]:
from getpass import getpass
from semantica.llms import Groq
GROQ_API_KEY = getpass("Enter your GROQ API key: ")
if not GROQ_API_KEY:
raise ValueError("GROQ API key is required")
groq_llm = Groq(
model="llama-3.1-8b-instant",
api_key=GROQ_API_KEY,
)
print(f"✓ Groq LLM initialized: {groq_llm.model}")✓ Groq LLM initialized: llama-3.1-8b-instant
In [3]:
import requests
from pathlib import Path
from semantica.parse import DoclingParser
parser = DoclingParser()
PRESS_RELEASE_URL = "https://filecache.investorroom.com/mr5ircnw_mda/677/MDA_Space_Ltd_Q3_2025_Press_Release_Nov_14_2025_FINAL.pdf"
TRANSCRIPT_URL = "https://filecache.investorroom.com/mr5ircnw_mda/681/MDA%20Space%20Ltd.%20Q3%202025%20Earnings%20Conference%20Call%20Transcript%20%28November%2014%202025%29.pdf"
download_dir = Path("downloads")
download_dir.mkdir(exist_ok=True)
press_release_pdf = download_dir / "mda_space_q3_2025_press_release.pdf"
transcript_pdf = download_dir / "mda_space_q3_2025_transcript.pdf"
if not press_release_pdf.exists():
press_release_pdf.write_bytes(requests.get(PRESS_RELEASE_URL).content)
if not transcript_pdf.exists():
transcript_pdf.write_bytes(requests.get(TRANSCRIPT_URL).content)
try:
press_release = parser.parse(press_release_pdf)
transcript = parser.parse(transcript_pdf)
except Exception as e:
print("Parsing failed")
print(e)
print("Using fallback empty documents.")
press_release = {"full_text": "", "tables": []}
transcript = {"full_text": "", "tables": []}
parsed_doc = {
"full_text": (
"# Press Release\n\n"
f"{press_release['full_text']}\n\n"
"# Transcript\n\n"
f"{transcript['full_text']}"
),
"tables": press_release["tables"] + transcript["tables"],
"metadata": {
"title": "MDA Space Ltd. Q3 2025 Earnings Analysis",
"company": "MDA Space Ltd.",
"quarter": "Q3 2025",
"date": "November 14, 2025",
},
}
print("Parsing completed")
print("Documents processed: 2")
print("Tables extracted:", len(parsed_doc["tables"]))[1;31m---------------------------------------------------------------------------[0m
[1;31mImportError[0m Traceback (most recent call last)
Cell [1;32mIn[3], line 5[0m
[0;32m 2[0m [38;5;28;01mfrom[39;00m[38;5;250m [39m[38;5;21;01mpathlib[39;00m[38;5;250m [39m[38;5;28;01mimport[39;00m Path
[0;32m 3[0m [38;5;28;01mfrom[39;00m[38;5;250m [39m[38;5;21;01msemantica[39;00m[38;5;21;01m.[39;00m[38;5;21;01mparse[39;00m[38;5;250m [39m[38;5;28;01mimport[39;00m DoclingParser
[1;32m----> 5[0m parser [38;5;241m=[39m [43mDoclingParser[49m[43m([49m[43m)[49m
[0;32m 7[0m PRESS_RELEASE_URL [38;5;241m=[39m [38;5;124m"[39m[38;5;124mhttps://filecache.investorroom.com/mr5ircnw_mda/677/MDA_Space_Ltd_Q3_2025_Press_Release_Nov_14_2025_FINAL.pdf[39m[38;5;124m"[39m
[0;32m 8[0m TRANSCRIPT_URL [38;5;241m=[39m [38;5;124m"[39m[38;5;124mhttps://filecache.investorroom.com/mr5ircnw_mda/681/MDA[39m[38;5;124m%[39m[38;5;124m20Space[39m[38;5;124m%[39m[38;5;124m20Ltd.[39m[38;5;124m%[39m[38;5;124m20Q3[39m[38;5;132;01m%202025%[39;00m[38;5;124m20Earnings[39m[38;5;124m%[39m[38;5;124m20Conference[39m[38;5;124m%[39m[38;5;124m20Call[39m[38;5;124m%[39m[38;5;124m20Transcript[39m[38;5;132;01m%20%[39;00m[38;5;124m28November[39m[38;5;132;01m%2014%[39;00m[38;5;124m202025[39m[38;5;124m%[39m[38;5;124m29.pdf[39m[38;5;124m"[39m
File [1;32mc:\Users\Mohd Kaif\AppData\Local\Programs\Python\Python311\Lib\site-packages\semantica\parse\__init__.py:198[0m, in [0;36mDoclingParser.__init__[1;34m(self, **config)[0m
[0;32m 196[0m error_msg [38;5;241m+[39m[38;5;241m=[39m [38;5;124mf[39m[38;5;124m"[39m[38;5;130;01m\n[39;00m[38;5;130;01m\n[39;00m[38;5;124mError: [39m[38;5;132;01m{[39;00mimport_error_msg[38;5;132;01m}[39;00m[38;5;124m"[39m
[0;32m 197[0m error_msg [38;5;241m+[39m[38;5;241m=[39m [38;5;124m"[39m[38;5;130;01m\n[39;00m[38;5;130;01m\n[39;00m[38;5;124mInstall it with: pip install docling[39m[38;5;124m"[39m
[1;32m--> 198[0m [38;5;28;01mraise[39;00m [38;5;167;01mImportError[39;00m(error_msg)
[1;31mImportError[0m: DoclingParser requires the 'docling' package to be installed and working.
Error: [WinError 1114] A dynamic link library (DLL) initialization routine failed. Error loading "c:\Users\Mohd Kaif\AppData\Local\Programs\Python\Python311\Lib\site-packages\torch\lib\c10.dll" or one of its dependencies.
Install it with: pip install doclingIn [ ]:
from semantica.normalize import TextNormalizer
normalizer = TextNormalizer()
normalized_text = normalizer.normalize(
parsed_doc["full_text"],
clean_html=False,
remove_extra_whitespace=False,
lowercase=False,
)
print("Normalization completed")
print("Normalized text length:", len(normalized_text))In [ ]:
from semantica.split import TextSplitter
CHUNK_SIZE = 1000
CHUNK_OVERLAP = 250
splitter = TextSplitter(
method="recursive",
chunk_size=CHUNK_SIZE,
chunk_overlap=CHUNK_OVERLAP,
)
chunks = splitter.split(normalized_text)
def get_chunk_text(chunk):
return getattr(chunk, "content", getattr(chunk, "text", ""))
print("Chunking completed")
print("Total chunks:", len(chunks))
print("Sample chunk:")
print(get_chunk_text(chunks[0]))In [ ]:
from semantica.semantic_extract import NERExtractor
ner = NERExtractor(
method="llm",
provider="groq",
llm_model="llama-3.1-8b-instant",
temperature=0.0,
api_key=GROQ_API_KEY,
)
ENTITY_TYPES = ["ORGANIZATION", "PERSON", "MONEY", "PERCENT", "DATE", "EVENT"]
all_entities = [
e
for c in chunks
for e in ner.extract_entities(
get_chunk_text(c),
entity_types=ENTITY_TYPES,
)
if get_chunk_text(c).strip()
]
print("Entities:", len(all_entities))In [ ]:
FINANCIAL_ENTITY_TYPES = [
"MONEY", "CURRENCY", "PERCENT", "PERCENTAGE",
"QUANTITY", "CARDINAL",
]
financial_entities = []
for chunk in chunks:
text = get_chunk_text(chunk)
if text.strip():
financial_entities += ner.extract_entities(
text,
entity_types=FINANCIAL_ENTITY_TYPES,
)
money, percentages, quantities = [], [], []
for e in financial_entities:
label = e.label.upper()
if label in ("MONEY", "CURRENCY"):
money.append(e.text)
elif label in ("PERCENT", "PERCENTAGE"):
percentages.append(e.text)
elif label in ("CARDINAL", "QUANTITY"):
quantities.append(e.text)
print("\nFinancial entity extraction completed")
print("Total financial entities:", len(financial_entities))
print("Money:", len(money))
print("Percentages:", len(percentages))
print("Quantities:", len(quantities))
if financial_entities:
print("Sample:", f"{financial_entities[0].text} ({financial_entities[0].label})")In [ ]:
from semantica.semantic_extract import RelationExtractor
relation_extractor = RelationExtractor(
method="llm",
confidence_threshold=0.6,
relation_types=[
"HAS_REVENUE",
"HAS_GROWTH",
"REPORTS",
"PROVIDES_GUIDANCE",
"IN_QUARTER",
"FOR_PERIOD",
"RELATED_TO",
],
provider="groq",
llm_model="llama-3.1-8b-instant",
api_key=GROQ_API_KEY,
temperature=0.0,
verbose=False,
)
# Process ALL chunks with ALL entities
relationships = []
total_chunks = len(chunks)
print(f"Processing {total_chunks} chunks with {len(all_entities)} entities each...")
print("This may take a while. Starting now...\n")
for i, c in enumerate(chunks):
text = get_chunk_text(c).strip()
if not text:
print(f"Chunk {i+1}/{total_chunks}: Skipped (empty)")
continue
# Use ALL entities for each chunk
chunk_entities = all_entities
# Show progress with remaining count
remaining = total_chunks - (i + 1)
print(f"Chunk {i+1}/{total_chunks} ({remaining} remaining) - Processing {len(chunk_entities)} entities...")
rels = relation_extractor.extract_relations(
text=text,
entities=chunk_entities,
verbose=True, # Enable verbose to see logs
)
relationships.extend(rels)
print(f"Chunk {i+1}: Found {len(rels)} relations")
print(f"\nComplete! Total relationships from all {total_chunks} chunks: {len(relationships)}")
# Show sample relations
if relationships:
print("\nSample relationships:")
for r in relationships[:15]:
print(f"- {r.subject.text} → {r.predicate} → {r.object.text}")In [ ]:
from semantica.conflicts import SourceTracker, SourceReference, ConflictDetector
source_tracker = SourceTracker()
conflict_detector = ConflictDetector(
source_tracker=source_tracker,
similarity_threshold=0.8,
confidence_threshold=0.7,
)
for entity in all_entities:
entity_id = getattr(entity, "id", None) or getattr(entity, "text", "")
entity_text = getattr(entity, "text", "")
entity_label = getattr(entity, "label", "UNKNOWN")
source_tracker.track_property_source(
entity_id,
"name",
entity_text,
# FIXED: Changed 'source' to 'document' to match SourceReference signature
source=SourceReference(
document="earnings_call", # Was incorrect: source="earnings_call"
timestamp="2024-Q1",
metadata={"entity_type": entity_label},
),
)
value_conflicts = conflict_detector.detect_value_conflicts(
[{"id": getattr(e, "id", ""), "name": getattr(e, "text", "")} for e in all_entities],
property_name="name",
)
relationship_conflicts = conflict_detector.detect_relationship_conflicts(relationships)
print("Conflict detection completed")
print("Value conflicts:", len(value_conflicts))
print("Relationship conflicts:", len(relationship_conflicts))In [ ]:
from semantica.conflicts import ConflictResolver
conflict_resolver = ConflictResolver(
default_strategy="voting",
source_tracker=source_tracker,
)
resolved_conflicts = []
for conflict in value_conflicts:
resolved_conflicts.append(
conflict_resolver.resolve_conflict(conflict, strategy="voting")
)
for conflict in relationship_conflicts:
resolved_conflicts.append(
conflict_resolver.resolve_conflict(conflict, strategy="voting")
)
print("Conflict resolution completed")
print("Total conflicts resolved:", len(resolved_conflicts))
In [ ]:
from semantica.deduplication import DuplicateDetector, EntityMerger
duplicate_detector = DuplicateDetector(
similarity_threshold=0.8,
confidence_threshold=0.7,
)
entity_dicts = [
{
"id": getattr(e, "id", ""),
"name": getattr(e, "text", ""),
"type": getattr(e, "label", "UNKNOWN"),
"confidence": getattr(e, "confidence", 1.0),
"metadata": getattr(e, "metadata", {}),
}
for e in resolved_entities
]
duplicates = duplicate_detector.detect_duplicates(entity_dicts)
entity_merger = EntityMerger(preserve_provenance=True)
merge_operations = entity_merger.merge_duplicates(
entity_dicts,
strategy="keep_most_complete",
)
merged_entities = [op.merged_entity for op in merge_operations]
print("Entity deduplication completed")
print("Original entities:", len(entity_dicts))
print("Merged entities:", len(merged_entities))
print("Duplicates removed:", len(entity_dicts) - len(merged_entities))
In [ ]:
from semantica.kg import GraphBuilder
graph_builder = GraphBuilder(
merge_entities=True,
entity_resolution_strategy="fuzzy",
)
triplet_relationships = [
{
"source": t.subject,
"predicate": t.predicate,
"target": t.object,
"confidence": t.confidence,
"metadata": t.metadata,
}
for t in validated_triplets
]
final_relationships = resolved_relationships + triplet_relationships
kg_data = {
"entities": merged_entities,
"relationships": final_relationships,
"triplets": validated_triplets,
"metadata": {
"source": "earnings_call_transcript",
"extraction_method": "Groq LLM",
},
}
knowledge_graph = graph_builder.build(
sources=[kg_data],
merge_entities=True,
)
print("Knowledge graph build completed")
print("Final entities:", len(knowledge_graph.get("entities", [])))
print("Final relationships:", len(knowledge_graph.get("relationships", [])))
In [ ]:
from semantica.kg import GraphAnalyzer
graph_analyzer = GraphAnalyzer()
analysis = graph_analyzer.analyze_graph(knowledge_graph)
centrality = graph_analyzer.calculate_centrality(
knowledge_graph,
method="degree",
)
communities = graph_analyzer.detect_communities(
knowledge_graph,
algorithm="louvain",
)
connectivity = graph_analyzer.analyze_connectivity(knowledge_graph)
metrics = graph_analyzer.compute_metrics(knowledge_graph)
top_entities = centrality.get("rankings", [])[:5]
num_communities = len(communities.get("communities", []))
print("Graph analysis completed")
print("Communities:", num_communities)
print("Top entities:", len(top_entities))
In [5]:
import os
os.environ["NEPTUNE_ENDPOINT"] = "your-cluster.us-east-1.neptune.amazonaws.com"
os.environ["NEPTUNE_PORT"] = "8182"
os.environ["AWS_REGION"] = "us-east-1"In [ ]:
from semantica.graph_store import GraphStore
import os
neptune_store = GraphStore(
backend="neptune",
endpoint=os.environ["NEPTUNE_ENDPOINT"],
port=int(os.environ.get("NEPTUNE_PORT", 8182)),
region=os.environ["AWS_REGION"],
iam_auth=True,
)
neptune_store.connect()
print("Connected to AWS Neptune")In [ ]:
for entity in knowledge_graph.get("entities", []):
neptune_store.create_node(
labels=[entity.get("type", "Entity")],
properties=entity,
)
for rel in knowledge_graph.get("relationships", []):
neptune_store.create_relationship(
start_node_id=rel["source"],
end_node_id=rel["target"],
rel_type=rel["predicate"],
properties=rel.get("metadata", {}),
)
print("Knowledge graph populated to AWS Neptune")
In [ ]:
# Verify data in Neptune
results = neptune_store.execute_query(
"MATCH (n) RETURN count(n) AS node_count"
)
print("Total nodes:", results.get("records", [{}])[0].get("node_count"))
results = neptune_store.execute_query(
"MATCH ()-[r]->() RETURN count(r) AS rel_count"
)
print("Total relationships:", results.get("records", [{}])[0].get("rel_count"))
# Sample query: list a few entities
results = neptune_store.execute_query(
"MATCH (n) RETURN labels(n), n.name LIMIT 5"
)
print("Sample nodes:")
for r in results.get("records", []):
print(r)
In [ ]:
from semantica.vector_store import VectorStore
from semantica.context import ContextRetriever
vector_store = VectorStore(backend="faiss")
vector_store.add(
texts=[parsed_doc["full_text"]],
metadata=[{"source": "earnings_call", "type": "transcript"}],
)
context_retriever = ContextRetriever(
knowledge_graph=knowledge_graph,
vector_store=vector_store,
hybrid_alpha=0.6,
use_graph_expansion=True,
max_expansion_hops=2,
)
queries = [
"What was the company's revenue guidance?",
"What were the key financial metrics discussed?",
]
retrieved_contexts = []
for query in queries:
results = context_retriever.retrieve(
query=query,
max_results=3,
min_relevance_score=0.2,
)
retrieved_contexts.append(results)
print("Hybrid GraphRAG configured")
print("Queries processed:", len(queries))
print("Sample results:", len(retrieved_contexts[0]) if retrieved_contexts else 0)
In [ ]:
from semantica.context import AgentMemory
agent_memory = AgentMemory(
vector_store=vector_store,
knowledge_graph=knowledge_graph,
retention_days=30,
)
memory_contents = [
f"Earnings call transcript: {parsed_doc['metadata'].get('title', 'Earnings Call')}",
f"Financial metrics extracted: {sum(len(v) for v in financial_metrics.values())}",
f"Key entities identified: {len(merged_entities)}",
]
memory_ids = []
for content in memory_contents:
memory_ids.append(
agent_memory.store(
content=content,
metadata={"source": "earnings_call", "type": "analysis"},
extract_entities=True,
extract_relationships=True,
)
)
financial_memories = agent_memory.retrieve(
query="financial metrics and earnings",
max_results=5,
)
memory_stats = agent_memory.get_statistics()
print("Agent memory configured")
print("Memories stored:", len(memory_ids))
print("Total memories:", memory_stats.get("total_memories", 0))
print("Retrieved memories:", len(financial_memories))
In [4]:
from semantica.context import AgentContext
agent_context = AgentContext(
vector_store=vector_store,
knowledge_graph=knowledge_graph,
use_graph_expansion=True,
max_expansion_hops=2,
hybrid_alpha=0.6,
retention_days=30,
)
memory_id = agent_context.store(
content=parsed_doc["full_text"][:1000],
metadata={"source": "earnings_call", "date": "2024-Q1"},
extract_entities=True,
extract_relationships=True,
link_entities=True,
)
results = agent_context.retrieve(
query="What was discussed about revenue growth?",
max_results=5,
expand_graph=True,
include_entities=True,
)
stats = agent_context.stats()
print("AgentContext configured")
print("Memory stored:", memory_id)
print("GraphRAG results:", len(results))
print("Total memories:", stats.get("total_memories", 0))
[1;31m---------------------------------------------------------------------------[0m
[1;31mNameError[0m Traceback (most recent call last)
Cell [1;32mIn[4], line 4[0m
[0;32m 1[0m [38;5;28;01mfrom[39;00m[38;5;250m [39m[38;5;21;01msemantica[39;00m[38;5;21;01m.[39;00m[38;5;21;01mcontext[39;00m[38;5;250m [39m[38;5;28;01mimport[39;00m AgentContext
[0;32m 3[0m agent_context [38;5;241m=[39m AgentContext(
[1;32m----> 4[0m vector_store[38;5;241m=[39m[43mvector_store[49m,
[0;32m 5[0m knowledge_graph[38;5;241m=[39mknowledge_graph,
[0;32m 6[0m use_graph_expansion[38;5;241m=[39m[38;5;28;01mTrue[39;00m,
[0;32m 7[0m max_expansion_hops[38;5;241m=[39m[38;5;241m2[39m,
[0;32m 8[0m hybrid_alpha[38;5;241m=[39m[38;5;241m0.6[39m,
[0;32m 9[0m retention_days[38;5;241m=[39m[38;5;241m30[39m,
[0;32m 10[0m )
[0;32m 12[0m memory_id [38;5;241m=[39m agent_context[38;5;241m.[39mstore(
[0;32m 13[0m content[38;5;241m=[39mparsed_doc[[38;5;124m"[39m[38;5;124mfull_text[39m[38;5;124m"[39m][:[38;5;241m1000[39m],
[0;32m 14[0m metadata[38;5;241m=[39m{[38;5;124m"[39m[38;5;124msource[39m[38;5;124m"[39m: [38;5;124m"[39m[38;5;124mearnings_call[39m[38;5;124m"[39m, [38;5;124m"[39m[38;5;124mdate[39m[38;5;124m"[39m: [38;5;124m"[39m[38;5;124m2024-Q1[39m[38;5;124m"[39m},
[1;32m (...)[0m
[0;32m 17[0m link_entities[38;5;241m=[39m[38;5;28;01mTrue[39;00m,
[0;32m 18[0m )
[0;32m 20[0m results [38;5;241m=[39m agent_context[38;5;241m.[39mretrieve(
[0;32m 21[0m query[38;5;241m=[39m[38;5;124m"[39m[38;5;124mWhat was discussed about revenue growth?[39m[38;5;124m"[39m,
[0;32m 22[0m max_results[38;5;241m=[39m[38;5;241m5[39m,
[0;32m 23[0m expand_graph[38;5;241m=[39m[38;5;28;01mTrue[39;00m,
[0;32m 24[0m include_entities[38;5;241m=[39m[38;5;28;01mTrue[39;00m,
[0;32m 25[0m )
[1;31mNameError[0m: name 'vector_store' is not definedIn [ ]:
financial_questions = [
"What were the key financial metrics discussed?",
"What guidance was provided for future quarters?",
]
generated_answers = []
for question in financial_questions:
retrieved_contexts = context_retriever.retrieve(
query=question,
max_results=3,
min_relevance_score=0.2,
)
context_text = "\n\n".join(
ctx.get("content", ctx.get("text", ""))
for ctx in retrieved_contexts
)[:1000]
entity_names = [
entity.get("name", "")
for entity in knowledge_graph.get("entities", [])[:5]
]
entities_text = ", ".join(entity_names) or "N/A"
prompt = f"""
Answer the question using only the context below.
If the answer is not present, say so.
Context:
{context_text}
Key entities: {entities_text}
Question:
{question}
Answer:
""".strip()
try:
answer = groq_llm.generate(
prompt,
temperature=0.7,
max_tokens=400,
)
except Exception as error:
answer = f"Answer generation failed: {error}"
generated_answers.append(answer)
print("Answer generation completed")
print("Questions answered:", len(generated_answers))
In [ ]:
from semantica.export import JSONExporter, RDFExporter
json_exporter = JSONExporter()
rdf_exporter = RDFExporter()
kg_json = json_exporter.export(knowledge_graph, format="json")
kg_rdf = rdf_exporter.export_to_rdf(knowledge_graph, format="turtle")
analysis_summary = {
"entities": len(knowledge_graph.get("entities", [])),
"relationships": len(knowledge_graph.get("relationships", [])),
"conflicts_resolved": len(resolved_conflicts),
"merged_entities": len(merged_entities),
"communities": num_communities,
"questions_answered": len(generated_answers),
"llm_model": groq_llm.model,
}
print("Export completed")
print("KG JSON entities:", analysis_summary["entities"])
print("KG RDF size (chars):", len(kg_rdf))
print("Questions answered:", analysis_summary["questions_answered"])
print("LLM model:", analysis_summary["llm_model"])