Compare commits

..
Author SHA1 Message Date
KaifAhmad1 50927f99b5 docs: surface explainability scope note near the top of the README
Moves a concise version of the system-level vs. foundation-model
explainability clarification up next to the opening pitch, so it's
visible before readers scroll to the high-stakes-domains section.
2026-08-16 17:46:12 +05:30
KaifAhmad1 476237952d docs: clarify explainability is system-level, not foundation-model internal
Adds a consistent scope note to README and docs (concepts, FAQ, index)
stating Semantica does not expose or reconstruct an LLM's internal
reasoning/chain-of-thought. It explains and audits the AI system
around the model: context, provenance, policies, decisions, and
execution history.
2026-08-16 17:39:23 +05:30
hariandZohaib Hassnain 70aa9d01bf fix(normalize): validate symbol currencies (#940)
* fix(normalize): validate symbol currencies

Signed-off-by: Mr-Neutr0n <harikp2002@gmail.com>

* fix(normalize): match currency codes by token boundaries

Signed-off-by: Mr-Neutr0n <harikp2002@gmail.com>

---------

Signed-off-by: Mr-Neutr0n <harikp2002@gmail.com>
Co-authored-by: Zohaib Hassnain <109234410+ZohaibHassan16@users.noreply.github.com>
2026-08-16 15:24:34 +05:30
Mohd Kaif c53ca4e84b docs: formalize issue assignment and duplicate-PR triage workflow (#1030)
* docs(contributing): formalize issue assignment and duplicate-PR triage workflow

Comments are no longer required before an issue can be assigned - maintainers
may assign directly based on recent activity. Also documents the duplicate-PR
priority order for triage (contributor PR, claimed issue, activity tiebreak,
late duplicates, overlapping scope).

* docs(contributing): clarify assignment precedence and define activity tiebreak

Addresses Qodo review feedback on PR #1030: the duplicate-PR priority list
now states these rules apply on top of the assignment workflow (opening a PR
pre-assignment doesn't grant priority), and the "most active" tiebreak now
specifies a concrete 60-day window and signals instead of being subjective.
2026-08-16 15:18:55 +05:30
pravit-ampandPravit Ampapathini 15171fd31a fix(parse): import get_progress_tracker in ExcelParser (#1016)
ExcelParser.__init__ called get_progress_tracker() without importing it,
so every instantiation raised NameError and the class was unusable. The
existing test imported ExcelParser but never constructed it, so nothing
caught it. Same defect as #530 in SimilarityCalculator, which was fixed
without sweeping the rest of the codebase.

Add construction coverage for every parser exported from semantica.parse,
driven off __all__ so later additions are covered automatically. These
live outside test_parse_comprehensive.py, whose setUp patches
get_progress_tracker into each parse module and would mock away the
interaction under test.

Closes #1014

Co-authored-by: Pravit Ampapathini <pravitampapathini@Pravits-MacBook-Air-3.local>
2026-08-16 14:07:10 +05:00
Guofang.Tang 8177d88753 fix(kg): preserve isolated nodes in graph analytics (#1011)
* fix(kg): preserve isolated nodes in graph analytics

* fix(kg): support node fallbacks and community payloads

---------
2026-08-16 11:23:24 +05:00
Shinde vinayak rao patil d94d8f6ab8 Feat/crewai integration (#988)
* feat(crewai): add first-class CrewAI integration (#962)

Add native CrewAI support so Crew agents can share a ContextGraph and
AgentContext via BaseTool subclasses and a BaseKnowledgeSource, matching
the existing agno integration pattern.

- SemanticaKGTool: 5 KG actions (extract_entities, extract_relations,
  add_to_graph, query_graph, find_related) with sync run()/async arun()
- SemanticaDecisionTool: 5 decision-intelligence actions
  (record_decision, find_precedents, trace_causal_chain,
  analyze_impact, check_policy) over AgentContext
- SemanticaKnowledgeSource: serializes a ContextGraph into crew
  knowledge storage; bridges legacy load_content() and current
  validate_content()/aadd() contracts for crewai>=0.80.0
- All classes degrade gracefully when crewai is absent
- New pip extra crewai=... included in the all bundle
- 70 new tests (stub-based present-case + subprocess degradation path)
- Docs: integrations/crewai.md, docs.json nav, README matrix updates

* fix(crewai): harden tools against real Semantica dataclass shapes (#962)

Bugs found during live testing with crewai 1.15.16:

- SemanticaKGTool.add_to_graph crashed on real Entity/Relation dataclasses
  ('str' object has no attribute 'end_char'): string names were passed to
  extract_relations(entities=...), which requires Entity objects, and the
  tool read .name/.source/.target instead of Entity's .text/.label and
  Relation's .subject/.object. Add shape-agnostic field helpers.
- SemanticaDecisionTool() created an AgentContext without a knowledge_graph,
  so _decision_backend was never set and record_decision raised 'Decision
  tracking is not enabled'. Wire in a ContextGraph.
- record_decision hard-failed when the agent omitted optional fields; fall
  back to category='general', reasoning='agent decision',
  outcome='recorded'.

Add tests covering real Entity/Relation dataclass shapes and the live
auto-created AgentContext path (now 77 crewai tests, 212 total).

* fix(crewai): make find_related traverse edges undirected (#962)

ContextGraph.get_neighbors only follows outgoing edges, so a node whose
only edge is incoming (A -> B) reported no related concepts. Rebuild a
bidirectional adjacency from find_edges() in SemanticaKGTool._find_related
so 'related' honors both directions.

* fix(crewai): harden tools for checkpoint serialization and correct action semantics (#962)

- Exclude live graph/context/extractor state from JSON serialization
  (model_dump(mode="json")) so CrewAI checkpointing no longer raises
  PydanticSerializationError; model_post_init self-heals defaults on restore
- query_graph now searches node content via graph.query() plus id/type
- trace_causal_chain returns an explicit error when causal tracing is
  unavailable instead of substituting similarity precedents; call
  trace_decision_causality(..., max_depth=...) with the correct kwarg name
- find_precedents propagates max_precedents/limit to the backend instead of
  being silently capped at 10
- Serialize add_to_graph batches under a module lock to prevent concurrent
  double-counting; skip nameless entities instead of creating repr()-junk nodes
- aadd() runs CPU-bound serialization in a thread executor
- Mirror crewai args_schema serialize/restore in the conftest stub and add
  serialization regression tests (crewai: 92 tests)

* fix(crewai): correct check_policy coercion, guard causal tracing, and harden concurrency (#962)

- _eval_rule now coerces rule values type-aware: bool("false") was truthy, so
  'enabled == false' reported a violation for enabled=false, and string datums
  like "0.90" were compared lexicographically instead of numerically
- _trace_causal_chain no longer raises AttributeError (which escaped _run) when
  the decision context lacks knowledge_graph; returns honest error JSON
- SemanticaKnowledgeSource storage failures log an actionable ERROR; without a
  configured crew embedder agents previously retrieved nothing silently
- add_to_graph uses a per-graph re-entrant lock (WeakKeyDictionary) instead of a
  process-global one: independent graphs no longer serialize each other and
  re-entrant extractor callbacks cannot deadlock
- entity/relation confidence=None normalizes to 1.0 instead of failing the
  whole extraction with float(None)
- add subprocess integration test against real crewai covering Crew-level
  serialization round-trip and checkpoint restore (stub tests cannot see it)
- docs: embedder requirement for SemanticaKnowledgeSource; resume contract note

* fix(crewai): surface knowledge-source save failures at ERROR when storage is wired (#962)

Re-verification against real crewai showed the embedder-missing failure raises
ValueError even though storage IS wired, so the old except-ValueError branch
mislabeled it as 'storage not wired' and logged DEBUG — hiding the failure.
Distinguish by storage presence instead of exception type: storage is None ->
DEBUG keep-in-memory (legitimate standalone use); storage wired but save()
raises -> actionable ERROR. Add regression test mirroring real crewai's
ValueError-on-missing-embedder behavior.

* fix(crewai): expose run()/arun() entry points in degraded mode (#962)

The public crewai contract is run()/arun(); without crewai installed they were
missing (only the private _run existed), so the documented 'usable without
crewai' path raised AttributeError at the entry point. Define them in degraded
mode only, leaving crewai's BaseTool implementations untouched when present.
Extend the degradation subprocess test to exercise run() and arun().

* fix(crewai): standardize query shape, field-name rules, and restore-state flag

- _query_graph: id/type matches now return the same schema as content
  matches (id/type/label/content/score) instead of a bare list
- _eval_rule: non-greedy field capture so hyphen/dot/space JSON keys
  (e.g. "risk-score >= 0.9") are addressable in policy rules
- add had_live_state/reconstructed_state so checkpoint-restored tools
  and knowledge sources signal that their live graph/context was lost
  and an empty one reconstructed; knowledge source no longer hides the
  loss by eagerly rebuilding its graph inside __init__ (pydantic calls
  __init__ during model_validate)

* fix(crewai): address Qodo review — confidence errors, string trim, holistic availability

- record_decision: stop calling float() in _run, so malformed confidence
  values surface as JSON errors (via _record_decision's handling) instead
  of crashing the tool
- _coerce_value: return the stripped string for non-numeric literals so
  whitespace-padded decision_data fields match policy rules
- centralize crewai availability in _availability.py so the exported
  CREWAI_AVAILABLE flag is holistic across tools and knowledge source
  (previously each module probed crewai independently and the package
  flag came from decision_tool only)

* ci: regenerate requirements-ci.txt for the crewai extra

The crewai extra in pyproject.toml brings in crewai, crewai-tools and
transitive deps (chromadb, lancedb, ...). Recompile with
uv==0.12.1 per CONTRIBUTING.md so the CI staleness check passes.

* ci: keep crewai out of the locked CI dependency set

crewai (all versions) hard-requires chromadb~=1.1.0, which carries a
pre-authentication code-injection advisory (CVE-2026-45829 / GHSA-f4j7-r4q5-qw2c)
with NO fixed release — even the latest 1.5.9 is affected. Keeping crewai in
the 'all' extra failed pip-audit and the safety check on requirements-ci.txt.

- drop crewai from the 'all' aggregate (standalone semantica[crewai] extra is
  unchanged and still installs crewai)
- stop listing crewai-tools in the extra: the integration only uses crewai core
  (BaseTool, BaseKnowledgeSource) and crewai-tools pulled extra transitive deps
- regenerate requirements-ci.txt: OSV/pip-audit 0 vulnerabilities, safety 0
  vulnerabilities, staleness check matches

* docs(crewai): document crewai extra scope and chromadb CVE-2026-45829

- CHANGELOG: extra is crewai>=0.80.0 only (no crewai-tools) and is not
  part of the 'all' bundle, with the chromadb CVE-2026-45829 reason
- integrations/crewai/README.md: add a security warning that installing
  the extra pulls chromadb~=1.1.0, which is affected by the unpatched
  pre-auth code-injection CVE-2026-45829

---------
2026-08-16 11:15:43 +05:00
5579851208 fix(export): harden YAML export input handling (#958)
* refactor(export): centralize graph-payload key normalization

Graph payloads circulate under two vocabularies, entities/relationships and nodes/edges, and consumers each reconciled them locally with competing idioms. The same payload could be exported, silently dropped, or rejected depending on which consumer read it.

Add normalize_graph_payload() to utils.helpers as the single place that decision is made. Both spellings present with one empty resolves to the populated one, which is the shape JSONExporter emits; both non-empty and different is refused, since there is no basis to prefer either and picking one would silently discard the other; a non-empty mapping with no recognized key raises rather than returning empty collections, with require_recognized=False for callers that should degrade.

Adopt it in the three exporters that genuinely alias. LPGExporter read nodes with entities as the default, so it dropped every entity when nodes was present but empty, losing everything on a JSON round-trip. ArangoAQLExporter had the same idiom plus a manual fallback. Neo4jCSVExporter routes its mapping branch through the shared resolver so the reference implementation cannot drift; its attribute branch stays local, since objects are not mappings.

Also feed LPGExporter._generate_indexes the resolved entities. It read entities directly, so a nodes/edges payload produced no indexes even once node generation was fixed.

CSVExporter and JSONExporter are deliberately excluded: they write entities, relationships, nodes and edges as separate outputs by design rather than reconciling two spellings of one collection, so normalizing there would rename output files.

* fix(export): reject non-mapping input to the YAML exporters

export_yaml declared Union[Dict[str, Any], List[Dict[str, Any]]], but both
YAML exporters read their payload by key, so a list reached .get() and
surfaced as a bare AttributeError from inside the exporter, naming neither
the offending argument nor the shape expected.

Reject rather than wrap. These formats distinguish entities from
relationships from triplets, so inferring which collection a bare list
represents would silently mislabel the records, and wrapping it under an
unrecognised key would write a structurally valid file with every
collection empty - trading a loud failure for silent data loss.

Validate in the exporters, matching the existing precedent in
Neo4jCSVExporter._normalize_graph, so direct users of the classes get the
same contract as callers of the convenience wrapper. Narrow the wrapper
type hint to Dict[str, Any] to match.

* fix(export): address YAML exporter review findings

- semantica/export/yaml_exporter.py — import Sequence from typing
  instead of collections.abc. `Sequence[str]` in _require_mapping's
  annotation is evaluated at function-definition time; collections.abc.Sequence
  only became subscriptable in Python 3.9, so on the 3.8 this project
  declares support for, importing this module raised TypeError.
  typing.Sequence has supported subscripting since 3.5.3. Mapping stays
  imported from collections.abc since it's only used for isinstance.
- tests/export/test_yaml_exporter_input_validation.py — clean up each
  test's tempfile.mkdtemp() dir via addCleanup instead of leaking it,
  and read exported YAML through a context manager instead of an
  unclosed yaml.safe_load(open(...)).

* fix(export): reject YAML export payloads with no recognized key

Both YAML exporters built their output from a fixed set of `.get(key, [])`
lookups, so a mapping keyed by anything else serialized to a structurally
valid file with every collection empty. Nothing signalled the loss: no
exception, no warning, and the progress log reported a completed export.
The only way to notice was to open the file. The realistic trigger is
re-exporting an `export_json` payload, whose `{"data", "count", "metadata"}`
envelope drops every record.

- SemanticNetworkYAMLExporter.export_semantic_network now resolves its
  collections through normalize_graph_payload(), which raises rather than
  returning empty collections for an unrecognized mapping. Adopting the
  shared resolver rather than repeating the check locally also brings the
  'nodes'/'edges' aliases, so ContextGraph.to_dict() — the most direct path
  from this library's own graph type to YAML, used in
  examples/capability_gap_context_graphs_example.py — exports its records
  instead of an empty file.
- export_for_pipeline built its nested semantic network from the same
  defaulted lookups and had the same defect; it goes through the resolver
  too.
- YAMLSchemaExporter.export_ontology_schema gets the equivalent check over
  its own key set. Schemas are a separate vocabulary with no aliasing, so
  _require_recognized_keys lives in this module rather than in the shared
  graph resolver.
- 'metadata' is deliberately not sufficient to make a payload recognized.
  An export_json envelope carries one, so accepting it would readmit the
  case this fix is most likely to be needed for.
- An empty mapping is still exported: an empty graph is legitimate and has
  no records to lose.
- SemanticNetworkYAMLExporter.export() serializes before creating the
  output directory, so a rejected export leaves nothing behind.

The two rejections keep distinct exception types, following what the
codebase already does: a payload of the wrong *type* cannot be exported at
all and raises ProcessingError, matching Neo4jCSVExporter._normalize_graph;
a mapping whose *contents* are unusable raises ValidationError, matching
normalize_graph_payload. _require_mapping therefore runs first at every
entry point, so a non-mapping never reaches the resolver.

Docstring Raises sections, export_usage.md and docs/reference/export.md
record the accepted input shapes and both failures.

Closes #953.

* fix(export): reject payloads whose records resolve to nothing

Addresses the Qodo findings on #958.

Presence-only recognition (finding 1): checking that a recognized key is
present answered "did the caller use our vocabulary" when the question that
matters is "did anything the caller supplied survive". A payload like
{"entities": [], "data": [...records...]} cleared the check, resolved to
empty, and dropped every record under 'data' -- the silent-empty export by a
narrower route.

- utils/helpers.py — split the check in two. _require_recognized_keys keeps
  the presence rule; _require_nothing_dropped runs after resolution and
  refuses a payload that resolved to nothing while an unread key still holds
  records. Only a non-empty list counts as evidence: ContextGraph.to_dict()
  always carries a populated 'statistics' dict, and an empty graph must stay
  exportable, so 'metadata', 'statistics' and 'count' are named as context
  rather than records.
- export/yaml_exporter.py — the schema path had the same hole and now runs
  both checks through the shared helpers rather than its own copy, so the
  two vocabularies cannot drift apart in what counts as a silent-empty
  export.

Progress reported success on a failed write (finding 3): export_semantic_
network stops its tracking as completed once serialization returns, but
export() then creates the directory and writes the file. A failure there
left the tracker showing a completed export with no output.

- export/yaml_exporter.py — the serialization span now says it serialized,
  not that it exported, and export() opens its own span around the
  filesystem work that stops as failed on error. Nothing reports a completed
  export until the bytes are on disk.

Finding 2 (export_yaml no longer accepts List[Dict]) is the intended
resolution of #952 rather than a regression: wrapping a bare list under a
guessed key is what would mislabel the records. The signature, docstring and
PR description already record the narrowed contract.

Tests cover both directions of each fix, including that an empty
ContextGraph still exports and that a failing write is not reported as
completed.

* fix(export): validate collection values and make Neo4j mappings strict

Two gaps at the boundary the shared normalizer is supposed to own.

_resolve_collection() resolved on truthiness alone, so a recognized key
could still hold something that is not a collection of records:
{"entities": "abc"} normalized to three single-character "records", and
{"entities": 42} surfaced as a raw TypeError from list() inside whichever
exporter happened to read it, naming the exporter rather than the payload
key at fault. Collection values are now validated before conversion --
strings, bytes, mappings, and non-iterable scalars are rejected by key
name, and each element must be a mapping or an attribute-carrying object,
the two record shapes the exporters actually read. None stays legal as an
absent collection, the spelling a JSON round-trip produces for []; it
cannot hide dropped records, since _require_nothing_dropped() still runs.
Every spelling present is validated, not just the one that wins, so a
malformed alias is not excused by a well-formed canonical key.

Neo4jCSVExporter._normalize_graph() opted out of the recognized-key check
for mappings, which left it able to turn {"data": [...]} into header-only
CSVs indistinguishable from a genuinely empty graph -- the exact failure
the rest of the change exists to prevent. Mapping payloads now go through
normalize_graph_payload() on its default terms. The attribute path for
graph objects is untouched. With no caller left opting out, the
require_recognized flag is removed rather than kept as a way back into
the silent-empty export.

Regression tests cover the malformed values end to end through every
export path that reads the normalizer, and assert the rejected Neo4j
export writes no CSV files.

* fix(export): close YAML schema and record validation gaps

Fix 1 -- _require_usable_schema silent data loss (P1):
_require_usable_schema() passed all values from _SCHEMA_KEYS into
_require_nothing_dropped() as evidence that records survived.  Scalar
metadata fields such as version='1.0' and uri='http://...' are truthy
strings, so any one of them caused _require_nothing_dropped() to return
early and silently discard records stored under an unread key alongside
them (e.g. {'version': '1.0', 'nodes': [{'id': 'c1'}]}).  Fixed by
building the resolved list from only non-empty list/tuple values of
recognised schema keys.

Fix 2 -- _is_record accepts modules and type objects (P2):
_is_record() accepted any object with __dict__, which includes Python
modules and class objects.  Elements that passed _coerce_records then
reached exporters and raised AttributeError (e.g. module 'math' has no
attribute 'get') rather than a ValidationError at the validation
boundary.  Fixed by excluding types.ModuleType and type from the
__dict__ branch while preserving support for all user-defined
attribute-bearing record objects.

Tests: 101 tests pass across
  tests/utils/test_normalize_graph_payload.py
  tests/export/test_yaml_exporter_key_recognition.py
  tests/export/test_yaml_exporter_input_validation.py
  tests/export/test_neo4j_csv_exporter.py

* fix(export): close exception-type and record-shape gaps in normalize_graph_payload

LPGExporter and ArangoAQLExporter called normalize_graph_payload() with no
type guard, so non-mapping input raised ValidationError from inside the
resolver while the YAML and Neo4j exporters raised ProcessingError for the
identical mistake -- inconsistent with the exception-type contract this PR
establishes. Both now use the shared _require_mapping() guard (moved from
yaml_exporter.py into utils/helpers.py so all three can use it).

Neo4jCSVExporter._normalize_graph checked isinstance(graph, dict), so a
non-dict Mapping (MappingProxyType, ChainMap) fell through to the
object-attribute branch and was rejected, even though the identical payload
exported fine via the other three exporters. Now checks isinstance(graph,
Mapping).

normalize_graph_payload() accepts dataclass/attribute-bearing object
records, but LPGExporter/ArangoAQLExporter call .get(...) directly on
resolved entities -- an object-shaped record passed validation only to
crash with a raw AttributeError once used, the exact failure this
boundary exists to prevent. Records are now converted to plain dicts at
the boundary (_coerce_records -> new _record_to_dict), so every consumer
gets a uniform shape regardless of which reading the caller used.

Two non-empty spellings of the same collection holding identical records
in a different order were rejected as conflicting, since the check used
plain list equality. Comparison is now an order-independent multiset of
each record's canonical JSON form.

* docs(changelog): add entry for #958 YAML export input hardening

Documents the full arc of #958 -- the normalize_graph_payload()
centralization, YAML input validation, both review rounds from
@Sameer6305, and the exception-type/record-shape follow-up fixes -- plus
closes #956, #952, #953.

---------

Co-authored-by: Pravit Ampapathini <pravitampapathini@Pravits-MacBook-Air-3.local>
Co-authored-by: Sameer Kadam <sskadam6305@gmail.com>
Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-08-15 22:10:04 +05:30
Lakshay Saini 115e7965cd fix(explorer): gate temporal requests on graph load (#1003)
Explorer was firing temporal requests before the graph even loaded.

When the backend is down, /api/graph/nodes fails but the temporal
bounds and snapshot effects didn't care , they fired anyway, off in
their own corner, ignoring whether the graph actually came up. Every
page load with no backend meant three failed requests instead of one,
and a scrubber that had nothing to scrub.

Added two small predicate functions and gated the temporal effects on
them. Basically: don't ask for time-based data until you know the
graph itself loaded. An empty graph still counts as loaded, so that
case isn't broken.

Confirmed with the backend down, before and after: three failing
requests down to one.

Fixes #982.
2026-08-15 17:47:35 +05:00
yzxcj797 8639cb9f16 fix(seed): pass connection string to DBIngestor and stop mislabeling OSError in load_from_database (#995)
Fix DBIngestor calls in load_from_database , it was never actually reaching the db.

execute_query/export_table need the connection string as their first arg,
but we were only passing it to the constructor's config dict, which
those methods don't read. Every call blew up with a TypeError before
connecting.

Also split the ImportError/OSError handling , they were caught together
so a real connection failure got reported as "module not available",
which sent people looking in the wrong place. OSError now surfaces as
an actual failure with the original exception chained via `from e`.

Fixes #973.
2026-08-15 17:18:36 +05:00
f1e7e64ad1 feat(context): add retraction and purge to ContextGraph (#957)
* feat(context): add retraction and purge to ContextGraph

ContextGraph had 56 public methods and none that removed anything: the only
option was clear(), which discards the whole graph. Removing one entity meant
exporting to a dict, filtering by hand and rebuilding, losing provenance.

Add two operations with deliberately different contracts.

retract_node/retract_edge close the entity's validity window. The entity stops
being active going forward, but state_at() before the retraction still returns
it, so decisions recorded against it remain explainable. This reuses the
valid_from/valid_until machinery already present rather than adding a new
subsystem.

purge_node/purge_edge remove the entity outright, from history as well as from
the active view, leaving a tombstone that records that a purge happened and why
but never the purged content. Scope is this graph only; copies in AgentMemory
or a bound vector store are not reached, so it is one step of an erasure
workflow rather than the whole of it.

Both record themselves through the existing mutation_callback path.
MutationRecord already documented REMOVE_NODE/REMOVE_EDGE in its operation
vocabulary, so retraction emits UPDATE_NODE and purge emits REMOVE_NODE with no
changes required to change_management.

Incident-edge lookup scans self.edges rather than _adjacency, which is keyed by
source only and would otherwise leave inbound edges pointing at a removed node.
Purge updates edges, edge_type_index and _adjacency together so the indexes
cannot drift, and clear() now resets the retraction and tombstone records.

* fix(context): address review findings on retraction and purge

* fix(context): close every duplicate when retracting/purging by edge_id

edge_id is content-derived and not yet guaranteed unique (#922, fix
pending in #926): two identical add_edge() calls produce two edge
objects sharing one id. retract_edge()/purge_edge() resolved "the
edge" via the first matching object only, so a duplicate was silently
left untouched (still live, still active) while the call returned
True and recorded a tombstone/retraction claiming it was fully
handled. Repeat purge_edge() calls also silently overwrote the
tombstone's reason/purged_at on each partial attempt instead of
no-op'ing once nothing remained to purge.

retract_node()'s cascade had the same root cause from the other
direction: it checked the live _retractions dict mid-loop, so the
first duplicate's just-written record made the second look already
handled and it was skipped outright, left permanently active.

retract_edge()/purge_edge() now act on every edge matching the id
under a single record; the cascade's dedup check is snapshotted
before the loop starts so within-call duplicates are still closed
rather than skipped.

Adds TestDuplicateEdgeId (5 tests) reproducing all three paths.

* docs(changelog): document retraction/purge feature

Adds an Unreleased/Added entry for #955/#957 covering retract_node,
retract_edge, purge_node, purge_edge and the get/list accessors, plus
the duplicate-edge_id fix caught and applied during review.

---------

Co-authored-by: Pravit Ampapathini <pravitampapathini@Pravits-MacBook-Air-3.local>
Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-08-15 17:12:10 +05:30
pravit-amp 6df97cf0a0 fix(triplet_store): stop CONSTRUCT detection matching inside a leading comment (#951)
CONSTRUCT_QUERY_RE skipped comments with a bare \#[^\n]*, whose trailing *
backtracks. For '# CONSTRUCT ...\nSELECT ...' the engine gave back everything
after the '#', so the CONSTRUCT inside the comment satisfied the query-form
keyword and a SELECT/ASK was reported as a CONSTRUCT.

All four SPARQL backends delegate to this regex, so such a query took the
CONSTRUCT branch of execute_sparql, which sends Accept: text/turtle and parses
the body as Turtle — failing with a misleading 'Failed to parse CONSTRUCT
response as Turtle'.

Require a comment to reach a line terminator. Both LF and CR are accepted
because the SPARQL grammar ends a comment at either; matching only LF would
regress CR-terminated comments into false negatives.

Add regression tests covering both directions across all four backends.>
2026-08-15 16:20:49 +05:00
84ce3c5155 fix(context): make ContextGraph.add_edge idempotent by deduping on edge_id (#926)
* fix(context): make ContextGraph.add_edge idempotent by deduping on edge_id (#922)

* docs(changelog): document add_edge dedupe fix

Adds an Unreleased/Fixed entry for #922/#926 so the ContextGraph
edge-dedupe bug and its fix are recorded per Keep a Changelog format.

---------

Co-authored-by: Pravit Ampapathini <pravitampapathini@Pravits-MacBook-Air-3.local>
Co-authored-by: Mohd Kaif <98801504+KaifAhmad1@users.noreply.github.com>
Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-08-15 16:09:33 +05:30
Guofang.Tang b8175ea801 fix(kg): make k-shortest path search side-effect free (#1000)
* fix(kg): make k-shortest path search side-effect free

* fix(kg): respect traversal direction for edge exclusion
2026-08-15 15:34:26 +05:00
557e29ee14 fix(explorer): repair /api/enrich/extract (always 503) and the /api/decisions routes (always 500) (#886)
* fix(explorer): repair /api/enrich/extract and the /api/decisions routes

Two Explorer API endpoints fail on every install.

/api/enrich/extract imported extract_entities and extract_relations from
semantic_extract.methods, where neither name is defined — that module ships
only the per-strategy variants (extract_entities_ml, extract_relations_regex,
...), and nothing re-exports a plain facade. The resulting ImportError was
caught and reported as "semantic_extract module not available. Ensure spacy
and transformers are installed.", so a wiring bug looked like a missing
dependency. The route now calls NamedEntityRecognizer and RelationExtractor
directly, the classes the README documents, and feeds the extracted entities
into relation extraction rather than re-deriving them. The 503 branch stays
for a genuinely absent module.

Every /api/decisions* route returned 500 once the graph held a decision:
record_decision() stores timestamp as datetime.now().timestamp(), a float,
while DecisionResponse types the field as str, so pydantic rejected the value
the library itself wrote. A before-mode field validator on DecisionResponse
normalizes float, int and datetime inputs to ISO-8601, covering every route
that builds the model instead of only the list endpoint.

The existing tests missed both: test_extract accepted 503 as a pass, and the
decision fixtures are hand-built nodes carrying no timestamp at all. Both are
tightened, and a TestRecordedDecisions class exercises the routes against
decisions created through record_decision().

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* perf(semantic_extract): cache spaCy models instead of loading one per call

extract_entities_ml(), extract_relations_similarity() and
extract_relations_dependency() called spacy.load() on every invocation, so the
model was re-read from disk and re-initialized per call. On a short sentence
that is ~120 ms of loading around ~2 ms of work, and successive calls never got
cheaper. The path is reachable from the CLI, the MCP extract_entities tool, the
pipeline ner_extract step and POST /api/enrich/extract, and process_batch()
multiplies it by the number of documents.

The module already had a cached loader for one code path — get_nlp_model() and
its _nlp_cache global — but the extraction functions bypassed it.

Adds load_spacy_model(), a process-level cache keyed by model name behind a
lock so concurrent callers do not each start a load, and routes the five call
sites through it. Errors are left uncached and propagate unchanged, so the
existing OSError fallbacks to pattern extraction still fire. get_nlp_model()
keeps its own entry: it loads with disable=["parser", "ner", "lemmatizer"] for
similarity work, so its model is not interchangeable with the NER one.

Cache entries record the spacy module object they came from. Several tests
patch methods.spacy with a mock and assert on load calls; without that guard a
name-keyed cache would hand a previous test's mock to a later one.

Measured on the same sentence, Python 3.12.13 / spacy 3.8.15 / en_core_web_sm:
extract_entities_ml() median 132 ms before, 2.1 ms after, identical entities.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(explorer): harden extraction and timestamp handling

* fix(explorer): catch OverflowError/OSError in decision timestamp validator

DecisionResponse._normalize_timestamp only guarded against NaN/inf via
math.isfinite(), but datetime.fromtimestamp() raises OverflowError or
OSError for finite epoch values outside the platform's representable
range (e.g. milliseconds stored where seconds were expected). Those
exceptions escaped the pydantic validator unhandled, reintroducing an
unhandled 500 on /api/decisions* for exactly the bug class this PR
closes. Also exclude bool from the numeric branch, since bool is an
int subclass and was being silently coerced to epoch 0/1.

* docs: add changelog entry for PR #886 (explorer extract/decisions fixes)

Documents the extraction 503, decisions timestamp 500, and folded-in
spaCy caching fixes, plus the review-round hardening from Sameer6305
and the timestamp overflow/bool fix from this follow-up commit.

---------

Co-authored-by: joseedson18jc <joseedson18jc@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Sameer Kadam <sskadam6305@gmail.com>
Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
Co-authored-by: Mohd Kaif <98801504+KaifAhmad1@users.noreply.github.com>
2026-08-15 13:48:26 +05:30
yzxcj797 c1be6dd7dc docs: fix dead allcontributors emoji-key link (#987) 2026-08-15 01:08:13 +05:30
Zohaib Hassnain 42afc06003 ci: refresh github/codeql-action pin to current v4 (#986)
The pin was 5595ccaf..., but upstream has since moved the v4 tag to
ff2f1c62.... The Verify Action Pins workflow flags this drift on every
PR that touches any workflow file, regardless of whether that PR
changed codeql.yml or defender-for-devops.yml.

Verified the new SHA against the GitHub API directly (not just the CI
error text) and confirmed .github/scripts/verify-action-pins.sh passes
clean locally (40/40 action references OK, exit 0).
2026-08-14 22:18:46 +05:00
Yunare MaiaandZohaib Hassnain 4513b61e40 ci: pin Python dependencies in requirements-ci.txt for reproducible CI (#945)
* ci: pin Python dependencies in requirements-ci.txt for reproducible CI

Adds a committed lockfile pinning all transitive dependencies at exact
versions (uv pip compile, Python 3.11, all extras — 1581 lines), the
Python equivalent of explorer/package-lock.json + npm ci.

- CI installs from requirements-ci.txt before building the wheel
- CI verifies the lockfile is byte-identical to a fresh compile (fails
  on staleness after pyproject.toml changes)
- CONTRIBUTING documents the regeneration command

Closes #938

Signed-off-by: Yunare Maia <yunare@gmail.com>

* ci: address Qodo review — security scans use pinned deps, exclude gpu extras

- security-scan.yml installs from requirements-ci.txt instead of
  "./[llm-litellm]" so Safety scans the exact CI/release dependency tree
- security.yml runs pip-audit -r requirements-ci.txt for the same parity
- lockfile regenerated with --extra all (the cross-platform set) instead
  of --all-extras, which pulled faiss-gpu/cupy from the Linux-only gpu
  extra and co-installed faiss-cpu + faiss-gpu in CI
- uv pinned to 0.12.1 (the version that generated the lockfile) in CI and
  CONTRIBUTING so regeneration is deterministic

Signed-off-by: Yunare Maia <yunare@gmail.com>

* ci: make lockfile staleness check immune to upstream releases

The previous check re-resolved pyproject.toml without constraints, so any
upstream package release (e.g. boto3 1.43.69 -> 1.43.70) failed CI even
when nothing in the repo changed — exactly the time-dependent drift Qodo
flagged. The check now re-resolves with requirements-ci.txt as a
constraint and compares only version lines, so it detects intentional
pyproject.toml changes but ignores upstream releases. CONTRIBUTING
updated to match.

Signed-off-by: Yunare Maia <yunare@gmail.com>

* ci: fix security workflows — install pip-audit; order tooling after pinned deps

Security workflow: the pip-audit install step was lost in the rebase
conflict merge — pip-audit was invoked but never installed (exit 127).

Security-scan workflow: installing safety first let the pinned
requirements-ci.txt overwrite its transitive deps (rich), breaking the
safety CLI at runtime (RuntimeError: Type not yet supported). Tooling is
now installed AFTER the pinned set.

Signed-off-by: Yunare Maia <yunare@gmail.com>

* fix(ci): address review — hashes, build isolation, release builds, docs (4/4)

ZohaibHassan16's review flagged 4 supply-chain gaps; all addressed:

1. **Release builds now use the lockfile**: release.yml installs
   requirements-ci.txt and runs `python -m build --no-isolation` so the
   sdist/wheel is built against the exact tested dependency set.
2. **Build isolation pinned**: [build-system].requires is now
   setuptools==84.0.0 + wheel==0.48.0 (exact pins, no ranges).
3. **Hashes**: requirements-ci.txt regenerated with --generate-hashes
   (5,708 sha256 hashes, verified against PyPI). Staleness check updated
   to strip the `\` line continuations hashes introduce.
4. **CONTRIBUTING.md documents the separate environment**: hashes,
   never-install-into-dev note, build-system pins, --no-isolation release
   builds.

Validated: stale-check diff clean, hash spot-check matches PyPI.
Signed-off-by: Yunare Maia <yunare@gmail.com>

* fix(ci): apply --no-isolation to CI build + align benchmark to Python 3.11

Follow-up to ZohaibHassan16's second review round:

1. ci.yml was still running `python -m build` with build isolation
   (unpinned setuptools/wheel from PyPI) — now `python -m build
   --no-isolation` against the pinned deps, matching release.yml.
2. benchmark.yml was on Python 3.12 while the lockfile is compiled for
   3.11 — aligned to 3.11 so every workflow runs the same environment.

Signed-off-by: Yunare Maia <yunare@gmail.com>

* fix(ci): install pinned wheel before --no-isolation build

python -m build --no-isolation failed with 'Missing dependencies:
wheel==0.48.0' because wheel is build-time only — uv's lockfile
excludes it, so installing requirements-ci.txt alone left the build
env without it. Both ci.yml and release.yml now install wheel==0.48.0
(the same pin [build-system] declares) before building. Validated
locally: wheel builds clean with --no-isolation.

Signed-off-by: Yunare Maia <yunare@gmail.com>

---------

Signed-off-by: Yunare Maia <yunare@gmail.com>
Co-authored-by: Zohaib Hassnain <109234410+ZohaibHassan16@users.noreply.github.com>
2026-08-14 22:10:23 +05:00
hsd2514andZohaib Hassnain 8a4ebafb9a fix(context): honor explicit causal edges in decision tracing (#983)
* fix(context): honor explicit causal edges in decision tracing

trace_decision_causality() inferred causes purely from shared NER entities
plus timestamp ordering, so relationships recorded through
add_causal_relationship() never affected the trace. When entity extraction
returned nothing, trace_decision_chain() came back empty even though an
explicit CAUSED edge was stored in the graph.

Traverse the explicit CAUSED/INFLUENCED/PRECEDENT_FOR edges first, since
they are the ground truth the caller recorded, and keep the entity and
timestamp inference as an additive fallback for pairs with no explicit
link. Edges whose source has no decision record (for example a graph
restored via from_dict) are skipped so a stale edge cannot abort the trace.

analyze_decision_influence() now reports explicitly linked decisions as
direct influence rather than surfacing them only as indirect, and no
longer lists the same decision under both direct and indirect.

Closes #975

* fix(context): address review feedback on causal edge tracing

Follow-up to the explicit causal edge fix, covering the issues raised in
review.

A stored edge weight of 0.0 was coerced to the 1.0 default by a truthiness
check, inflating confidence_decay in the causal chain report. add_edge() is
public and can create causal edges with any weight, so use an explicit None
check instead.

Explicit causes were collected into a dict keyed by source_id, so multiple
causal edges between the same pair of decisions overwrote each other and
only the last was traced. Collect every edge instead, keeping a separate set
of source ids for the entity fallback exclusion.

Cycle detection used a single traversal-wide visited set, so a decision
reached through one branch became unreachable through another and branching
graphs silently lost valid chains. Detect cycles per path instead; max_depth
still bounds the traversal.

Build a reverse index of causal edges once per call rather than scanning the
edge list at every visited node, and use edge_type_index in the influence
analysis. The three causal edge types are now a shared constant.

Adds regression tests for zero weights, parallel edges, branching graphs and
cycle termination.

* fix(context): bound causal trace and report truncation

Per-path cycle detection keeps branching graphs correct but makes the
traversal combinatorial in max_depth: on a densely connected graph the
number of distinct causal paths grows by roughly the branching factor per
level, so a raised max_depth could return hundreds of thousands of chain
reports and take seconds of CPU.

Add a max_chains bound, defaulting to 10000. Rather than dropping chains
silently, which is the exact failure this fix set out to eliminate, the
traversal stops at the bound and appends a {"truncated": True, ...} marker
so callers can always tell the trace is incomplete. A warning is logged with
the same detail. Pass max_chains=None for the previous unbounded behaviour.

Graphs that fit within the bound are unaffected.

---------

Co-authored-by: Zohaib Hassnain <109234410+ZohaibHassan16@users.noreply.github.com>
2026-08-14 21:51:04 +05:00
manjunath bhaskar 80b9bea0d5 fix(ingest): lock the repo host DNS resolve cache against concurrent mutation (#979)
* fix(ingest): lock the repo host DNS resolve cache against concurrent mutation

_REPO_HOST_RESOLVE_CACHE is a module level OrderedDict shared by every
RepoIngestor instance and thread. _resolve_repo_host_ips and
_prune_repo_host_resolve_cache read, wrote, and iterated it with no lock,
so concurrent ingest_repository() calls (e.g. from a thread pool) could
mutate the dict while another thread was iterating it during pruning.
This reliably raised RuntimeError: OrderedDict mutated during iteration
under ordinary concurrent usage, not just adversarial input.

Reproduced with 32 threads hammering _resolve_repo_host_ips with a low
TTL and small cache cap so pruning and eviction happen on nearly every
call; the crash showed up within the first few hundred iterations on
every run before the fix and did not reproduce at all after it.

Fix adds a threading.Lock guarding every read, write, and prune of the
cache. The blocking socket.getaddrinfo call stays outside the lock so a
slow DNS lookup for one host cannot stall cache access for other hosts.

Added a regression test, TestRepoHostResolveCacheThreadSafety, that
drives 32 threads through _resolve_repo_host_ips with a short TTL and
small cache cap and asserts no exception is raised.

Full test suite: 4088 passed, 332 failed, 140 errors both before and
after this change (same counts on main), all from missing optional
dependencies in this local environment (snowflake, sqlite-vec, spaCy
models, faiss/torch version mismatches), not from this fix. The ingest
and SSRF focused test files pass cleanly: 106 passed, 0 failed.

* test(ingest): fail fast on the first hung thread in the resolve-cache race test

join(timeout=30) alone doesn't fail the test if a worker hangs -- it
just returns after the timeout with the thread still running, and the
test falls through to the errors check, which trivially passes since
a hung thread never got far enough to append one. A future deadlock
could slip past this test looking green.

Assert immediately after each individual join rather than after the
whole loop: checking only once every thread has been joined means a
mass hang costs up to 32*30s = 16 minutes before the test even reaches
the check. Failing on the first hung thread caps the worst case at
~30s instead. Worker threads are daemon=True so a genuine hang can't
also block the test process from exiting.

Verified the assertion is load-bearing, not cosmetic: temporarily
injected an artificial 9999s sleep into the first worker in a
throwaway copy of the test and confirmed the test now fails in ~31s
with a clear message, instead of the ~16 minutes a mass hang would
otherwise cost. That copy was never committed.

Addresses the review comment on #979 from ZohaibHassan16 and Qodo's
automated review.

* test(ingest): fail fast on the first hung thread, for real this time

The previous commit (f94e3b38) claimed to check is_alive() right after
each individual join, but a git staging mistake meant it actually
committed the old batched version instead (checking all 32 threads
only after the whole join loop finished) -- ZohaibHassan16 caught this
by timing it directly, 5 hanging threads took ~5x longer than 1
hanging thread, which the per-thread version would not do.

This commit was built by resetting to the current branch tip, verifying
byte-for-byte against a separately saved copy of the intended fix, and
confirming the actual committed git object (not just `git diff`) has
the inline check before pushing anything.

Assert immediately after each individual join rather than after the
whole loop: checking only once every thread has been joined means a
mass hang costs up to 32*30s = 16 minutes before the test even reaches
the check. Failing on the first hung thread caps the worst case at
~30s regardless of how many threads hang.

---------
2026-08-14 20:14:13 +05:00
Lakshay Saini 5bc09a5f5a refactor(explorer): remove dead graph workspace shell (#984)
* refactor(explorer): remove dead graph workspace shell

* refactor(explorer): remove unused graph runtime stage
2026-08-14 19:46:41 +05:00
75f88b1c40 Fix/explorer backend failure states (#980)
* fix(explorer): show a retryable error when the graph fails to load

The dependency-pre-bundle overlay had no failure path: on a fetch
error it kept rendering the last progress frame forever with no
retry. Route isError/error out of the load query, surface a real
error card with the underlying message, and let retry re-fetch
without a full page reload.

* fix(explorer): reflect real backend connectivity on the landing page

The status dot and 'System Online' text were static, so a dead
backend still looked healthy. Track checking/online/offline explicitly
and drive both off the same state so they can't disagree.

* feat(explorer): let search results be dismissed, round relevance scores

The results strip had no close affordance and stayed pinned until the
next search. Add a header row with a dismiss button, and round scores
to whole numbers instead of showing three decimals of a raw relevance
value nobody can act on.

* feat(explorer): add typeahead suggestions to graph search

Typing in the search box now debounces a query against the existing
search endpoint and shows a combobox dropdown, with arrow-key
navigation, Enter/click to jump straight to a node, and Escape to
dismiss. Previously nothing happened until the full form was
submitted.

* fix(explorer): abort stale typeahead requests and clear suggestions on error

Clearing the search box while a suggestion fetch was in flight never
aborted it, so a late response could reopen the dropdown with results
for a query that was no longer typed. A non-OK response also left
whatever suggestions were already on screen untouched instead of
clearing them. Abort on every effect cleanup (not just unmount) and
clear suggestions on any non-abort failure.

* docs(changelog): add entry for Explorer backend failure states fix

Documents the (#980, closes #977) fix in the Unreleased/Fixed section.

---------

Co-authored-by: Sameer Kadam <sskadam6305@gmail.com>
Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-08-14 17:36:42 +05:30
Shubham SrivastavaandMohd Kaif 80b1cca07b test(semantic_extract): guard openai-dependent tests and assert on the logger, not stdout (#935)
* test(semantic_extract): skip openai-dependent tests when the SDK is absent, assert logs not stdout

* test(semantic_extract): pass logger name to assertLogs to match suite convention

All 11 existing assertLogs call sites in the suite pass a logger name
string rather than a Logger instance; tests/reasoning/test_reasoner.py
uses this exact .logger.name form. Behaviour is unchanged.

---------

Co-authored-by: Mohd Kaif <98801504+KaifAhmad1@users.noreply.github.com>
2026-08-14 16:56:04 +05:30
1c0cebb1c3 security(context): harden Markdown import against TOCTOU symlink races (#932)
* security(context): harden Markdown import against TOCTOU symlink races

Closes #856

* fix(context): harden markdown import security tests

* docs(changelog): add entry for Markdown import TOCTOU symlink hardening

Documents the (#932, closes #856) fix in the Unreleased/Fixed section.

---------

Co-authored-by: Sameer Kadam <sskadam6305@gmail.com>
Co-authored-by: Mohd Kaif <98801504+KaifAhmad1@users.noreply.github.com>
Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-08-14 16:40:51 +05:30
Guofang.Tang 94d0c3dc07 fix(kg): remap relationship endpoints after entity resolution (#978)
* fix(kg): remap relationship endpoints after entity resolution

* fix(kg): harden relationship endpoint remapping
2026-08-14 15:37:56 +05:00
Ikko Eltociear Ashimine c0a051903f docs: update CONTRIBUTING.md (#976)
fix GiHub link.
2026-08-14 11:51:16 +05:30
sushuaiyu 09c4b1b570 test(context): skip symlink test without Windows privilege (#908)
* test(context): skip symlink test without Windows privilege

* test(context): name Windows privilege error code

---------
2026-08-14 10:15:12 +05:00
Yunare Maia c5d13a45db feat(seed): allow_private_ips opt-in for trusted internal API sources (#959)
* feat(seed): add allow_private_ips opt-in for trusted internal API sources (Closes #943)

SeedDataManager.load_from_api now delegates to the shared SSRF guard
(semantica/ingest/ssrf.py, added in #906) instead of raw requests.get,
gaining redirect validation and bounded DNS resolution for free.

New config option allow_private_ips (parsed via the shared parse_bool
helper) lets trusted internal deployments load from private APIs while
the secure default (block private/loopback/link-local) is unchanged.

Tests updated to mock request_with_ssrf_guard; new tests cover the
block-by-default behavior and the opt-in flag reaching the guard.
19/19 green in test_seed_manager.py, 25/25 across both seed suites.

Signed-off-by: Yunare Maia <yunare@gmail.com>

* fix(ssrf): strip sensitive headers on cross-host redirects (Qodo finding)

request_with_ssrf_guard reused the caller's headers on every redirect hop,
so an Authorization bearer token from load_from_api could leak to a
different redirect target host. Now strips Authorization and
Proxy-Authorization when the redirect origin (netloc) changes, while
keeping them for same-host hops (matching requests semantics).

2 new tests: cross-host redirect drops the credential; same-host keeps it.
37/37 green in test_ssrf_protection.py. load_from_api docstring now also
documents cloud-metadata blocking and per-hop redirect validation.

Signed-off-by: Yunare Maia <yunare@gmail.com>

* fix(ssrf): strip credentials on https->http downgrade redirects (review feedback)

_should_strip_auth now mirrors requests' should_strip_auth semantics:
strip on hostname change, port change, or scheme downgrade; keep the
credential only for the safe http->https upgrade on default ports.
Previously only netloc was compared, so an https->http redirect on the
same host replayed the Authorization header in cleartext.

---------

Signed-off-by: Yunare Maia <yunare@gmail.com>
2026-08-14 10:07:52 +05:00
611874e63e security: apply SSRF guard to feed ingestion requests (#928)
* security: apply SSRF guard to feed ingestion requests

FeedIngestor and FeedMonitor fetched feed and website URLs with plain
requests.get/head calls, bypassing the SSRF validation already used by
web_ingestor.py and api_ingestor.py. This allowed feed URLs pointing at
loopback, link-local, or other private network addresses to be fetched
directly.

Route all outbound requests in feed_ingestor.py through
request_with_ssrf_guard, gated by the same allow_private_ips config
option the other ingestors expose.

* test: mock the correct request boundary in test_discover_feeds_empty

The test still patched requests.get after discover_feeds() moved to
request_with_ssrf_guard(), which calls requests.request and performs
real DNS resolution. That left the test hitting live network/DNS.

* docs(changelog): document FeedIngestor SSRF guard fix (#928, closes #927)

Records the SSRF guard applied to all 5 feed-ingestion request sites,
the Qodo-flagged test-mock fix, independent PoC verification, and the
carried-over exception-swallowing behavior in discover_feeds().

---------

Co-authored-by: Sameer Kadam <sskadam6305@gmail.com>
Co-authored-by: Mohd Kaif <98801504+KaifAhmad1@users.noreply.github.com>
Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-08-13 23:07:35 +05:30
43bac6170c fix(vector_store): make VectorManager methods work on persistent backends (#855) (#914)
* fix(vector_store): make VectorManager methods work on persistent backends (#855)

maintain_store() and collect_statistics() reached into VectorStore
internals (.vectors/.metadata), which only exist for the inmemory
backend — any persistent backend (FAISS, Qdrant, Pinecone, Milvus,
...) crashed with AttributeError.

Add a public backend-agnostic VectorStore.count() accessor following
the get_vector()/get_metadata() precedent (#843) and the
NotImplementedError-on-unsupported-capability precedent of
_filter_by_metadata() (#848): inmemory counts its dict, persistent
backends delegate to count() when available, and raise
NotImplementedError otherwise. VectorManager methods now go through
count(); maintain_store() keeps the exact inmemory semantics (separate
vector/metadata dict counts) and reports a 1:1 count for persistent
backends, where metadata is stored alongside each vector.

Tests: 10 hermetic unit tests covering inmemory, delegation and the
NotImplementedError path. Core vector_store suite: 40 passed.

* fix(vector_store): raise NotImplementedError when count() unavailable

Address Qodo review findings on #914:
- Persistent backend with no wrapped store no longer silently returns 0
  (which masked a missing initialization as an empty, healthy store);
  it now raises NotImplementedError like get_vector()/get_metadata().
- A mis-shaped adapter exposing a non-callable 'count' attribute now
  surfaces a clean NotImplementedError instead of a TypeError, via a
  getattr + callable() capability check.

Adds regression tests for both cases.

* fix(vector_store): implement count() on FAISS/SQLite/PgVector backends (#914)

- FAISSStore.count(): returns len(index.vector_ids); 0 when no index exists yet
- SQLiteVecStore.count(): delegates to get_stats()[vector_count] (SELECT COUNT(*))
- PgVectorStore.count(): delegates to get_stats()[vector_count] (SELECT COUNT(*))
- VectorStore.count(): fix misleading NotImplementedError message; now describes
  how to add count() support to a backend adapter rather than claiming only the
  inmemory backend can ever support counting
- VectorManager.maintain_store(): split inmemory and persistent paths:
  * inmemory: independently reads len(vectors) and len(metadata) and compares
    them as an integrity check (original semantics preserved)
  * persistent: calls store.count(); returns metadata_count=None because
    metadata is co-located with vectors in the backend and cannot be counted
    independently; never manufactures metadata_count=vector_count as a vacuous
    tautology (#914 Qodo review)
- Tests: rewrite test_vector_manager_persistent.py with 31 tests covering
  dispatch logic, inmemory divergence detection, persistent metadata_count=None
  invariant, FAISSStore/PgVectorStore via mocks, and SQLiteVecStore via real
  in-memory SQLite (skipped when sqlite-vec absent)

* docs(changelog): document VectorManager persistent-backend count fix (#914, closes #855)

Records the VectorStore.count() accessor, the FAISS/SQLite/PgVector
implementations added during review, and the maintain_store()
metadata_count fix (no longer fabricates equality for persistent backends).

---------

Co-authored-by: Sameer6305 <sskadam6305@gmail.com>
Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
Co-authored-by: Mohd Kaif <98801504+KaifAhmad1@users.noreply.github.com>
2026-08-13 22:42:34 +05:30
91d02a0f29 fix(ingest): harden RepoIngestor GitPython clone surface (#868) (#905)
* fix(ingest): harden RepoIngestor against GitPython URL and option injection

Bump GitPython to >=3.1.58, allowlist clone kwargs, and validate repo URLs
before clone_from to close env-var exfiltration and option-injection paths.

* fix(ingest): accept scp-like SSH remotes in RepoIngestor URL validation

* fix(ingest): resolve repo hostnames to block SSRF via private IPs

* fix(ingest): map malformed repo URL parse errors to ValidationError

* fix(ingest): bound and prune repo host resolve cache

Cap the repository host DNS cache, prune expired entries on access, and evict the oldest entries so long-running processes cannot accumulate unbounded host lookups from user-supplied repo URLs.

* fix(ingest): cap host resolve cache and tighten env-var token checks

Bound the repo host DNS cache with pruning and oldest-entry eviction, and narrow URL env-var blocking to actual $VAR/${VAR} tokens so literal dollar signs are not rejected.

* fix(ingest): preserve repo path compatibility and NAT64 support

* docs(changelog): document RepoIngestor GitPython hardening (#905, closes #868)

Records the clone-surface hardening (GitPython floor, clone-option
allowlist, URL/SSRF validation), the two fixes made during review
(NAT64 false-positive, local-path regression), and a known residual
gap: the SSRF host check doesn't classify RFC 6598 CGNAT space
(100.64.0.0/10) as blocked since ipaddress.is_private doesn't cover it.

---------

Co-authored-by: Pravit Ampapathini <pravitampapathini@wifi-10-43-175-99.wifi.berkeley.edu>
Co-authored-by: Pravit Ampapathini <pravitampapathini@Pravits-MacBook-Air-3.local>
Co-authored-by: Sameer Kadam <sskadam6305@gmail.com>
Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-08-13 22:17:25 +05:30
7c3372c062 fix(explorer): align dev esbuild target (#966)
Co-authored-by: le-czs <243511553+le-czs@users.noreply.github.com>
Co-authored-by: Mohd Kaif <98801504+KaifAhmad1@users.noreply.github.com>
2026-08-13 17:51:54 +05:30
2cfb5de43d feat(export): add opt-in metric_errors column to DistanceExporter (#960)
* feat(export): add opt-in metric_errors column to DistanceExporter

Add a 'metric_errors' field to compute_pairs() output that lets
downstream consumers programmatically distinguish legitimate 'no path'
(None) from computation failures (None + error name).

Usage:
    rows = exporter.compute_pairs(include=[..., 'metric_errors'])
    # row['metric_errors'] == '' → all metrics succeeded
    # row['metric_errors'] == 'hop_count,weighted_distance' → those failed

Design decisions:
- Opt-in: column only appears when explicitly requested via include=
- Default export schema unchanged (backward compatible)
- Comma-separated metric names (not exception messages) — stable for
  programmatic filtering without exposing internal error details
- Helpers now return (value, error_name | None) tuples internally

Follow-up to #879, as discussed in its review thread.

* fix: address Qodo findings — track betweenness errors and remove unused constant

1. _betweenness() now returns (dict, error) tuple like the other helpers,
   so betweenness computation failures appear in metric_errors.
2. Removed unused _ERROR_COLUMNS constant (dead code).

All 77 tests in tests/export/ pass.

* docs(changelog): add entry for opt-in metric_errors column (#960)

---------

Co-authored-by: Mohd Kaif <98801504+KaifAhmad1@users.noreply.github.com>
Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-08-13 15:54:42 +05:30
0fa3483b96 fix(context): clarify get_node_property not-found contract (#877) (#882)
* fix(context): clarify get_node_property not-found contract (#877)

Add default= param to get_node_property and get_node_attributes so
callers can distinguish node-missing from property-missing using a
sentinel. Fix add_node_attribute calling mutation_callback outside
the lock. Tests added for all cases.

* fix(context): address Qodo review findings (#877)

* fix(context): wrap add_node_attribute mutation_callback in try/except (#877)

The PR claimed to move the callback back inside `with self._lock`, but
the diff only dropped a stray blank line -- the call stayed outside the
lock, unchanged. That's actually correct: self._lock is an RLock, and
_add_internal_node/_add_internal_edge deliberately release the lock
before invoking the callback too, so a slow/misbehaving callback never
holds up other threads. The real gap was that, unlike those two
siblings, this call site didn't catch exceptions from the callback.
Wrapped it the same way, with a regression test.

---------

Co-authored-by: Mohd Kaif <98801504+KaifAhmad1@users.noreply.github.com>
Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-08-13 12:40:30 +05:30
18f1d55d77 test(normalize): make optional tests deterministic (#881)
* test(normalize): make optional tests deterministic

Signed-off-by: aoright <102943475+aoright@users.noreply.github.com>

* docs(changelog): add entry for #881 / #860 normalize test determinism fixes

---------

Signed-off-by: aoright <102943475+aoright@users.noreply.github.com>
Co-authored-by: Mohd Kaif <98801504+KaifAhmad1@users.noreply.github.com>
Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-08-13 00:44:10 +05:30
b0080c3602 fix(export): log DistanceExporter metric computation failures instead of swallowing them (#879)
* fix(export): log DistanceExporter metric computation failures instead of swallowing them

The four private metric helpers in DistanceExporter (_betweenness,
_hop_distance, _weighted_distance, _semantic_similarity) each catch a bare
Exception and return None/{} with no signal. That makes an exported None
indistinguishable from a legitimate "no path exists" result, corrupting
downstream CSV/JSONL/DataFrame exports with no way to tell a real gap from a
swallowed error.

Log each caught exception at warning level with the offending source/target
before returning the existing sentinel. The exported row shape and values are
unchanged; only the observability of the failure changes.

Fixes #874

* fix(export): route DistanceExporter warnings through the semantica logger tree

get_logger(__name__) doubled the semantica. prefix (__name__ is already
semantica.export.distance_exporter), so the warnings this PR adds landed on
semantica.semantica.export.distance_exporter, a branch setup_logging() never
configures and does not reach the app's log handler. Also reworded the three
except-Exception log messages: they said "recording as no path", which
overclaims what a generic exception means.

Addresses review feedback from @KaifAhmad1 on #879.

* docs(changelog): add DistanceExporter logging fix entry

---------

Co-authored-by: Mohd Kaif <98801504+KaifAhmad1@users.noreply.github.com>
Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-08-13 00:14:25 +05:30
Shubham Srivastava 1ee3f2f214 fix(kg): align GraphBuilder raw-text extraction defaults with the documented contract (#941)
* fix(kg): align GraphBuilder raw-text extraction defaults with the documented contract

_extract_from_text() defaulted ner_method, relation_method and
triplet_method to "llm" and ran relation extraction unconditionally,
contradicting the build() docstring ("ml"/"pattern"/False) and the
standalone extractor defaults. Any raw-text build() therefore required a
provider, an API key, and network access without saying so.

Defaults are now ml/pattern/pattern with extract_relations=False. LLM
extraction is unchanged and now opt-in via explicit kwargs.

Also documents relation_method and extract_triplets, which the docstring
never listed, and drops the stale "Default to LLM methods as per
requirement" comment.

Closes #930

* perf(kg): reuse extractors across texts instead of rebuilding per source

Addresses review feedback on #941. NERExtractor.__init__ loads its spaCy
model eagerly when the method includes "ml", so switching the default
from "llm" to "ml" made _extract_from_text() reload the model once per
source in a multi-document build.

Extractors are now cached per (kind, method) on the builder. Adds tests
asserting single construction across repeated texts, that distinct
methods still get distinct extractors, and that the default path runs
end to end without any provider call.

* fix(kg): keep fallback method lists working with the extractor cache

The extractor cache keyed directly on `method`, but all three extractors
accept a list for fallback ordering (e.g. ner_method=["pattern", "ml"]),
so a list argument raised TypeError: unhashable type: 'list' before
extraction started. Lists are now converted to tuples for the cache key
only; the extractor still receives the original value.

Also seeds _extraction_stats in __init__. It was previously created only
in build(), so calling _extract_from_text() directly — as the report's
repro does — raised an AttributeError that the broad except swallowed and
logged as "Entity extraction failed".

Adds coverage for list methods on all three extractors, cache reuse for
equal lists, and distinct entries for different orderings.

* fix(kg): forward extracted relations into triplet extraction

_extract_from_text() passed only entities= to extract_triplets(), so
TripletExtractor re-derived relations itself whenever relations is None,
using a method taken from triplet_method rather than relation_method.
That duplicated work and could yield triplets inconsistent with the
relations already extracted.

relations is now initialized to None, holds the extracted list when
extract_relations=True succeeds, and is forwarded to extract_triplets().
When extraction is disabled or fails, None is passed and
TripletExtractor's existing self-derivation is unchanged.

Folded in at maintainer request rather than tracked as #944.

* docs(changelog): note that #878 documented the LLM defaults before this landed

#878 merged while this was in review and resolved the same code/docstring
mismatch in the opposite direction. Records that #930's decision makes
the code the side that changes, and that #878's docstring formatting is
retained.
2026-08-12 23:37:09 +05:30
1a3dd5038a docs(kg): document GraphBuilder public methods (#878)
* docs(kg): document GraphBuilder public methods

* test(kg): skip module-level doctest to fix suite run

* docs(kg): restore GraphBuilder option documentation

* docs(kg): document default values for build() extraction options

extract_relations, extract_triplets, ner_method, relation_method, and
triplet_method all have concrete defaults in _extract_from_text(), but
the build() docstring only stated a default for extract, inconsistent
with CONTRIBUTING.md's docstring convention of noting parameter
defaults.

* docs: add changelog entry for GraphBuilder docstrings (#878, #876)

---------

Co-authored-by: Zohaib Hassnain <109234410+ZohaibHassan16@users.noreply.github.com>
Co-authored-by: Sameer Kadam <sskadam6305@gmail.com>
Co-authored-by: Mohd Kaif <98801504+KaifAhmad1@users.noreply.github.com>
Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-08-12 23:05:58 +05:30
c1154b6ed6 fix(security): header injection, link-prediction DoS, import ID sanitization (#912)
* fix(security): sanitize node_id in Content-Disposition to prevent header injection (CWE-113)

* fix(security): cap link prediction at 10k nodes with semaphore to prevent OOM DoS (CWE-770)

* fix(security): sanitize imported node IDs to prevent stored header injection chain (CWE-20)

* test(security): add self-contained PoC runner with real measured output

* test(security): add regression tests for header injection, DoS cap, import sanitization

* fix(security): comprehensive fix for header injection, DoS, and import ID sanitization

* fix: move semaphore to wrap entire data-load+scoring region, use node-specific edge queries (Qodo #2, #3)

* fix: sanitize edge source/target IDs to match sanitized node IDs (Qodo #4)

* fix: scope 999_999 check to predict_links function via AST (Qodo #1)

* fix: add explicit None guard to _sanitize_import_node_id

* fix(security): close import-sanitizer bypass, enforce link-prediction cap before the expensive scan

Follow-up to the fixes in this PR, found in review:

- export_import.py's "properties" in raw_node fast path stored the id
  verbatim, completely skipping _sanitize_import_node_id() -- a node
  payload of {"id": "<crlf>", "properties": {}} (the shape this app's
  own /api/export produces) bypassed the VULN-3 fix entirely. That
  branch now sanitizes id before storing.

- The link-prediction 10k-node cap checked `total` only after calling
  session.get_nodes()/get_edges(), which normalize the graph's entire
  matching set before applying `limit` -- so the DoS guard ran after
  the expensive work it exists to prevent had already happened, on
  every request regardless of graph size. Added
  GraphSession.get_raw_counts(), an O(1) check against the raw
  len(graph.nodes)/len(graph.edges), and moved the size check ahead of
  the normalizing calls (also added an edge-count cap).

- 5 of the existing regression tests asserted that literal words like
  "Set-Cookie"/"Content-Type" disappear from the sanitized value -- the
  sanitizer strips \r\n\x00"\ , not letters, so those assertions failed
  against this PR's own fix as submitted. Corrected to assert on the
  actual security property (no \r/\n survives), and added end-to-end
  tests that exercise the real /api/import -> /api/provenance/report
  route chain so the properties-key bypass has regression coverage.

Full explorer suite: 241 passed. tests/test_security_regression_pr2.py: 30 passed.

---------

Co-authored-by: Zohaib Hassnain <109234410+ZohaibHassan16@users.noreply.github.com>
Co-authored-by: Mohd Kaif <98801504+KaifAhmad1@users.noreply.github.com>
Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-08-12 21:14:55 +05:30
687a180721 fix(context): take the lock in ContextGraph.to_dict() (#929)
* fix(context): take the lock in ContextGraph.to_dict()

to_dict() iterated self.nodes.values() and self.edges without holding
self._lock, so a concurrent writer raised "RuntimeError: dictionary changed
size during iteration". It was the only reader on the class that did not take
the lock -- stats(), density(), find_nodes(), find_edges(), get_neighbors(),
get_nodes_by_label(), state_at() and save_to_file() all hold it.

Commit 1d1ae398 introduced the RLock and added 26 "with self._lock:" blocks;
to_dict already existed and was not among them. save_to_file is safe only
incidentally -- it holds the lock and builds its payload inline rather than
delegating to to_dict, so it never reaches the unguarded loops.

Beyond the RuntimeError, the unguarded body could also return a torn snapshot:
the statistics block reads len(self.nodes)/len(self.edges) after building the
node and edge lists, so a write landing in between yields counts that
contradict the payload they describe.

self._lock is an RLock, so this composes with the callers that already hold it
(build_from_conversation and build_from_documents both return self.to_dict()
from inside a locked block). Neither external caller -- agent_context's
_capture_checkpoint_state nor triplet_store's knowledge-graph conversion --
defines a lock of its own, so there is no ordering inversion.

Add tests/context/test_context_graph_thread_safety.py: a deterministic check
that to_dict() blocks while another thread holds _lock (no race window
needed), a reentrancy check, and three checks under concurrent writes covering
the RuntimeError, statistics/payload agreement, and duplicate node ids. Four
of the five fail against the unfixed method.

Closes #923

* test(context): make to_dict lock tests deterministic and hang-proof

Wait for the worker thread to actually start before asserting to_dict()
blocks on _lock, and run the reentrancy check in a joined worker so a
non-reentrant lock fails the test instead of hanging CI.

* test(context): assert worker threads actually stopped after timed joins

A join(timeout=...) on a daemon thread returns even if the thread is
still running, so a deadlock would leak a live thread into subsequent
tests instead of failing. Assert not is_alive() after each timed join.

---------

Co-authored-by: Pravit Ampapathini <pravitampapathini@Pravits-MacBook-Air-3.local>
Co-authored-by: Zohaib Hassnain <109234410+ZohaibHassan16@users.noreply.github.com>
2026-08-12 20:02:45 +05:00
bc63e962c9 test(seed): use a real file for CSV loading (#873)
Co-authored-by: nightcityblade <nightcityblade@gmail.com>
Co-authored-by: Sameer Kadam <sskadam6305@gmail.com>
Co-authored-by: Mohd Kaif <kaifahmad087@gmail.com>
2026-08-12 17:43:10 +05:30
9ec7959899 Bump fastapi minimum version to fix PYSEC-2024-38 (starlette DoS) (#871)
* security(deps): bump fastapi floor to >=0.109.1 (PYSEC-2024-38)

The [explorer] extra declared fastapi>=0.100.0, which allows the
vulnerable 0.109.0 (PYSEC-2024-38, HTTP response splitting). Raise the
floor to 0.109.1, the patched release. One-line change, no functional
impact -- the 0.109.x API is stable and backward-compatible.

Fixes #869

* fix(deps): bump fastapi to >=0.109.2 and python-multipart to >=0.0.7 for PYSEC-2024-38

PYSEC-2024-38 (CVE-2024-24762 / GHSA-2jv5-9r88-3w3p) is a ReDoS in
python-multipart < 0.0.7: an attacker sends a crafted Content-Type header
that causes catastrophic backtracking in the multipart regex, stalling the
event loop and causing a DoS on any endpoint that parses form data.

The original PR bumped fastapi to >=0.109.1, but that version pins
starlette<0.36.0,>=0.35.0 and cannot install starlette 0.36.2+ (which
contains the fix via python-multipart>=0.0.7). FastAPI 0.109.2 is the
first version that pins starlette>=0.36.3 (verified against PyPI metadata).

Two changes are necessary:
1. fastapi>=0.109.1 -> fastapi>=0.109.2: ensures starlette>=0.36.3 is
   installed as a transitive dependency, which in turn pulls the fixed
   python-multipart>=0.0.7.
2. python-multipart>=0.0.6 -> python-multipart>=0.0.7: closes the direct
   dependency path. python-multipart is listed explicitly in the explorer
   extra, so without this floor a resolver could still install 0.0.6 and
   leave the vulnerability present even with the fastapi bump.

The fix targets only the 'explorer' optional dependency group, which is
the only code surface where FastAPI and form-data parsing are used.
No functional API changes between 0.109.1 and 0.109.2; 239 Explorer tests
pass without modification.

* ci(security): gate pip-audit on explorer-extra dependency PRs, add changelog entry for PYSEC-2024-38

The Security workflow's pip-audit job ran weekly against a bare Python
env with none of Semantica's optional extras installed, and always
continue-on-error'd -- it would never have flagged the vulnerable
fastapi/python-multipart floors this PR fixes, or the first attempt at
the fix that left python-multipart>=0.0.6 in place. security-scan.yml's
Safety check has the same blind spot (only installs [llm-litellm]).

pip-audit now also runs on pull_request when pyproject.toml changes,
installs semantica[all] so it can actually see extras like [explorer],
and fails the build on findings for that trigger. Scheduled/dispatch
runs stay non-blocking pending a full pass over the [all] tree.

Also documents the fix (#871, closes #869) in CHANGELOG.md, including
the correction made during review after the original fastapi-only bump
turned out not to close the vulnerability.

* fix(deps): raise setuptools floor to >=83.0.0 (CVE-2026-59890), harden audit env

The new pull_request pip-audit gate (previous commit) caught this on its
first run: pip install -e ".[all]" resolved setuptools==79.0.1, vulnerable
to CVE-2026-59890 / GHSA-h35f-9h28-mq5c / PYSEC-2026-3447 (Unicode
normalization lets a MANIFEST.in exclude/prune pattern be bypassed on
macOS APFS/HFS+, leaking excluded files into a built sdist). Fixed in
setuptools 83.0.0.

[build-system] requires had the same too-permissive floor this whole PR
is about (setuptools>=61.0). Raised to >=83.0.0. Also upgrade pip/
setuptools explicitly in the Security workflow before running pip-audit,
since [build-system] requires only governs isolated build environments,
not the ambient one actions/setup-python provisions and pip-audit scans.

---------

Co-authored-by: Sameer Kadam <sskadam6305@gmail.com>
Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-08-12 16:32:06 +05:30
Mohd Kaif 22bf581094 Merge pull request #870 from oiahoon/fix/mcp-server-version
fix(mcp): report package version
2026-08-12 14:15:54 +05:30
KaifAhmad1 6328bfe52d docs(changelog): add entry for MCP server version fix (#870, closes #863) 2026-08-12 14:09:40 +05:30
KaifAhmad1 81bb5f2ed8 fix(mcp): report package version in standalone mcp/ server too
semantica/mcp_server/__init__.py was fixed to stop hardcoding 0.4.0,
but the separate top-level mcp/ package (run via `python -m
mcp.server`, documented in mcp/__init__.py as a supported way to
configure Claude Desktop/Windsurf/etc. from a source checkout) still
hardcoded 0.4.0 in three places: mcp/__init__.py, mcp/server.py, and
mcp/resources/registry.py.

Reuses semantica.__version__ directly, matching the pattern just
adopted in semantica/mcp_server/__init__.py, so both implementations
stay in sync with the package version going forward.
2026-08-12 14:07:47 +05:30
Sameer6305 b8e8b2f227 fix(mcp): use semantica.__version__ as authoritative MCP version source
The previous implementation used importlib.metadata.version('semantica') as
the primary version source with a PackageNotFoundError fallback to
semantica.__version__. This caused two of the three new regression tests to
fail in editable/development installs, where dist-info (egg-info) is written
at install time and is not automatically updated on subsequent version bumps.

In this repo, pyproject.toml declares version as a static field (not dynamic),
and semantica/__init__.py maintains __version__ in sync with it by convention.
semantica.__version__ is therefore the authoritative source of truth and is
always present whenever semantica.mcp_server is importable -- the importlib
.metadata indirection adds no value and can return a stale value.

Changes:
- semantica/mcp_server/__init__.py: replace the importlib.metadata try/except
  block with a direct 'from semantica import __version__ as _SEMANTICA_VERSION'
- tests/test_mcp_server_version.py: rewrite tests to assert both MCP version
  surfaces (SERVER_INFO['version'] and semantica://schema/info) against
  semantica.__version__ as the single ground truth; add 0.4.0 regression
  canaries and a cross-surface consistency assertion; remove the mirrored
  importlib.metadata resolution that masked the staleness problem

The root-level mcp/ directory (a separate unpublished companion implementation
not included in the built package) is intentionally left unchanged -- it is
outside the scope of issue #863 which targets the semantica-mcp entry point.
2026-08-12 13:56:02 +05:30
Sameer Kadam f821fa7e2e Merge branch 'main' into fix/mcp-server-version 2026-08-12 13:01:29 +05:30
Mohd Kaif 229cb69c50 Merge pull request #857 from TaherTadpatri/fix/AttributError_in_filter_by_metadata_on_persistent_backend
Added custom _filter_by_metadata for each memory backend
2026-08-12 12:57:54 +05:30
Sameer Kadam 8ef7c9f760 Merge branch 'main' into fix/mcp-server-version 2026-08-12 12:49:31 +05:30
KaifAhmad1 cab995dc97 fix: address code review findings in backend metadata filtering
- pinecone_store: call self.index.describe_index_stats() instead of the
  nonexistent self.describe_index_stats(), and use a unit query vector
  instead of an all-zero vector so filter_by_metadata() works on
  cosine-metric indexes (the library's own default)
- pgvector_store: apply the existing lowercase true/false bool handling
  to the list-filter branch too, and use the jsonb ?| operator so
  list-valued metadata fields match on intersection instead of being
  compared as a single JSON-text blob
- sqlite_vec_store: use json_each() with a json_type guard so list-valued
  metadata fields match on intersection, mirroring the in-memory
  backend's set-intersection semantics
- faiss_store: filter_by_metadata(limit=0) now returns [] instead of one
  result
- milvus_store: reject NaN/Infinity filter values up front with a clear
  ValidationError instead of building an invalid expression that gets
  silently swallowed
- update the #848 FAISS NotImplementedError test to reflect that FAISS
  now implements real filter_by_metadata() (this PR's whole point)
- add regression tests for each fix; sqlite tests run against the real
  sqlite-vec extension
2026-08-12 12:46:23 +05:30
KaifAhmad1 4d88218221 Merge remote-tracking branch 'origin/main' into fix/AttributError_in_filter_by_metadata_on_persistent_backend
# Conflicts:
#	tests/vector_store/test_vector_store.py
2026-08-12 12:22:00 +05:30
918830a821 fix(pipeline): resolve broken import and missing run() in PipelineWithProvenance (#862)
* fix(pipeline): resolve broken import and missing run() in PipelineWithProvenance

Fix two bugs in pipeline_provenance.py:

1. Wrong import path: `from .pipeline import Pipeline` fails because
   `semantica/pipeline/pipeline.py` does not exist. Pipeline lives in
   `pipeline_builder.py`. Fixed to `from .pipeline_builder import Pipeline`.

2. Pipeline dataclass has no run() method. PipelineWithProvenance.run()
   now delegates to ExecutionEngine.execute_pipeline(), which is the
   intended execution path for built pipelines.

Additional changes:
- Constructor now accepts a built Pipeline instance (breaking the previous
  unusable API that tried to instantiate a dataclass with **config).
- Replace deprecated datetime.utcnow() with datetime.now(timezone.utc).
- Add test suite covering import, instantiation, execution, attribute
  delegation, and provenance graceful degradation.

Fixes #858

* test: address Qodo review findings

- Remove redundant test_import_succeeds (module-level import already
  guards against import regression at collection time).
- Fix test_provenance_disabled_when_import_fails to deterministically
  simulate ImportError via sys.modules patch and assert provenance is
  actually toggled off (runner.provenance is False).

* fix(pipeline): update provenance callers for Pipeline API

---------

Co-authored-by: Sameer Kadam <sskadam6305@gmail.com>
Co-authored-by: Russell Jurney <russell.jurney@gmail.com>
2026-08-11 16:49:54 -07:00
Mohd Kaif 5b319560fb chore: bump version to 0.6.5 (#918)
Security release bundling fixes for GHSA-j4mq (missing auth), GHSA-8c7v
(SSRF via redirect bypass), GHSA-482h (Cypher injection), GHSA-8vgg
(SPARQL injection), GHSA-4643 (WebSocket Origin validation), and a
CodeQL-flagged ReDoS in the SPARQL route validator.
2026-08-11 22:41:19 +05:30
Mohd KaifandSameer Kadam f29c4310a1 security: validate WebSocket Origin against the CORS allowlist (GHSA-4643) (#917)
CORSMiddleware doesn't cover WebSocket handshakes at all (Starlette's CORS
support only wraps HTTP), so under SEMANTICA_ALLOW_ANONYMOUS=true --
the mode docker-compose.dev.yml ships -- is_valid_api_key's anonymous
bypass accepted a /ws/graph-updates connection from any origin. Loopback
binding isn't a boundary against a browser: any page the operator has
open can still reach ws://localhost:8000/ws/graph-updates directly, and
ConnectionManager.broadcast sends every graph_mutation to every
connected socket with no per-connection scoping. Combined with
/api/import accepting multipart/form-data (a CORS-safelisted content
type that skips preflight), a hostile page could write to the graph
over REST and read the result back over the unauthenticated WebSocket
-- demonstrated end-to-end in the report with a real client.

Not affected: any deployment with SEMANTICA_API_KEY configured -- the
handshake already rejects without a valid key in that mode. This is an
anonymous-mode-only, development-configuration exposure.

Fix: check the handshake's Origin header against
app.state.explorer_settings['allowed_origins'], the same list
CORSMiddleware already enforces for HTTP, before the key check. A
missing Origin (native/CLI clients, which never set the header --
only browsers do) is still allowed through, since the browser is the
only threat this closes.

4 new tests in test_explorer_auth.py: hostile Origin rejected under
anonymous mode; hostile Origin rejected even with a correct key
(Origin is checked before the key, so a leaked key alone can't
hijack the socket); an allowlisted Origin still connects under
anonymous mode; a missing Origin still connects under anonymous mode
(native clients keep working). Full explorer suite: 226 passed.

Co-authored-by: Sameer Kadam <sskadam6305@gmail.com>
2026-08-11 22:11:17 +05:30
Mohd Kaif a2886a4e41 Merge pull request #916 from semantica-agi/security/ssrf-dns-pinning-and-object-iri
security: DNS check-then-use pinning for SSRF fetcher, close object-IRI gap
2026-08-11 21:36:42 +05:30
Sameer Kadam a0aa415fc4 Merge branch 'main' into security/ssrf-dns-pinning-and-object-iri 2026-08-11 20:49:18 +05:30
Mohd Kaif ae4f1d4030 Merge pull request #915 from Sameer6305/fix/redos-prefix-decl-regex
fix: resolve ReDoS in _PREFIX_DECL regex (CodeQL py/polynomial-redos #1897)
2026-08-11 19:56:57 +05:30
KaifAhmad1 ea3416ed32 fix: enforce a definitive no-proxy policy for the pinned SSRF fetcher
Qodo's re-review confirmed the multi-IP fallback fix but kept the proxy
finding open: logging-and-falling-back when a proxy applies still let
the DNS-pinning protection be silently skipped under proxy
configuration, rather than enforcing a clear policy either way.

Implemented Qodo's preferred option: proxies are now disabled outright
for this SSRF-sensitive fetcher via session.trust_env = False, so
HTTP_PROXY/HTTPS_PROXY/NO_PROXY env vars are never consulted in the
first place (a configured proxy would perform its own DNS resolution
of the target host outside this process's control, reopening the
DNS check-then-use race pinning exists to close). The adapter also
keeps a fail-closed backstop: if a proxy is somehow still configured
despite trust_env=False (e.g. set explicitly by future code), it now
raises a clear 502 instead of silently connecting through the proxy
unpinned.

_validate_fetch_url's destination classification (blocking private/
internal targets) is unaffected either way — it runs before any of
this and doesn't depend on proxy configuration.

4 new tests: trust_env is disabled on every pinned session; an
HTTP_PROXY env var pointed at an address that would fail if contacted
is confirmed genuinely unused (real local-server fetch still succeeds
directly); and the fail-closed backstop actually raises when a proxy
is forced onto the session. Full explorer + triplet_store suite: 572
passed.
2026-08-11 19:16:29 +05:30
KaifAhmad1 154a7347cd fix: address CI/review findings on DNS pinning (multi-IP fallback, TLS min version)
Four findings from PR #916's automated review, all addressed:

- CodeQL (HIGH): the test HTTPS server's SSLContext allowed TLSv1/TLSv1.1
  by not setting a minimum version. Added
  ssl_ctx.minimum_version = ssl.TLSVersion.TLSv1_2.
- github-code-quality: unused `cryptography` local in
  _make_self_signed_cert — importorskip's return value was never used.
- Qodo (reliability): _validate_fetch_url() only returned the first
  validated IP, and _make_pinned_session() pinned to just that one
  address, so a fetch would fail outright if the first-returned A/AAAA
  record happened to be unreachable even though a later one would work.
  _validate_fetch_url() now returns every validated IP (deduplicated,
  in resolution order); _make_pinned_session() takes the full list and
  falls back through each one via a custom Connection._new_conn
  override, matching the fallback behavior a normal DNS-resolving
  connection would already get for free. Verified with a real test:
  pin to an unreachable loopback address followed by a real one, confirm
  the fetch still succeeds by falling back; and a real test confirming
  it still raises (rather than silently re-resolving the hostname) when
  every pinned address is unreachable.
- Qodo (security): when an HTTP(S) proxy applies, the adapter falls back
  to the unpinned path rather than pinning. This is a real, but
  architecturally unavoidable, limitation from the client side: for a
  forward proxy, the *proxy* performs its own DNS resolution of the
  target host on the application's behalf, a resolution this process
  has no visibility into or control over — there's no client-side pin
  that closes that race. _validate_fetch_url's destination
  classification still fully applies either way; only the secondary
  DNS-pinning hardening doesn't extend through a proxy. Added an info
  log when this fallback path is taken so it's observable rather than
  silent, and expanded the code comment to make the reasoning explicit
  for the next reader/reviewer rather than looking like an oversight.

Tests: 3 new tests in test_ontology_dns_pinning.py (multi-IP fallback
success, all-unreachable failure, deduplicated multi-record resolution).
Full explorer + triplet_store suite: 569 passed.
2026-08-11 19:10:01 +05:30
KaifAhmad1 f2f1d6787d docs(changelog): add PR #916 (DNS pinning + object-IRI gap) entry 2026-08-11 18:57:07 +05:30
KaifAhmad1 646c70ce63 security: DNS check-then-use pinning for SSRF fetcher, close object-IRI gap
Two follow-up hardening items flagged as secondary/deferred during
GHSA-8c7v-62gr-hj6g and GHSA-8vgg-8mr4-r236's fixes:

1. DNS check-then-use (TOCTOU) window in the ontology URL fetcher.
   _validate_fetch_url() resolved and validated a hostname once, but
   _fetch_url_sync() then let requests resolve the same hostname again
   independently at connect time — a low-TTL or rebinding DNS answer
   could differ between the two lookups, reopening the SSRF window the
   validation exists to close.

   _validate_fetch_url() now returns the validated IP, and a new
   _make_pinned_session() builds a per-hop requests.Session whose
   connection pool is pinned directly to that IP (bypassing DNS
   resolution for the connection entirely), while explicitly restoring
   the real hostname as the outgoing HTTP Host header and, for HTTPS,
   the TLS SNI server_hostname/assert_hostname — so the connection
   reaches the validated IP but still presents (and is verified
   against) the real hostname's identity, keeping virtual hosting and
   certificate validation correct.

   Note: an earlier version of this fix set `_dns_host` post-construction
   assuming it was decoupled from `host`, matching some other urllib3
   releases; in the installed version (2.7.0), `host` is a property
   that reads/writes `_dns_host` directly, so that approach silently
   changed the Host header too. Verified with a real (non-mocked) local
   HTTP server, a real local HTTPS server with a self-signed cert
   (proving SNI/cert-hostname verification checks the real hostname,
   not the pinned IP), and a negative control confirming a hostname/cert
   mismatch is still correctly rejected — not silently bypassed.

2. Pre-wrapped object IRIs skipped full validation in
   _format_object_for_sparql/_format_object_for_ntriples (Blazegraph,
   RDF4J). A triplet object already wrapped in `<...>` only had its
   inner content checked for a literal space or `>`, not run through
   sparql_escaping.validate_uri() like the unwrapped-object branch —
   flagged by automated review during GHSA-8vgg-8mr4-r236's fix. Both
   branches now validate identically.

Tests: tests/explorer/test_ontology_dns_pinning.py (6 tests, including
2 real local-server end-to-end checks and 2 real-TLS checks with a
generated self-signed cert, gracefully skipped if `cryptography` isn't
installed); updated tests/explorer/test_ontology_ssrf.py for the new
per-hop session construction; 4 new tests in
tests/triplet_store/test_sparql_injection.py for the object-IRI fix.
Full explorer + triplet_store suite: 566 passed.
2026-08-11 18:52:26 +05:30
Sameer6305 c5981aa306 fix: address qodo review findings on _PREFIX_DECL and query-length guard
Two follow-up fixes to the initial ReDoS patch (CodeQL py/polynomial-redos
#1897), raised during code review:

--- Fix 1: _PREFIX_DECL regression — inline prologues and CRLF (#review-1) ---

The first ReDoS fix replaced the ambiguous trailing \s* with [ \t]*(?:\n|$),
but that introduced a behavioral regression:

  * Inline prologues — PREFIX ex: <...> SELECT ... on a single line were no
    longer stripped because the mandatory (?:\n|$) anchor never matched when
    non-whitespace content followed the IRI on the same line.
  * CRLF line endings — PREFIX ex: <...>\r\n failed because \r is not in
    [ \t]* and the anchor expected a bare \n.

Root cause: the end-of-line anchor was unnecessary; the only thing needed
to eliminate backtracking ambiguity is ensuring the IRI body character class
and the trailing whitespace quantifier are disjoint.

Fix: change the IRI body from <[^>]*> to <[^>\r\n]*>, which:
  - excludes CR and LF from the IRI match (semantically correct — SPARQL
    IRIs cannot span line boundaries)
  - makes [^>\r\n]* and the trailing [ \t]* have zero character overlap,
    eliminating all backtracking ambiguity without any end-of-line anchor

No anchor is used, so both inline prologues and CRLF/LF endings work
naturally. ReDoS payloads (base< + !< x 10,000) still complete in <1 ms.

--- Fix 2: oversized-query length guard obscured error (#review-2) ---

The initial patch placed the _SPARQL_MAX_QUERY_LEN guard inside
_is_read_only_query(), which caused execute_sparql() to return the same
generic 'Only SELECT' error for both genuinely disallowed query types and
oversized inputs. Clients could not distinguish the two rejection reasons.

Fix: move the length check out of _is_read_only_query() and into
execute_sparql() as an explicit early gate, alongside the other resource
limits (_SPARQL_MAX_ROWS, _SPARQL_MAX_GRAPH_NODES). Oversized queries now
return a specific message naming the limit, the received length, and the
remediation step. _is_read_only_query() is documented to be length-agnostic.
_SPARQL_MAX_QUERY_LEN is relocated to the resource-limits block with the
other constants.

--- Tests added ---

tests/test_security_regression.py:
  - test_inline_prefix_before_select_allowed   (Fix 1 regression)
  - test_crlf_line_endings_with_prefix         (Fix 1 regression)
  - test_crlf_multiple_prefixes_then_select    (Fix 1 regression)
  - test_inline_prefix_before_insert_still_blocked (Fix 1 security check)
  - test_long_valid_query_not_rejected_by_is_read_only (Fix 2 separation)

tests/explorer/test_sparql_route.py:
  - test_oversized_query_returns_distinct_length_error (Fix 2 error message)
  - test_oversized_query_never_touches_the_graph       (Fix 2 short-circuit)
  - test_query_exactly_at_length_limit_is_accepted     (Fix 2 boundary)

All 82 tests pass.
2026-08-11 18:37:50 +05:30
Sameer6305 d507fda1b0 fix: resolve ReDoS in _PREFIX_DECL regex (CodeQL #1897)
The _PREFIX_DECL pattern used \s* as a trailing quantifier after
<[^>]*>. On inputs that start with ase< but contain no closing >
(e.g. ase<!<<!<<!<...), the regex engine explores exponentially many
ways to split the match between [^>]* and \s*, causing polynomial
backtracking against user-controlled SPARQL query input.

Fix:
- Replace ^\s* / \s+ / \s* with ^[ \t]* / [ \t]+ / [ \t]*
  so the leading/internal whitespace quantifiers only match horizontal
  whitespace (no overlap with the <[^>]*> IRI part).
- Replace the ambiguous trailing \s* with [ \t]*(?:\n|$), which
  matches only horizontal whitespace followed by a hard line boundary.
  [^>]* and [ \t]* have disjoint character sets, eliminating the
  backtracking ambiguity entirely.
- Add _SPARQL_MAX_QUERY_LEN = 10_000 guard at the top of
  _is_read_only_query as defence-in-depth: rejects oversized input
  before any regex work, bounding worst-case cost even if a future
  pattern change reintroduces ambiguity.

Verified: ReDoS payload ase< + !< x 5000 completes in <1 ms.
Normal PREFIX/BASE stripping and read-only query detection unchanged.

Fixes: CodeQL py/polynomial-redos alert #1897
CWE: CWE-1333, CWE-730, CWE-400
2026-08-11 18:05:29 +05:30
Mohd Kaif 7bf7474ac1 Merge pull request #911 from semantica-agi/security/sparql-injection
security: validate triplet IRIs before SPARQL interpolation (GHSA-8vgg)
2026-08-11 16:41:16 +05:30
KaifAhmad1 546e27cec5 Merge remote-tracking branch 'origin/security/sparql-injection' into security/sparql-injection 2026-08-11 16:30:33 +05:30
KaifAhmad1 a8330874d3 Merge remote-tracking branch 'origin/main' into security/sparql-injection
# Conflicts:
#	CHANGELOG.md
2026-08-11 16:29:39 +05:30
Sameer6305 1c3ac66fd9 fix(rdf4j): preserve literal objects in delete_triplet 2026-08-11 16:27:52 +05:30
Mohd KaifandSameer6305 b846ff88d4 security: sanitize Cypher labels/relationship types/property keys (GHSA-482h) (#910)
* security: sanitize Cypher labels/relationship types/property keys (GHSA-482h-hw99-h62p)

Node labels and property keys passed to create_node/create_relationship
were interpolated directly into Cypher strings in the Neptune, Neo4j, and
FalkorDB graph stores. Property values are parameterized, but labels and
keys can't be bound as parameters, and nothing validated them, so a
document-derived entity type or property name could close the current
Cypher token early and append arbitrary statements (e.g. DETACH DELETE),
running with the application's database credentials.

- New shared semantica/graph_store/query_sanitize.py: sanitize_identifier()
  generalizes age_store.py's existing _sanitize_label/_sanitize_rel_type
  (the only backend that already validated this) into a helper the other
  backends can import without an import cycle with graph_store.py/methods.py.
- Applied at every label/relationship-type/property-key interpolation site
  in amazon_neptune.py, neo4j_store.py, falkordb_store.py, graph_store.py
  (degree_centrality's own query builder), and methods.py
  (update_relationship's own query builder) — create_node, create_nodes,
  create_relationship, get_nodes, get_relationships, get_neighbors,
  shortest_path, update_node, create_index, and all relationship-type
  filters.
- depth/max_depth path-length parameters are also cast to int before
  interpolation as defense-in-depth (they're already typed int, but
  Python doesn't enforce that at runtime).

Added tests/graph_store/test_cypher_injection.py (12 tests covering the
sanitizer directly and reproducing the advisory's injection payload
against Neptune/Neo4j/FalkorDB create_node/create_relationship — asserts
the malicious query is never built or sent), plus regression tests for
graph_store.py's degree_centrality and methods.py's update_relationship.
Full graph_store test suite (224 tests) passes with no regressions.

* fix(graph-store): prevent depth-based Cypher injection

* test(graph-store): tighten injection regression assertions

* docs(changelog): add PR #910 (GHSA-482h Cypher injection) entry

---------

Co-authored-by: Sameer6305 <sskadam6305@gmail.com>
2026-08-11 16:24:28 +05:30
KaifAhmad1 69b79e3d67 docs(changelog): add PR #911 (GHSA-8vgg SPARQL injection) entry 2026-08-11 16:19:06 +05:30
KaifAhmad1 9012492c97 Merge remote-tracking branch 'origin/main' into security/sparql-injection 2026-08-11 16:18:20 +05:30
Mohd Kaif 6ec546b551 Merge pull request #898 from Sunil56224972/security/fix-critical-vulnerabilities
security: fix 4 critical vulnerabilities (RCE, SSRF, XXE, DoS)
2026-08-11 15:50:06 +05:30
KaifAhmad1 6002965c55 docs(changelog): document PR #898's full scope, including the maintainer follow-up fixes 2026-08-11 15:39:11 +05:30
KaifAhmad1 abc10bc8e0 fix(security): restore GHSA-j4mq auth enforcement, fix SPARQL comment-regex bug
Two issues in the last round of commits:

1. explorer/auth.py added a new, opt-in APIKeyAuthMiddleware
   (EXPLORER_API_KEY) and wired it into create_app(), but in doing so
   removed the Depends(require_auth) dependency from every router and
   deleted the /ws/graph-updates handshake check entirely. The new
   middleware also fails OPEN (allows all requests) when its key is
   unset, the opposite of require_auth's fail-closed design. Since
   GHSA-j4mq-hprp-987v (the unauthenticated-Explorer-API advisory) is
   already merged into main via require_auth, this would have reverted
   a merged Critical fix the moment this branch merges. Removed
   explorer/auth.py, restored the per-router dependencies and the
   WebSocket auth check. Kept auth.py's one genuine improvement (adding
   X-API-Key to the CORS allow_headers list) by folding it into the
   existing CORS middleware config.

2. sparql.py's new _is_read_only_query() hardening (comment/PREFIX
   stripping + forbidden-keyword scan) used `#[^\n]*` to strip SPARQL
   comments, but a bare '#' also appears inside standard RDF namespace
   IRIs (e.g. ".../1999/02/22-rdf-syntax-ns#") — the regex struck
   everything after that '#' as a "comment", corrupting the query and
   rejecting any legitimate SELECT using rdf:/rdfs:-style PREFIX
   declarations. Confirmed by the fact the new hardening's own inlined
   test copy failed against two of its own cases. Fixed by only
   treating '#' as a comment-start at line-start or after whitespace,
   which distinguishes ".../ns#" (preceded by a word character) from an
   actual comment (preceded by whitespace/newline in every realistic
   case, including the attacker's own comment-hiding PoC). Also fixed
   the companion PREFIX/BASE regex, which required a prefix-name token
   between the keyword and the IRI even for bare `BASE <...>`
   declarations (which have none).

tests/test_security_regression.py's SPARQL section now imports the real
_is_read_only_query instead of maintaining a parallel inlined copy that
had silently drifted from — and shared the same bug as — the real
implementation; removed its TestAPIKeyAuth class (tested the now-deleted
auth.py) since equivalent, more thorough coverage already exists in
tests/explorer/test_explorer_auth.py. Updated tests/explorer/test_sparql_route.py's
multi-statement-injection test to reflect that the keyword scan now
catches "SELECT ... ; DROP ALL" itself rather than relying on rdflib's
parser, and added a new test confirming the parser still catches
multi-statement syntax that doesn't contain any forbidden keyword.

Full explorer/vector_store/security-regression/age_store suite: 543
passed (the only failures are 6 pre-existing, unrelated Pinecone-client
mocking issues).
2026-08-11 15:36:49 +05:30
Sunil 44f585ffce test(security): add regression tests for all security fixes 2026-08-11 15:17:23 +05:30
Sunil f5332589d5 fix(security): harden SPARQL read-only check against comment/prefix bypass 2026-08-11 15:17:21 +05:30
Sunil 9a21ca9834 fix(security): prevent Cypher injection via graph_name and dollar-delimiter breakout 2026-08-11 15:17:19 +05:30
Sunil a169cf3fb9 feat(security): wire API key auth middleware into Explorer app 2026-08-11 15:17:17 +05:30
Sunil 656baa7aee feat(security): add opt-in API key auth middleware for Explorer API 2026-08-11 15:17:15 +05:30
KaifAhmad1 e1725fd763 fix(ontology): close the final (non-redirect) response in _fetch_url_sync
The previous rework of the redirect loop closed the response on each
redirect hop but dropped the try/finally around the success path, so the
terminal response (the one actually read and returned) was left
unclosed, leaking the connection back to the pool unclosed under load.
2026-08-11 15:11:07 +05:30
Zohaib Hassnain 1f053e005c fix object injection and test flakiness 2026-08-11 14:40:49 +05:00
Mohd Kaif 7ed1d49625 Merge branch 'main' into security/fix-critical-vulnerabilities 2026-08-11 15:05:45 +05:30
Sunil c94be3f9a6 fix(ontology): resolve relative redirects with urljoin, close resp on redirect 2026-08-11 15:01:34 +05:30
Sunil 142707db93 fix(sparql): wrap graph cap ValueError in SparqlResponse instead of 500 2026-08-11 15:01:31 +05:30
Sunil 3357c14ee3 fix(vector_store): use v.tolist() for numpy array serialization 2026-08-11 15:01:29 +05:30
KaifAhmad1 9ecae47a8a security: validate triplet IRIs before SPARQL interpolation (GHSA-8vgg-8mr4-r236)
Triplet.subject and Triplet.predicate (and, in some builders, .object)
were interpolated directly into SPARQL update/query strings in the
Blazegraph and RDF4J stores, and into a SELECT filter in the Jena store.
A subject containing '>' closes the '<...>' IRI token early, so the rest
of the value is parsed as more SPARQL. Entity names are document text in
the normal ingest pipeline, so anyone whose content gets processed could
append operations like CLEAR ALL, running with the application's store
credentials.

Applied the existing sparql_escaping.validate_uri (already used by
anzo_store.py, the one backend that was already hardened) at every
subject/predicate/object interpolation site:

- blazegraph_store.py: _build_insert_data, _triplets_to_rdf (unreachable
  dead code today but same fix applied for consistency/future-proofing),
  bulk_load's graph option, get_triplets's filter, delete_triplet.
- rdf4j_store.py: _triplets_to_ntriples, get_triplets's filter,
  delete_triplet. (add_triplets's graph option was already validated.)
- jena_store.py: get_triplets's filter — the only vulnerable site;
  add_triplets/delete_triplet already use rdflib's native Python API
  (Graph.add/.remove with URIRef) rather than building query strings, so
  they were never exploitable this way.

Added tests/triplet_store/test_sparql_injection.py (12 tests) reproducing
the advisory's own injection payload against all three backends' write
and read paths, asserting the malicious query is never built or sent.
Full triplet_store suite (330 tests) passes with no regressions.

Note: while adding read-path test coverage, found that jena_store.py's
get_triplets() WHERE-clause filter syntax is malformed SPARQL (missing a
FILTER()/separator before the equality conditions) — a pre-existing
correctness bug unrelated to this fix, worth a separate follow-up.
2026-08-11 14:58:40 +05:30
Mohd KaifandZohaib Hassnain 3496d62335 security: require API-key auth on all Explorer API routes (GHSA-j4mq) (#909)
* security: require API-key auth on all Explorer API routes (GHSA-j4mq-hprp-987v)

Every Explorer route (bulk import/export, delete, LLM-backed ontology
generation, SPARQL, etc.) was mounted with no authentication, and both
server entrypoints bind 0.0.0.0 by default. Anyone reaching the port got
full read/write/delete on the graph.

- Add require_auth dependency (explorer/dependencies.py): checks
  X-API-Key against SEMANTICA_API_KEY, fails closed with 503 if
  unconfigured (not silently anonymous), 401 on wrong/missing key.
  SEMANTICA_ALLOW_ANONYMOUS=true opts out explicitly for local dev.
- Wire dependencies=[Depends(require_auth)] into all 11 API routers in
  both explorer/app.py and server.py. /health, /api/info, static assets,
  and the SPA catch-all stay public.
- /ws/graph-updates handshake now checks the same key via header or
  ?api_key= query param (browsers can't set custom WS headers) before
  accepting the connection.
- Default bind changed from 0.0.0.0 to 127.0.0.1 in server.py's main()
  and cli.py's `server start`; the CLI warns if a non-loopback host is
  passed explicitly without a key configured.
- Startup logging reports the resolved auth mode in both app factories.
- Document/generate SEMANTICA_API_KEY in the deploy recipes that expose
  a public endpoint by default: docker-compose, Railway, Fly, Render.

Added tests/explorer/test_explorer_auth.py covering fail-closed default,
wrong/missing/correct key, anonymous opt-in, public-route exemptions, and
the WS handshake. Added tests/explorer/conftest.py defaulting the
pre-existing ~200 explorer tests to SEMANTICA_ALLOW_ANONYMOUS=true so
they keep exercising route logic without needing a key.

* fix CORS

---------

Co-authored-by: Zohaib Hassnain <109234410+ZohaibHassan16@users.noreply.github.com>
2026-08-11 14:05:00 +05:00
pravit-ampandPravit Ampapathini 64f6c5cba2 test(split): cover untested chunker classes (#864) (#904)
* test(split): add coverage for untested chunker classes

* test(split): address Qodo gaps for chunker coverage

Cover exported KG/structural/sliding-window helpers, assert heading
boundaries, and use importorskip instead of mocking optional deps.

* fix(split): normalize sliding-window stride when omitted

* fix(split): pass entities to relation extraction and harden graph-based tests

---------

Co-authored-by: Pravit Ampapathini
2026-08-11 14:00:40 +05:00
pravit-ampandPravit Ampapathini ab5c12f9af fix(ingest): SSRF protection for Web and API ingestors (#867) (#906)
* fix(ingest): add SSRF protection for WebIngestor and RESTIngestor

Block non-http(s) schemes and private/loopback/link-local targets before
outbound requests, with allow_private_ips opt-in for trusted deployments.

* fix(ingest): fail closed on SSRF DNS resolution errors

* fix(ingest): validate SSRF targets on every HTTP redirect hop

* fix(ingest): avoid blocking on SSRF DNS executor shutdown

* fix(ingest): parse allow_private_ips without truthy-string pitfalls

* docs(ingest): clarify robots.txt SSRF/validation comment

---------

Co-authored-by: Pravit Ampapathini
2026-08-11 13:52:15 +05:00
Mohd Kaif fc9af2ebf8 Merge branch 'main' into security/fix-critical-vulnerabilities 2026-08-11 14:04:08 +05:30
KaifAhmad1 3e9ba1b7fb fix: address Qodo review findings on security PR (numpy/JSON, relative redirects, SPARQL 500)
- vector_store.save(): use v.tolist() instead of list(v) so numpy float32
  vectors round-trip through JSON instead of raising TypeError.
- ontology._fetch_url_sync(): resolve relative Location headers via urljoin
  before re-validating (previously any relative redirect was rejected
  outright), and close every response instead of leaking the connection
  across redirect hops.
- sparql.execute_sparql(): move _build_rdflib_graph inside the handler's
  error handling so the graph-size cap returns a clean SparqlResponse
  error instead of an unhandled 500.
- add regression tests for all three.
2026-08-11 14:01:07 +05:30
pravit-ampandPravit Ampapathini 51cf97765d test(deduplication): cover ClusterBuilder, MergeStrategyManager, and batch paths (#907)
* test(deduplication): cover ClusterBuilder, MergeStrategyManager, and batch paths
Add focused coverage for union-find clustering, property merge rules,
merge_duplicates, embedding similarity, and incremental detection (#866).

* test(deduplication): assert unrelated clusters without conditional skip
Make cluster-separation coverage fail closed by using mocked pairs and
unconditional assertions for distinct Apple vs Microsoft cluster IDs.

* test(deduplication): tighten update_clusters attachment assertions
Require the incremental path to place the new near-duplicate in the
same rebuilt cluster instead of accepting a vacuous cluster-count check.

* test(deduplication): strengthen incremental detect_duplicates wrapper checks
Assert real DuplicateCandidate matches, score threshold, and new×existing
routing instead of only checking that the wrapper returns a list.

* test(deduplication): verify metadata provenance behavior

Assert that preserve_provenance writes metadata.provenance fields and add a disabled-path test so regressions do not pass through merge_entities metadata alone.

---------

Co-authored-by: Pravit Ampapathini <pravitampapathini@users.noreply.github.com>
2026-08-11 12:52:15 +05:00
Sunil 8fa2037619 fix: re-push vector_store.py with correct UTF-8 encoding 2026-08-10 22:28:48 +05:30
Sunil 5573ab7a9f fix: re-push ontology.py with correct UTF-8 encoding 2026-08-10 22:28:27 +05:30
Sunil 26f236923c fix: re-push pyproject.toml with correct UTF-8 encoding 2026-08-10 22:28:06 +05:30
Sunil c35899711d fix: re-push sparql.py with correct UTF-8 encoding 2026-08-10 22:27:45 +05:30
Sunil 0a113b9702 fix: make defusedxml required, fail closed if missing (reviewer feedback) 2026-08-10 22:26:49 +05:30
Sunil 2de6ff898a Merge branch 'main' into security/fix-critical-vulnerabilities 2026-08-10 22:01:52 +05:30
Sunil 22ea189d0b security: replace unsafe pickle with JSON in vector store (CWE-502) 2026-08-10 21:53:11 +05:30
Sunil 30d5fef180 security: fix SSRF via redirect bypass in ontology URL fetcher (CWE-918) 2026-08-10 21:52:47 +05:30
Sunil c85df419ae security: add defusedxml to explorer dependencies for XXE protection 2026-08-10 21:52:03 +05:30
Sunil 55f3ee6f84 security: fix SPARQL DoS via unbounded graph materialization (CWE-770) 2026-08-10 21:51:56 +05:30
Sunil 924765b042 security: fix XXE vulnerability in RDF/XML parser (CWE-611) 2026-08-10 21:51:27 +05:30
Sameer Kadam bde6e2d68e Merge branch 'main' into fix/mcp-server-version 2026-08-10 21:38:16 +05:30
Mohd Kaif 6f310d1d7a docs: link CONTRIBUTING.md issue workflow from PR template (#896)
Surfaces the comment-before-you-PR workflow from CONTRIBUTING.md
directly on the PR creation page to reduce duplicate PRs on the
same issue.
2026-08-10 21:18:01 +05:30
Sameer Kadam 01bd908f86 Merge branch 'main' into fix/mcp-server-version 2026-08-10 20:50:49 +05:30
Sameer Kadam bd35d6031b docs: clarify contributor issue workflow (#895) 2026-08-10 19:45:54 +05:30
Joey@macstudio 00f4e79d3e fix(mcp): report package version 2026-08-10 20:29:19 +08:00
Sameer Kadam 7654d8c6c7 Merge branch 'main' into fix/AttributError_in_filter_by_metadata_on_persistent_backend 2026-08-10 17:33:48 +05:30
Sameer6305 70109133b5 fix(vector-store): harden metadata filtering across backends 2026-08-10 17:26:39 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 53caefaf58 ci(deps): bump actions/attest-build-provenance (#880)
Bumps the github-actions group with 1 update: [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance).


Updates `actions/attest-build-provenance` from 4.1.1 to 4.2.2
- [Release notes](https://github.com/actions/attest-build-provenance/releases)
- [Changelog](https://github.com/actions/attest-build-provenance/blob/main/RELEASE.md)
- [Commits](https://github.com/actions/attest-build-provenance/compare/0f67c3f4856b2e3261c31976d6725780e5e4c373...4d101475d8b20a2381f78447822ac1eab6504dd8)

---
updated-dependencies:
- dependency-name: actions/attest-build-provenance
  dependency-version: 4.2.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-10 16:18:08 +05:30
TaherTadpatri 7ce05a3848 Merge remote-tracking branch 'origin/fix/AttributError_in_filter_by_metadata_on_persistent_backend' into fix/AttributError_in_filter_by_metadata_on_persistent_backend 2026-08-10 12:56:08 +05:30
TaherTadpatri 21f5f3d9b3 Merge remote-tracking branch 'upstream/main' into fix/AttributError_in_filter_by_metadata_on_persistent_backend
# Conflicts:
#	semantica/vector_store/vector_store.py
2026-08-10 12:55:04 +05:30
Mohd Kaif fc2083aa17 Merge pull request #854 from Sameer6305/fix/848-decision-context-persistent-backends
fix(vector_store): stop bypassing backend abstraction in build_decision_context/explain_decision/_filter_by_metadata (closes #848)
2026-08-10 11:32:25 +05:30
Mohd Kaif 1258edfe7f Merge branch 'main' into fix/848-decision-context-persistent-backends 2026-08-10 11:14:15 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 5048665d35 chore(deps): bump dompurify from 3.4.12 to 3.4.13 in /explorer (#872)
Bumps [dompurify](https://github.com/cure53/DOMPurify) from 3.4.12 to 3.4.13.
- [Release notes](https://github.com/cure53/DOMPurify/releases)
- [Commits](https://github.com/cure53/DOMPurify/compare/3.4.12...3.4.13)

---
updated-dependencies:
- dependency-name: dompurify
  dependency-version: 3.4.13
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-09 21:31:11 +05:30
Mohd Kaif 7dce9f1b69 Merge pull request #853 from Sameer6305/fix/845-standardize-search-vectors-output-schema
fix(vector-store): standardize search_vectors() output schema across backend implementations (#845)
2026-08-09 17:37:22 +05:30
Mohd Kaif 1b09f1ca5b Merge branch 'main' into fix/845-standardize-search-vectors-output-schema 2026-08-09 17:29:06 +05:30
KaifAhmad1 03ed4b94e9 fix(vector-store): preserve ranking for unbounded scores in Pinecone/Qdrant
The 1.0 / (1.0 + max(0.0, 1.0 - score)) normalization added in the last
commit clamped every raw score >= 1.0 to an identical 1.0, collapsing
result ranking for dot-product-metric indexes (unbounded), which cosine
(bounded to [-1, 1]) never exercised. Replaced with x/(1+|x|) rescaled
to (0, 1), which is strictly monotonic for any real score.

Also adds regression tests for scores >= 1 and a CHANGELOG entry.
2026-08-09 17:28:05 +05:30
SaurabhandKaifAhmad1 9059a44731 fix(vector-store): reconstruct FAISS vectors (#850)
* fix(vector-store): reconstruct FAISS vectors

* fix(vector-store): surface FAISS reconstruction failures

---------

Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-08-09 16:42:24 +05:30
Taher Tadpatri 772d22448a Merge branch 'main' into fix/AttributError_in_filter_by_metadata_on_persistent_backend 2026-08-09 14:56:59 +05:30
TaherTadpatri b6497ace41 fixed/weavit_store,pinecone_store,milvus_store 2026-08-09 14:50:59 +05:30
TaherTadpatri b094268525 Added custom _filter_by_metadata for each memory backend 2026-08-08 23:15:00 +05:30
Mohd Kaif e90bd048e1 Add Trendshift badge to README
Added Trendshift badge to README for repository tracking.
2026-08-08 21:37:59 +05:30
Sameer6305 8e0419c864 fixed qodo review
Adds similarity_unavailable marker and warning logs to build_decision_context and explain_decision when a persistent backend (like FAISS) fails to reconstruct a vector. Updates docstrings to explicitly state this degraded-path behavior and guarantees schema stability. Adds regression tests to test vector retrieval failure behavior via caplog and context assertions.
2026-08-08 13:16:50 +05:30
Sameer6305 0d51608547 fix(vector_store): stop bypassing backend abstraction in build_decision_context/explain_decision/_filter_by_metadata (closes #848)
- build_decision_context() and explain_decision(include_paths=True) both
  accessed self.vectors directly, which is only initialized for the
  inmemory backend, crashing with AttributeError on any persistent
  backend (FAISS, Qdrant, Pinecone, etc.). Replaced with self.get_vector()
  (#843's backend-agnostic accessor) + an is-not-None check — a verified
  1:1 behavioral equivalent for the old 'decision_id in self.vectors'
  guard on the inmemory path.
- Found a third, undocumented instance of the same bug during
  verification: _filter_by_metadata() also accessed self.metadata/
  self.vectors directly. Initial fix silently returned [] for persistent
  backends, which was itself a new silent-failure bug (indistinguishable
  from a genuine zero-match result). Reconciled to raise
  NotImplementedError instead, matching the established precedent from
  get_vector()/get_metadata() (#843) for 'backend exists but doesn't
  support this operation' — confirmed via full grep of all 7 backend
  wrapper classes that none currently implement filter_by_metadata,
  so this path was previously dead-code-masked-as-working.

Tests: 14 new tests across two rounds — inmemory behavioral equivalence,
real (non-mocked) FAISS backend regression tests for all three methods,
and explicit coverage proving the NotImplementedError fires with a clear
message rather than the old silent-[] behavior. Full suite: 53 passed,
0 failed, 0 regressions across the 39 pre-existing tests.
2026-08-08 12:57:42 +05:30
Mohd Kaif aa7b7fe525 Merge pull request #847 from Sameer6305/fix/843-vectorstore-persistent-backend-accessors
fix(vector-store): fix get_vector/get_metadata crash on persistent backends (#843)
2026-08-08 11:57:04 +05:30
KaifAhmad1 916d3974e3 fix(vector-store): guard save()/load() indexer access for persistent backends
self.indexer is only set for backend="inmemory", so save()/load() still
raised AttributeError for persistent backends (faiss, qdrant, etc.) even
after this PR's getattr() guards on self.vectors/self.metadata, since the
unguarded `self.indexer` access happened first. Guard it the same way and
delegate to the backend store's native save_index/load_index (currently
only FAISSStore implements these) so persistent-backend saves actually
persist instead of silently no-oping.
2026-08-08 11:50:09 +05:30
Sameer6305 f75469f472 Merge remote-tracking branch 'semantica-agi/main' into fix/843-vectorstore-persistent-backend-accessors 2026-08-07 22:23:39 +05:30
Sameer6305 40b81d0582 fixed qodo reviews: standardize search results schema, score metric, and ID types
- Removed total=False from SearchResult TypedDict so all fields are strictly required

- Ensured distance: None is returned from backends that don't natively expose distance (Qdrant, Pinecone, SQLite, pgvector, in-memory)

- Standardized search result score to a consistent 0.0 - 1.0 similarity metric scale across all backend adapters

- Relaxed SearchResult id type to Union[str, int] to accommodate native integer IDs from Milvus and Qdrant without casting

- Updated schema verification tests
2026-08-07 21:34:43 +05:30
Sameer6305 5db0adc18a fix(vector-store): standardize search_vectors output schema 2026-08-07 19:39:14 +05:30
Mohd Kaif 50758f6f25 Merge pull request #846 from SaurabhScripts/codex/fix-markdown-import-path-errors
fix(context): preserve Markdown import path errors
2026-08-07 16:49:24 +05:30
Saurabh 2756916573 Merge branch 'main' into codex/fix-markdown-import-path-errors 2026-08-07 16:17:17 +05:30
Mohd Kaif f47c730f7e Merge pull request #842 from Sameer6305/fix/839-decisionembeddingpipeline-backend-support
Fix #839: Support persistent backends in DecisionEmbeddingPipeline
2026-08-07 16:05:25 +05:30
KaifAhmad1 721a2f0e9c Fix candidate-embeddings loop dropping matches when pool exhausted
_get_candidate_embeddings()'s expand-and-retry loop widens the search
pool (up to limit*10) when post-filtering leaves too few candidates.
If the backend keeps returning a full page and filtered matches never
reach `limit`, the loop exited via the while condition instead of the
break branch, so the pre-loop empty embeddings/metadata/scores lists
were returned instead of the matches actually found in the final
iteration. This silently returned [] for filtered queries against
large persistent-backend stores even when matches existed - exactly
the scenario this PR adds support for.

Falls back to the last collected batch instead of discarding it.
Also documents this PR and #839 in the changelog.
2026-08-07 15:53:55 +05:30
Sameer6305 dd42b7fa95 fix(milvus): add backward compatibility alias and sanitize query
- Added insert_vectors alias to add_vectors for backward compatibility.
- Sanitized vector_id in get_vector and get_metadata to prevent query injection.
2026-08-07 12:11:48 +05:30
Sameer6305 248d028b09 fixed qodo reviews
- FAISSStore: get_metadata now correctly retrieves from self.metadata instead of raising NotImplementedError.
- MilvusStore:
  - Changed schema to support String IDs (VARCHAR) instead of auto-generated INT64, preventing loss of IDs during insert.
  - Added metadata storage using JSON.
  - Replaced insert_vectors with add_vectors accepting ids and metadata (added insert_vectors alias for backward compatibility).
  - Implemented get_vector and get_metadata with safe parameterized querying to prevent query injection.
- PgVectorStore & SQLiteVecStore:
  - Fixed get_vector and get_metadata to call self.get([vector_id]) instead of the non-existent get_vectors([vector_id]), fixing the silent None return bug.
2026-08-07 12:04:47 +05:30
Saurabh Meena 77a2ab7b18 fix(context): retain path inspection diagnostics 2026-08-07 11:27:52 +05:30
Sameer6305 c8b59b47f5 fix(vector-store): fix get_vector/get_metadata crash on persistent backends (#843)
- VectorStore.get_vector() and get_metadata() were hardcoded to access
  self.vectors and self.metadata dicts, which are only initialized for
  the inmemory backend, causing AttributeError on all persistent backends
  (FAISS, Qdrant, Pinecone, Milvus, Weaviate, PgVector, SQLiteVec).

Changes:
- Refactor VectorStore.get_vector() and get_metadata() to branch on
  self.backend == 'inmemory' (zero behavior change) and delegate to
  self._backend_store otherwise.
- Harden save() to use getattr(self, 'vectors', {}) / getattr(self,
  'metadata', {}) to prevent crash when saving a persistent backend store.
- Add get_vector() and get_metadata() to all 7 backend wrappers:
  - FAISSStore: get_vector uses index.reconstruct(); get_metadata raises
    NotImplementedError (FAISS has no metadata storage natively).
  - QdrantStore: uses client.retrieve() with with_vectors/with_payload.
  - PineconeStore: wraps existing fetch_vectors() call.
  - MilvusStore: raises NotImplementedError (auto_id=True schema discards
    string IDs at insert time, making by-ID lookup impossible in this
    wrapper's current schema).
  - WeaviateStore: uses collection.query.fetch_object_by_id().
  - PgVectorStore: wraps existing get_vectors() SQL method.
  - SQLiteVecStore: wraps existing get_vectors() SQL method.
- Add TestVectorStoreRetrieval regression tests covering inmemory and
  FAISS backends with real (non-mocked) assertions.

All 28 tests pass.
2026-08-07 11:21:42 +05:30
Saurabh Meena c0b6a80480 fix(context): preserve Markdown path errors 2026-08-07 01:15:55 +05:30
Sameer Kadam 36071819b5 Merge branch 'main' into fix/839-decisionembeddingpipeline-backend-support 2026-08-06 20:15:05 +05:30
Sameer6305 a4dac2342b fixed qodo reviews 2026-08-06 19:44:23 +05:30
Sameer6305 7d272f40e8 Fix #839: Support persistent backends in DecisionEmbeddingPipeline
- Replace direct .vectors and .metadata access with VectorStore.search_vectors().
- Add a fallback in HybridSimilarityCalculator (via ind_similar_decisions) to use the search score when backend vector databases do not natively return the raw vector array.
- Fix get_decision_statistics to gracefully fall back when .metadata is not fully supported by the underlying DB.
- Add regression tests utilizing the real FAISS and inmemory backends directly without mocking.
2026-08-06 19:11:35 +05:30
Mohd Kaif 3e5d2672ad Merge pull request #841 from divyankshah/fix/gh-840-qdrant-metadata-key
fix(vector_store): normalize QdrantStore.search_vectors() to return "metadata"
2026-08-06 19:10:42 +05:30
KaifAhmad1 b4b10a4928 docs(changelog): document Qdrant metadata key normalization
Adds an Unreleased/Fixed entry for #841 (closes #840) — QdrantStore
search results were keyed "payload" instead of "metadata", breaking
HybridSearch.filter_by_metadata() for Qdrant results.
2026-08-06 18:53:40 +05:30
Mohd Kaif 48c58a0753 Merge branch 'main' into fix/gh-840-qdrant-metadata-key 2026-08-06 17:26:02 +05:30
Mohd Kaif 6b143ef401 Merge pull request #838 from Linxiushen/feat/embedded-triplet-store
feat(triplet-store): add embedded Oxigraph backend
2026-08-06 16:33:13 +05:30
Mohd Kaif 49f458e927 Merge branch 'main' into feat/embedded-triplet-store 2026-08-06 16:22:56 +05:30
KaifAhmad1 c77184bd77 docs(changelog): document embedded Oxigraph backend and ImportError fix
Adds an Unreleased/Added entry for #838 (closes #834), including the
follow-up fix that preserves ImportError for a missing pyoxigraph
install instead of masking it as a generic ProcessingError.
2026-08-06 16:15:47 +05:30
Mohd Kaif fa77f5cc47 Merge pull request #836 from Sameer6305/fix/830-temporal-panel-render-loop
fix(explorer): resolve infinite render loop preventing Temporal panel from rendering (#830)
2026-08-06 13:24:17 +05:30
KaifAhmad1 7bddee0111 ci: update stale github/codeql-action v4 pin
Upstream moved the v4 tag to 5595ccaf912efad79be6eef63a5619ff05969be3
(v4.37.6), which the repo's own verify-action-pins.sh now (correctly)
flags as a mismatch against the previously-pinned commit. Pre-existing
drift unrelated to #830/#836, but it was failing this PR's required
"verify" check, so fixing it here.
2026-08-06 13:10:13 +05:30
KaifAhmad1 5cd4407e57 fix(explorer): review follow-ups for #830 render-loop fix
- Wire the Explorer frontend's node --test suites (test:graph-store,
  test:graph-workspace, and the new test:plugin-registry regression
  test) into CI. Previously only `npm run build` ran, so none of the
  frontend tests -- including this fix's own regression coverage --
  executed anywhere except a contributor's local machine.
- Broaden the diagnostics dedup's structureLayer comparison to also
  cover disabledReason/curveCount/bridgeCurveCount/backboneCurveCount,
  not just cacheKey/lastDrawAt/enabled, so a disabledReason-only
  transition doesn't leave the dev diagnostics panel stale.
2026-08-06 13:03:56 +05:30
KaifAhmad1 1850cdd617 Merge remote-tracking branch 'origin/main' into fix-830-followup
# Conflicts:
#	CHANGELOG.md
2026-08-06 13:03:28 +05:30
Mohd Kaif 5f00c00be3 Merge pull request #837 from semantica-agi/fix/833-hybridsearch-attributeerror-non-inmemory-backends
fix: HybridSearch.search() crashes with AttributeError on non-inmemor…
2026-08-06 12:06:18 +05:30
Mohd Kaif dee55112ef Merge branch 'main' into fix/833-hybridsearch-attributeerror-non-inmemory-backends 2026-08-06 11:59:30 +05:30
shah b7ac05b6f2 fix(vector_store): normalize QdrantStore.search_vectors() to return "metadata"
QdrantStore.search_vectors() returned results keyed by "payload" while
HybridSearch and PineconeStore both expect/return "metadata". This silently
dropped metadata from Qdrant results and caused HybridSearch.filter_by_metadata
to reject every candidate when a filter was applied (empty result sets).

Fixes #840
2026-08-06 02:33:50 +02:00
Mohd Kaif d9118410bc Merge pull request #829 from Sameer6305/feat/793-temporal-diff-ui
feat(explorer): add temporal diff comparison UI to the Temporal panel (#793)
2026-08-05 21:30:30 +05:30
Mohd Kaif d16db085d8 Merge branch 'main' into feat/793-temporal-diff-ui 2026-08-05 21:23:59 +05:30
Sameer6305 cb716cec61 Merge semantica-agi/main into fix/833-hybridsearch-attributeerror-non-inmemory-backends 2026-08-05 21:12:11 +05:30
Sameer6305 712a6e6d4c test(hybrid_search): add backend delegation regression coverage 2026-08-05 20:31:59 +05:30
林SO b52cdd5bbe fix(triplet-store): preserve missing backend dependency errors 2026-08-05 22:36:19 +08:00
Mohd KaifandSameer6305 d0e018a1c9 fix(vector_store): stop dropping metadata for add_vectors-only backends (#835)
* fix(vector_store): stop dropping metadata for add_vectors-only backends

VectorStore.store_vectors() previously discarded the metadata argument
whenever the backend only exposed add_vectors() (e.g. FAISSStore), even
though add_vectors() supports it. Now metadata is forwarded, and is only
passed when the backend's add_vectors() signature actually accepts it
(checked via inspect.signature), avoiding a TypeError for stricter
backend signatures.

Fixes #832

* fix(vector_store): guard signature introspection in store_vectors

inspect.signature() can raise ValueError/TypeError for some callables
(e.g. certain C-implemented or dynamically built methods). Wrap the
add_vectors() signature probe in try/except, consistent with the same
pattern already used in ProvenanceManager.trace_lineage(), defaulting
to attempting to pass metadata when introspection fails.

* docs(changelog): document VectorStore metadata-drop fix (#832, #835)

* test(vector_store): add regression coverage for metadata forwarding

---------

Co-authored-by: Sameer6305 <sskadam6305@gmail.com>
2026-08-05 19:59:53 +05:30
Sameer6305 bd8d6c5913 docs: add #830 Explorer Temporal panel fix to CHANGELOG.md 2026-08-05 18:06:45 +05:30
Sameer6305 667e69a0c1 refactor: tighten comments across #830 changes for clarity
- pluginRegistryPredicates.ts: consolidate 8-line JSDoc to 5 lines,
  removing redundant detail that restated implementation mechanics
  already obvious from the code.

- GraphWorkspace.tsx: shorten the lastScrubberMsRef comment from 5 lines
  to 2; trim the handleDiagnosticsChange block comment by removing the
  'rather than bailing out' implementation-alternative sentence; tighten
  the distanceVisual inline comment.

- pluginRegistry.temporal.test.mjs: replace 17-line file-level JSDoc
  with 9 lines focused on the invariant rather than the root-cause
  narrative (already covered in pluginRegistryPredicates.ts); remove
  two tsx loader implementation-detail comments; tighten two test-level
  inline comments.

No logic, types, or test assertions changed. All 42 tests pass.
2026-08-05 17:52:21 +05:30
KaifAhmad1 9c7dd16126 docs: add CHANGELOG entry for HybridSearch AttributeError fix (#833, #837) 2026-08-05 17:45:13 +05:30
KaifAhmad1 94adcf7ad3 fix: address code review findings on backend-delegated search path
- Legacy top_k kwarg was read but not removed from options, so it got
  forwarded via **options into VectorStore.search_vectors(), colliding
  with backends that call search(..., top_k=k, **options) (e.g. sqlite,
  pgvector) and raising "got multiple values for keyword argument
  'top_k'". Now popped instead of just read.
- VectorStore.search_vectors()'s dispatch only recognized backend
  methods named search/search_similar, so HybridSearch's delegation
  still hit NotImplementedError for qdrant/milvus/pinecone, which name
  their method search_vectors() with a differently-named count
  parameter (limit vs k). Added a third dispatch branch that binds the
  count positionally so it works regardless of the backend's parameter
  name.
- Backend-delegated results defaulted a missing "distance" to the raw
  score, silently reusing the local path's cosine-similarity convention
  (distance = 1 - score) even for backends using unrelated metrics
  (L2, inner product). A missing distance is now left as None instead
  of a fabricated, metric-inconsistent value.
2026-08-05 17:41:31 +05:30
Sameer6305 80de3652cf fixed qodo findings
Two issues addressed:

1. Plugin-loading useEffect unnecessarily depended on temporalState.
   After the #830 fix, no shouldLoad predicate reads temporalState, but
   the effect's dep array still included it, causing extra re-runs on
   every scrubber update. Removed temporalState from the dep array and
   the shouldLoad call site. Made temporalState optional in the
   LazyPluginRegistryEntry shouldLoad context type to match.

2. Regression test imported a local copy of shouldLoad instead of the
   production predicate. Extracted all three shouldLoad predicates into
   pluginRegistryPredicates.ts (pure module, no React/DOM dependencies),
   wired GraphWorkspace.tsx to use the imported functions, and updated
   the test to import and exercise the real production code via tsx.
   Verified: introducing the old broken condition causes the test to fail;
   the correct implementation passes all 7 assertions.
2026-08-05 17:37:33 +05:30
林SO 0e1b88a593 feat(triplet-store): add embedded Oxigraph backend 2026-08-05 20:06:20 +08:00
KaifAhmad1 b4f820568a fix: HybridSearch.search() crashes with AttributeError on non-inmemory backends
HybridSearch.search() directly accessed self.vector_store.vectors, a dict
that VectorStore only creates for backend="inmemory". Every other backend
(faiss, weaviate, qdrant, milvus, pinecone, pgvector, sqlite) raised
AttributeError. It now delegates to VectorStore.search_vectors() for
non-inmemory backends, applying metadata_filter as a post-filter and
normalizing results to a consistent {id, score, distance, metadata} shape.

Also fixes two related bugs surfaced while testing the backend-delegated
path end to end:
- vector_ids could remain None when explicit vectors/metadata were passed
  without vector_ids, crashing downstream indexing.
- query_vector passed as a plain list crashed backend stores (e.g.
  FAISSStore.search_similar) that call .ndim on it; now normalized to a
  numpy array up front.

And in vector_store.py: VectorStore.store_vectors() silently dropped
metadata for FAISS (and any add_vectors-only backend) because it called
add_vectors(vectors, **options) without forwarding metadata, even though
FAISSStore.add_vectors() accepts it. This blocked HybridSearch's metadata
filtering from working at all against FAISS.

Fixes #833
2026-08-05 17:16:56 +05:30
Sameer6305 8d52281cdf chore(explorer): clean up #830 branch — remove #793 file, add regression test
temporalDiffState.ts belongs to feat/793-temporal-diff-ui and should not
appear in the #830 diff. Remove it from this branch's tracked files.

Add the pluginRegistry.temporal.test.mjs regression test that covers the
shouldLoad fix committed in the main #830 commit (it was never committed).

Add test:plugin-registry script to package.json so the regression test
can be run via npm run test:plugin-registry.
2026-08-05 16:26:24 +05:30
Sameer6305 6a0eecbe02 fix(explorer): resolve #830 — Maximum update depth exceeded on Temporal panel open
Two independent render loops were causing the Temporal panel to remain
stuck on 'Loading temporal...' in npm run dev:

Loop 1 — diagnostics state churn (GraphWorkspace.tsx):
  handleDiagnosticsChange unconditionally called setGraphDiagnosticsState
  with a new object on every invocation. buildEffectAvailability (called
  inside GraphCanvas's diagnostics useEffect) always returns a new object,
  so setGraphDiagnosticsState was called on every effect run, creating a
  cycle: setGraphDiagnosticsState  graphDiagnosticsState new
  diagnosticsSnapshot new  pluginContext new  handleInteractionStateChange
  new  GraphCanvas re-renders  diagnostics effect fires again.

  Fix: before calling setGraphDiagnosticsState, compare the incoming
  diagnostics field-by-field against the last accepted snapshot via a ref
  (lastDiagnosticsRef). All effectAvailability entries, edgeClasses.updatedAt,
  structureLayer.cacheKey/lastDrawAt/enabled, and distanceVisual identity
  must differ for a state update to proceed. The ref approach avoids
  scheduling a re-render at all, rather than bailing out inside a functional
  updater after the render has already been committed.

Loop 2 — scrubberTime churn (GraphWorkspace.tsx + GraphWorkspaceShell.tsx):
  TimelinePanel.tsx calls onTimeChange(defaultTime) whenever its useEffect
  re-runs. React 18 concurrent mode re-runs effects with structurally-new
  Date objects for the same timestamp when speculative renders discard
  useMemo caches, causing setScrubberTime to be called repeatedly with a
  new Date that has the same millisecond value — triggering temporalState
  churn, the diagnostics effect, and eventually the same loop.

  Fix: wrap setScrubberTime in an onTimeChange useCallback that compares the
  incoming time's millisecond value against the last sent value (via
  lastScrubberMsRef). Redundant calls with the same timestamp are dropped
  before reaching setScrubberTime. Stable useCallback identity also prevents
  TimelinePanel's useEffect from re-firing solely due to prop identity churn.

Both fixes applied to GraphWorkspace.tsx and identically to
GraphWorkspaceShell.tsx which has the same pattern.

Verified:
- npm run dev: 0 'Maximum update depth exceeded' errors
- Temporal panel renders with real data in dev mode
- Effects and Neighbors panels unaffected
- npm run build + preview: identical behavior, 0 errors
- All 42 frontend tests pass (34 graph-workspace, 1 graph-store, 7 plugin-registry)
2026-08-05 16:01:18 +05:30
Sameer6305 aa85535d47 fixed copilot review 2026-08-04 16:45:33 +05:30
Sameer6305 7d936d0f7c fix(explorer): write diff highlights to displayGraph as well as store graph
fixed qodo review

applyDiffHighlight/clearDiffHighlight were writing baseColor only to
graphStore.graph (the store singleton), but Sigma is constructed with
displayGraphRef.current and the nodeReducer reads attributes from that
instance. When the display graph is a derived copy (aggregated,
focused, or grouped view), the store write has no effect on the
currently-rendered frame -- sigma.scheduleRefresh() flushes the
reducer over the display graph, which did not receive the mutation.

Fix: introduce writeBaseColor(context, nodeId, color) which writes to
BOTH the store graph (so the color propagates into the next display
graph rebuild via aggregateDisplayGraph's shallow attribute copy) AND
context.displayGraph (the live Graph instance currently bound to
Sigma, so the change is visible in the current frame immediately).

The dg !== graph guard skips the display-graph write when they happen
to be the same object (non-aggregated full view), avoiding a redundant
double-write in that case.

Original baseColor is still captured from the store graph (the
authoritative source, since aggregateDisplayGraph copies from there),
so restore remains correct across all view modes.
2026-08-04 16:32:04 +05:30
Sameer6305 47531d8365 feat(explorer): add temporal diff comparison to the Temporal panel
Adds a Compare section to the existing Temporal Context panel
(temporalOverlayPlugin.tsx) that lets a user pick two ISO timestamps
and diff the graph's node set between them via the existing, previously
UI-less GET /api/temporal/diff backend route.

- New temporalDiffState.ts: typed fetch wrapper (fetchTemporalDiff)
  matching the route's added_nodes/removed_nodes response shape.
- Diff results recolor affected nodes via baseColor (not
  ringColor/haloColor -- traced and confirmed those are only read by
  the sigma reducer for hovered/selected/path-state nodes and are
  silently discarded for default-state nodes).
- Validates both timestamps are present, parseable, and from < to
  before firing a request.
- Distinct idle/loading/error/empty/success states -- an empty diff
  (no changes) is rendered as its own state, not as an error.
- Cancels any in-flight request via AbortController on re-submission
  and on unmount; restores each highlighted node's original baseColor
  (captured before overwrite, not cleared to a fallback default) on
  both paths.
- Reuses existing theme tokens (GRAPH_THEME.palette.semantic[2],
  ui.control.dangerText) and existing button/input/loading/error
  visual patterns already established in this same plugins directory
  and in GraphInspectorPanel.tsx, rather than introducing new styling.
2026-08-04 15:57:53 +05:30
Mohd Kaif 86f115d200 docs: surface pip install command at the top of README and docs (#828)
Makes the install command the first actionable thing visible on both
the README and docs landing page, ahead of the fold.
2026-08-04 12:55:51 +05:30
Mohd Kaif 9c5c3c4ce0 Merge pull request #826 from Sameer6305/fix/785-provenance-storage-failure-tests
test(provenance): expand storage failure regression coverage (#785)
2026-08-04 12:13:41 +05:30
Mohd Kaif 26a5c4a1fb Merge branch 'main' into fix/785-provenance-storage-failure-tests 2026-08-04 12:08:23 +05:30
Mohd Kaif 2adc67e25e Merge pull request #827 from semantica-agi/feat/825-provenance-prov-o-compliance
Provenance: close PROV-O compliance gaps and high-stakes trust blockers
2026-08-04 11:33:42 +05:30
Sameer6305 e9e05fedbd fix(provenance): reset in-memory chain state on clear 2026-08-04 00:01:50 +05:30
KaifAhmad1 0a8330cbb0 fix(provenance): address code review findings on PR #827
- SQLiteStorage now migrates an existing (pre-#825) provenance.db in place
  via ALTER TABLE ADD COLUMN for any columns introduced since, instead of
  only ever running CREATE TABLE IF NOT EXISTS. Without this, opening an
  older database with the new code would break on the first insert/select
  since the row width and _row_to_entry's fixed indices grew past the old
  schema. Added test_migrates_pre_existing_old_schema_database.

- verify_chain() now also checks that sequence_id is exactly the
  predecessor's plus one (no gap, no duplicate), in addition to the existing
  previous_checksum comparison. Hardens against the narrow case where
  compute_checksum()'s deliberate exclusion of entity_id could let two
  distinct rows coincidentally share a checksum, which alone would let a
  checksum-only comparison miss a gap. Added
  test_verify_chain_detects_tampered_sequence_gap.

- Explorer provenance route: edge ids now include direction
  (f"{src}-{eid}-{direction}") to match the seen_edges dedupe key, which
  already included it. The same (src, target) pair can legitimately appear
  in both the upstream and downstream chains (cycles/overlap), and without
  this the two edges collided on the same id. Added
  test_add_chain_edges_ids_distinguish_direction.

- Removed an unused `Any` import in parse_provenance.py.
2026-08-03 22:52:34 +05:30
KaifAhmad1 db4361ad46 feat(provenance): close PROV-O compliance gaps and high-stakes trust blockers (closes #825)
Part A - high-stakes trust blockers:
- Invalidation tombstones via ProvenanceManager.invalidate() (archive-then-append,
  never mutates or deletes) instead of hard delete
- Hash-chained integrity: sequence_id/previous_checksum chain every entry to its
  predecessor; new verify_chain() detects wholesale row deletion that a lone
  per-row checksum cannot
- Typed Agent (AgentRecord: agent_type/is_automated) and Activity (ActivityRecord:
  start/end timing), wired through all 18 *_provenance.py wrappers
- Split parent_entity_id into previous_version_id (correction) vs derived_from_id
  (cross-source derivation), additive alongside the legacy combined field
- Downstream/descendant lineage traversal (get_descendants/trace_descendants,
  reverse BFS) closing the dead direction="downstream" code path in the
  Explorer's provenance route
- Qualified Association+hadRole and Invalidation in export_prov()
- New CLI: provenance invalidate|verify-chain|descendants

Part B - general PROV-O spec completeness:
- Qualified Generation/Usage/Derivation in export_prov()
- wasAssociatedWith, actedOnBehalfOf, wasInformedBy relations
- Bitemporal fields (valid_from/valid_until/revision_type/supersedes) plus
  revision_history()/query_recorded_between(), closing the deprecated
  kg.ProvenanceTracker's "no direct equivalent yet" migration gaps
- prov:Bundle/hadMember membership via bundle_id
- Configurable base_uri (--base-uri CLI flag), shared by RDFExporter's
  NamespaceManager and OWLExporter's default ontology_uri so KG/OWL/PROV
  exports co-resolve under one namespace instead of three hardcoded ones

Bugs fixed along the way:
- agent_id was a dead field: no track_* method read it from kwargs
- track_entities_batch silently absorbed typed kwargs into the metadata blob
- compute_checksum() had to exclude entity_id itself: hashing it made
  track_entity's versioning-archive relabel permanently orphan any entry
  already chained from the pre-relabel checksum, a false-positive "broken
  chain" for a legitimate rename
- InMemoryStorage.get_chain_head() ignored the committed head whenever the
  current transaction had staged entries, corrupting the next chain link
- several new ProvenanceEntry fields were wired into the dataclass and
  export_prov() but not into SQLiteStorage's DDL/INSERT/row-mapping;
  InMemoryStorage masked the gap. Added a permanent round-trip regression
  test to catch this class of bug for future field additions

Flagged, not fixed (separate pre-existing issues, out of scope for #825):
- pipeline/pipeline_provenance.py imports a nonexistent module and wraps a
  Pipeline dataclass with no run() method
- most *_provenance.py wrappers' backing classes are themselves missing or
  incomplete (context_manager, deduplicator, normalizer, etc.)
- kg_provenance.py passes entity_type inside metadata={} instead of as a
  top-level track_entity() kwarg across most of its call sites
2026-08-03 22:28:48 +05:30
Sameer6305 74c093facb fixed issues from qodo and copilot 2026-08-03 21:37:26 +05:30
Sameer6305 aae4c946ea test(provenance): expand storage failure regression coverage (#785) 2026-08-03 21:05:26 +05:30
Mohd KaifandSameer6305 b59211ea7f security: SHA-pin all Actions, harden release pipeline, add pin verification (#824)
* security: SHA-pin all Actions, harden release pipeline, add pin verification

Hardens the CI/CD supply chain against the LiteLLM/Trivy-style attack (a
compromised third-party Action with a mutable tag stealing a long-lived
publishing token) and closes several related gaps found in an audit of the
actual repository state.

- Pin every third-party GitHub Action across all workflows to a full commit
  SHA (tag kept as a trailing comment); add verify-action-pins.yml, a CI
  check that confirms via the GitHub API that each pin still matches its
  tag, on every workflow change, push to main, and weekly.
- Scope release.yml permissions to the job level (workflow defaults to
  contents: read); add a concurrency group so simultaneous tag pushes can't
  race the publish job.
- Add SLSA build provenance attestation (actions/attest-build-provenance)
  for every released wheel.
- Fix a latent bug in security-scan.yml: the PR-comment step was missing
  pull-requests: write and silently failing; add bounded artifact retention
  for uploaded scan reports.
- Group github-actions Dependabot updates to cut review noise.
- Document the resulting posture in SECURITY.md for auditors/regulated
  adopters, including what's enforced and what a fork needs to reconfigure
  for itself (environment/branch protection, Trusted Publishing trust).

Also (via GitHub API, not in this diff): created a protected `pypi`
environment with a required reviewer restricted to v* tags, and enabled
branch protection on main (required review, required status checks, no
force-push/deletion).

* fix: harden verify-action-pins per PR #824 bot review

Addresses real findings from the automated review on #824:

- The script previously only matched uses: lines that already contained a
  40-hex SHA, so a newly added mutable-tag action (e.g. some/action@v1)
  would never be scanned at all and the check would pass silently. It now
  matches every uses: line and hard-fails on any ref that isn't a full
  commit SHA.
- A tag that fails to resolve via the GitHub API (rate limit, deleted tag)
  previously only logged a warning and continued; that's now a hard
  failure too, since an unverifiable pin is exactly the failure mode this
  check exists to catch.
- verify-action-pins.yml only triggered on .github/workflows/** changes,
  so an edit to the verifier script itself wouldn't run the check that
  verifies it. Added the script path to both trigger filters.

The reviewer's claim that slash-containing tag comments (release/v1) break
the API lookup did not reproduce - tested directly against
pypa/gh-action-pypi-publish@release/v1 and GitHub's commits API resolves
multi-segment refs natively - so no change was needed there.

Verified with a synthetic test workflow containing a mutable-tag action,
a correctly-pinned SHA, and a deliberately mismatched SHA: the updated
script now catches the first and third cases and passes the second. Also
re-ran against the real workflow tree (40/40 pins still verify clean).

* fix: repair broken Safety scan and PR comment formatting

The "Comment PR with Security Results" step was producing garbled output
(literal \n characters instead of newlines, "undefined:" labels) because:

- Every line in the JS comment builder used \n (escaped backslash-n)
  inside template literals, which JS renders as the literal two-character
  string \n, not a newline.
- The Semgrep section read issue.rule_id, but Semgrep's JSON field is
  check_id - hence "undefined: <path>" for every entry.

Rewrote the comment builder to construct each section as an array of
lines joined with a real '\n', with correct field names, and collapsed
long finding lists into a <details> block instead of a flat list.
Verified by extracting the exact script and running it under node against
synthetic fixtures matching each tool's real JSON schema (found/clean/
missing-report paths all render correctly).

While tracing the "undefined" and always-empty Safety section, found the
Safety step itself was silently broken:

- `safety check --json --output safety-report.json` is invalid in
  Safety 3.x: --output now selects a console format (json/text/screen),
  not a file path. The command errored on every run (swallowed by
  `|| true`), so safety-report.json was never created and the PR comment
  always fell back to a generic "scan completed" message. Switched to
  `--save-json`, which is the correct flag for writing a JSON report to
  disk, and confirmed against the real safety 3.8.1 CLI locally.
- Even with a report, the code read vuln.package - the real field is
  package_name.
- The job never installed Semantica's own dependencies before scanning,
  so `safety check` (which defaults to scanning the environment) was
  auditing the scanner tools' own dependencies, not Semantica's. Added
  `pip install -e ".[llm-litellm]"` so the project's actual dependency
  tree - including the LiteLLM extra this whole hardening effort is
  about - is what gets scanned.

Also updated the corresponding SECURITY.md bullet to describe what Safety
actually covers now.

* fix: remove unused pypdf2 dependency (CVE-2023-36464)

Now that the Safety scan step actually runs (see previous commit), it
correctly failed this PR's checks on CVE-2023-36464 in pypdf2==3.0.1 - a
real, pre-existing vulnerability that was invisible until the scan was
fixed.

PyPDF2 is not a patchable dependency here: the project is discontinued
(merged into `pypdf`), 3.0.1 is its final release, and there is no fixed
version to upgrade to. Grepping the repo for `import PyPDF2` / `from
PyPDF2` turns up nothing - it was never actually imported anywhere. Its
only presence outside pyproject.toml was in docstrings describing a
"PyPDF2.PdfReader() fallback" for PDF parsing that was never implemented
in code; pdfplumber is the library actually used. Removed the dependency
and corrected the stale docstrings in parse/__init__.py, parse/methods.py,
parse/pdf_parser.py, and ingest/email_ingestor.py accordingly.

* fix: suppress Bandit B324 false positives on non-cryptographic MD5 use

Same pattern as the previous pypdf2 commit: fixing the Safety scan
surfaced this PR's own Bandit HIGH-severity gate actually blocking on 10
pre-existing findings, all Bandit B324 ("Use of weak MD5 hash for
security").

Checked each of the 10 call sites: every one uses hashlib.md5() to build
a short deterministic cache key, entity ID, or IRI suffix from already-
non-secret input (query text, entity text/type, class/property names) -
none are used for passwords, tokens, or integrity verification of
untrusted data. This is exactly the case Bandit's own message points at
("Consider usedforsecurity=False").

Did not use usedforsecurity=False itself: that keyword argument was
added to hashlib in Python 3.9, and pyproject.toml declares
`requires-python = ">=3.8"` - adding it unconditionally risks a TypeError
on 3.8. Used a targeted `# nosec B324` comment with a one-line
justification instead, which suppresses only this specific check and
carries no runtime behavior change on any supported Python version.

Verified locally: bandit -r semantica/ -ll now reports 0 HIGH-severity
findings (was 10).

* docs: add CHANGELOG entry for #824 CI/CD supply-chain hardening

Covers the SHA-pinning + verify-action-pins.yml enforcement, release.yml
hardening (job-scoped permissions, concurrency, SLSA provenance), the
pypi environment/branch protection GitHub-side config, the
security-scan.yml Safety/comment-formatting fixes, and the two
vulnerabilities those fixes surfaced (pypdf2 CVE-2023-36464 removal,
Bandit B324 suppression).

* fix: close two remaining gaps missed by upstream bot-review fixes

verify-action-pins.sh:
- Quoted uses: lines (e.g. uses: owner/action@SHA) were not matched
  by the existing regex, so a SHA-pinned action written with quotes would
  silently skip verification. Updated the main ERE to accept an optional
  leading/trailing single or double quote around the owner/action@ref
  value, and excluded quote chars from the inner character classes so the
  ref is still extracted cleanly.
- The grep input glob only covered *.yml. GitHub also treats *.yaml as a
  valid workflow extension. Added *.yaml to the glob and a 2>/dev/null
  guard so the command doesn't fail when no *.yaml files exist.

security-scan.yml (on top of Kaif's --save-json fix in 67c7ec2a):
- Kaif's fix kept the '|| echo 0' fallback on the VULNS= line, so all
  five scanner-failure modes (file missing, empty file, malformed JSON,
  valid JSON with no 'vulnerabilities' key, vulnerabilities: null) still
  silently produce VULNS=0 or VULNS=null and pass the merge-blocker check.
- Added guard 1: '[ ! -s safety-report.json ]' fails loudly if Safety
  crashed before writing a report (covers missing and empty-file cases).
- Dropped the '|| echo 0' fallback and added guard 2: '[[ ! VULNS =~
  ^[0-9]+$ ]]' fails loudly on non-integer VULNS (covers malformed JSON,
  missing key, and null cases). Both guards emit ::error:: annotations.
- Verified with a 7-case simulation: all 5 failure modes now exit 1;
  genuine zero-vuln and real-vuln cases still behave correctly.

* fix: correct bash [[ =~ ]] quoting that broke verify-action-pins.sh in CI

The regex for matching uses: lines was embedded directly inline in a
[[ =~ ]] test with literal \" and \' escape sequences. Bash's conditional-
expression parser interprets these as shell syntax rather than regex
literals, producing:

  syntax error in conditional expression: unexpected token ')'

at line 27 on every CI run.

Fix: move the regex into a USES_PATTERN variable using safe single-quote
shell-string concatenation so the [[ =~ ]] parser receives an unquoted
variable reference ($USES_PATTERN) rather than a literal pattern containing
bash-special characters. The regex semantics are identical: optional
leading/trailing quote around owner/action@ref, quote chars excluded from
capture groups.

Verified in real bash 5.2.21 (Git for Windows):
  No syntax error on the real 40-pin workflow tree (Checked 40)
  Unquoted SHA pin:      MATCH, correct repo+ref extracted
  Double-quoted SHA pin: MATCH, correct repo+ref extracted
  Single-quoted SHA pin: MATCH, correct repo+ref extracted
  .yaml extension file:  MATCH, correct repo+ref extracted
  ./local-action:        NO MATCH (correct)
  docker://:             NO MATCH (correct)

* docs: add 3 missing items to fork-reconfiguration checklist in SECURITY.md

The checklist covered Trusted Publishing trust, protected environment,
branch protection, and Dependabot github-actions entry. Three non-forking
controls described elsewhere in SECURITY.md were omitted:

- GitHub secret scanning and push protection (repo settings, not copied
  on fork)
- GitGuardian (GitHub App installation scoped to this specific repo,
  requires separate install on any fork)
- CodeQL Default Setup vs Advanced Setup state (repo setting that affects
  whether the upload-sarif step in codeql.yml does anything)

Added as items 5, 6, 7 matching the existing numbered bullet style.

* fix: update github/codeql-action pins to v4 tip (SHA drift caught by verify check)

verify-action-pins caught that github/codeql-action@v4 tag was re-pointed
upstream:

  old: f205ea1c3313d32999d8d6a48b4f6530d4437b38
  new: d1ba80a13dd99fba24a470575428917156a28b43

Updated all 8 occurrences across codeql.yml (init x3, autobuild, analyze,
upload-sarif) and defender-for-devops.yml (upload-sarif x2). Tag comment
# v4 unchanged — the tag itself hasn't changed, only what commit it points to.

---------

Co-authored-by: Sameer6305 <sskadam6305@gmail.com>
2026-08-03 19:17:10 +05:30
Mohd Kaif c7c7250d88 Merge pull request #823 from Sameer6305/fix/775-ontology-atomic-writes
fix(explorer): complete atomic ontology refresh writes - #775
2026-08-03 13:40:08 +05:30
Mohd Kaif 21365cb0e6 Merge branch 'main' into fix/775-ontology-atomic-writes 2026-08-03 13:13:07 +05:30
Sameer KadamandKaifAhmad1 76edaeb1c0 fix(agno): make _eval_rule raise instead of silently returning compliant=True on unevaluable rules (closes #778) (#822)
* fix(agno): surface unevaluable policy rules (#778)

* fix(agno): fixed qodo reviews

check_policy previously let unevaluable policy rules silently return
compliant=True with no signal (issue #778): a rule referencing a field
missing from decision_data, or a rule string not matching the expected
<field> <op> <value> format, both fell through _eval_rule's `return
True` and were treated as passed.

Both now raise ValueError, which routes through check_policy's existing
exception handler and surfaces as a `warnings` entry instead. compliant/
violations semantics are unchanged for every case that previously worked
correctly; an unevaluable rule is not counted as a violation since it's
genuinely unknown whether it would have passed.

Follow-up fixes from code review:
- policy_rules decoded via json.loads without checking it was a list;
  a JSON-encoded bare string decoded to a Python str, so iterating it
  evaluated one "rule" per character, amplifying a single input-shape
  mistake into a wall of per-character warnings. A decoded string is
  now treated as a single rule; any other non-list shape or non-string
  list element produces exactly one warning instead.
- _eval_rule used `data.get(field) is None` to detect a missing field,
  which can't distinguish an absent key from a key present with JSON
  null - both produced the same "undefined field" warning. Field
  presence is now checked with `field not in data` first, and a
  present-but-null value gets its own distinct message.

Added regression tests for all of the above in
tests/integrations/agno/test_decision_kit.py (38 tests in the file,
128 passing across tests/integrations/agno/).

* fix(agno): reject non-object decision_data in check_policy

check_policy only validated that decision_data was well-formed JSON,
not that it decoded to an object. When it decoded to a list, `field
not in data` in _eval_rule silently became list-membership testing
of values instead of a dict key check - e.g. "confidence" not in
["confidence", 0.95] evaluates to False - so a matching rule fell
through to data["confidence"], raising a raw internal TypeError
("list indices must be integers or slices, not str") instead of any
meaningful diagnostic. Numbers, strings, and bools produced similarly
opaque TypeErrors deep inside _eval_rule.

check_policy now checks isinstance(data, dict) right after decoding
and rejects any other shape with a single clear violations entry,
the same way it already rejects malformed JSON.

Added 5 regression tests in tests/integrations/agno/test_decision_kit.py
covering list/number/string/bool/null decision_data shapes (43 tests
in the file, 133 passing across tests/integrations/agno/).

Addresses Copilot PR review comment on the #778 fix branch.

* docs(changelog): reference PR #822 in the check_policy changelog entry

---------

Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-08-03 12:23:49 +05:30
Mohd Kaif 1ad00075a3 Merge pull request #821 from Sameer6305/fix/779-record-decision-logging
fix(agno): log shared context decision tracking failures - #779
2026-08-02 13:19:12 +05:30
KaifAhmad1 46dcbbe731 Merge remote-tracking branch 'origin/main' into fix/779-record-decision-logging
# Conflicts:
#	CHANGELOG.md
2026-08-02 13:10:20 +05:30
Mohd KaifandKaifAhmad1 0d447560bc fix(provenance): log tracking failures and return None on storage error (closes #783) (#820)
* fix(provenance): log tracking failures and return None on storage error (closes #783)

* docs(provenance): document Optional return types and failure behavior (#783)

* fixed qodo reviews

- split.ProvenanceTracker: Check _unified_manager.track_chunk() return value and fall back to legacy storage when None is returned (storage failure)

- split.ProvenanceTracker: Initialize legacy stores (_provenance_store and _chunk_registry) unconditionally in __init__ so legacy fallback storage works safely even when initialized with use_unified=True

- split.ProvenanceTracker: Update get_provenance() to check legacy storage when no record is found in unified storage, ensuring fallback-tracked chunks remain retrievable

- SourceTracker: Change track_sources_batch() condition from if entry is not None: to if ok: to respect the boolean return contract (-> bool) of track_entity_source(), track_property_source(), and track_relationship_source()

- Tests: Add regression test test_unified_tracking_returns_none_triggers_fallback and update test_track_sources_batch_failure_not_counted to test boolean failure return_value=False

---------

Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-08-01 20:52:55 +05:30
KaifAhmad1 5094235ce1 Merge branch 'main' into fix/783-tracking-methods-honest-failures
Resolves CHANGELOG.md conflict with #819's SKOS cycle-detection entry
by keeping both entries.
2026-08-01 20:43:53 +05:30
Sameer6305 6f6c825f3d fixed qodo reviews
- Removed unused `validate_skos_hierarchy` import from
  test_ontology_subissue3.py (flake8 F401); the test uses a `wraps=` spy
  on the real add_nodes_and_edges instead of calling the helper directly.
- refresh_ontology tests now percent-encode the ontology URI with
  urllib.parse.quote before interpolating it into the {ontology_uri:path}
  request path, matching the already-encoded unknown-uri refresh test in
  the same file instead of embedding a raw http://... URI with slashes.
- Reworded the cyclic-SKOS refresh test's comment and section header:
  GraphSession.add_nodes_and_edges() documents pre-write validation and
  lock-based mutual exclusion, not transactional rollback, so "atomic"
  was replaced with "single combined add_nodes_and_edges() call" to avoid
  implying rollback guarantees that don't exist.

Verified: tests/explorer/test_ontology_subissue3.py (34 passed) and
tests/explorer/ (204 passed), no regressions.
2026-08-01 19:36:16 +05:30
Sameer6305 d293ca6009 fix(explorer): complete atomic ontology refresh writes (#775) 2026-08-01 19:12:00 +05:30
Mohd Kaif 352db64b33 Merge pull request #819 from mikemikimike/agent/validate-skos-cycles
Reject cyclic SKOS hierarchies at write time
2026-08-01 12:02:46 +05:30
KaifAhmad1andmikemikimike bc75768afe Fix two review findings in SKOS cycle validation
- validate_skos_hierarchy() re-walked every existing hierarchy edge in
  the graph on each write, so one pre-existing cycle anywhere would
  block all unrelated future SKOS writes. It now only traverses
  concepts touched by the edges actually being written, while still
  checking those against existing edges for cross-boundary cycles.
- In /api/ontology/load, `except HTTPException: raise` sat after a
  broader `except Exception` clause that already matched HTTPException,
  so a 422 raised after a successful OntologyIngestor parse was
  silently swallowed and retried via the fallback RDF parser instead of
  reaching the caller. Reordered the except clauses.

Co-authored-by: mikemikimike <13286568797@163.com>
2026-08-01 11:46:13 +05:30
mikemikimike f992504227 Make SKOS hierarchy imports atomic 2026-07-31 23:23:10 +05:30
mikemikimike d41530930d Centralize SKOS cycle validation 2026-07-31 23:07:58 +05:30
mikemikimike 692260cc76 Reject cyclic SKOS hierarchies 2026-07-31 23:07:58 +05:30
Sameer6305 aa58b46d4d Merge semantica-agi/main into fix/779-record-decision-logging 2026-07-31 18:28:20 +05:30
Sameer6305 633d485045 fixed qodo review
- Added exc_info=True to both store failed and record_decision failed warning logs in _AgentScopedStore.upsert_memory() to preserve full traceback context for debugging

- Updated CHANGELOG.md entry to document traceback preservation
2026-07-31 18:19:50 +05:30
Mohd Kaif 424b63a27d Merge pull request #818 from Sameer6305/fix/780-agno-tool-registration-validation
fix(agno): fail fast on toolkit registration failures - #780
2026-07-31 18:17:40 +05:30
KaifAhmad1 6e44d98d46 Merge remote-tracking branch 'origin/main' into pr818-fix
# Conflicts:
#	CHANGELOG.md
2026-07-31 17:51:53 +05:30
Sameer6305 d21e5e9944 fix(agno): log decision tracking failures in shared context - #779 2026-07-31 17:50:40 +05:30
KaifAhmad1 67aed43997 docs: add changelog entry for Agno toolkit fail-fast fix (#780, #818) 2026-07-31 17:44:14 +05:30
Sameer6305 ac64943965 Merge remote-tracking branch 'semantica-agi/main' into fix/783-tracking-methods-honest-failures
# Conflicts:
#	CHANGELOG.md
2026-07-31 16:11:32 +05:30
Sameer6305 4dea295f0d fixed qodo reviews
- split.ProvenanceTracker: Check _unified_manager.track_chunk() return value and fall back to legacy storage when None is returned (storage failure)

- split.ProvenanceTracker: Initialize legacy stores (_provenance_store and _chunk_registry) unconditionally in __init__ so legacy fallback storage works safely even when initialized with use_unified=True

- split.ProvenanceTracker: Update get_provenance() to check legacy storage when no record is found in unified storage, ensuring fallback-tracked chunks remain retrievable

- SourceTracker: Change track_sources_batch() condition from if entry is not None: to if ok: to respect the boolean return contract (-> bool) of track_entity_source(), track_property_source(), and track_relationship_source()

- Tests: Add regression test test_unified_tracking_returns_none_triggers_fallback and update test_track_sources_batch_failure_not_counted to test boolean failure return_value=False
2026-07-31 16:05:37 +05:30
Mohd Kaif 6c6cb3f3b5 Merge pull request #817 from Sameer6305/fix/781-causal-chain-error-signaling
fix(mcp): improve causal chain fallback error handling - #781
2026-07-31 15:42:07 +05:30
Sameer6305 1ae1e6d57a docs(provenance): document Optional return types and failure behavior (#783) 2026-07-31 15:01:33 +05:30
Sameer6305 495e29d543 fix(provenance): log tracking failures and return None on storage error (closes #783) 2026-07-31 15:00:23 +05:30
KaifAhmad1 04d2a726b9 Merge remote-tracking branch 'origin/main' into pr817-conflict-fix
# Conflicts:
#	CHANGELOG.md
2026-07-31 13:17:10 +05:30
KaifAhmad1 62a027d6fd fix(mcp): call backend get_causal_chain only once on internal TypeError
Signature introspection and the resulting call were sharing one
try/except, so a genuine bug inside a backend's get_causal_chain
(raising an unrelated TypeError) was misread as a signature mismatch,
causing an identical retry call before the real error surfaced.
Split introspection from the call so a successfully-introspected call
happens exactly once; the trial-and-error cascade now only runs when
inspect.signature itself fails. Also adds the CHANGELOG entry for
#781/#817, which was missing.
2026-07-31 13:14:29 +05:30
Mohd Kaif 7cab35bbc0 Merge pull request #816 from Sameer6305/fix/782-track-entity-atomic-write
fix(provenance): make track_entity's two-step write atomic (closes #782)
2026-07-31 12:14:12 +05:30
KaifAhmad1 938f846dde docs: cite PR number alongside issue in CHANGELOG for track_entity fix
Follow-up to the #782 entry — other entries in this section cite both
the issue and PR number, this one was missing the PR reference.
2026-07-31 12:08:52 +05:30
Sameer6305 66be1630fa fix(agno): fail fast on toolkit registration failures - #780 2026-07-30 20:43:26 +05:30
Sameer6305 a1f835c9b2 refactor(mcp): harden handle_get_causal_chain inputs and signature introspection (#781)
- Add safe input validation and bounds clamping on max_depth (1..100) to prevent DoS/memory exhaustion

- Use inspect.signature for accurate keyword dispatch with precise TypeError fallback

- Prevent masking of genuine internal TypeError exceptions inside graph backends

- Add security and input hardening regression tests
2026-07-30 19:09:22 +05:30
Sameer6305 12172d03b4 fix(mcp): fixed qodo reviews (#781)
- Support legacy (depth kwarg) and positional-only get_causal_chain backend signatures in fallback path

- Add regression tests for signature compatibility
2026-07-30 19:04:00 +05:30
Sameer6305 60f362817f fix(mcp): return error when causal chain analysis unsupported (#781)
- Return explicit error dictionary when graph lacks get_causal_chain instead of silent empty list

- Forward direction and max_depth in fallback graph.get_causal_chain call

- Add regression tests for error signaling and parameter forwarding
2026-07-30 18:11:04 +05:30
Sameer6305 4d1e5cf37c fixed qodo reviews 2026-07-30 16:58:19 +05:30
Sameer6305 16893c28a4 docs(provenance): document atomic rollback behavior (#782) 2026-07-30 16:28:40 +05:30
Sameer6305 577967a549 fix(provenance): make track_entity writes atomic (closes #782) 2026-07-30 16:28:40 +05:30
Mohd KaifandSameer6305 7fb94b6528 feat(triplet_store): add Altair Anzo triplet store backend (#814)
* feat(triplet_store): add Altair Anzo triplet store backend

Adds AnzoStore as a fourth peer to BlazegraphStore/RDF4JStore/JenaStore,
speaking plain SPARQL 1.1 over HTTP (no new dependency needed). The one
structural difference from the existing backends is that Anzo addresses
data by a dataset/graphmart URI rather than a short namespace/repository
name, so the endpoint path percent-encodes it. Reuses the shared
sparql_escaping.py helpers and wires "anzo" into TripletStore's backend
dispatch and config env vars.

Closes #813

* fix(triplet_store): correct AnzoStore SPARQL syntax and validate IRIs

Addresses review findings from Qodo and Codex on PR #814:

- get_triplets(): constraints are now expressed via FILTER(...) instead of
  bare equality expressions appended inside the WHERE group graph pattern
  (e.g. "?s ?p ?o ?s = <...>"), which is not valid SPARQL and was rejected
  by standards-compliant endpoints.
- bulk_load(): named-graph inserts now nest the GRAPH block inside the
  INSERT DATA braces (INSERT DATA { GRAPH <g> { ... } }) per the SPARQL 1.1
  Update grammar, instead of "INSERT DATA GRAPH <g> { ... }".
- bulk_load()/_build_insert_data()/delete_triplet()/get_triplets() now
  validate subject/predicate/graph URIs via sparql_escaping.validate_uri
  before interpolating them into SPARQL Update/Query strings, closing an
  injection path where a value containing ">" or "}" could break out of
  the intended <...> token.
- Corrected the store_type docstring/usage example: Anzo's linked-data-set
  store type is "lds", not "dataset".

Extended tests/triplet_store/test_anzo_store.py with coverage for the
corrected query shapes and the new validation/injection-rejection paths
(38 tests total, up from 32). Full tests/triplet_store/ suite: 299/299
passing.

* test(triplet_store): expand AnzoStore regression coverage

---------

Co-authored-by: Sameer6305 <sskadam6305@gmail.com>
2026-07-30 11:22:52 +05:30
Sameer KadamandKaifAhmad1 e197977172 refactor(provenance): centralize duplicated store-and-swallow logic into _save_entry() (#784) (#815)
* refactor(provenance): centralize duplicated store-and-swallow logic into _save_entry() (closes #784)

- Added ProvenanceManager._save_entry(entry) as the single shared
  checksum-compute + storage.store() + graceful-failure-swallow
  pipeline, previously duplicated identically across track_entity,
  track_relationship, track_chunk, and track_property_source.
- track_entities_batch/track_chunks_batch already delegate to
  track_entity/track_chunk in a loop, so they inherit the fix for
  free — left untouched, confirmed no direct duplication there.
- Byte-for-byte preserves today's swallow-and-continue behavior and
  comment text; this is an architecture-only refactor. The silent-
  failure behavior itself is unchanged and out of scope here — a fix
  to it now only needs to happen in one place instead of four.
- Added 4 new regression tests (previously 0 of the 4 single-item
  methods had failure-path coverage) proving storage.store() raising
  is still caught and each method still returns its ProvenanceEntry.

Tests: tests/provenance/ 228 passed (+4 new), tests/explorer/test_provenance_manager_wiring.py 8 passed. 236/236, 0 failed.

* fix(provenance): drop out-of-transaction store attempt in track_entity fallback

The _save_entry refactor changed track_entity's pre-build exception
fallback (entry is None branch) to call _save_entry(), which makes a
real self.storage.store(entry) call. The original code only computed
a checksum here and never attempted storage again, since this branch
fires when something already failed before the entry was built inside
the atomic transaction. Storing outside that transaction bypasses the
BEGIN IMMEDIATE serialization #807 added, risking the same race it
fixed. Restored checksum-only behavior and added a regression test
asserting storage.store is not called on this path.

Also removed an untested hasattr(_store_with_conn) defensive branch
added during the refactor that wasn't in the original code, and added
a changelog entry.

---------

Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-07-29 22:18:05 +05:30
Mohd Kaif ecd0a26a8d Merge pull request #812 from Sameer6305/fix/807-sqlite-storage-performance
perf(provenance): optimize SQLite transaction lifecycle, concurrency, and lineage traversal
2026-07-29 13:04:36 +05:30
KaifAhmad1 f99241ca88 fix(provenance): stop reads from taking the writer lock, fix batch count inflation
Two review findings on #807/#812:

- retrieve() and trace_lineage() were routed through transaction()'s
  BEGIN IMMEDIATE, so plain reads took SQLite's writer lock and
  serialized behind every other read/write, defeating the WAL
  concurrency this PR was meant to add. They now use a dedicated
  _read_connection() (configured, no explicit BEGIN).

- track_entity()/track_chunk() swallowed all internal storage
  exceptions unconditionally, so a single item's failure inside
  track_entities_batch()/track_chunks_batch()'s shared transaction
  never reached the batch loop's per-item except, inflating
  tracked_count for entries that were never persisted. Both now
  re-raise when called with a shared _conn (batch context) while
  still degrading gracefully on standalone calls.

Added regression tests for both, corrected the CHANGELOG entry and
docs that described the prior (overly broad) behavior.
2026-07-29 12:54:43 +05:30
Sameer6305 dabeb0e833 docs: add changelog entry for #807 2026-07-28 23:44:14 +05:30
Sameer6305 9458cf5b2b docs: update provenance documentation for SQLiteStorage WAL and batch tracking (#807) 2026-07-28 23:42:14 +05:30
Sameer6305 3db35a6871 fixed qodo reviews 2026-07-28 23:37:46 +05:30
Sameer6305 b1daf238ca perf(provenance): optimize SQLite transaction lifecycle and lineage traversal 2026-07-28 23:06:23 +05:30
Mohd Kaif 0205ecd711 Merge pull request #811 from semantica-agi/ai-findings-autofix/SECURITY.md
Potential fixes for 3 code quality findings
2026-07-28 21:03:31 +05:30
Mohd Kaif 9677f25d27 Merge pull request #810 from semantica-agi/ai-findings-autofix/semantica-triplet_store-query_engine.py
Potential fixes for 2 code quality findings
2026-07-28 21:03:00 +05:30
Mohd KaifandCopilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> 4a221554de Apply suggested fix to SECURITY.md from Copilot Autofix
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-07-28 18:49:06 +05:30
Mohd KaifandCopilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> 7e513a12a3 Apply suggested fix to SECURITY.md from Copilot Autofix
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-07-28 18:49:06 +05:30
Mohd KaifandCopilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> 39bebe7da9 Apply suggested fix to SECURITY.md from Copilot Autofix
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-07-28 18:49:05 +05:30
Mohd KaifandCopilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> 9537bf17b3 Apply suggested fix to semantica/triplet_store/query_engine.py from Copilot Autofix
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-07-28 18:47:35 +05:30
Mohd KaifandCopilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> e8cb337946 Apply suggested fix to semantica/triplet_store/query_engine.py from Copilot Autofix
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-07-28 18:47:35 +05:30
Mohd Kaif 840629762f Merge pull request #809 from Sameer6305/fix/792-provenance-manager-wiring
fix(explorer): wire ProvenanceManager into provenance routes (closes #792)
2026-07-28 18:17:54 +05:30
KaifAhmad1 df6c30653c docs: add changelog entry for ProvenanceManager Explorer wiring (#792, #809) 2026-07-28 18:11:17 +05:30
Sameer6305 8d3c99ba30 perf(provenance): optimize lineage integrity checks and clean up top-level imports (#792)
- Reuse lineage['integrity_verified'] in _build_provenance in O(1) time when available, eliminating redundant SHA-256 verification loops across lineage chains.

- Improve compute_checksum dictionary handling in semantica/provenance/integrity.py so None values fall back cleanly to ProvenanceEntry defaults.

- Move json and verify_checksum imports to module top-level in semantica/provenance/manager.py to avoid function-local import overhead during get_lineage calls.
2026-07-28 17:25:01 +05:30
Sameer6305 c2079d8e92 fix(explorer): preserve audit evidence fields in provenance nodes and reports (#792)
- Extend ProvenanceNode in semantica/explorer/schemas.py with audit evidence fields: source_document, source_location, source_quote, confidence, and checksum.

- Update _transform_audit_lineage in semantica/explorer/routes/provenance.py to populate these evidence fields for each lineage node from ProvenanceEntry records, while keeping default None values for orphan nodes.

- Include source_document, confidence, and checksum in markdown report rendering (_render_markdown) so exported markdown reports surface attribution and integrity evidence.

- Add unit test test_provenance_audit_evidence_fields_preserved in test_provenance_manager_wiring.py verifying that evidence fields are present across /api/provenance JSON responses and exported JSON/markdown reports.
2026-07-28 17:15:49 +05:30
Sameer6305 cc864362aa fix(provenance): verify checksum integrity of lineage entries before labeling source as audit (#792)
- Update verify_checksum and compute_checksum in semantica/provenance/integrity.py to support both ProvenanceEntry objects and serialized dictionary entries.

- Add integrity_verified flag computed via verify_checksum to the dictionary returned by ProvenanceManager.get_lineage().

- Update _build_provenance in semantica/explorer/routes/provenance.py to verify every returned lineage entry before labeling the result as source=audit. If verification fails due to missing checksums or corrupted records, log a warning and fall back cleanly to graph traversal.

- Add unit test test_provenance_manager_wiring_checksum_failure_falls_back in test_provenance_manager_wiring.py verifying that tampered lineage entries trigger fallback to source=graph_traversal.
2026-07-28 17:08:12 +05:30
Sameer6305 d30aea79a7 fix(explorer): classify multi-hop audit lineage as upstream and enforce provenance storage configuration (#792)
- Fix _transform_audit_lineage to classify all non-downstream ancestor derivation edges as 'upstream' instead of 'lateral', correcting multi-hop lineage direction in JSON and markdown reports.

- Add GraphSession.set_provenance_storage_path() to explicitly reject conflicting preconfigured storage paths or path mutations after provenance_manager initialization.

- Update create_app() to call active_session.set_provenance_storage_path(prov_path), preventing silent retention of conflicting paths or un-redirectable cached managers.

- Remove unused logging import in app.py.

- Add comprehensive unit tests in test_provenance_manager_wiring.py for upstream edge classification, markdown report grouping, conflicting path rejection, and manager initialization lockouts.
2026-07-28 16:57:38 +05:30
Sameer6305 2ee4da0b47 fix(explorer): wire ProvenanceManager into provenance routes (closes #792)
- Explorer's /api/provenance now queries the audit-grade ProvenanceManager
  (SQLite-backed, checksummed) first, falling back to the naive 2-hop
  graph traversal when no audit records exist for a node.
- Fixed a process-global mutable-state risk in the initial approach:
  provenance storage path is threaded per-session via GraphSession,
  not via ProvenanceManager's global set_default_storage_path classmethod.
- Added source: 'audit' | 'graph_traversal' to the response so callers
  can distinguish which path served the data.
- Documented a known limitation: ProvenanceManager currently only
  traces upstream/ancestor lineage, not descendants — the naive
  fallback remains the only source for downstream relationships until
  ProvenanceManager gains a reverse lookup (tracked separately).
- Warns (rather than silently no-ops) if a provided session's
  provenance_manager was already constructed before create_app()
  applied a provenance_storage_path.
- Never lets a provenance-manager failure crash the route; degrades
  to the naive path with a logged warning instead.

Tests: 5 new tests in test_provenance_manager_wiring.py covering the
audit path, empty-record fallback, storage-failure degradation, app
startup wiring, and cross-session storage isolation. Full
tests/explorer/ + tests/provenance/ suite passing, order-invariant.
2026-07-28 16:23:02 +05:30
Mohd Kaif 016661463e Merge pull request #808 from semantica-agi/docs-enterprise-data-platforms-databricks-snowflake
docs: highlight Databricks/Snowflake enterprise data ingestion, fix ingest doc bugs
2026-07-28 15:53:14 +05:30
Sameer6305 ce914b396a docs: clarify Snowflake OAuth auth, add ArrowIngestor and non-re-exported ingestors (PR #808) 2026-07-28 15:00:02 +05:30
KaifAhmad1 b105b8ea97 fix: address review feedback on Databricks/Snowflake docs (PR #808)
- README: get_table_lineage() takes table_name first, then catalog/schema
  keyword args — the example had them in the wrong order, which would have
  queried lineage for the wrong fully-qualified table when copy-pasted.
- modules.md: the ingest example used DatabricksIngestor without importing
  it, causing a NameError if copy-pasted as-is.
- guides/ingest.md: corrected the claim that Databricks/Snowflake ingestors
  return "the same shape as DBIngestor" — DBIngestor.execute_query() returns
  a raw List[Dict] with no wrapper, unlike DatabricksData/SnowflakeData.
2026-07-28 12:10:37 +05:30
KaifAhmad1 6ed5aea993 docs: highlight Databricks/Snowflake enterprise data ingestion, fix ingest doc bugs
Makes enterprise lakehouse/warehouse ingestion (Databricks Unity Catalog +
Delta Lake, Snowflake) a first-class, prominently documented capability
across the README and guides, and adds matching runnable examples to
docs/guides/ingest.md. Also fixes several pre-existing inaccuracies caught
while auditing the ingest module docs against the actual source:
WebIngestor has no ingest_urls() (only singular ingest_url()), XMLIngestor's
XSD option is schema_path (not validate_xsd) and belongs on ingest() not the
constructor, and the "Available ingestors" list was missing DatabricksIngestor
while listing several classes not actually exported from semantica.ingest.
2026-07-28 11:58:09 +05:30
Mohd Kaif 80bce453c3 Merge pull request #805 from Sameer6305/fix/773-sparql-test-coverage
test(explorer): add coverage for SPARQL route (#773)
2026-07-27 19:33:07 +05:30
KaifAhmad1 d102584af6 fix(explorer): dedupe SPARQL row-cap logic and cover CONSTRUCT/DESCRIBE truncation
Extracts the row-cap-and-truncate loop (duplicated between the
CONSTRUCT/DESCRIBE and SELECT branches) into a shared _cap_rows()
helper, and adds a test for the previously-uncovered CONSTRUCT/DESCRIBE
truncation path. Addresses review nits on PR #805.
2026-07-27 19:27:35 +05:30
Mohd Kaif 4f27d3dcae Merge pull request #804 from Sameer6305/fix/772-live-shacl-validation-v2
fix(ontology): wire live SHACL validation into /shacl/validate and /health (closes #772) #803
2026-07-27 19:05:32 +05:30
KaifAhmad1 35f8c0527c Merge remote-tracking branch 'origin/main' into fix/772-live-shacl-validation-v2
# Conflicts:
#	CHANGELOG.md
2026-07-27 18:58:16 +05:30
KaifAhmad1 28fe304f76 fix(ontology): address review follow-ups on live SHACL validation (#804)
- Revert create_ontology silently falling back to a near-empty ontology on
  generation failure; restores the HTTPException(500) behavior from #770/#787
  that this PR had accidentally undone (and re-enables TestOntologyCreateFailures)
- Fold sh:Warning/sh:Info severity pySHACL results into the /shacl/validate
  response's violations array instead of silently dropping them, so a
  non-conforming report is never returned with an empty violations list
- Share a single nodes/edges fetch between _generated_shacl_for_uri and
  _data_graph_turtle_for_uri via new _fetch_analysis_graph(), so /health
  no longer re-queries and re-truncation-checks the same ontology twice
2026-07-27 18:28:11 +05:30
Mohd KaifandSameer6305 9eea49a070 fix(security): restrict Neptune cookbook SG, add VPC flow logs, harden IaC scan suppressions (#806)
* fix(security): restrict Neptune cookbook SG, add VPC flow logs, harden IaC scan suppressions

Addresses open GHAS code scanning alerts:
- Neptune cookbook stack (neptune-setup.yaml) no longer opens the Bolt/OpenCypher
  port to 0.0.0.0/0; a required ClientCidr parameter must be supplied instead.
  Updated 21_Amazon_Neptune_Store.ipynb deploy instructions to match.
- Added VPC Flow Logs (CloudWatch Logs + IAM role) to the same stack.
- Documented why an account-wide IAM password policy resource does not belong
  in a disposable per-learner CFN stack, with a justified ts:skip.
- Added inline `checkov:skip` / `ts:skip` comments to the knowledge-explorer
  Helm templates (deployment/service/configmap) as a second suppression path
  for the CKV_K8S_21/AC_K8S_0086/AC_K8S_0080 false positives, since the prior
  annotation-only suppression was not being honored by the scanner.

* docs(changelog): document the Neptune and Helm chart security scan fixes

* fix(security): correct flow-log IAM scope and ClientCidr regex from review

- FlowLogRole granted logs:CreateLogStream/PutLogEvents on the bare log
  group ARN, but those actions apply to log streams, not the group itself;
  scoped them to "${FlowLogGroup.Arn}:log-stream:*" instead and moved the
  Describe* actions (which don't support group/stream-level resource
  restriction) to Resource: "*", matching AWS's documented flow-log IAM
  policy shape. Without this, flow log delivery could silently fail.
- ClientCidr's AllowedPattern only checked digit count (1-3 digits per
  octet), so malformed values like 999.999.999.999/32 passed parameter
  validation and would only fail later when CloudFormation tried to
  create the security group rule. Tightened the regex to enforce valid
  IPv4 octet ranges (0-255) and prefix lengths (0-32).

* fix(security): harden IAM policy in neptune-setup and standardize Helm chart scan suppressions

- neptune-setup.yaml: split FlowLogRole policy into account-level statement (CreateLogGroup, DescribeLogGroups, DescribeLogStreams with Resource: '*') and log-group-scoped statement (CreateLogStream, PutLogEvents with !GetAtt FlowLogGroup.Arn) per AWS VPC Flow Logs least-privilege documentation.
- deployment.yaml: remove unreliable file-header skip comments (# checkov:skip / # ts:skip) and replace with resource-level metadata.annotations (checkov.io/skip and runterrascan.io/skip). Update seccomp rule ID from CKV_K8S_28 to checkov's actual seccomp rule CKV_K8S_31 on both Deployment and pod-template metadata.
- configmap.yaml / service.yaml: remove stale # ts:skip=AC_K8S_0086 file-header comments and add runterrascan.io/skip resource-level metadata annotations for consistency across all chart templates.
- .checkov.yaml: update documentation to explain resource-level metadata.annotations and reference CKV_K8S_31.

---------

Co-authored-by: Sameer6305 <sskadam6305@gmail.com>
2026-07-27 17:54:38 +05:30
Sameer6305 db95cedf34 fixed qodo reviews and hardened implementation 2026-07-27 15:42:49 +05:30
Sameer6305 3a9c7c082f Merge branch 'main' into fix/773-sparql-test-coverage 2026-07-27 15:08:14 +05:30
Mohd Kaif 4a3cf37679 Merge pull request #802 from Sameer6305/feat/provenance-shared-storage-wiring
feat(provenance): implement global default storage pattern and fix CLI lineage integration
2026-07-27 15:06:29 +05:30
Sameer6305 dd7b090aec test(explorer): add coverage for SPARQL route (#773)
sparql.py handles direct SPARQL query execution against the live graph with no test coverage anywhere in the repo. Adds coverage for the read-only allowlist (the actual security boundary here), row/timeout limits, error handling, and RDF projection fidelity.
2026-07-27 15:00:47 +05:30
KaifAhmad1 eaa0f823a8 Merge remote-tracking branch 'origin/main' into pr-802
# Conflicts:
#	CHANGELOG.md
2026-07-27 14:24:46 +05:30
KaifAhmad1 7045d7b94e fix(provenance): address review nits and add CHANGELOG entry
- track_entity() no longer aliases a caller-supplied used_entities list
  (it stored the reference directly and later mutated it via .append())
- Remove dead fallback branches in orchestrator.py/manager.py that
  duplicated what Config.get()'s dotted-path resolution already does
- Add local --dry-run to `provenance audit` for parity with
  `provenance export`
- `provenance check --strict` now warns instead of printing a success
  checkmark before raising on a failed check
2026-07-27 14:19:51 +05:30
Sameer6305 8430d4a56e fix(ontology): address qodo review findings for SHACL validation
- DoS guardrails: enforce byte size, triple count, concurrency, and timeout limits on /shacl/validate

- Slash namespace resolution: preserve trailing slash in _resolve_uri so local terms match SHACL shapes

- Truncation safety: raise GraphTruncationError and report unavailable/413/critical when graphs exceed analysis limits

- JSON-LD dict lists: unwrap uri/id/@id in _as_uri_list and _data_graph_turtle_for_uri property loops

- Observability & efficiency: add warning logs on truncation and avoid duplicate UTF-8 encoding in size check
2026-07-27 14:01:45 +05:30
Mohd Kaif ea71ea1823 Merge pull request #786 from SaurabhScripts/codex/agent-memory-markdown-round-trip
Add Markdown round-trip support to AgentMemory
2026-07-27 13:19:56 +05:30
Sameer6305 3a1ab1a8a6 fix(ontology): wire live SHACL validation into /shacl/validate and /health
Closes #772
2026-07-27 13:02:04 +05:30
KaifAhmad1 87714ec1ad Merge remote-tracking branch 'origin/main' into codex/agent-memory-markdown-round-trip
# Conflicts:
#	CHANGELOG.md
2026-07-27 12:51:12 +05:30
KaifAhmad1 1c590c622b docs(changelog): document AgentMemory Markdown round-trip support
Add an Unreleased/Added entry for #786 covering the new export/import
Markdown format, idempotency and rollback guarantees, and the
Explorer/ContextGraph scoping decision from #765.
2026-07-27 12:48:10 +05:30
Sameer6305 40d6fa05d0 fix(context): normalize timestamps in _markdown_record_matches for idempotency 2026-07-26 18:49:30 +05:30
Sameer6305 ac69c27b86 fixes reviews from qodo free for open source 2026-07-26 16:56:06 +05:30
Sameer6305 a16bd9600b fixes reviews from qodo free for open source 2026-07-26 16:46:50 +05:30
Sameer6305 fd84be8b66 fixed qodo reviews 2026-07-26 16:26:47 +05:30
Sameer6305 6cc2e67b92 fix(provenance): populate entries alias in get_lineage and read lineage_chain in lineage()
- Add entries alias in get_lineage() return dictionary so CLI and programmatic callers can access lineage entries via either key

- Update lineage() wrapper method to fallback to lineage_chain when entries is missing

- Add assertions in test_cli_lineage confirming lineage and entries lists are non-empty
2026-07-26 16:10:26 +05:30
Sameer6305 2dd06aadcd feat(provenance): implement Global Default Storage pattern, CLI methods, and orchestrator config wiring
- Add _default_storage_path, set_default_storage_path(), and test-isolation context manager default_storage_path() in ProvenanceManager

- Accept config kwarg in ProvenanceManager.__init__ to fix CLI initialization bug

- Implement audit_log(), lineage(), export_prov(), and check() on ProvenanceManager matching cli.py expectations

- Wire provenance.storage_path in Semantica.__init__ before pipeline stages execute

- Add comprehensive unit tests in tests/provenance/test_manager.py for CLI methods and test isolation
2026-07-26 16:01:50 +05:30
Mohd Kaif 86db4f923d Merge pull request #796 from Sameer6305/fix/769-lint-effect-setstate
Fix #769: Eradicate react-hooks/set-state-in-effect cascading renders project-wide
2026-07-26 13:50:16 +05:30
KaifAhmad1 dcd936a9ab fix: restore error surfacing dropped by inlined mount-effect fetches
The set-state-in-effect refactor inlined each initial-fetch effect as a
standalone `fetchInitial`, duplicating the logic of the existing
reload/fetchOverview/fetchRegistry/loadVersions callbacks instead of
reusing them (required, since eslint-plugin-react-hooks v7 flags calling
an outside setState-touching function directly from an effect body, even
through an async gap - verified via a local lint probe). The duplicates
dropped the setError/flashMsg calls the originals had, so a failed
initial page load in AlignmentsTab, KGOverviewTab, OntologyManager, and
VersionsTab now failed silently instead of showing an error - a
regression of the exact bug #767/#790 fixed for these same files.

Also fixes LineageDiagram only clearing nodes/edges when the new
activeId was falsy, leaving the previous lineage view's stale diagram
on screen while switching directly between two ids.
2026-07-26 13:40:16 +05:30
KaifAhmad1 b663c6bbbf Merge branch 'main' into fix/769-lint-effect-setstate 2026-07-26 13:22:45 +05:30
Saurabh Meena 5ab21c089e Address AgentMemory Markdown review feedback 2026-07-26 09:42:23 +05:30
Mohd Kaif 84775fdea0 Merge pull request #801 from semantica-agi/fix/779-checkov-default-namespacet
fix: suppress CKV_K8S_21 default-namespace false positive on knowledge-explorer Helm chart
2026-07-25 18:24:40 +05:30
Sameer6305 2a0bc7051a fix(security): switch to metadata.annotations for CKV_K8S_21 suppressions 2026-07-25 17:45:53 +05:30
KaifAhmad1 8beca57238 fix: wrap checkov:skip comment to respect yamllint's 120-char line-length limit
The single-line checkov:skip=CKV_K8S_21 comment added in ed44260 was 286
characters, exceeding the repo's yamllint line-length rule (max 120,
.pre-commit-config.yaml). Split into three short comment lines: the skip
directive itself, then the rationale, in service.yaml, deployment.yaml,
and configmap.yaml.
2026-07-25 16:59:26 +05:30
KaifAhmad1 ed44260ec3 fix: suppress CKV_K8S_21 false positive on knowledge-explorer Helm chart
Checkov's helm framework renders the chart without a namespace override,
so metadata.namespace (set to .Release.Namespace, bound only at install
time) always resolves to "default" and trips CKV_K8S_21 on service.yaml,
deployment.yaml, and configmap.yaml even though the chart is
namespace-agnostic by design.

Suppressed via per-file checkov:skip comments, following the same
convention already used for the Cloud Run false positives in
deploy/gcp/cloudrun-service.yaml.
2026-07-25 16:49:25 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 8f09d4f57b chore(deps): bump dompurify from 3.4.11 to 3.4.12 in /explorer (#800)
Bumps [dompurify](https://github.com/cure53/DOMPurify) from 3.4.11 to 3.4.12.
- [Release notes](https://github.com/cure53/DOMPurify/releases)
- [Commits](https://github.com/cure53/DOMPurify/compare/3.4.11...3.4.12)

---
updated-dependencies:
- dependency-name: dompurify
  dependency-version: 3.4.12
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-25 16:36:25 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 7e05f196b5 chore(deps): bump postcss from 8.5.10 to 8.5.23 in /explorer (#799)
Bumps [postcss](https://github.com/postcss/postcss) from 8.5.10 to 8.5.23.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.5.10...8.5.23)

---
updated-dependencies:
- dependency-name: postcss
  dependency-version: 8.5.23
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-25 16:33:19 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 47c7f4adbe chore(deps): bump brace-expansion and eslint in /explorer (#797)
Bumps [brace-expansion](https://github.com/juliangruber/brace-expansion) to 5.0.8 and updates ancestor dependency [eslint](https://github.com/eslint/eslint). These dependencies need to be updated together.


Updates `brace-expansion` from 5.0.6 to 5.0.8
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/v5.0.6...v5.0.8)

Updates `eslint` from 9.39.4 to 10.8.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v9.39.4...v10.8.0)

---
updated-dependencies:
- dependency-name: brace-expansion
  dependency-version: 5.0.8
  dependency-type: indirect
- dependency-name: eslint
  dependency-version: 10.8.0
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-25 16:31:43 +05:30
Mohd KaifandKaifAhmad1 0698ba7656 fix(#768): Prevent application crashes by wrapping workspaces in Error Boundaries (#794)
* fix(#768): add ErrorBoundary to workspace Suspense blocks

* fix(#768): ensure ErrorBoundary retryCount only resets on recovery transition

* fix(#768): remove componentDidUpdate auto-reset to avoid premature reset on Suspense fallback

* fix(#768): reset ErrorBoundary retryCount only after a retry settles

Previously retryCount never reset on success (removed in adb4613 to
avoid resetting mid-Suspense-fallback), so unrelated transient errors
across a session could permanently exhaust the 3-retry budget even
though each prior retry had actually recovered. Now the counter resets
via a short settle timer after a retry stays error-free, avoiding both
the premature-reset and never-reset failure modes.

---------

Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-07-25 16:15:21 +05:30
KaifAhmad1 33d8f806c2 Merge remote-tracking branch 'origin/main' into fix/768-error-boundaries-review
# Conflicts:
#	CHANGELOG.md
2026-07-25 16:11:15 +05:30
KaifAhmad1 530e297d17 fix(#768): reset ErrorBoundary retryCount only after a retry settles
Previously retryCount never reset on success (removed in adb4613 to
avoid resetting mid-Suspense-fallback), so unrelated transient errors
across a session could permanently exhaust the 3-retry budget even
though each prior retry had actually recovered. Now the counter resets
via a short settle timer after a retry stays error-free, avoiding both
the premature-reset and never-reset failure modes.
2026-07-25 16:09:07 +05:30
Sameer6305 be2f8f8cd8 Implement global default storage pattern for ProvenanceManager 2026-07-24 23:27:58 +05:30
Sameer6305 a2f10dfbdd trigger CI re-run for failed runners 2026-07-24 23:16:10 +05:30
Sameer6305 495c2a2fbd fixes qodo reviews 2026-07-24 21:39:57 +05:30
Sameer6305 eb8156ddb3 Fix GraphWorkspace infinite render loop by tracking stringified open panel IDs 2026-07-24 21:19:54 +05:30
Sameer6305 1c3d2b949f Merge main to fix conflicts 2026-07-24 21:09:34 +05:30
Sameer6305 343d2bc418 Fix #769: Resolve all react-hooks/set-state-in-effect lint errors project-wide 2026-07-24 20:56:11 +05:30
Mohd Kaif 297d959f63 Merge pull request #790 from Sameer6305/fix/767-frontend-silent-failures
Fix #767: Harden workspaces against silent failures and handle 207 Partial Success
2026-07-24 16:39:26 +05:30
KaifAhmad1 161d47f4d9 Merge remote-tracking branch 'origin/main' into fix/767-frontend-silent-failures
# Conflicts:
#	CHANGELOG.md
2026-07-24 16:29:56 +05:30
KaifAhmad1 99b0a517fd Fix remaining silent-failure gaps flagged in review of #790
KGOverviewTab dropped the nodes-fetch 207 warning whenever stats also
returned 207; HealthTab and AlignmentsTab still had the exact
silent-swallow pattern this PR set out to fix elsewhere in the same
folder. Also documents all of #790's fixes in the changelog.
2026-07-24 16:25:21 +05:30
Sameer6305 adb46134c4 fix(#768): remove componentDidUpdate auto-reset to avoid premature reset on Suspense fallback 2026-07-24 16:15:34 +05:30
Sameer6305 d2d38a0509 fix(#768): ensure ErrorBoundary retryCount only resets on recovery transition 2026-07-24 16:11:14 +05:30
Sameer6305 9a21e523f0 fix(#768): add ErrorBoundary to workspace Suspense blocks 2026-07-24 15:53:33 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ca11a48fef deps(deps): update httpx requirement from <0.28.0 to <0.29.0 (#791)
Updates the requirements on [httpx](https://github.com/encode/httpx) to permit the latest version.
- [Release notes](https://github.com/encode/httpx/releases)
- [Changelog](https://github.com/encode/httpx/blob/master/CHANGELOG.md)
- [Commits](https://github.com/encode/httpx/compare/0.0.1...0.28.1)

---
updated-dependencies:
- dependency-name: httpx
  dependency-version: 0.28.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-24 15:41:00 +05:30
Mohd KaifandKaifAhmad1 aa171c16f1 Fix #788: pin httpx<0.28.0 globally to fix Explorer test suite TestClient breakage (#789)
* Fix #788: pin httpx<0.28.0 globally to fix TestClient breakage

Adds an explicit httpx<0.28.0 constraint to [project.dependencies] so it
applies globally across all environments, not just dev. Without this,
different environments could resolve an incompatible transitive httpx
version and hit the same Starlette TestClient breakage independently.

Verified via git stash comparison: the unmodified baseline fails to even
collect the test suite (TestClient TypeError during collection), so this
pin doesn't just fix tests, it's what allows the full suite to run at all.

Closes #788

* Add CHANGELOG entry for #788 httpx pin fix

Documents the httpx<0.28.0 global pin from this PR under Unreleased/Fixed.

---------

Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-07-24 13:18:36 +05:30
KaifAhmad1 cb5e93ebc7 Merge remote-tracking branch 'origin/main' into fix/788-httpx-pin
# Conflicts:
#	CHANGELOG.md
2026-07-24 13:13:41 +05:30
KaifAhmad1 a256a77277 Add CHANGELOG entry for #788 httpx pin fix
Documents the httpx<0.28.0 global pin from this PR under Unreleased/Fixed.
2026-07-24 13:10:59 +05:30
Sameer6305 e7696f462a fixing qodo findings 2026-07-24 00:38:49 +05:30
Sameer6305 d6c7154fa9 Fix #767: Harden workspaces against silent error swallowing and 207 statuses
Ensures network errors, API failures, and 207 Partial Success statuses are surfaced correctly to the user instead of failing silently in the frontend UI.
2026-07-24 00:16:46 +05:30
Mohd Kaif b473e0bd8a Merge pull request #787 from Sameer6305/fix/770-explorer-200-on-failure
Fix #770: Explorer backend routes return proper error status codes instead of 200 OK on failure
2026-07-23 18:26:13 +05:30
KaifAhmad1 b1deed5857 Address review: harden analytics status codes, add failure-path tests
207 alone is indistinguishable from 200 to callers that only check
response.ok, so /api/analytics now raises 500 when every requested
metric fails and reserves 207 for genuine partial failure. Adds
regression tests for the temporal, analytics, and ontology-create
failure paths introduced in this PR, and logs the fix in the
changelog's Unreleased section.
2026-07-23 18:13:26 +05:30
Sameer6305 637bfe7314 Fix #788: pin httpx<0.28.0 globally to fix TestClient breakage
Adds an explicit httpx<0.28.0 constraint to [project.dependencies] so it
applies globally across all environments, not just dev. Without this,
different environments could resolve an incompatible transitive httpx
version and hit the same Starlette TestClient breakage independently.

Verified via git stash comparison: the unmodified baseline fails to even
collect the test suite (TestClient TypeError during collection), so this
pin doesn't just fix tests, it's what allows the full suite to run at all.

Closes #788
2026-07-23 16:36:58 +05:30
Sameer6305 9b7a33031c solving qodo review 2026-07-23 15:34:53 +05:30
Sameer6305 443a9b78d7 Fix #770: Explorer backend routes return proper error status codes instead of 200 OK on failure
- routes/temporal.py: temporal_patterns raises HTTPException(500) instead of
  silently returning an empty-but-valid TemporalPatternResponse on exception
- routes/analytics.py: preserves existing partial-success body shape
  (frontend already parses this), but sets response.status_code = 207 when
  any individual metric computation fails, so callers get a real signal
  instead of an indistinguishable 200
- routes/ontology.py: POST /create now raises HTTPException(500) on
  generation failure instead of silently falling back to a partial/minimal
  ontology and returning 200 with a misleading nodes_added count

Verified via git stash comparison that pre-existing test suite failures
(58 errors, Starlette TestClient/httpx version mismatch) are unrelated to
this change - identical failure count on modified and unmodified code.

Closes #770
2026-07-23 15:09:44 +05:30
Saurabh Meena 36856cc92a Add Markdown round-trip support to AgentMemory 2026-07-22 22:56:16 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 6e4ff0c7c5 ci(deps): bump actions/setup-node from 6 to 7 (#760)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 6 to 7.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-22 14:30:44 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 095d7d3e52 ci(deps): bump actions/setup-dotnet from 5 to 6 (#759)
Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 5 to 6.
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](https://github.com/actions/setup-dotnet/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/setup-dotnet
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-22 14:19:16 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1b706e539f ci(deps): bump actions/setup-python from 4 to 7 (#758)
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 7.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v4...v7)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-22 14:11:25 +05:30
KaifAhmad1 9c9ab6a23f chore: bump version to 0.6.0
Promotes the Unreleased changelog section (Databricks connector, SQLite
vector store, SPARQL CONSTRUCT templates, JenaStore named-graph support)
to 0.6.0 and syncs version references across pyproject.toml, __init__.py,
and docs.
2026-07-21 16:04:18 +05:30
Mohd Kaif 47db03c72f Merge pull request #766 from semantica-agi/readme-merge-platform-reference
docs: merge PLATFORM_REFERENCE.md into README, audit examples against source
2026-07-21 13:00:55 +05:30
KaifAhmad1 4119c21b6e fix: correct schema mismatches and invalid enum values in README examples
- TemporalGraphQuery.query_time_range() and RDFExporter.export() both
  expect {entities/relationships} (or {relationships} with source_id/
  target_id keys), not ContextGraph.to_dict()'s {nodes, edges} shape.
  Map the output before passing it in, and add an actual temporally-
  bounded edge to the Temporal Intelligence example so the query has
  something to find.
- add_causal_relationship() only accepts relationship_type values of
  CAUSED, INFLUENCED, or PRECEDENT_FOR; replace the invented "triggers"/
  "enables" values used in Decision Intelligence and the audit-trail
  recipe, which would otherwise raise ValueError immediately.
2026-07-21 12:53:51 +05:30
KaifAhmad1 6cf5504585 fix: correct pipeline example chaining in README
PipelineBuilder.add_step() returns the created PipelineStep, not the
builder, so chaining .add_step().add_step() raised AttributeError.
Only connect_steps() and set_parallelism() return the builder and can
be chained.
2026-07-21 12:48:34 +05:30
KaifAhmad1 f4f077f443 docs: merge PLATFORM_REFERENCE.md into README and audit examples against source
Consolidates the platform reference into a single, premium README with
collapsible module/recipe sections so the docs and the deep-dive reference
no longer live in two places. Every code example was checked against the
actual semantica/ source and corrected where the API had drifted:
resolve_conflicts, register_source, ValidationResult.valid, clean_data,
execute_pipeline, ParquetExporter/LPGExporter/ReportGenerator calls,
graph.to_dict(), TemporalGraphQuery/TemporalNormalizer usage, the
Reasoner/ExplanationGenerator API, and the REST endpoint paths. Also
removed duplicated titles, snippets, and repeated example scenarios that
had crept in during the merge.
2026-07-21 12:40:24 +05:30
Mohd Kaif 4a1dab8062 Merge pull request #764 from semantica-agi/fix/codeql-econnreset-retry
ci: retry CodeQL init on transient bundle-download ECONNRESET
2026-07-20 21:32:44 +05:30
KaifAhmad1 836eff3e55 ci: retry CodeQL init on transient bundle-download ECONNRESET
The CodeQL Analyze Python job failed on the #757 merge commit with
ECONNRESET while streaming the CodeQL bundle download in
codeql-action/init's "Setup CodeQL tools" step. This is unrelated to
the merged code — it's a known, currently-unaddressed gap in
codeql-action: the download error is retryable but the action doesn't
retry it internally (confirmed via codeql-action's issue tracker and
changelog).

Since a `uses:` step can't be wrapped by a shell-level retry action,
Initialize CodeQL now runs up to 3 times, cascading to the next
attempt only if the previous one failed, so the common case (success
on attempt 1) costs nothing extra.
2026-07-20 21:27:11 +05:30
Mohd Kaif 1b87da7ce3 Merge pull request #757 from Sameer6305/feat/756-jena-named-graphs
Add named-graph support to JenaStore via Dataset migration (#756)
2026-07-20 21:22:26 +05:30
KaifAhmad1 51953a0367 fix: scope delete_triplet to the default graph only
Dataset.remove() on a bare 3-tuple resolves context=None internally,
which the underlying store treats as a wildcard and deletes the
matching triple from every graph, not just the default graph the
docstring promises. Pass self.graph.default_graph explicitly as the
context so delete_triplet stays scoped to the default graph, matching
the isolation guarantee default_union=False is meant to provide.

Also corrects a misleading comment: SPARQLStore is graph_aware=True
too, so graph-awareness isn't what requires SPARQLUpdateStore here —
it's SPARQLStore being read-only (.add()/.remove() raise TypeError).

Adds regression tests and a CHANGELOG entry for PR #757.
2026-07-20 19:40:23 +05:30
Mohd Kaif 62a0a55be7 Update README with new features and organization 2026-07-20 18:10:16 +05:30
Mohd Kaif 48b9cb7d33 Update README to remove listed domains
Removed specific domains from the built for section.
2026-07-20 17:22:37 +05:30
Mohd Kaif 0a05e9d936 docs: refresh README hero with premium tagline and regulated-domains callout (#763)
Updates the tagline, adds Ontology Management/SKOS to the feature pills, swaps
the yellow-highlight subhead for a cleaner italic style, and surfaces a
regulated-domains teaser linking to the existing "Built for High-Stakes
Domains" section.
2026-07-20 17:21:34 +05:30
Mohd Kaif 4aab2a0248 Update README with enhanced formatting and content 2026-07-20 15:53:50 +05:30
Mohd Kaif 3b3463eae3 docs: rewrite README around a sharper narrative, split module reference out (#761)
Trims the README from a full module/API dump into a scannable pitch (hero,
why-Semantica, quick start, architecture, decision intelligence, one flagship
audit-trail recipe) and moves the exhaustive per-module reference, extra
recipes, and full integrations matrix into a new PLATFORM_REFERENCE.md.
2026-07-20 15:30:21 +05:30
Sameer6305 614222ae87 fix: address three Qodo review findings in JenaStore
1. Endpoint derivation regression: Detect if self.endpoint already contains
   a Fuseki service suffix (/query, /update, /sparql) to prevent double-appending
   (e.g., /ds/query/query). If it does, derive the base and construct both
   paths properly.
2. Misleading serialize warning: Limit the named-graph data loss warning
   to single-graph serializer formats (turtle, xml, n3, etc.). Multi-graph
   formats (trig, nquads, nt) will correctly serialize all graphs without warning.
3. Zero-added error misdiagnosis: Track malformed triples accurately in
   add_triplets(). If every triplet fails the local validation (ValueError/
   AttributeError), raise a formatting-oriented ProcessingError instead of
   assuming a store connectivity issue.

Includes comprehensive regression tests for all three cases.
2026-07-20 14:04:36 +05:30
Sameer6305 a9559a6d7a feat: migrate JenaStore from Graph to Dataset(default_union=False) for named-graph support
Migrates JenaStore from rdflib.Graph to rdflib.Dataset with default_union=False
explicitly set, per maintainer-confirmed architecture for issue #756.

Changes:

- _initialize_graph: construct self.graph as Dataset(default_union=False) for
  the in-memory path, and Dataset(store=SPARQLUpdateStore(...), default_union=False)
  for the remote path.  SPARQLUpdateStore.graph_aware=True satisfies Dataset's
  hard requirement.  Both paths verified against rdflib source.

- add_triplets: accept and honor graph= option.  When supplied, Dataset.graph(uri)
  creates/retrieves the named-graph context and the triple is written via a
  4-tuple (which SPARQLUpdateStore maps to INSERT DATA { GRAPH <uri> { ... } }).
  When graph= is omitted, the 3-tuple path routes to Dataset's default graph,
  preserving pre-migration semantics exactly.

- serialize: add WARNING log when named-graph content would be silently dropped
  by a single-graph serializer (turtle/xml/n3).  Log includes triple count and
  recommends trig/nquads formats.  No warning when only the default graph is used.

- create_model: document that triplet_count now counts triples across all graphs
  (default + named) as a consequence of this migration.  Semantics shift made
  visible, not silent.

- delete_triplet: document that graph= parity is a known gap, deferred to a
  future follow-up per maintainer's stated scope (add_triplets only).

Decisions applied:
  1. triplet_count semantics shift: documented in create_model docstring
  2. delete_triplet graph= parity: explicitly out of scope, noted in docstring
  3. Existing store.graph=Graph() tests: left unchanged; new tests added
     to cover the real _initialize_graph path

Tests added (TestJenaStoreDatasetMigration):
- test_initialize_graph_produces_dataset_not_graph
- test_initialize_graph_dataset_has_default_union_false
- test_add_triplets_with_graph_option_writes_to_named_graph
- test_add_triplets_without_graph_option_writes_to_default_graph
- test_add_triplets_named_graph_isolated_from_default_query
- test_serialize_logs_warning_when_named_graph_content_present
- test_serialize_no_warning_when_only_default_graph_used

Also updated test_add_triplets_remote_endpoint_fires_insert_data_via_update_store
to patch Dataset instead of Graph (the remote path now creates Dataset(store=...)).

Full suite: 269 passed, 0 failed (tests/triplet_store/ + tests/pipeline/)
2026-07-20 13:38:16 +05:30
Sameer6305 e3931e0923 docs: update construct_templates docstring to reflect dual add_triplets failure signalling
The exception-propagation comment and Raises docstring in
execute_construct_template stated that add_triplets signals failure
exclusively via a returned dict. This became stale after the JenaStore fix
(previous commit) which introduced ProcessingError propagation for complete
batch failures.

Updated to document both paths:
- dict-based failure: success=False in returned dict (BlazegraphStore, RDF4J, etc.)
- raised ProcessingError: JenaStore full-batch failure now raises directly

No logic changed. 262 tests pass.
2026-07-20 13:21:58 +05:30
Sameer6305 10e26cb570 fix: JenaStore remote endpoint uses SPARQLUpdateStore instead of read-only SPARQLStore
The remote-endpoint path in _initialize_graph was instantiating the read-only
rdflib SPARQLStore, causing every add_triplets() call against a remote Fuseki
endpoint to silently fail: SPARQLStore.add() raises TypeError which was swallowed
by the broad except Exception per-triplet handler and returned as success=True/added=0.

Changes:
- Import SPARQLUpdateStore alongside SPARQLStore
- _initialize_graph: use SPARQLUpdateStore(query_endpoint=<base>/query,
  update_endpoint=<base>/update) per standard Fuseki REST API conventions
- Fix constructor: self.endpoint=config.get('endpoint') always returned None
  because the named positional 'endpoint' param captures the kwarg before **config;
  now uses endpoint or config.get('endpoint')
- Narrow per-triplet except to (ValueError, AttributeError); add ProcessingError
  when entire batch fails to prevent misleading success=True/added=0 return

Tests added (TestJenaStoreRemoteEndpointUsesUpdateStore): 4 new test cases

Full suite: 262 passed (tests/triplet_store/ + tests/pipeline/)
2026-07-20 13:14:55 +05:30
Mohd Kaif 219ebd0631 Merge pull request #755 from Sameer6305/feat/754-rdf4j-jena-construct
Add SPARQL CONSTRUCT template support to RDF4J backend (#754)
2026-07-19 22:42:11 +05:30
KaifAhmad1 d781d052c2 docs: update CHANGELOG for RDF4J/Jena CONSTRUCT support (#755) 2026-07-19 22:37:24 +05:30
KaifAhmad1 d98135d9b5 fix: RDF4JStore serializes plain literals as invalid IRIs
_format_object_for_ntriples decided IRI vs. literal purely from the
presence of datatype/lang metadata, defaulting anything without it to
<obj>. Any plain literal object (e.g. typical NER/extraction output
like "Alice", or an untyped Turtle literal round-tripped through the
new CONSTRUCT path) was wrapped as an invalid IRI instead of a quoted
literal, diverging from BlazegraphStore's _is_uri_value-first check.

Port _is_uri_value from BlazegraphStore so RDF4JStore checks whether
the object is actually URI-shaped before falling back to literal
handling, with a plain-quoted-literal fallback instead of <obj>.
2026-07-19 22:35:01 +05:30
Sameer6305 77026122fc Add SPARQL CONSTRUCT support to Jena backend (#754)
Extends CONSTRUCT support to JenaStore, which uses rdflib.Graph natively rather
than an HTTP protocol - CONSTRUCT results come as native 3-tuples with no
Accept-header/parsing dance needed, unlike Blazegraph/RDF4J.

- CONSTRUCT-aware execute_sparql: reuses shared sparql_escaping.CONSTRUCT_QUERY_RE,
  extracts datatype/language from rdflib Literal objects into the same 4-tuple
  metadata contract used by Blazegraph/RDF4J
- Non-CONSTRUCT path (SELECT/ASK) confirmed byte-for-byte unchanged (Property 9)
- execute_construct_template confirmed backend-agnostic against JenaStore, zero
  changes needed
- Named-graph support explicitly out of scope - JenaStore wraps a single
  rdflib.Graph with no named-graph concept; add_triplets continues to silently
  ignore graph= exactly as before. Tracked separately as a follow-up issue
  requiring a Graph -> ConjunctiveGraph/Dataset migration.
2026-07-19 17:26:28 +05:30
Sameer6305 b3245613f5 Address Qodo review: fix literal serialization corruption in add_triplets, validate context graph URI, validate result_format 2026-07-19 16:59:34 +05:30
Sameer6305 a0462269db Add SPARQL CONSTRUCT template support to RDF4J backend (#754)
Extends the Blazegraph-only CONSTRUCT support from #322 (commit 4f2c6c82's
approved pattern) to RDF4JStore:
- CONSTRUCT-aware execute_sparql: Accept: text/turtle, rdflib Turtle parsing,
  4-tuple (s, p, o, metadata) contract with datatype/language preservation
- Named-graph writes via RDF4J's REST context parameter, N-Triples-encoded
  (angle-bracket-wrapped IRI), confirmed against RDF4J's Protocol.java source
- graph=None preserves existing behavior exactly (no context param sent,
  not context=null - verified as a distinct, deliberate choice)
- _CONSTRUCT_QUERY_RE moved to sparql_escaping.py as a shared, backend-agnostic
  constant; Blazegraph now delegates to it, zero behavioral change confirmed
- execute_construct_template (construct_templates.py) required zero changes -
  confirmed backend-agnostic via end-to-end integration tests against RDF4JStore

29 new tests, full suite 245/245 passing. Jena support remains out of scope
for this PR - tracked separately in #754's remaining scope.
2026-07-19 16:34:37 +05:30
Mohd Kaif c6acd62380 Merge pull request #752 from Sameer6305/feat/322-construct-templates
Add SPARQL CONSTRUCT query templates (Blazegraph-only)
2026-07-19 15:48:56 +05:30
KaifAhmad1 a1b38efbd8 Merge remote-tracking branch 'origin/main' into pr-752-review
# Conflicts:
#	CHANGELOG.md
2026-07-19 15:35:25 +05:30
Sameer6305 ec7979b6ca Add CHANGELOG entry for SPARQL CONSTRUCT templates (#322) 2026-07-18 21:44:28 +05:30
Mohd Kaif 638a8c60df Merge pull request #753 from semantica-agi/chore/update-org-metadata
chore: update package organization and maintainer email
2026-07-18 19:06:49 +05:30
KaifAhmad1 084fb44f05 fix: update stale org and email references in SECURITY.md and SUPPORT.md
Replace remaining Hawksight-AI GitHub org links and the old
semantica-dev noreply email with the current semantica-agi org
and kaif@getsemantica.ai contact, so security/support contacts
match pyproject.toml.
2026-07-18 18:44:10 +05:30
KaifAhmad1 4e973fcc30 chore: update package organization and maintainer email
Replace Hawksight AI with Semantica as the project author/maintainer,
and update the contact email to kaif@getsemantica.ai.
2026-07-18 18:35:56 +05:30
Mohd Kaif 4daa8ff3a7 Merge pull request #748 from semantica-agi/feat/747-databricks-connector
Add Databricks connector (Unity Catalog + Delta Lake ingestion)
2026-07-18 18:10:00 +05:30
Sameer6305 cb213ee371 Add pipeline-level target_graph regression test (addresses Qodo #6) 2026-07-18 14:59:37 +05:30
Sameer6305 4f2c6c8229 Address Qodo review: reject unknown params, preserve literal datatype/lang, check backend success, fix options collision, tighten CONSTRUCT detection, fix docs, add validator integration for construct_template steps 2026-07-18 14:44:33 +05:30
Sameer6305 c4e971c91c Add SPARQL CONSTRUCT query templates (Blazegraph-only)
Implements #322: ConstructTemplate/ParameterDescriptor/ConstructTemplateRegistry
with injection-safe {{param}} rendering, Blazegraph CONSTRUCT-aware execute_sparql
extension, execute_construct_template (render->execute->parse->persist), and a
construct_template pipeline step. RDF4J/Jena support deferred to a follow-up issue.

Closes #322
2026-07-18 12:33:04 +05:30
Mohd Kaif 28b71c922f Merge pull request #751 from semantica-agi/deprecate/744-kg-provenance-tracker
Deprecate kg.ProvenanceTracker and remove tests for unimplemented compatibility APIs
2026-07-17 15:52:43 +05:30
KaifAhmad1 3806883093 docs: add CHANGELOG entry for kg.ProvenanceTracker deprecation (#744)
Documents the 9 pre-existing test failures caused by never-implemented
kg.ProvenanceTracker compatibility methods, the deprecation fix, and
the follow-up migration guide addition in this PR.
2026-07-17 15:46:01 +05:30
KaifAhmad1 581dbf8301 docs: add missing kg.ProvenanceTracker migration guide
Every deprecation warning added in this PR (and the class docstring)
points to docs/migration/kg-provenance-tracker.md, but the file was
never added, so the reference was dead. Adds the guide with a
method-mapping table to semantica.provenance.ProvenanceManager.
2026-07-17 15:40:30 +05:30
Sameer Kadam 738698a75b Use pytest.approx for float sum comparison in test_llm_cost_tracking (fixes #745) (#746) 2026-07-17 12:46:17 +05:30
Sameer6305 fd7ac7465c test: strengthen KG provenance coverage and avoid duplicate deprecation warnings 2026-07-16 23:43:43 +05:30
Sameer6305 947ecf186a Deprecate kg.ProvenanceTracker in favor of ProvenanceManager 2026-07-16 22:44:39 +05:30
Sameer Kadam 18c8ba58ef docs: improve MCP server guide onboarding and integration guidance (#704)
* docs: improve MCP server guide onboarding and integration guidance

* docs: fix MCP server implementation mismatches
2026-07-16 21:51:55 +05:30
Sameer6305 bdcbaa3173 Fix OAuth M2M auth using credentials_provider instead of unsupported client_id/client_secret kwargs for sql.connect() (addresses Codex P1) 2026-07-16 20:11:58 +05:30
Mohd Kaif c843f09cb5 docs(readme): simplify hero line to just Polyglot Graph Storage (#750) 2026-07-16 13:09:34 +05:30
Mohd Kaif 5909f23180 Merge pull request #749 from semantica-agi/readme/rdf-lpg-highlight
docs(readme): highlight dual RDF + LPG graph storage support
2026-07-16 13:02:34 +05:30
KaifAhmad1 d71d4191aa docs(readme): fix Qodo review findings on backend install docs and terminology
Add graph-apache-age extra (psycopg2-binary) which was previously
undeclared despite age_store.py depending on it, wire it into
graph-all, and document install commands for FalkorDB/AGE/Neptune
alongside Neo4j. Note that RDF triple stores need no extra since they
talk SPARQL over HTTP via the core `requests` dependency. Also align
README's "Triplet Stores" table label to "Triple Stores (RDF)" to
match the standard term used elsewhere in the docs, while keeping the
TripletStore interface name in backticks.
2026-07-16 12:57:30 +05:30
KaifAhmad1 5b357c47cc docs(readme): highlight dual RDF + LPG graph storage support
Semantica already ships both an RDF triplet-store stack (Blazegraph,
Apache Jena, Eclipse RDF4J via a unified TripletStore/SPARQL interface)
and an LPG graph-store stack (Neo4j, FalkorDB, Apache AGE, AWS Neptune
via Cypher), but the README only surfaced the LPG side. Add a hero
highlight line, a "What Semantica gives you" bullet, and split the
Features-at-a-Glance table row so both formats and all backends are
named explicitly.
2026-07-16 12:49:29 +05:30
KaifAhmad1 2d5bd18fa4 Address review: column lineage, connection reuse, UC name validation
- get_table_lineage() gains include_column_lineage=True, resolving
  per-column upstream/downstream references via Unity Catalog's
  column-lineage API (one request per column, opt-in)
- DatabricksConnector.connect() now reuses an already-open connection
  instead of opening a second one; ingest_table()/ingest_query() only
  close the connection they opened themselves, so using the ingestor
  as a context manager no longer leaks the connection opened by
  __enter__
- get_table_schema()/get_table_lineage()/list_tables() now validate
  both catalog and schema are resolved before calling Unity Catalog,
  matching list_tables()'s existing catalog check
- 8 new regression tests (35 total)
2026-07-15 22:25:39 +05:30
KaifAhmad1 d74b650643 Add Databricks connector (Unity Catalog + Delta Lake ingestion)
Adds DatabricksIngestor to semantica/ingest/, mirroring SnowflakeIngestor's
structure and public API shape: table/query ingestion via
databricks-sql-connector, Unity Catalog metadata and lineage via
databricks-sdk, and export-as-documents for KG construction.

Closes #747
2026-07-15 22:07:25 +05:30
Mohd Kaif fabff5d9ec Merge pull request #743 from semantica-agi/fix/742-retrack-parent-override
Fix track_entity re-track silently overriding explicit parent_entity_id/derived_from
2026-07-15 15:49:18 +05:30
KaifAhmad1 c90b7fb02b Merge remote-tracking branch 'origin/main' into fix/742-retrack-parent-override
# Conflicts:
#	CHANGELOG.md
2026-07-15 15:44:47 +05:30
KaifAhmad1 869083e0f6 Avoid duplicating archived history id in used_entities when no explicit parent was supplied; add CHANGELOG entry for #742
Review follow-up: only append archived_history_id to used_entities when
explicit_parent_supplied is True. Previously it was appended unconditionally,
so the no-explicit-parent re-track path ended up with the same history id in
both parent_entity_id and used_entities, duplicating the reference in
get_lineage() output.
2026-07-15 15:36:48 +05:30
Sameer6305 e81baca5a8 Address Qodo review: cover derived_from in explicit-parent check, keep archived history entries reachable via used_entities 2026-07-15 14:28:13 +05:30
Mohd Kaif eb3663e737 Merge pull request #741 from semantica-agi/fix/735-provenance-lineage-derived-from
Fix ProvenanceManager.get_lineage not linking entities via derived_from
2026-07-15 14:08:45 +05:30
Sameer6305 37c890bee2 Fix track_entity re-track silently overriding explicit parent_entity_id/derived_from (fixes #742) 2026-07-15 14:06:54 +05:30
KaifAhmad1 62b079a9c3 Merge main, resolve CHANGELOG.md conflict with #732 2026-07-15 13:18:21 +05:30
Mohd Kaif 716e47ce8f Merge pull request #740 from semantica-agi/fix/732-add-rule-dedup
Fix Reasoner.add_rule missing deduplication (#732)
2026-07-15 13:02:46 +05:30
Sameer6305 506b7060a1 Warn and document confidence-discard behavior on rule dedup (review follow-up for #732) 2026-07-15 12:47:44 +05:30
KaifAhmad1 de0357aec8 Fix code review findings: metadata precedence and Mapping support
- get_lineage() aggregated metadata by iterating trace_lineage()'s BFS
  order and calling dict.update() on each entry, so ancestor metadata
  (now reachable via derived_from chains) could overwrite the queried
  entity's own metadata on conflicting keys. Reverse the iteration so
  the queried entity (always lineage_entries[0]) is applied last and
  wins, matching the documented "most recent entry's metadata takes
  precedence" intent.
- track_entity()'s derived_from guard only accepted a concrete dict,
  silently ignoring other collections.abc.Mapping implementations
  (e.g. types.MappingProxyType). Switch the isinstance check to
  Mapping so any mapping-like metadata is honored.

Addresses Qodo review findings on PR #741.
2026-07-15 12:27:42 +05:30
KaifAhmad1 7d83b6744f Fix ProvenanceManager.get_lineage not linking entities via derived_from
track_entity() only auto-linked a parent by looking up `source` as an
existing entity_id, so two entities sharing a real source URL (e.g. a
document and a decision derived from it) never got connected, and
metadata["derived_from"] was stored but never consulted by any linking
or traversal code.

track_entity() now treats metadata["derived_from"] as an explicit
parent link (unless parent_entity_id was already passed directly), so
the existing BFS in trace_lineage() picks it up for free.

Closes #735
2026-07-15 12:14:53 +05:30
KaifAhmad1 d90929730d Re-sort rules on duplicate-add path (#732 review follow-up)
Rule is a mutable dataclass, so an already-registered rule's priority
could change after being added; the dedup early-return skipped the
priority re-sort, so re-adding a rule after mutating its priority
left self.rules stale relative to that change. The duplicate branch
now re-sorts before returning, matching the append path.
2026-07-15 11:54:07 +05:30
KaifAhmad1 7455ed254c Address review: warn on duplicate rule, guard non-string conditions
- add_rule()'s duplicate-skip path now logs at warning level instead
  of debug, so a skipped duplicate is visible by default rather than
  silent in typical logging configs
- The duplicate-rule log message now stringifies conditions via
  map(str, ...) before joining, since Rule.conditions is List[Any]
  and non-string entries would otherwise raise TypeError
2026-07-15 11:52:10 +05:30
KaifAhmad1 1d502d5e74 Fix Reasoner.add_rule missing deduplication (#732)
add_rule() unconditionally appended to self.rules, so re-running the
same setup code on an existing Reasoner instance (e.g. re-executing a
Jupyter cell) duplicated every rule; forward_chain() would then match
the duplicated rules but silently return no new results since the
conclusions were already in self.facts, with no error or warning.

add_rule() now compares an incoming rule's rule_type, conditions, and
conclusion against existing rules and returns the existing Rule
instead of appending a duplicate, keeping repeated add_rule() calls
with the same definition idempotent.
2026-07-15 11:42:38 +05:30
Mohd Kaif babff350ff Merge pull request #739 from Sameer6305/fix/733-explanation-premises
Populate InferenceResult.premises in forward_chain and backward_chain
2026-07-14 23:00:49 +05:30
KaifAhmad1 49e5430aa3 docs: add changelog entry for InferenceResult.premises fix (#739) 2026-07-14 22:47:50 +05:30
KaifAhmad1 8157fa5fd5 Merge remote-tracking branch 'origin/main' into fix/733-explanation-premises 2026-07-14 22:47:21 +05:30
Sameer KadamandKaifAhmad1 bbcf27a6a3 Fix NodeEmbedder AttributeError masked in ContextGraph.analyze_graph_with_kg (#738)
* Fix NodeEmbedder.generate_embeddings AttributeError in analyze_graph_with_kg (fixes #734)

* docs(changelog): add entry for NodeEmbedder AttributeError fix (#734)

by @Sameer6305

---------

Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-07-14 22:05:01 +05:30
Sameer6305 e8c9e221ef Address Copilot review: fix forward-chain semantics regression, sorted() hot spot, add premises test coverage 2026-07-14 21:52:26 +05:30
Sameer6305 38f02956aa fix: make inference provenance deterministic 2026-07-14 21:18:59 +05:30
Sameer6305 9aa6d14081 Thread matched facts through forward_chain and backward_chain into InferenceResult.premises (fixes #733) 2026-07-14 21:04:35 +05:30
Sameer KadamandKaifAhmad1 b3b7d8ad1d Add missing shacl extra to pyproject.toml (#737)
* Add shacl extra to pyproject.toml (fixes #736)

* docs(changelog): add entry for shacl extra fix (#736)

by @Sameer6305

---------

Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-07-14 21:01:19 +05:30
Mohd Kaif 7fecdae119 Merge pull request #703 from Sameer6305/docs/improve-change-management-guide
docs: improve change management guide onboarding and workflow guidance
2026-07-12 15:57:04 +05:30
KaifAhmad1 b5e0529709 docs: fix contradictory storage-behavior wording in change management guide
'By default, initializing ... with storage_path=...' read as if passing
storage_path were the default, contradicting the very next sentence
about the no-argument in-memory default. Rephrased so the in-memory
default isn't undercut by the first sentence.
2026-07-12 15:52:25 +05:30
Mohd KaifandKaifAhmad1 5b8d5c5ff6 docs: improve SHACL validation guide onboarding and workflow guidance (#702)
* docs: improve SHACL validation guide onboarding and workflow guidance

* docs: fix SHACL validation implementation mismatches

* docs: fix stale violation URIs and drop unused imports in SHACL guide

Step 5's illustrative explain_violations() output still referenced the
old cti.example.org/data/... node URIs after Step 4's data_ttl was
rewritten to use example.org/... URIs. Also removes now-unused
export_rdf/tempfile/os imports left over from replacing dynamic RDF
export with inline Turtle strings in five of the code examples.

---------

Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-07-12 15:37:50 +05:30
KaifAhmad1 f0828b1ff6 docs: fix stale violation URIs and drop unused imports in SHACL guide
Step 5's illustrative explain_violations() output still referenced the
old cti.example.org/data/... node URIs after Step 4's data_ttl was
rewritten to use example.org/... URIs. Also removes now-unused
export_rdf/tempfile/os imports left over from replacing dynamic RDF
export with inline Turtle strings in five of the code examples.
2026-07-12 15:33:15 +05:30
Mohd Kaif adb6878b00 Update feature list in README
Removed 'Explainable' from the feature list in the README.
2026-07-09 15:16:26 +05:30
Mohd Kaif edf3aeb90c Update README.md 2026-07-09 15:13:12 +05:30
Mohd Kaif 4316f9b2fe docs: drop CLI demo badge and ASCII mockups in favor of full reference link (#730)
Condense the CLI section to the essential install/usage snippet and
command groups, pointing to docs.getsemantica.ai for the full
reference instead of maintaining static terminal mockups in the README.
2026-07-09 11:50:39 +05:30
Mohd Kaif 8800c2c85a Merge pull request #729 from semantica-agi/readme-category-defining-refresh
docs: reposition README as category-defining accountability layer
2026-07-09 11:40:35 +05:30
KaifAhmad1 0e2dc7462c docs: fix nonexistent semantica benchmark CLI reference
semantica.cli has no benchmark subcommand. Point to the actual
runnable benchmark suite under tests/vector_store instead.
2026-07-09 11:35:46 +05:30
KaifAhmad1 20b1455480 docs: reposition README as category-defining accountability layer
Consolidate the hero around a single category claim (Context and
Accountability Layer for AI agents), drop the named-competitor
comparison table (LangChain/LlamaIndex/Mem0/Zep/Palantir Foundry),
remove GitHub alert-box tips/notes, cut redundant module/changelog
sections, strip vanity feature counts, and trim em dashes for a
cleaner, more premium read.
2026-07-09 11:29:08 +05:30
Mohd Kaif a765fd5a3a Merge pull request #726 from Luffy2208/feature/240-sqlite-vec-support
feat: implement sqlite-vec vector store backend (#240)
2026-07-08 18:57:57 +05:30
KaifAhmad1andLuffy2208 ada5aa7615 docs: add changelog entry for sqlite-vec vector store backend
Co-Authored-By: Luffy2208 <209925020+Luffy2208@users.noreply.github.com>
2026-07-08 18:53:37 +05:30
KaifAhmad1andLuffy2208 94c83697b0 fix: address sqlite-vec review findings (tests, WAL/sync, batching)
- Add SQLITE_VEC_AVAILABLE flag via importlib.util.find_spec so the test
  suite's skipif actually reflects whether sqlite-vec is installed; it was
  previously undefined, causing all sqlite vector store tests to be
  silently skipped regardless of installation state.
- Actually apply PRAGMA synchronous=NORMAL alongside journal_mode=WAL when
  use_wal=True, matching the documented behavior; document use_wal as an
  opt-in kwarg in the docstring and usage guide.
- Correct _is_safe_identifier error messages (regex never allowed hyphens).
- Batch get() and update() with IN(...)/executemany instead of per-id
  round trips, consistent with add()/delete().
- Fix flaky test_update_vectors assertion that relied on list.index()
  over dicts containing numpy arrays.
- Reorder sqlite_vec_store import alphabetically in vector_store/__init__.py.

Co-Authored-By: Luffy2208 <209925020+Luffy2208@users.noreply.github.com>
2026-07-08 18:49:34 +05:30
Mohd Kaif a6db33b0fe docs: refine README hero badges and subtitle styling (#728)
Revert badge rows to flat-square (for-the-badge rendered as
mismatched oversized blocks), convert the subtitle to a native
blockquote for GitHub's built-in muted-grey text styling, and
trim "The" from the tagline.
2026-07-08 16:01:11 +05:30
Mohd Kaif 58f3216cd4 docs: reposition README as open-source Palantir alternative (#727)
Update the hero tagline, subtitle, and comparison table to frame
Semantica as Palantir-grade knowledge/decision intelligence that is
open source, self-hostable, and priced for startups through
Fortune 500, not just enterprise budgets.
2026-07-08 15:42:36 +05:30
Mohd KaifandKaifAhmad1 611be57ee6 docs: improve conflict resolution guide onboarding and workflow guidance (#701)
* docs: improve conflict resolution guide onboarding and workflow guidance

* docs: fix conflict resolution implementation mismatches

* docs: correct credibility-weighted example output values

Fix stale/incorrect weight and confidence figures in the conflict
resolution guide that don't match actual resolver output, and update
a leftover credibility_score field reference in Common Pitfalls.

---------

Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-07-07 16:07:47 +05:30
KaifAhmad1 6bfb9c719c docs: correct credibility-weighted example output values
Fix stale/incorrect weight and confidence figures in the conflict
resolution guide that don't match actual resolver output, and update
a leftover credibility_score field reference in Common Pitfalls.
2026-07-07 16:03:19 +05:30
Mohd Kaif 05fe7d81d7 Merge pull request #700 from Sameer6305/docs/improve-deduplication-guide
docs: improve deduplication guide onboarding and workflow guidance
2026-07-07 15:13:13 +05:30
KaifAhmad1 f7821ec350 docs: use merge_entity_group() where the guide says to
The merging example told readers to use merge_entity_group() for
already-confirmed duplicate groups, but the code right below it still
called merge_duplicates() on group.entities, which re-runs duplicate
detection redundantly. Update the call to match the stated guidance.
2026-07-07 15:05:13 +05:30
luffy2208 62ac59705b fix: resolve qodo review issues for sqlite backend (#240) 2026-07-06 21:40:24 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 5e9ed6772d security(deps-dev): update opentelemetry-instrumentation requirement (#725)
Updates the requirements on [opentelemetry-instrumentation](https://github.com/open-telemetry/opentelemetry-python-contrib) to permit the latest version.
- [Release notes](https://github.com/open-telemetry/opentelemetry-python-contrib/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-python-contrib/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-python-contrib/commits)

---
updated-dependencies:
- dependency-name: opentelemetry-instrumentation
  dependency-version: 0.64b0
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-06 12:05:16 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 3ff8c11235 security(deps-dev): update opentelemetry-semantic-conventions requirement (#724)
Updates the requirements on [opentelemetry-semantic-conventions](https://github.com/open-telemetry/opentelemetry-python) to permit the latest version.
- [Release notes](https://github.com/open-telemetry/opentelemetry-python/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-python/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-python/commits)

---
updated-dependencies:
- dependency-name: opentelemetry-semantic-conventions
  dependency-version: 0.64b0
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-06 11:56:54 +05:30
luffy2208 11836023ee feat: implement sqlite-vec vector store backend (#240) 2026-07-05 20:40:05 +05:30
Mohd Kaif 9680dece2e Merge pull request #699 from Sameer6305/docs/improve-policy-engine-guide
docs: improve Policy Engine guide onboarding and implementation accuracy
2026-07-05 13:29:16 +05:30
KaifAhmad1 aa2cd00f07 docs: fix inverted pitfall wording and broken required_* pattern in mortgage example
- Correct the unsupported-rule-key pitfall: absent keys fail compliance,
  present keys (any value) pass — the previous wording had this backwards.
- Remove required_ltv/pd/lgd/dsti/credit_score: True from the mortgage
  example. required_* checks equality against the given value, so True
  against a real numeric field silently marks compliant decisions as
  non-compliant (verified: a fully passing decision still returned False).
  The min_/max_ rules already enforce presence of ltv/dsti/credit_score.
2026-07-05 13:24:22 +05:30
Mohd Kaif 9094f1ed95 Merge pull request #698 from Sameer6305/docs/improve-multi-agent-guide
docs: improve multi-agent guide onboarding and coordination guidance
2026-07-04 13:25:33 +05:30
Mohd KaifandKaifAhmad1 4011f80eff docs: improve export guide onboarding and workflow guidance (#697)
* docs: improve export guide onboarding and workflow guidance

* docs: fix export guide implementation mismatches

* docs: revert .content to .text in export guide examples

FileObject.content is raw bytes; AgentContext.store() only accepts str/list and raises ValueError on bytes, so the previous fix commit broke both domain examples.

---------

Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-07-04 13:10:00 +05:30
KaifAhmad1 7e70508ac9 docs: revert .content to .text in export guide examples
FileObject.content is raw bytes; AgentContext.store() only accepts str/list and raises ValueError on bytes, so the previous fix commit broke both domain examples.
2026-07-04 13:04:28 +05:30
Sameer Kadam d336898f77 docs: improve provenance guide onboarding and practical guidance (#696)
* docs: improve provenance guide onboarding and practical guidance

* docs: fix provenance implementation mismatches
2026-07-04 12:31:08 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 4cc9c8efd1 deps(deps): update protobuf requirement (#723)
Updates the requirements on [protobuf](https://github.com/protocolbuffers/protobuf) to permit the latest version.
- [Release notes](https://github.com/protocolbuffers/protobuf/releases)
- [Commits](https://github.com/protocolbuffers/protobuf/commits)

---
updated-dependencies:
- dependency-name: protobuf
  dependency-version: 7.35.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-04 12:13:48 +05:30
Mohd Kaif 53e1769956 Merge pull request #695 from Sameer6305/docs/improve-semantic-extraction-guide
docs: improve semantic extraction guide onboarding and workflow guidance
2026-07-04 12:11:02 +05:30
Mohd Kaif 6e7c388242 docs: improve LLM integrations guide onboarding and provider guidance (#694)
* docs: improve llm integrations guide onboarding and provider guidance

* docs: fix LLM integration implementation mismatches
2026-07-04 12:05:27 +05:30
Mohd Kaif 92fb7b0826 Merge pull request #693 from Sameer6305/docs/improve-decision-intelligence-guide
docs: improve decision intelligence guide onboarding and practical guidance
2026-07-03 12:51:51 +05:30
KaifAhmad1 69d61384fd docs: clarify VectorStore omission error type in decision tracking info box
Distinguishes the TypeError from leaving the argument out entirely vs.
the ValueError raised when vector_store=None is passed explicitly.
2026-07-03 12:39:44 +05:30
Sameer Kadam 12067840a5 docs: improve distance intelligence guide onboarding and concepts (#692) 2026-07-03 12:05:44 +05:30
Sameer KadamandKaifAhmad1 7a4810893a docs: improve agent memory guide onboarding and usage guidance (#691)
* docs: improve agent memory guide onboarding and usage guidance

* docs: align Agent Memory guide with persistence implementation

* docs: fix misleading index_path persistence claim across guides

VectorStore's index_path kwarg is silently absorbed into FAISSStore's
**config and never read anywhere in faiss_store.py, so it does not make
the FAISS index persist across restarts as several docs implied. Real
persistence requires an explicit VectorStore.save()/.load() call, or
AgentContext.save()/.load() which cascades to it.

- docs/reference/context.md: rewrite the "Persist your vector store"
  tip to explain the actual save()/load() mechanism instead of the
  dead index_path kwarg.
- docs/guides/graphrag.md, decision-intelligence.md, ingest.md,
  semantic-extraction.md: drop the dead index_path=... kwarg from
  VectorStore(backend="faiss", ...) constructor calls.

Follow-up to #691, which fixed the same false claim in
docs/guides/agent-memory.md but missed these other files.

---------

Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-07-02 17:40:42 +05:30
Sameer Kadam 5430167dbc docs: improve GraphRAG guide onboarding and practical guidance (#690)
* docs: improve GraphRAG guide onboarding and practical guidance

* docs: align GraphRAG guide with retrieval implementation
2026-07-02 16:24:55 +05:30
Mohd Kaif 46540df5f0 Merge pull request #689 from Sameer6305/docs/improve-visualization-guide
docs: improve visualization guide onboarding and workflow guidance
2026-07-02 13:29:12 +05:30
KaifAhmad1 9ac3066fe1 docs: fix node-count inconsistency in performance warning 2026-07-02 13:24:16 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 0a793171dd docker(deps): bump python from 3.12-slim to 3.14-slim (#721)
Bumps python from 3.12-slim to 3.14-slim.

---
updated-dependencies:
- dependency-name: python
  dependency-version: 3.14-slim
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-02 13:07:22 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> fcd986d7b1 docker(deps): bump node from 22-alpine to 26-alpine (#720)
Bumps node from 22-alpine to 26-alpine.

---
updated-dependencies:
- dependency-name: node
  dependency-version: 26-alpine
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-02 13:01:16 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 0c2e54e583 security(deps): update pillow requirement from >=11.3.0 to >=12.2.0 (#719)
Updates the requirements on [pillow](https://github.com/python-pillow/Pillow) to permit the latest version.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/11.3.0...12.2.0)

---
updated-dependencies:
- dependency-name: pillow
  dependency-version: 12.2.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-30 17:41:52 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 47db405737 security(deps): update grpcio requirement from >=1.71.2 to >=1.81.1 (#718)
Updates the requirements on [grpcio](https://github.com/grpc/grpc) to permit the latest version.
- [Release notes](https://github.com/grpc/grpc/releases)
- [Commits](https://github.com/grpc/grpc/compare/v1.71.2...v1.81.1)

---
updated-dependencies:
- dependency-name: grpcio
  dependency-version: 1.81.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-30 17:35:56 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 8b2155d3d9 security(deps): update tqdm requirement from >=4.64.0 to >=4.68.3 (#717)
Updates the requirements on [tqdm](https://github.com/tqdm/tqdm) to permit the latest version.
- [Release notes](https://github.com/tqdm/tqdm/releases)
- [Commits](https://github.com/tqdm/tqdm/compare/v4.64.0...v4.68.3)

---
updated-dependencies:
- dependency-name: tqdm
  dependency-version: 4.68.3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-30 12:00:45 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 6491690dfe security(deps): update torch requirement from >=1.12.0 to >=1.13.1 (#716)
Updates the requirements on [torch](https://github.com/pytorch/pytorch) to permit the latest version.
- [Release notes](https://github.com/pytorch/pytorch/releases)
- [Changelog](https://github.com/pytorch/pytorch/blob/main/RELEASE.md)
- [Commits](https://github.com/pytorch/pytorch/compare/ciflow/torchtitan/157149...v1.13.1)

---
updated-dependencies:
- dependency-name: torch
  dependency-version: 1.13.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-30 11:03:12 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 7ba05ac6b5 security(deps-dev): update pre-commit requirement (#714)
Updates the requirements on [pre-commit](https://github.com/pre-commit/pre-commit) to permit the latest version.
- [Release notes](https://github.com/pre-commit/pre-commit/releases)
- [Changelog](https://github.com/pre-commit/pre-commit/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pre-commit/pre-commit/compare/v2.19.0...v4.6.0)

---
updated-dependencies:
- dependency-name: pre-commit
  dependency-version: 4.6.0
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-30 10:58:00 +05:30
Sameer6305 8cf19a6b2e docs: fix change management implementation mismatches 2026-06-29 20:00:50 +05:30
Sameer6305 6f4db5a693 docs: fix SHACL validation implementation mismatches 2026-06-29 19:37:24 +05:30
Sameer6305 8d60f68fcd docs: fix conflict resolution implementation mismatches 2026-06-29 17:00:01 +05:30
Sameer6305 6742b08743 docs: fix deduplication implementation mismatches 2026-06-29 16:27:18 +05:30
Sameer6305 ccb9e070b4 docs: fix policy engine implementation mismatches 2026-06-29 16:17:56 +05:30
Sameer6305 d243143316 docs: fix multi-agent implementation mismatches 2026-06-29 16:04:02 +05:30
Sameer6305 0ecf43f5f6 docs: fix export guide implementation mismatches 2026-06-29 15:40:50 +05:30
KaifAhmad1 064daca6f9 docs(readme): bump to 0.5.1, add What's New section with deployment platform badges 2026-06-29 15:37:28 +05:30
KaifAhmad1 0de843067b chore(release): bump version to 0.5.1 2026-06-29 15:20:26 +05:30
Sameer6305 4d784d0aea docs: fix LLM integration implementation mismatches 2026-06-29 15:05:25 +05:30
Sameer6305 dcff5e3b3d docs: align decision intelligence guide with implementation 2026-06-29 14:42:30 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> b32e88644d security(deps-dev): update docling requirement from >=1.0.0 to >=2.107.0 (#713)
Updates the requirements on [docling](https://github.com/docling-project/docling) to permit the latest version.
- [Release notes](https://github.com/docling-project/docling/releases)
- [Changelog](https://github.com/docling-project/docling/blob/main/CHANGELOG.md)
- [Commits](https://github.com/docling-project/docling/compare/v1.0.0...v2.107.0)

---
updated-dependencies:
- dependency-name: docling
  dependency-version: 2.107.0
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-29 13:08:40 +05:30
Sameer6305 0ad1f64cc9 docs: fix visualization guide implementation alignment 2026-06-29 13:05:01 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> c90d80f663 security(deps-dev): update pyarrow requirement (#712)
Updates the requirements on [pyarrow](https://github.com/apache/arrow) to permit the latest version.

Updates `pyarrow` to 24.0.0
- [Release notes](https://github.com/apache/arrow/releases)
- [Commits](https://github.com/apache/arrow/compare/apache-arrow-21.0.0...apache-arrow-24.0.0)

---
updated-dependencies:
- dependency-name: pyarrow
  dependency-version: 24.0.0
  dependency-type: direct:development
  dependency-group: arrow-features
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-29 12:32:56 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> a67ef4a655 security(deps-dev): update snowflake-connector-python requirement (#711)
Updates the requirements on [snowflake-connector-python](https://github.com/snowflakedb/snowflake-connector-python) to permit the latest version.

Updates `snowflake-connector-python` to 4.6.0
- [Release notes](https://github.com/snowflakedb/snowflake-connector-python/releases)
- [Commits](https://github.com/snowflakedb/snowflake-connector-python/compare/v4.5.0...v4.6.0)

---
updated-dependencies:
- dependency-name: snowflake-connector-python
  dependency-version: 4.6.0
  dependency-type: direct:development
  dependency-group: snowflake-features
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-29 12:24:15 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> e4cac17a9b security(deps): update requests requirement (#710)
Updates the requirements on [requests](https://github.com/psf/requests) to permit the latest version.

Updates `requests` to 2.34.2
- [Release notes](https://github.com/psf/requests/releases)
- [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md)
- [Commits](https://github.com/psf/requests/compare/v2.32.5...v2.34.2)

---
updated-dependencies:
- dependency-name: requests
  dependency-version: 2.34.2
  dependency-type: direct:production
  dependency-group: security-critical
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-29 12:14:49 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 9a649c5581 deps(deps): update scikit-learn requirement from >=1.6.1 to >=1.7.2 (#708)
Updates the requirements on [scikit-learn](https://github.com/scikit-learn/scikit-learn) to permit the latest version.
- [Release notes](https://github.com/scikit-learn/scikit-learn/releases)
- [Commits](https://github.com/scikit-learn/scikit-learn/compare/1.6.1...1.7.2)

---
updated-dependencies:
- dependency-name: scikit-learn
  dependency-version: 1.7.2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-29 11:40:00 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 14424eafa6 deps(deps): update chardet requirement from >=5.1.0 to >=7.4.3 (#707)
Updates the requirements on [chardet](https://github.com/chardet/chardet) to permit the latest version.
- [Release notes](https://github.com/chardet/chardet/releases)
- [Changelog](https://github.com/chardet/chardet/blob/main/docs/changelog.rst)
- [Commits](https://github.com/chardet/chardet/compare/5.1.0...7.4.3)

---
updated-dependencies:
- dependency-name: chardet
  dependency-version: 7.4.3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-29 11:33:24 +05:30
Mohd Kaif f9cd9eb4db Merge pull request #706 from semantica-agi/dependabot/pip/main/click-gte-8.4.2
deps(deps): update click requirement from >=8.1.0 to >=8.4.2
2026-06-29 11:28:55 +05:30
Mohd KaifandKaifAhmad1 aa712b9110 feat: implement Apache Arrow and Feather file ingestion support (#235) (#705)
* feat: implement Apache Arrow and Feather file ingestion support (#235)

* fix(arrow): eliminate double full-scan and clean up reader wrapper

- Replace _read_batches with _read_batches_with_info which collects
  batch metadata (total_rows, record_batches) during the same pass as
  the data read, so ingest_file no longer calls _file_metadata before
  _read_batches. For a limit=1 read on a large file this previously
  scanned every batch twice; now it stops after the first batch.

- _file_metadata is now only invoked for include_data=False (where a
  full scan is unavoidable to report accurate row counts).

- Remove the dead num_record_batches property from _ArrowReaderWrapper;
  it was never called by production code and its is_table branch
  materialised all batches just to count them.

- Fix _open_file exception chain: raise ... from file_err instead of
  from feather_err so the most diagnostic IPC error appears in the
  Python traceback chain, not the least informative fallback error.

* docs(changelog): add [Unreleased] entries for Arrow ingestion (#705)

---------

Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-06-28 12:57:17 +05:30
KaifAhmad1 f2c60256f1 docs(changelog): add [Unreleased] entries for Arrow ingestion (#705) 2026-06-28 12:49:52 +05:30
KaifAhmad1 006f37c062 fix(arrow): eliminate double full-scan and clean up reader wrapper
- Replace _read_batches with _read_batches_with_info which collects
  batch metadata (total_rows, record_batches) during the same pass as
  the data read, so ingest_file no longer calls _file_metadata before
  _read_batches. For a limit=1 read on a large file this previously
  scanned every batch twice; now it stops after the first batch.

- _file_metadata is now only invoked for include_data=False (where a
  full scan is unavoidable to report accurate row counts).

- Remove the dead num_record_batches property from _ArrowReaderWrapper;
  it was never called by production code and its is_table branch
  materialised all batches just to count them.

- Fix _open_file exception chain: raise ... from file_err instead of
  from feather_err so the most diagnostic IPC error appears in the
  Python traceback chain, not the least informative fallback error.
2026-06-28 12:40:28 +05:30
Mohd Kaif c93af4a514 Merge pull request #688 from Sameer6305/docs/improve-graph-analytics-guide
docs: improve graph analytics guide onboarding and practical guidance
2026-06-27 21:19:37 +05:30
KaifAhmad1 9c379e1a0e fix(docs): align node threshold and consolidate data quality guidance
- Remove duplicate Data Quality Info block; content moved into Common Pitfalls as a dedicated pitfall entry, keeping the critical advanced_analytics=True warning as the sole callout
- Align node count threshold: Common Pitfalls now consistently references 100+ nodes (was '< 50 nodes'), matching the When To Use recommendation
2026-06-27 21:13:34 +05:30
Mohd Kaif 231cbc613b Merge pull request #687 from Sameer6305/docs/improve-reasoning-guide
docs: improve reasoning guide onboarding and practical guidance
2026-06-27 21:03:04 +05:30
KaifAhmad1 355b811e59 fix(docs): correct factual errors and tab placement in reasoning guide
- Fix CVE in SUNBURST example: CVE-2024-3400 → CVE-2020-10148, matching context-graphs.md
- Correct load_from_graph fact format: predicates/args are lowercased (threatactor(apt29), not ThreatActor(APT29)); scoped to DatalogReasoner only; removed incorrect metadata-to-predicate claim
- Move Common Pitfalls section after </Tabs> so it renders outside the tab component and is visible to all readers
2026-06-27 20:54:06 +05:30
Mohd Kaif 5c27e539ec Merge pull request #686 from Sameer6305/docs/improve-context-graphs-guide
docs: improve context graph guide onboarding and practical guidance
2026-06-26 21:39:21 +05:30
KaifAhmad1 d74477bf94 fix(docs): correct API inaccuracies in context graph guide
- Replace non-existent shortest_path() with get_neighbors() + path_to_anchor
- Remove non-existent extract_subgraph() calls from all domain tab examples
- Clarify automated extraction requires knowledge_graph= constructor arg and list input
- Distinguish save_to_file() (graph only) from AgentContext.save() (graph + FAISS + memory)
- Add resolve_links() step to serialization section for cross-graph link restoration
- Link duplicate entities pitfall to the deduplication guide and its API
2026-06-26 21:32:55 +05:30
dependabot[bot] c4359b4995 deps(deps): update click requirement from >=8.1.0 to >=8.4.2
Updates the requirements on [click](https://github.com/pallets/click) to permit the latest version.
- [Release notes](https://github.com/pallets/click/releases)
- [Changelog](https://github.com/pallets/click/blob/main/CHANGES.md)
- [Commits](https://github.com/pallets/click/compare/8.1.0...8.4.2)

---
updated-dependencies:
- dependency-name: click
  dependency-version: 8.4.2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-26 09:05:49 +00:00
Mohd Kaif 4f6db9601c Merge pull request #685 from Sameer6305/docs/improve-ontology-guide
docs: improve ontology guide onboarding and practical guidance
2026-06-26 12:09:55 +05:30
KaifAhmad1 5f6cac0a77 fix(docs): correct code errors in ontology guide simple example
- Replace ctx.store() + graph.to_dict() with direct entity/relationship
  dict to avoid key mismatch (to_dict() returns nodes/edges; generator
  reads entities/relationships)
- Fix prop type filter: 'datatype' → 'data' (value set by PropertyGenerator)
- Fix domain/range printing: both are stored as lists, not scalars
- Clarify Reasoning bullet: OWL inference requires an external reasoner,
  Semantica only exports the ontology
- Remove duplicate LLM-vs-graph-generator pitfall already covered by the
  Info callout in the LLMOntologyGenerator section
2026-06-26 11:53:56 +05:30
Sameer KadamandKaifAhmad1 e87f0832a3 docs: improve pipeline guide onboarding and workflow guidance (#683)
* docs: improve pipeline guide onboarding and workflows

* fix(docs): correct broken pipeline guide examples from review

- Remove Option 1 (register_step_handler + string name): ExecutionEngine
  never resolves string handler names via step_registry, so it raised
  TypeError at runtime; replace with the single working pattern
- Add missing step_type positional arg to all new add_step() calls
- Use connect_steps() for checkpoint dependency instead of the
  dependencies= kwarg, consistent with every other example in the file
- Move extract_entities definition above its call site to fix NameError
- Replace docstring on save_checkpoint with inline comment to match
  the no-docstring convention used by all other handlers in the file

---------

Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-06-25 12:53:08 +05:30
Mohd Kaif 48c706ef88 Merge pull request #682 from Sameer6305/docs/improve-ingest-guid
docs: improve ingest guide onboarding, workflows, and real-world examples
2026-06-25 12:16:52 +05:30
KaifAhmad1 dd7d1b8bc5 fix(docs): address review findings in ingest guide
- Expand intro to cover Git (dict/code_files) and stream (StreamMessage/.content) return shapes, which the previous two-class split omitted
- Add missing imports and AgentContext setup to the Source 1 internal-docs snippet (NameError on copy-paste)
- Add advanced_analytics=True to ContextGraph in both Business Examples (required for extract_entities=True to populate graph analytics)
- Replace bare `pass` credential with YOUR_DB_PASSWORD placeholder to match the YOUR_*_KEY convention used elsewhere
- Guard nullable description/resolution columns in ticket_texts with `(r[...] or '')` to prevent TypeError on NULL rows
- Replace misleading time.sleep() rate-limit advice with accurate description of RESTIngestor's built-in 429 retry/backoff and how to tune it
2026-06-25 11:59:06 +05:30
Mohd Kaif df5b4e31c3 Merge pull request #684 from semantica-agi/issue-681-knowledge-explorer-deploy-templates
Add Knowledge Explorer deployment templates
2026-06-24 23:21:54 +05:30
KaifAhmad1 445c487fcc fix(helm): add namespace: .Release.Namespace to all Helm templates
Without an explicit namespace in metadata, checkov (CKV_K8S_21) flags
every resource as using the default namespace. Using .Release.Namespace
lets helm install --namespace semantica --create-namespace correctly
scope all resources to the target namespace.
2026-06-24 23:09:10 +05:30
KaifAhmad1 2440c5adb4 fix(ci): make .checkov.yaml a valid YAML mapping to prevent NoneType parse error
An empty/comment-only YAML file is parsed as NoneType by PyYAML.
Checkov requires a dict; adding skip-check: [] satisfies the parser
without globally suppressing any checks.
2026-06-24 23:01:28 +05:30
KaifAhmad1 b9e069301f fix(deploy): address security and correctness blockers from PR review
- gcp/cloudrun-service.yaml: add comment + README sed one-liner so PROJECT_ID
  is substituted before gcloud run services replace (was a literal placeholder
  that caused image-pull failure on the declarative deploy path)
- azure/main.parameters.json: replace wildcard allowedOrigins "*" with a
  REPLACE_ME placeholder; add README note to set the real URL after first deploy
- kubernetes/networkpolicy.yaml + helm networkpolicy template: add from: selector
  (ingress-nginx namespace + same-namespace pods) so ingress is no longer
  allow-all; restrict egress to FalkorDB port 6379 and DNS port 53 instead of
  the allow-all egress: - {} wildcard
- helm/values.yaml: expose networkPolicy.ingressNamespace and falkordbPort values
- kubernetes/deployment.yaml: add secretRef for knowledge-explorer-secrets so
  FALKORDB_PASSWORD is actually injected into the container
- app.py: add _mutation_bridge_installed guard to prevent closure stacking when
  the same GraphSession is passed to create_app() more than once; remove
  duplicate app.state.allowed_origins assignment (single source of truth is
  app.state.explorer_settings); add comment on falkordb_host/port dead config
- tests: update allowed_origins assertions to use explorer_settings dict
- .checkov.yaml: remove global CKV_K8S_21/28/30 suppressions; rely on per-file
  inline checkov:skip comments in cloudrun-service.yaml so future real K8s
  manifests are not silently exempted
2026-06-24 22:55:18 +05:30
luffy2208 914a87aaa8 feat: implement Apache Arrow and Feather file ingestion support (#235) 2026-06-24 22:37:00 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 4a5333dca9 ci(deps): bump actions/setup-node from 4 to 6 (#678)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4 to 6.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v4...v6)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-24 22:09:34 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 637dff45dc ci(deps): bump actions/checkout from 4 to 7 (#677)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 7.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-24 22:01:27 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> da0fb718c7 security(deps-dev): update azure-storage-blob requirement (#675)
Updates the requirements on [azure-storage-blob](https://github.com/Azure/azure-sdk-for-python) to permit the latest version.
- [Release notes](https://github.com/Azure/azure-sdk-for-python/releases)
- [Commits](https://github.com/Azure/azure-sdk-for-python/compare/azure-storage-blob_12.12.0...azure-storage-blob_12.30.0)

---
updated-dependencies:
- dependency-name: azure-storage-blob
  dependency-version: 12.30.0
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-24 21:55:21 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ed32fa45d0 security(deps): update lxml requirement from >=4.9.0 to >=6.1.1 (#674)
Updates the requirements on [lxml](https://github.com/lxml/lxml) to permit the latest version.
- [Release notes](https://github.com/lxml/lxml/releases)
- [Changelog](https://github.com/lxml/lxml/blob/master/CHANGES.txt)
- [Commits](https://github.com/lxml/lxml/compare/lxml-4.9.0...lxml-6.1.1)

---
updated-dependencies:
- dependency-name: lxml
  dependency-version: 6.1.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-24 21:50:43 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> cfe4895e7b security(deps): update pydantic requirement from >=2.0.0 to >=2.13.4 (#673)
Updates the requirements on [pydantic](https://github.com/pydantic/pydantic) to permit the latest version.
- [Release notes](https://github.com/pydantic/pydantic/releases)
- [Changelog](https://github.com/pydantic/pydantic/blob/main/HISTORY.md)
- [Commits](https://github.com/pydantic/pydantic/compare/v2.0...v2.13.4)

---
updated-dependencies:
- dependency-name: pydantic
  dependency-version: 2.13.4
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-24 21:47:09 +05:30
Sameer6305 f6e9ef6627 docs: improve change management guide onboarding and workflow guidance 2026-06-24 20:29:52 +05:30
Sameer6305 9bda477847 docs: improve SHACL validation guide onboarding and workflow guidance 2026-06-24 20:10:27 +05:30
Zohaib Hassnain a1bcf02fb3 Scope security scan workflow permissions 2026-06-24 19:02:11 +05:00
Zohaib Hassnain 6ddcc974f4 Fix Checkov MSDO workflow scan 2026-06-24 18:54:12 +05:00
Zohaib Hassnain 795557f08a Fix deployment template security scan blockers 2026-06-24 18:43:59 +05:00
Sameer6305 5c512a5011 docs: improve conflict resolution guide onboarding and workflow guidance 2026-06-24 17:30:53 +05:30
Sameer6305 d9b24d0630 docs: improve deduplication guide onboarding and workflow guidance 2026-06-24 16:43:22 +05:30
Sameer6305 527726faa3 docs: improve policy engine onboarding and rule guidance 2026-06-24 16:16:15 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 4f4c6ac20d security(deps-dev): update watchdog requirement from >=3.0.0 to >=6.0.0 (#672)
Updates the requirements on [watchdog](https://github.com/gorakhargosh/watchdog) to permit the latest version.
- [Release notes](https://github.com/gorakhargosh/watchdog/releases)
- [Changelog](https://github.com/gorakhargosh/watchdog/blob/master/changelog.rst)
- [Commits](https://github.com/gorakhargosh/watchdog/compare/v3.0.0...v6.0.0)

---
updated-dependencies:
- dependency-name: watchdog
  dependency-version: 6.0.0
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-24 16:06:50 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> a051856d57 security(deps-dev): update kafka-python requirement (#671)
Updates the requirements on [kafka-python](https://github.com/dpkp/kafka-python) to permit the latest version.
- [Release notes](https://github.com/dpkp/kafka-python/releases)
- [Changelog](https://github.com/dpkp/kafka-python/blob/master/docs/changelog.rst)
- [Commits](https://github.com/dpkp/kafka-python/compare/3.0.0...3.0.2)

---
updated-dependencies:
- dependency-name: kafka-python
  dependency-version: 3.0.2
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-24 16:01:46 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 721af4f2d2 security(deps-dev): update websockets requirement (#670)
Updates the requirements on [websockets](https://github.com/python-websockets/websockets) to permit the latest version.
- [Release notes](https://github.com/python-websockets/websockets/releases)
- [Commits](https://github.com/python-websockets/websockets/compare/11.0...15.0.1)

---
updated-dependencies:
- dependency-name: websockets
  dependency-version: 15.0.1
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-24 15:47:47 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 4fd5791e91 security(deps): update scikit-learn requirement from >=1.0.0 to >=1.6.1 (#669)
Updates the requirements on [scikit-learn](https://github.com/scikit-learn/scikit-learn) to permit the latest version.
- [Release notes](https://github.com/scikit-learn/scikit-learn/releases)
- [Commits](https://github.com/scikit-learn/scikit-learn/compare/1.0...1.6.1)

---
updated-dependencies:
- dependency-name: scikit-learn
  dependency-version: 1.6.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-24 15:36:50 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> c06f58fcc1 security(deps-dev): update isort requirement from >=5.10.0 to >=6.1.0 (#668)
Updates the requirements on [isort](https://github.com/PyCQA/isort) to permit the latest version.
- [Release notes](https://github.com/PyCQA/isort/releases)
- [Changelog](https://github.com/PyCQA/isort/blob/main/CHANGELOG.md)
- [Commits](https://github.com/PyCQA/isort/compare/5.10.0...6.1.0)

---
updated-dependencies:
- dependency-name: isort
  dependency-version: 6.1.0
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-24 15:32:05 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> dafc7c9e85 security(deps): update plotly requirement from >=5.10.0 to >=6.8.0 (#667)
Updates the requirements on [plotly](https://github.com/plotly/plotly.py) to permit the latest version.
- [Release notes](https://github.com/plotly/plotly.py/releases)
- [Changelog](https://github.com/plotly/plotly.py/blob/main/CHANGELOG.md)
- [Commits](https://github.com/plotly/plotly.py/compare/v5.10.0...v6.8.0)

---
updated-dependencies:
- dependency-name: plotly
  dependency-version: 6.8.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-24 15:26:23 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> d24b70a8bb security(deps): update seaborn requirement from >=0.11.0 to >=0.13.2 (#666)
Updates the requirements on [seaborn](https://github.com/mwaskom/seaborn) to permit the latest version.
- [Release notes](https://github.com/mwaskom/seaborn/releases)
- [Commits](https://github.com/mwaskom/seaborn/compare/v0.11.0...v0.13.2)

---
updated-dependencies:
- dependency-name: seaborn
  dependency-version: 0.13.2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-24 15:19:19 +05:30
KaifAhmad1 bacc37ab77 fix(ci): suppress CKV_K8S_21 false-positive on Cloud Run Knative YAML
checkov scans deploy/gcp/cloudrun-service.yaml as a Kubernetes resource
because it has apiVersion: serving.knative.dev/v1. It flags CKV_K8S_21
('default namespace should not be used') because Cloud Run services have
no metadata.namespace field — they are project/region scoped, not
namespace scoped. Add CKV_K8S_21 to .checkov.yaml skip-check and to the
inline skip comment in cloudrun-service.yaml.
2026-06-24 14:13:00 +05:30
KaifAhmad1 52e8f38361 fix(ci): move checkov out of MSDO into standalone bridgecrewio/checkov-action
Root cause of 6 consecutive CI failures:
MSDO 0.215.0's guardian.cmd wrapper breaks the build whenever checkov exits
with code 1. Checkov exits 1 on ANY violation, including MEDIUM/LOW findings
that are all 'below minimum severity'. This makes Active results = 0 and
'Found no breaking results', yet Guardian still raises BreakException because
it treats the tool's exit code as a first-class breaking signal. The
.checkov.yaml soft-fail setting was never read because the MSDO runner
bypasses repository config files.

Fix:
- Remove checkov from the MSDO tools list (stops the guardian.cmd crash)
- Add a dedicated 'checkov' job on ubuntu-latest using the official
  bridgecrewio/checkov-action@v12, which runs a current checkov release,
  runs on Linux, and correctly reads .checkov.yaml and respects soft_fail
- Set soft_fail: true in the action so low/medium findings appear in the
  Security tab without ever blocking the build
- MSDO continues to run eslint, templateanalyzer (Bicep/ARM), and terrascan;
  these tools all have well-behaved exit codes and produce no active results
  after the security fixes applied earlier in this PR

.checkov.yaml:
- Replace soft-fail: true (was a failed workaround for MSDO) with
  skip-check: [CKV_K8S_28, CKV_K8S_30] — correct suppression for the
  Knative false-positives (Cloud Run enforces seccomp + AppArmor at
  platform level without requiring K8s annotations)
2026-06-24 14:05:26 +05:30
KaifAhmad1 3f57bab9d3 fix(ci): suppress false-positive checkov K8s checks on Knative YAML; drop redundant seccomp annotation
checkov scans deploy/gcp/cloudrun-service.yaml as a Kubernetes resource
(it has apiVersion: serving.knative.dev/v1) and raises CKV_K8S_28 /
CKV_K8S_30. Adding those annotations to spec.template.metadata.annotations
caused checkov to crash (exit 1 with no SARIF output) — likely a bug in
checkov's AppArmor check when it tries to match the annotation container
name against containers in a Knative RevisionSpec. Fix:
  - Remove the AppArmor / seccomp annotations from the template metadata
  - Add checkov:skip comments at the file top so the false-positive checks
    are suppressed cleanly (Cloud Run enforces these at platform level)

Also drop the legacy seccomp.security.alpha.kubernetes.io/pod annotation
from deploy/helm/knowledge-explorer/values.yaml: run #186 confirmed that
the modern podSecurityContext.seccompProfile.type: RuntimeDefault field
already satisfies CKV_K8S_28 for the Helm chart without the annotation.
Adding the annotation alongside the modern field was causing the same
crash in checkov's Helm-rendered output.
2026-06-24 13:55:41 +05:30
KaifAhmad1 ef74ecf3a8 fix(ci): remove Knative pod-level securityContext and fix Bicep null ternary
checkov crashes (exit 1) on two constructs introduced in earlier commits:

1. deploy/gcp/cloudrun-service.yaml: pod-level spec.template.spec.securityContext
   is not part of Knative RevisionSpec. checkov's Knative parser panics on
   this unknown field. Remove it — CKV_K8S_28 (seccomp) and CKV_K8S_30
   (AppArmor) are already satisfied by the legacy annotations in
   spec.template.metadata.annotations; the container-level securityContext
   that IS valid in Cloud Run Gen 2 is kept.

2. deploy/azure/main.bicep: 'vnetInternal ? { ... } : null' compiles to
   ARM null() which crashes checkov's Bicep/ARM parser. Replace the inline
   null ternary with two concrete variable objects (vnetConfigInternal and
   vnetConfigExternal) so both branches are well-typed objects.
2026-06-24 13:41:28 +05:30
KaifAhmad1 2f73c1c91d fix(ci): add .checkov.yaml soft-fail to silence tool-error break in MSDO
Active results are 0 and 'Found no breaking results' but MSDO still fails
because checkov exits with code 1 whenever it finds any violation
(including MEDIUM/LOW below the minimum severity threshold). MSDO v1.12.0
treats a non-zero tool exit code as a breaking result even when Guardian
reports no active findings.

soft-fail: true makes checkov exit 0 in all cases. MSDO Guardian still
reads the full SARIF output and would surface any HIGH/CRITICAL findings
as active results that break the build, so the security posture is
unchanged.
2026-06-24 13:31:19 +05:30
KaifAhmad1 a8043418a1 fix(ci): fix 2 TemplateAnalyzer ERROR findings in Azure Bicep (AZR-000361/363)
AZR-000363 (Azure.ContainerApp.PublicAccess) — line 29 managedEnvironment:
- Add vnetConfiguration.internal: true (default) so the environment uses
  an internal load balancer instead of a public IP
- Parameterize with vnetInternal (bool, default true) and
  infrastructureSubnetId so operators can provide their subnet on deploy

AZR-000361 (Azure.ContainerApp.ManagedIdentity) — line 40 containerApp:
- Add identity.type = SystemAssigned so the Container App can
  authenticate to Azure services without storing credentials

Also update main.parameters.json and README with the new parameters.
2026-06-24 13:24:23 +05:30
Sameer6305 7f6f0c4213 docs: improve multi-agent guide onboarding and coordination guidance 2026-06-24 13:23:29 +05:30
KaifAhmad1 8b5f75160a fix(ci): fix 2 remaining checkov HIGH findings and Terrascan seccomp warnings
The 2 active checkov HIGH results (CKV_K8S_28 + CKV_K8S_30) were coming
from deploy/gcp/cloudrun-service.yaml — checkov scans it as a Kubernetes
resource (apiVersion: serving.knative.dev/v1) and flagged missing AppArmor
and seccomp on that file, regardless of the fixes made to the k8s/ and
helm/ manifests.

deploy/gcp/cloudrun-service.yaml:
- Add container name (explorer) so AppArmor annotation key matches
- Add AppArmor annotation to pod template metadata (CKV_K8S_30)
- Add legacy seccomp annotation (AC_K8S_0080 / CKV_K8S_28)
- Add pod-level seccompProfile: RuntimeDefault (CKV_K8S_28)
- Add container securityContext (runAsNonRoot, allowPrivilegeEscalation)
  Cloud Run Gen 2 supports all of these fields

deploy/kubernetes/deployment.yaml:
- Pin image tag from ':latest' to ':0.5.0' (AC_K8S_0068 / AC_K8S_0069)
- Add legacy seccomp pod annotation alongside existing seccompProfile field

deploy/helm/knowledge-explorer/values.yaml:
- Add legacy seccomp annotation to podAnnotations so it renders into
  the Helm-generated pod template alongside the modern seccompProfile
2026-06-24 13:15:41 +05:30
KaifAhmad1 095e8c8714 fix(ci): resolve MSDO/checkov and Terrascan failures on K8s and Helm manifests
checkov HIGH (2 breaking results, CKV_K8S_30):
- Add AppArmor annotation to k8s deployment pod template
  (container.apparmor.security.beta.kubernetes.io/explorer: runtime/default)
- Add AppArmor annotation via Helm values.yaml podAnnotations so it
  renders into the Helm-generated pod template

Terrascan warnings (AC_K8S_0087 / AC_K8S_0080 / AC_K8S_0073):
- Add runAsNonRoot: true and seccompProfile: RuntimeDefault at container
  securityContext level in both k8s deployment and Helm values (these
  were only at pod spec level before)

Terrascan AC_K8S_0002 (noHttps):
- Add nginx ssl-redirect annotation to k8s ingress so HTTPS enforcement
  is explicit at the ingress controller layer

Terrascan AC_K8S_0013 (noOwnerLabel):
- Add owner label to k8s namespace.yaml

Terrascan AC_K8S_0068 (imageWithLatestTag):
- Change Helm values.yaml image.tag from 'latest' to '' (falls back to
  .Chart.AppVersion at render time)
- Pin values.prod.yaml to explicit release tag 0.5.0
2026-06-24 13:01:54 +05:30
Sameer6305 76201b7587 docs: improve export guide onboarding and workflow guidance 2026-06-24 13:01:16 +05:30
KaifAhmad1 b2c949f7de fix(deploy): harden security in deployment templates and explorer app
- GCP: remove --allow-unauthenticated, restrict ingress to
  internal-and-cloud-load-balancing, replace wildcard ALLOWED_ORIGINS=*
  with a substitution variable (_ALLOWED_ORIGINS) so operators supply a
  real URL at deploy time; same fix in cloudrun-service.yaml
- Fly.io: replace hardcoded FALKORDB_HOST=localhost with the correct
  .internal private-network hostname pattern; update README accordingly
- docker-compose.dev.yml: add missing top-level networks: block so the
  frontend service can join the semantica network without --file layering
- K8s/Helm: add readOnlyRootFilesystem: true + runAsUser: 1000 to
  container securityContext; mount an emptyDir /tmp so uvicorn can write
  temp files
- app.py: fix _read_explorer_settings() or-chain, use in os.environ
  checks so an explicit ALLOWED_ORIGINS="" produces an empty allow-list
  instead of silently falling through to localhost defaults; remove dead
  app.state.falkordb_host/port attributes
- docs: update four locations that still documented {"status":"healthy"}
  to reflect the new {"status":"ok"} health response
- tests: update test assertion to read falkordb settings from
  app.state.explorer_settings instead of removed top-level attributes
2026-06-24 12:51:09 +05:30
Sameer6305 25bee71a46 docs: improve semantic extraction guide onboarding and workflow guidance 2026-06-24 12:18:19 +05:30
f3dc2a449d feat(export): implement Neo4j Bulk CSV Exporter and update registry docs (#261) (#665)
* feat(export): implement Neo4j Bulk CSV Exporter and update registry docs (#261)

* fix(export): address review bugs in Neo4j CSV exporter

- _write_csv: filter **options to known csv.writer dialect params only,
  preventing TypeError when callers pass kwargs like delimiter= or encoding=
  that would reach csv.writer twice or as unknown arguments
- export_neo4j_csv: split kwargs into constructor-level init_params vs
  per-call call_kwargs before forwarding, eliminating the double-pass that
  caused dialect params to collide inside _write_csv
- _prepare_export: remove dead node_id_lookup dict that was built but never
  consumed by any caller
- export_knowledge_graph dispatch: drop the ambiguous "neo4j" format alias
  (kept "neo4j_csv" and "neo4j-csv"); "neo4j" conflicts with the codebase's
  established meaning of the live Bolt/Cypher store backend; add inline
  comment clarifying that file_path is treated as an output directory for
  this format
- export_usage.md: fix all three wrong API examples — constructor params
  node_label_sep/strict_validation corrected to label_separator/strict,
  non-existent nodes_path/rels_path kwargs removed, convenience-method
  example updated to show the correct positional output_dir argument

Co-Authored-By: KaifAhmad1 <kaif2208@gmail.com>

* docs(changelog): add Neo4j Bulk CSV Export entry for PR #665

Documents the new Neo4jCSVExporter feature contributed by @Luffy2208
and the five follow-up bug fixes (TypeError on dialect kwargs,
double-pass kwargs split, dead node_id_lookup removal, ambiguous
format="neo4j" alias removal, and wrong API examples in docs).

Co-Authored-By: KaifAhmad1 <kaif2208@gmail.com>

---------

Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
Co-authored-by: KaifAhmad1 <kaif2208@gmail.com>
2026-06-23 21:15:25 +05:30
Sameer6305 9020973498 docs: improve llm integrations guide onboarding and provider guidance 2026-06-23 19:31:46 +05:30
Sameer6305 b84441066d docs: improve decision intelligence guide onboarding and practical guidance 2026-06-23 19:11:20 +05:30
Sameer6305 3126905e2d docs: improve visualization guide onboarding and workflow guidance 2026-06-23 16:35:22 +05:30
Sameer6305 3f009a3ae5 docs: improve graph analytics guide onboarding and practical guidance 2026-06-23 16:21:34 +05:30
Sameer6305 252a7e76b3 docs: improve reasoning guide onboarding and practical guidance 2026-06-23 16:08:56 +05:30
Sameer6305 2d5d615f42 docs: improve context graph guide onboarding and concepts 2026-06-23 15:46:31 +05:30
Sameer6305 b693a9cd9f docs: improve ontology guide onboarding and concepts 2026-06-23 15:16:30 +05:30
Zohaib Hassnain 21ddee94f7 Add Knowledge Explorer deployment templates 2026-06-23 13:37:25 +05:00
Sameer6305 d00a5e53b4 docs: improve ingest guide onboarding and examples 2026-06-23 13:04:21 +05:30
Mohd Kaif a450f9eddc Merge pull request #680 from semantica-agi/feat/code-block-polish
docs: lighten code block hover animation
2026-06-22 16:47:13 +05:30
KaifAhmad1 5e7c929ca8 docs: lighten code block hover — subtle lift + faint ring 2026-06-22 16:43:27 +05:30
Mohd Kaif beced2c85b Merge pull request #679 from semantica-agi/feat/premium-docs-animations
docs: premium design system for custom.css
2026-06-22 16:34:40 +05:30
KaifAhmad1 350b0a953f docs: upgrade custom.css to premium design system
Replace uniform cursor-bar hover effects with a differentiated,
light animation layer per element type. Adds global polish:
smooth scroll, custom scrollbar, brand-colored text selection,
page fade-in entrance, emerald focus rings, H1 gradient underline
accent, styled blockquotes, gradient HR dividers, uppercase table
headers, and CTA button glow — all tuned to the #080C10 dark
background and #10B981 emerald brand color.
2026-06-22 16:27:16 +05:30
Mohd KaifandSameer6305 5db740100e Align guides with current source APIs (#676)
* align guides with current source APIs

* docs(guides): align context graph and visualization examples with source APIs

* docs(guides): fix reasoning and approval chain examples

* docs(graphrag): fix multiline string examples

* docs(pipeline): align handler examples with execution engine

* docs(ontology): align graph serialization example with ContextGraph API

* docs(llm): fix Triplet example attribute access

---------

Co-authored-by: Sameer6305 <sskadam6305@gmail.com>
2026-06-22 15:58:17 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 144c348ed7 deps(deps): update loguru requirement from >=0.6.0 to >=0.7.3 (#654)
Updates the requirements on [loguru](https://github.com/Delgan/loguru) to permit the latest version.
- [Release notes](https://github.com/Delgan/loguru/releases)
- [Changelog](https://github.com/Delgan/loguru/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/Delgan/loguru/compare/0.6.0...0.7.3)

---
updated-dependencies:
- dependency-name: loguru
  dependency-version: 0.7.3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-21 11:43:53 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> a45000a3de deps(deps): update pillow requirement from >=9.2.0 to >=11.3.0 (#653)
Updates the requirements on [pillow](https://github.com/python-pillow/Pillow) to permit the latest version.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/9.2.0...11.3.0)

---
updated-dependencies:
- dependency-name: pillow
  dependency-version: 11.3.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-21 11:39:56 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> af00d5bac0 deps(deps): update python-docx requirement from >=0.8.11 to >=1.2.0 (#652)
Updates the requirements on [python-docx](https://github.com/python-openxml/python-docx) to permit the latest version.
- [Changelog](https://github.com/python-openxml/python-docx/blob/master/HISTORY.rst)
- [Commits](https://github.com/python-openxml/python-docx/compare/v0.8.11...v1.2.0)

---
updated-dependencies:
- dependency-name: python-docx
  dependency-version: 1.2.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-21 11:38:36 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> aefa51baa5 chore(deps): bump dompurify from 3.4.10 to 3.4.11 in /explorer (#663)
Bumps [dompurify](https://github.com/cure53/DOMPurify) from 3.4.10 to 3.4.11.
- [Release notes](https://github.com/cure53/DOMPurify/releases)
- [Commits](https://github.com/cure53/DOMPurify/compare/3.4.10...3.4.11)

---
updated-dependencies:
- dependency-name: dompurify
  dependency-version: 3.4.11
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-20 22:35:54 +05:30
Sameer Kadam f35a976a03 docs: align onboarding examples with current APIs (#664) 2026-06-20 22:18:16 +05:30
Sameer Kadam 011a21d0b3 docs: fix broken links and stale notebook references (#660) 2026-06-20 19:27:30 +05:30
Mohd Kaif 447ac3fa7c Merge pull request #662 from semantica-agi/fix/changelog-ci-retrigger
docs: add mintlify export to PR validation — catch page failures before merge
2026-06-20 17:05:26 +05:30
KaifAhmad1 e1b7b072b8 docs: replace Changelog tab with GitHub Releases external link; update inline links 2026-06-20 17:01:00 +05:30
KaifAhmad1 a9a72977a9 docs: fix trailing comma in docs.json after tab removal 2026-06-20 16:53:44 +05:30
KaifAhmad1 c5c27b35aa docs: remove Changelog tab from nav — diagnose export failure (step 2) 2026-06-20 16:49:57 +05:30
KaifAhmad1 eddb7dd914 docs: strip changelog to minimal stub — diagnose export failure 2026-06-20 16:45:28 +05:30
KaifAhmad1 4547e43dda docs: show first 60 lines of mintlify output to identify failing page 2026-06-20 16:40:35 +05:30
KaifAhmad1 5ee1548c90 docs: resolve merge conflict — keep plain-text header in changelog 2026-06-20 16:30:50 +05:30
KaifAhmad1 59aa3f1d86 docs: add mintlify export + JSX balance checks to docs_check.py; run on PRs 2026-06-20 16:28:02 +05:30
Mohd Kaif 2df8de03e0 docs: rewrite changelog as flat markdown — remove heavy accordion nesting (#661) 2026-06-20 16:16:45 +05:30
KaifAhmad1 ce8344aa73 docs: rewrite changelog as flat markdown — remove heavy accordion nesting 2026-06-20 16:11:48 +05:30
Mohd Kaif b4a4cf9bd8 Merge pull request #659 from semantica-agi/fix/changelog-ci-retrigger
docs: fix changelog CI — add unreleased note, tighten defaultOpen syntax
2026-06-20 15:58:06 +05:30
KaifAhmad1 5515390269 docs: add unreleased note and tighten changelog defaultOpen syntax 2026-06-20 15:53:59 +05:30
Mohd Kaif 1bd2ecfef2 docs: add Changelog tab and clean up overview page (#658)
* docs: add Changelog tab and clean up overview page

- Remove v0.5.0 release banner and stats grid from docs/index.md
- Add Changelog navigation tab to docs/docs.json after FAQ
- Create docs/changelog.md sourced from CHANGELOG.md with full Mintlify
  formatting: one accordion per release (Unreleased → v0.0.1), icons,
  pip install snippets, Added/Fixed/Security sub-sections, and a change
  type legend

* docs: fix broken index#whats-new link in quickstart — point to changelog
2026-06-20 15:43:41 +05:30
Sameer KadamandKaifAhmad1 b882579e2d docs: fix onboarding examples for GraphBuilder and temporal queries (#656)
* docs: fix onboarding examples for GraphBuilder and temporal queries

* docs: fix provenance import, hollow example, and query comment (#656 follow-up)

- Fix wrong import: ProvenanceTracker lives in semantica.kg, not semantica.provenance
- Replace hollow provenance accordion with actual track_entity/get_all_sources example
- Annotate query="" in TemporalGraphQuery.query_at_time as reserved for future use

* docs: use ProvenanceManager from semantica.provenance in W3C PROV-O example

---------

Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-06-20 13:47:20 +05:30
Sameer Kadam 6b14253157 docs: align Explorer module references with actual CLI usage (#655) 2026-06-19 16:24:06 +05:30
Sameer KadamandKaifAhmad1 926d4c1653 docs: add choose-your-module onboarding guide (#651)
* docs: add choose-your-module onboarding guide

* fix(docs): correct export code examples against actual API signatures

- export_to_rdf() returns a string; use export() for file output
- format="json-ld" is invalid; correct value is "jsonld"
- ParquetExporter/LPGExporter/ArangoAQLExporter take file_path as a
  required positional arg, not output= / output_dir= kwargs
- ArangoAQLExporter().export(graph) was missing file_path entirely,
  which would raise TypeError at runtime
- Remove misleading 'with provenance embedded' comment (no such param)

---------

Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-06-19 13:15:06 +05:30
Mohd Kaif 12d61b92df docs: add Temporal & Distance Intelligence reference pages with accurate API (#650)
- Add docs/reference/temporal.md: full Temporal Intelligence reference covering
  bi-temporal model (TemporalBound.OPEN sentinel, BiTemporalFact.from_relationship()
  factory), TemporalGraphQuery (query_at_time, reconstruct_at_time, query_time_range,
  find_temporal_paths, analyze_evolution, validate_temporal_consistency),
  TemporalPatternDetector, TemporalReasoningEngine with all 13 Allen interval
  relations over TemporalInterval objects, TemporalNormalizer (returns
  Optional[Tuple[datetime, datetime]]), TemporalQueryRewriter.rewrite() returning
  TemporalQueryResult, and TemporalVersionManager with SQLite storage and correct
  method names (list_versions, compare_versions, get_version, apply_revision,
  validate_snapshot, verify_checksum)

- Add docs/reference/distance.md: Distance Intelligence reference with corrected
  SimilarityCalculator API (pairwise_similarity, batch_similarity, find_most_similar)
  and semantic neighborhood / proximity-blended retrieval patterns

- Update docs/reference/kg.md: expand Exported Classes table to include all
  TemporalPatternDetector, TemporalInterval, IntervalRelation, TemporalQueryResult,
  AlgorithmTrackerWithProvenance, AlgorithmRegistry, ProvenanceTracker, SeedManager,
  KGConfig; fix all temporal code examples to use correct constructors and method names

- Update docs/reference/context.md: add Distance Intelligence section

- Update docs/index.md: add v0.3.0 release accordion with feature highlights

- Update docs/docs.json: wire temporal and distance pages into Modules navigation
2026-06-18 13:37:24 +05:30
Zohaib Hassnain 0765cfea77 docs: add CLI demo gif (#649) 2026-06-18 01:52:24 +05:30
Mohd Kaif 25289023fe docs: replace all CardGroup/Card blocks with animated bullet points across all 50 docs pages (#648)
- Fix What's new → link in Info banner (now a proper <a> tag, always clickable)
- Replace 4-stat CardGroup on index with inline premium stats row
- Convert every <CardGroup>/<Card> block site-wide to markdown bullet lists:
  content sections → bold-title bullets with sub-bullets, nav cards → [Title](href) — description
- Add cursor-animated list item hover effects to custom.css:
  green inset left border, subtle background tint, marker color change on hover
- Affects index, getting-started, quickstart, concepts, modules, faq, architecture,
  installation, cookbook, glossary, learning-more, explorer-setup, cli-setup,
  community, contributing-guide, governance, citation, project-license,
  all integrations pages, and all 20+ reference module pages
2026-06-17 23:18:40 +05:30
Mohd Kaif 0f9a651527 remove: delete domain-specific use_cases cookbooks and docs (#647)
Removes all notebooks, data files, and exports under cookbook/use_cases/
(advanced_rag, biomedical, blockchain, capability_gap_defense, cybersecurity,
finance, intelligence, renewable_energy, supply_chain) and the corresponding
docs/use-cases.md page.

Cleans up all references in docs/cookbook.md, docs/docs.json,
docs/concepts.md, docs/modules.md, and docs/learning-more.md.
2026-06-17 22:13:50 +05:30
Mohd Kaif e04dc12e6e docs: premium UI improvements — navbar links, hover effects, inline tips, accordion troubleshooting (#646)
- Move Discord, GitHub, PyPI, and Follow on X links from sidebar anchors to top-right navbar
- Lock dark mode as default via appearance.strict and hide theme toggle
- Add custom.css with hover highlighting for tables, code blocks, cards, callouts, and inline code
- Move all Tips and Common Pitfalls sections inline next to their relevant content across all 25 reference docs
- Polish context.md: remove duplicates, condense callouts, upgrade Cookbooks to CardGroup
- Convert Troubleshooting and Performance Optimization sections in installation.md, cli-setup.md, explorer-setup.md, learning-more.md, and faq.md from plain headers to AccordionGroup
- Change navigation-hint Tip callouts to Info in concepts.md, faq.md, glossary.md, and modules.md
2026-06-17 18:59:25 +05:30
Mohd Kaif a326c7d3bd Fix/mintlify theme (#645)
* fix: replace invalid Mintlify theme 'venus' with 'mint'

* docs: replace em dashes with colons across all docs files

* fix: strip UTF-8 BOM from all docs files (broke frontmatter detection)
2026-06-17 13:26:19 +05:30
Mohd Kaif 8f2910fc33 fix: replace invalid Mintlify theme 'venus' with 'mint' (#644) 2026-06-17 13:11:07 +05:30
Mohd Kaif 1f3cea5f0a docs: upgrade all docs pages with Mintlify premium components (#642)
Replace plain markdown lists, tables, and numbered steps with interactive
Mintlify v3 MDX components across all 50+ documentation files:

- Tabs: provider/parser/method selection guides, citation formats, component details
- Steps: setup flows, pipeline stages, connection initialization
- CardGroup/Card: feature overviews, "what you get" sections, navigation footers
- AccordionGroup: FAQ entries
- Check/Warning/Tip/Note/Info: callouts replacing plain bold text and inline notes

Files improved span the full docs surface: reference modules (context, llms,
kg, reasoning, embeddings, deduplication, provenance, parse, ontology, core,
semantic_extract), integrations (agno, docling, snowflake), graph/vector
store backends (apache_age, pgvector), and top-level guides (contributing,
governance, glossary, citation, community-projects, learning-more).
2026-06-17 12:58:44 +05:30
Mohd Kaif 0e95de4622 Merge pull request #606 from Sameer6305/docs/developer-experience-improvements
docs(llms): improve onboarding and practical provider setup guidance
2026-06-16 21:55:12 +05:30
KaifAhmad1 850f47625f docs: address review feedback across 7 modules
- llms.md: use showcase models (llama-3.3-70b-versatile, gpt-4o) in
  provider examples and use-case tables; clarify defaults vs recommended
  in Defaults and Reproducibility section
- split.md: document that chunk_size is in characters with migration note
- ingest.md: add Note that glob patterns are not supported by ingest()
- explorer-setup.md: remove hardcoded "1.5 seconds" timing claim
- cli-setup.md: expand semantica-worker description with concrete usage
- mcp_server.md: clarify turtle/ttl are aliases for the same RDF format
- semantic_extract.md: remove emoji from code comments
2026-06-16 21:49:20 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 9c58b9df7c chore(deps-dev): bump @babel/core from 7.29.0 to 7.29.6 in /explorer (#640)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.29.0 to 7.29.6.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.29.6/packages/babel-core)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-version: 7.29.6
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-16 19:58:37 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> e3562daa89 chore(deps-dev): bump js-yaml from 4.1.1 to 4.2.0 in /explorer (#639)
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.1.1 to 4.2.0.
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/commits)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 4.2.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-16 19:57:13 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 9c164d15d7 ci(deps): bump microsoft/security-devops-action from 1.6.0 to 1.12.0 (#634)
Bumps [microsoft/security-devops-action](https://github.com/microsoft/security-devops-action) from 1.6.0 to 1.12.0.
- [Release notes](https://github.com/microsoft/security-devops-action/releases)
- [Commits](https://github.com/microsoft/security-devops-action/compare/v1.6.0...v1.12.0)

---
updated-dependencies:
- dependency-name: microsoft/security-devops-action
  dependency-version: 1.12.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-16 19:51:36 +05:30
Sameer6305 f608b1f75f docs: add CLI and Explorer setup guides 2026-06-16 19:22:42 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> e35a399bee ci(deps): bump github/codeql-action from 3 to 4 (#633)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-16 19:01:52 +05:30
Mohd KaifandCopilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> 893048ef2a Potential fix for code scanning alert no. 34: Workflow does not contain permissions (#641)
Adds an explicit top-level `permissions` block to the GitHub Actions CI workflow.

This change sets the `GITHUB_TOKEN` permission scope to the minimum required level (`contents: read`), following the principle of least privilege and addressing the CodeQL alert `actions/missing-workflow-permissions`.

The workflow only requires read access to repository contents for checkout and CI tasks, so no additional permissions are needed.

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2026-06-16 18:49:51 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 979c234679 ci(deps): bump actions/setup-dotnet from 4 to 5 (#632)
Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 4 to 5.
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](https://github.com/actions/setup-dotnet/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-dotnet
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-16 16:31:04 +05:30
Sameer6305 fc22805e44 docs(change_management): align versioning APIs with implementation 2026-06-16 15:17:38 +05:30
Sameer6305 f2bf1e2159 docs(conflicts): align conflict APIs with implementation 2026-06-16 15:03:28 +05:30
Sameer6305 7e5db2b38d docs(utils): align utility APIs with implementation 2026-06-16 14:49:26 +05:30
Mohd KaifandZohaib Hassnain 46447d1f3f fix(explorer): resolve blank dashboard UI and ship frontend bundle in wheel (#638)
* fix(explorer): resolve blank dashboard UI and ship frontend bundle in wheel

Fixes #631 — the Explorer server started successfully but the browser showed
a blank page because semantica/static/ was gitignored and never present after
a fresh install or clone.

Changes:
- ci.yml / release.yml: add Node 20 setup + npm ci && npm run build before
  python -m build so every wheel contains a CI-built frontend bundle
- pyproject.toml: add package-data patterns (static/*, static/assets/*) so
  setuptools includes the bundle in the wheel; add MANIFEST.in for sdist coverage
- app.py: replace silent empty-HTML fallback with a 200 page that clearly
  explains the missing bundle and links to /docs; fix CORS allow_credentials
  to default false, gated behind EXPLORER_CORS_CREDENTIALS env var to prevent
  credentialed cross-origin requests on unauthenticated endpoints
- __init__.py: warn at startup when --host is non-loopback (unauthenticated
  network exposure)
- explorer/README.md: full rewrite covering pip-install mode (primary path,
  no Node required) and dev-server mode (contributors), CLI flags, env vars,
  workspace table, troubleshooting for the blank-page symptom
- README.md: update Knowledge Explorer section with correct command and link
  to the new setup guide

* fix(explorer): set build.target esnext to fix esbuild CI failure

esbuild >=0.28 (forced via npm overrides) conflicts with Vite 6 defaults on
Linux CI — it tries to lower destructuring syntax for the implicit browser
target list but errors out. Explicit target: 'esnext' tells esbuild to emit
native syntax unchanged, bypassing the transpilation error entirely. Safe for
a developer tool that runs in modern browsers.

* test(explorer): verify packaged frontend bundle

---------

Co-authored-by: Zohaib Hassnain <109234410+ZohaibHassan16@users.noreply.github.com>
2026-06-16 14:38:24 +05:30
Sameer6305 74b8170015 docs(mcp_server): align tools and resources with implementation 2026-06-16 14:38:14 +05:30
Sameer6305 2b661f1c2c docs(visualization): align visualizer APIs with implementation 2026-06-16 14:13:22 +05:30
Sameer6305 50d4c1e849 docs(export): align exporters and format support with implementation 2026-06-16 13:41:57 +05:30
Sameer6305 c62ff0238a docs(explorer): align explorer routes, CLI flags, and APIs with implementation 2026-06-16 11:34:14 +05:30
Sameer6305 d9f3e11eb3 docs(context): align context and policy APIs with implementation 2026-06-16 11:18:19 +05:30
Sameer6305 4275d6ee34 docs(deduplication): align entity resolution APIs with implementation 2026-06-16 11:18:19 +05:30
Sameer6305 55ea3fb4d5 docs(normalize): align normalization APIs with implementation 2026-06-16 11:18:19 +05:30
Sameer6305 4cb2154803 docs(ingest): align ingestion APIs and return types with implementation 2026-06-16 11:18:19 +05:30
Sameer6305 babeaf0e61 docs(evals): align placeholder documentation with implementation 2026-06-16 11:18:19 +05:30
Sameer6305 777deaa4e3 docs(provenance): align lineage and provenance APIs with implementation 2026-06-16 11:18:19 +05:30
Sameer6305 21b15f4b8e docs(reasoning): fix Python 3.8 type annotation compatibility 2026-06-16 11:18:19 +05:30
Sameer6305 ca0133227b docs(reasoning): align inference and reasoning APIs with implementation 2026-06-16 11:18:19 +05:30
Sameer6305 f7e8e87734 docs(triplet_store): align SPARQL and storage APIs with implementation 2026-06-16 11:18:18 +05:30
Sameer6305 10c53c9dd7 docs(graph_store): align graph APIs and backend documentation 2026-06-16 11:18:18 +05:30
Sameer6305 05ff590212 docs(vector_store): align vector store docs with implementation 2026-06-16 11:18:18 +05:30
Sameer6305 a28cb68098 docs(embeddings): align embedding docs with implementation 2026-06-16 11:18:18 +05:30
Sameer6305 13805d40e3 docs(ontology): align ontology docs with implementation 2026-06-16 11:18:18 +05:30
Sameer6305 2fc3261b89 docs(seed): align seed data docs with implementation 2026-06-16 11:18:18 +05:30
Sameer6305 441515a66c docs(kg): align graph and temporal APIs with implementation 2026-06-16 11:18:18 +05:30
Sameer6305 4982ce8d4b docs(pipeline): align examples and templates with implementation 2026-06-16 11:18:18 +05:30
Sameer6305 273f37dd05 docs(core): align configuration examples with implementation 2026-06-16 11:18:18 +05:30
Sameer6305 7e0e29282e docs(split): remove unsupported APIs and fix examples 2026-06-16 11:18:18 +05:30
Sameer6305 f170e1bab6 docs(split): align chunking docs with implementation 2026-06-16 11:18:18 +05:30
Sameer6305 4bb7a49e08 docs(parse): align docling installation instructions 2026-06-16 11:18:18 +05:30
Sameer6305 8cebb052c8 docs(parse): improve onboarding and align with implementation 2026-06-16 11:18:18 +05:30
Sameer6305 366add9fa0 docs(semantic_extract): improve onboarding and workflow guidance 2026-06-16 11:18:18 +05:30
Sameer6305 db8d3581aa docs(llms): clarify implementation defaults for reproducibility 2026-06-16 11:18:17 +05:30
Sameer6305 4ed8c34f78 docs(llms): fix config examples and snippet imports 2026-06-16 11:18:17 +05:30
Sameer6305 4561faa254 docs(llms): improve onboarding and provider setup guidance 2026-06-16 11:18:17 +05:30
Mohd Kaif dfd96784a2 Merge pull request #637 from semantica-agi/fix/cli-demo-blockers
Fix CLI demo blockers
2026-06-16 11:12:16 +05:30
KaifAhmad1andZohaib Hassnain 496b80cf2b tests: fix CodeQL lint in progress tracker regression tests
Consolidate dual import (module alias + from-import) to a single
`import ... as progress_module` alias and qualify all references.
Replace bare `BaseException` catch with `Exception` in the thread
runner helper.

Co-Authored-By: Zohaib Hassnain <zohaib179949@gmail.com>
Co-Authored-By: KaifAhmad1 <kaifahmad087@gmail.com>
2026-06-16 11:03:45 +05:30
Sameer Kadam 76382158d1 Remove duplicate root route registration (#635) 2026-06-16 05:16:08 +05:00
Zohaib Hassnain 9e244ddff6 Fix CLI demo blockers 2026-06-16 04:43:45 +05:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> e0b2f5b1e9 security(deps): update gitpython requirement from >=3.1.30 to >=3.1.50 (#630)
Updates the requirements on [gitpython](https://github.com/gitpython-developers/GitPython) to permit the latest version.
- [Release notes](https://github.com/gitpython-developers/GitPython/releases)
- [Changelog](https://github.com/gitpython-developers/GitPython/blob/main/CHANGES)
- [Commits](https://github.com/gitpython-developers/GitPython/compare/3.1.30...3.1.50)

---
updated-dependencies:
- dependency-name: gitpython
  dependency-version: 3.1.50
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-15 19:06:23 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 0906dac841 security(deps-dev): update instructor requirement (#629)
Updates the requirements on [instructor](https://github.com/instructor-ai/instructor) to permit the latest version.
- [Release notes](https://github.com/instructor-ai/instructor/releases)
- [Changelog](https://github.com/567-labs/instructor/blob/main/CHANGELOG.md)
- [Commits](https://github.com/instructor-ai/instructor/compare/1.0.0...v1.15.3)

---
updated-dependencies:
- dependency-name: instructor
  dependency-version: 1.15.1
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-15 18:57:09 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 3202e2d602 security(deps): update numpy requirement from >=1.21.0 to >=2.0.2 (#628)
Updates the requirements on [numpy](https://github.com/numpy/numpy) to permit the latest version.
- [Release notes](https://github.com/numpy/numpy/releases)
- [Changelog](https://github.com/numpy/numpy/blob/main/doc/RELEASE_WALKTHROUGH.rst)
- [Commits](https://github.com/numpy/numpy/compare/v1.21.0...v2.0.2)

---
updated-dependencies:
- dependency-name: numpy
  dependency-version: 2.0.2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-15 18:46:33 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 512d307298 security(deps-dev): update kafka-python requirement (#627)
Updates the requirements on [kafka-python](https://github.com/dpkp/kafka-python) to permit the latest version.
- [Release notes](https://github.com/dpkp/kafka-python/releases)
- [Changelog](https://github.com/dpkp/kafka-python/blob/master/docs/changelog.rst)
- [Commits](https://github.com/dpkp/kafka-python/compare/2.0.0...3.0.0)

---
updated-dependencies:
- dependency-name: kafka-python
  dependency-version: 3.0.0
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-15 18:06:28 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> a3ba62b8b7 security(deps-dev): update cryptography requirement (#625)
Updates the requirements on [cryptography](https://github.com/pyca/cryptography) to permit the latest version.

Updates `cryptography` to 49.0.0
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/48.0.0...49.0.0)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-version: 49.0.0
  dependency-type: direct:development
  dependency-group: snowflake-features
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-15 16:54:22 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> be8080c5a3 deps(deps): update openpyxl requirement from >=3.0.10 to >=3.1.5 (#615)
Updates the requirements on [openpyxl](https://openpyxl.readthedocs.io) to permit the latest version.

---
updated-dependencies:
- dependency-name: openpyxl
  dependency-version: 3.1.5
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-15 16:49:17 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 4c333374b2 deps(deps): update beautifulsoup4 requirement from >=4.11.0 to >=4.15.0 (#614)
Updates the requirements on [beautifulsoup4](https://www.crummy.com/software/BeautifulSoup/bs4/) to permit the latest version.

---
updated-dependencies:
- dependency-name: beautifulsoup4
  dependency-version: 4.15.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-15 16:47:54 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> b83a28ddc4 deps(deps): update matplotlib requirement from >=3.5.0 to >=3.9.4 (#613)
Updates the requirements on [matplotlib](https://github.com/matplotlib/matplotlib) to permit the latest version.
- [Release notes](https://github.com/matplotlib/matplotlib/releases)
- [Commits](https://github.com/matplotlib/matplotlib/compare/v3.5.0...v3.9.4)

---
updated-dependencies:
- dependency-name: matplotlib
  dependency-version: 3.9.4
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-15 12:43:20 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 5439553aa9 security(deps-dev): update litellm requirement from >=1.0.0 to >=1.83.9 (#600)
Updates the requirements on [litellm](https://github.com/BerriAI/litellm) to permit the latest version.
- [Release notes](https://github.com/BerriAI/litellm/releases)
- [Commits](https://github.com/BerriAI/litellm/commits)

---
updated-dependencies:
- dependency-name: litellm
  dependency-version: 1.83.9
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-15 12:37:54 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 36a15ca0a5 security(deps-dev): update opentelemetry-instrumentation requirement (#599)
Updates the requirements on [opentelemetry-instrumentation](https://github.com/open-telemetry/opentelemetry-python-contrib) to permit the latest version.
- [Release notes](https://github.com/open-telemetry/opentelemetry-python-contrib/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-python-contrib/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-python-contrib/commits)

---
updated-dependencies:
- dependency-name: opentelemetry-instrumentation
  dependency-version: 0.62b1
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-15 12:30:34 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> a4978cad18 security(deps): update umap-learn requirement from >=0.5.0 to >=0.5.12 (#598)
Updates the requirements on [umap-learn](https://github.com/lmcinnes/umap) to permit the latest version.
- [Release notes](https://github.com/lmcinnes/umap/releases)
- [Changelog](https://github.com/lmcinnes/umap/blob/master/doc/release_notes.rst)
- [Commits](https://github.com/lmcinnes/umap/compare/0.5.0...release-0.5.12)

---
updated-dependencies:
- dependency-name: umap-learn
  dependency-version: 0.5.12
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-15 11:49:12 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 262dd3e8e4 security(deps): update onnxruntime requirement from >=1.17.0 to >=1.20.1 (#597)
Updates the requirements on [onnxruntime](https://github.com/microsoft/onnxruntime) to permit the latest version.
- [Release notes](https://github.com/microsoft/onnxruntime/releases)
- [Changelog](https://github.com/microsoft/onnxruntime/blob/main/docs/ReleaseManagement.md)
- [Commits](https://github.com/microsoft/onnxruntime/compare/v1.17.0...v1.20.1)

---
updated-dependencies:
- dependency-name: onnxruntime
  dependency-version: 1.20.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-15 11:47:56 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 559bb45565 security(deps-dev): update graphviz requirement from >=0.20.0 to >=0.21 (#596)
Updates the requirements on [graphviz](https://github.com/xflr6/graphviz) to permit the latest version.
- [Changelog](https://github.com/xflr6/graphviz/blob/master/CHANGES.rst)
- [Commits](https://github.com/xflr6/graphviz/compare/0.20...0.21)

---
updated-dependencies:
- dependency-name: graphviz
  dependency-version: '0.21'
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-14 22:12:05 +05:30
Mohd Kaif 4a8dded448 docs: README polish, competitive comparison table, and complement positioning (#624)
* docs: polish README and add competitive comparison table

- Remove all em dashes from prose, headings, and code comments;
  replaced with colons, semicolons, or natural sentence flow
- Add 16-row competitive comparison table (LangChain, LlamaIndex,
  MS GraphRAG, Mem0, Zep) with checkmark/cross visual indicators
- Expand LLM providers from generic "100+ via LiteLLM" to named list:
  OpenAI, Anthropic, Gemini, Mistral, Llama, Groq, Cohere, Azure,
  Bedrock, Ollama, DeepSeek, Perplexity, Together AI, Fireworks AI,
  Replicate, HuggingFace — all marked as already supported today
- Restructure Agentic Frameworks section into three tiers:
  Native Integration (Agno), Already Supported via REST API and MCP,
  and Native SDK Integration Coming Soon
- Add [!IMPORTANT] callout making clear Semantica complements, not
  replaces, existing LLM/vector store/agent framework stacks
- Strengthen hero tagline and Why Semantica prose to reinforce
  complement positioning

* docs: trim comparison table to core intelligence capabilities only

Remove infrastructure/product rows (REST API, MCP server, vector store,
LLM providers) — these are table noise, not differentiators.

Keep 10 rows focused on what makes Semantica genuinely different:
knowledge graph, decision tracking, provenance, explainable reasoning,
ontology, conflict detection, bi-temporal graph, entity resolution,
multi-agent context, and policy enforcement.
2026-06-14 22:03:50 +05:30
Mohd Kaif 4a99938a10 Add DeepWiki badge to README
Added DeepWiki badge to README.
2026-06-14 21:11:23 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> bb4f5d34a7 security(deps): update gensim requirement from >=4.3.0 to >=4.4.0 (#595)
Updates the requirements on [gensim](https://github.com/RaRe-Technologies/gensim) to permit the latest version.
- [Release notes](https://github.com/RaRe-Technologies/gensim/releases)
- [Changelog](https://github.com/piskvorky/gensim/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/RaRe-Technologies/gensim/compare/4.3.0...4.4.0)

---
updated-dependencies:
- dependency-name: gensim
  dependency-version: 4.4.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-14 15:10:35 +05:30
Mohd KaifandCopilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> 1b4d85bfc1 Apply suggested fix to ARCHITECTURE.md from Copilot Autofix (#623)
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-06-13 14:13:43 +05:30
Mohd KaifandCopilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> c8b10747f5 Apply suggested fix to ARCHITECTURE.md from Copilot Autofix (#622)
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-06-13 14:01:15 +05:30
Mohd KaifandCopilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> abb652bea3 Apply suggested fix to CHANGELOG.md from Copilot Autofix (#621)
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-06-13 13:54:54 +05:30
Mohd Kaif bd0b33590c security: document removal of leaked Groq keys from 4 additional notebooks (#620)
Updates CHANGELOG [Unreleased] to record that hardcoded GROQ_API_KEY
fallback values were stripped from advanced_rag/01, advanced_rag/02,
blockchain/01_DeFi_Protocol_Intelligence, and biomedical/01 — covering
secret scanning alerts #1–#6 (gsk_SLLE0, gsk_S4dBVJ, gsk_SLOv6,
gsk_lR6Qcj, gsk_ToJis6, gsk_LmbQBr). Keys already removed from HEAD
in a5da533; all 6 keys must be revoked in the Groq console.
2026-06-13 13:37:40 +05:30
Mohd Kaif 6d9a690bcd security: force esbuild ^0.28.1; remove leaked Groq API keys from notebooks (#619)
- Add esbuild ^0.28.1 npm override in explorer/package.json (Dependabot #15,
  GHSA-gv7w-rqvm-qjhr); npm audit now reports 0 vulnerabilities
- Strip hardcoded GROQ_API_KEY values from 6 cookbook notebooks; fallback
  replaced with empty string (secret scanning alerts #1-#6)
  Affected: supply_chain/01, intelligence/01, cybersecurity/01 & 02,
  finance/01, blockchain/02
- CHANGELOG: document both fixes under [Unreleased] ### Security
2026-06-13 13:04:49 +05:30
Mohd Kaif c8519470bc security: fix 9 Dependabot/CodeQL alerts (DOMPurify, vite, uuid, workflow permissions) (#617)
* security: fix 9 Dependabot/CodeQL alerts — DOMPurify, vite, uuid, workflow permissions

- Add explicit permissions block to defender-for-devops.yml (CodeQL #25)
- Upgrade vite 5.4.x → 6.4.3; bundled esbuild 0.21.5 → 0.25.12 (Dependabot #2, #7)
- Force dompurify ^3.4.0 via npm overrides; resolves 6 DOMPurify XSS alerts (#4–#6, #8–#11)
- Force uuid ^13.0.1 via npm overrides; fixes buffer bounds check (Dependabot #12)

* fix(ci): exclude bandit from MSDO scan on windows-latest

bandit_runner.exe builds a per-file command line; on a large Python repo
the total command string exceeds the Windows CreateProcess limit and the
process fails to start (Win32 ERROR_FILENAME_EXCED_RANGE 206).
Exclude bandit via the tools param and retain checkov, eslint,
templateanalyzer, terrascan, and binskim.

* fix(ci): drop binskim (no binaries), enable Neptune audit logging

- Remove binskim from MSDO tools: repo has no compiled binaries so
  BinSkim raises AnalyzeArgumentNoValuesException and breaks the run
- Add EnableCloudwatchLogsExports: [audit] to NeptuneCluster to fix
  Checkov CKV_AWS_101 (the one error-level result breaking the build)
2026-06-13 12:40:15 +05:30
Mohd Kaif df6fedf619 Add Microsoft Defender for DevOps workflow 2026-06-13 01:13:32 +05:30
Mohd Kaif 9ba8b012bd docs: premium README overhaul + ARCHITECTURE.md with Mermaid diagrams (#616)
- Rewrote README with verified code examples for all 18 modules
- Added sections for semantica.split, semantica.conflicts, semantica.normalize
- Added Recipes section (GraphRAG pipeline, audit trail, AML engine, ontology-to-KG)
- Added REST API curl examples and MCP tools reference table
- Added 9 contextual GitHub admonitions (NOTE/TIP/IMPORTANT/WARNING/CAUTION)
- Fixed semantica.temporal (does not exist as standalone module — moved under semantica.kg)
- Added ARCHITECTURE.md with two Mermaid flowcharts:
  · Full data pipeline (all sources → processing → storage → outputs)
  · Decision intelligence lifecycle (record → link → query → govern → audit)
- Linked ARCHITECTURE.md from README nav and Architecture section
2026-06-12 22:42:46 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 946d826555 security(deps-dev): update pytest-cov requirement (#594)
Updates the requirements on [pytest-cov](https://github.com/pytest-dev/pytest-cov) to permit the latest version.

Updates `pytest-cov` to 7.1.0
- [Changelog](https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest-cov/compare/v3.0.0...v7.1.0)

---
updated-dependencies:
- dependency-name: pytest-cov
  dependency-version: 7.1.0
  dependency-type: direct:development
  dependency-group: benchmark-tools
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-12 18:05:46 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> b1be9bec51 security(deps-dev): update pyarrow requirement (#593)
Updates the requirements on [pyarrow](https://github.com/apache/arrow) to permit the latest version.

Updates `pyarrow` to 21.0.0
- [Release notes](https://github.com/apache/arrow/releases)
- [Commits](https://github.com/apache/arrow/compare/go/v10.0.0...apache-arrow-21.0.0)

---
updated-dependencies:
- dependency-name: pyarrow
  dependency-version: 21.0.0
  dependency-type: direct:development
  dependency-group: arrow-features
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-12 18:03:35 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 56fb5d4e1e security(deps-dev): bump the snowflake-features group with 2 updates (#592)
Updates the requirements on [snowflake-connector-python](https://github.com/snowflakedb/snowflake-connector-python) and [cryptography](https://github.com/pyca/cryptography) to permit the latest version.

Updates `snowflake-connector-python` to 4.5.0
- [Release notes](https://github.com/snowflakedb/snowflake-connector-python/releases)
- [Commits](https://github.com/snowflakedb/snowflake-connector-python/compare/v3.0.0...v4.5.0)

Updates `cryptography` to 48.0.0
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/3.4...48.0.0)

---
updated-dependencies:
- dependency-name: snowflake-connector-python
  dependency-version: 4.5.0
  dependency-type: direct:development
  dependency-group: snowflake-features
- dependency-name: cryptography
  dependency-version: 48.0.0
  dependency-type: direct:development
  dependency-group: snowflake-features
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-12 17:55:10 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 5f1fa1ff32 security(deps): update requests requirement (#591)
Updates the requirements on [requests](https://github.com/psf/requests) to permit the latest version.

Updates `requests` to 2.32.5
- [Release notes](https://github.com/psf/requests/releases)
- [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md)
- [Commits](https://github.com/psf/requests/compare/v2.28.0...v2.32.5)

---
updated-dependencies:
- dependency-name: requests
  dependency-version: 2.32.5
  dependency-type: direct:production
  dependency-group: security-critical
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-12 17:53:53 +05:30
Mohd Kaif 0dfdf66f82 docs(readme): remove dividers, rebrand to semantica-agi org (#612)
Remove all horizontal rule dividers for a cleaner premium look.
Replace all Hawksight-AI references with semantica-agi org URLs and update footer attribution from Hawksight AI to Semantica.
2026-06-11 21:14:12 +05:30
Mohd Kaif dba24c6ddb Remove architecture section from README
Removed architecture diagram and related content from README.
2026-06-11 20:13:52 +05:30
Mohd Kaif d7931f478a docs(readme): full module showcase, verified API examples, improved Mermaid chart (#611)
- Add working code examples for every module: semantica.ingest,
  semantica.semantic_extract, semantica.kg, semantica.reasoning,
  semantica.vector_store, semantica.provenance, semantica.ontology,
  semantica.deduplication, semantica.pipeline, semantica.temporal,
  semantica.export, semantica.visualization
- Verify all class names and method signatures against real source:
  add_node/add_edge (not add_entity/add_relationship), get_neighbors(hops=),
  state_at(), AgentContext(vector_store=, knowledge_graph=),
  WebIngestor.ingest_url(), DBIngestor.ingest_database(),
  EventDetector.detect_events(), GraphAnalyzer.identify_bridges(),
  DatalogReasoner (not DatalogEngine), store_decision(scenario=),
  OntologyValidator.validate(ontology), BiTemporalFact from semantica.kg
- Replace flat 4-blob Mermaid diagram with 7-layer flowchart showing
  all 14 modules as individual color-coded nodes with data-flow edges
- Expand Why Semantica comparison table from 7 to 10 rows
- Add temporal, provenance, and export to module table descriptions
2026-06-11 20:10:01 +05:30
Mohd Kaif 91fdfbc12b docs(readme): improve README — remove stats row, fix star history and contributors repo (#610) 2026-06-11 18:29:25 +05:30
Mohd Kaif a618b632c6 docs(readme): premium traction-focused rewrite with CLI banner (#608)
- Reposition as Context and Accountability Layer with auditable/governance messaging
- Add animated demo GIF, YouTube thumbnail, stats row, nav bar
- Add Context Graphs and Decision Intelligence sections with code examples
- Add comparison table, architecture diagram, performance benchmarks
- Add star history chart, contributors wall, star CTAs
- Fix CLI startup dashboard to match exact Rich output (centered banner, rounded panel, emoji feature labels)
2026-06-11 18:15:24 +05:30
Mohd Kaif 6a2428ab34 Merge pull request #607 from semantica-agi/chore/remove-benchmarks-extract-to-own-repo
chore: remove benchmarks/ — extracted to semantica-benchmarks repo
2026-06-10 18:27:59 +05:30
KaifAhmad1andClaude Sonnet 4.6 98cec956fe chore: remove benchmarks/ — extracted to semantica-benchmarks repo
Benchmarks moved to https://github.com/KaifAhmad1/semantica-benchmarks

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 18:16:45 +05:30
Mohd Kaif b42fbd978f Merge pull request #602 from Luffy2208/feature/236-public-api-ingestion-support
feat(ingest): add public API ingestion support
2026-06-10 15:58:05 +05:30
KaifAhmad1 a2047d696d docs(changelog): add PR #602 public API ingestion entries to Unreleased
Documents all added features, hardening fixes, and follow-up patches
from PR #602 (PublicAPIIngestor) including contributors Luffy2208 and
Sameer6305.
2026-06-10 15:47:11 +05:30
KaifAhmad1 fbdeb6873a fix(ingest): prevent mutable options mutation in batch/multi-example calls
Deep-copy **options in ingest_examples and batch_public_apis so that
mutable values (e.g. params dicts) are not shared across iterations.
Add rate_limit_delay to the config_only_key strip list in ingest_public_api
so it is not forwarded twice when passed via kwargs.
2026-06-10 15:38:30 +05:30
Sameer6305 b535839003 fix(ingest): harden public API auth validation 2026-06-10 12:47:36 +05:30
Mohd Kaif 504eacb1c0 docs(readme): remove GIF, keep only YouTube video section (#605) 2026-06-10 12:47:36 +05:30
Mohd Kaif 58a5d0abf4 docs(readme): add YouTube platform tour video and improve demo section (#603)
Replaces the bare GIF with a structured "See Semantica in Action"
section featuring a clickable YouTube thumbnail for the Knowledge
Explorer Tour (https://youtu.be/QfnNZg4-dZA) above the original GIF,
with named subsections and a feature-list subtitle.
2026-06-10 12:47:36 +05:30
Sameer Kadam baddfc3cdd fix(benchmarks): restore Python 3.8 compatibility in runner (#601) 2026-06-10 12:47:35 +05:30
Zohaib Hassnain b544f93493 docs(readme): add Knowledge Explorer demo gif (#590) 2026-06-10 12:47:35 +05:30
Sameer KadamandKaifAhmad1 de9fee05e0 test(benchmarks): add git-lfs infrastructure validation checks (#575) (#589)
* test(benchmarks): add git-lfs infrastructure validation checks

* fix(benchmarks): add assertions and skip markers to LFS validation tests

Three tests had no assertions and always passed vacuously. Replace with
real assertions gated by pytest.mark.skip so logic is reviewed now and
enforcement is enabled later by removing the decorator. Also fix fragile
CWD-relative paths to use Path(__file__)-anchored roots, drop unused os
import and dead expected_patterns list, replace os.walk with Path.rglob,
and add missing newline at EOF.

---------

Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-06-10 12:47:35 +05:30
luffy2208 4d64c09ad3 fix(ingest): harden public API xml parsing 2026-06-09 14:10:40 +05:30
Mohd Kaif 484b9582a4 docs(readme): remove GIF, keep only YouTube video section (#605) 2026-06-09 13:03:42 +05:30
Mohd Kaif 43865a27a7 docs(readme): add YouTube platform tour video and improve demo section (#603)
Replaces the bare GIF with a structured "See Semantica in Action"
section featuring a clickable YouTube thumbnail for the Knowledge
Explorer Tour (https://youtu.be/QfnNZg4-dZA) above the original GIF,
with named subsections and a feature-list subtitle.
2026-06-09 12:52:31 +05:30
luffy2208 22382c2cf2 feat(ingest): add public API ingestion support 2026-06-09 06:52:58 +05:30
Sameer Kadam fe426532c3 fix(benchmarks): restore Python 3.8 compatibility in runner (#601) 2026-06-08 22:10:37 +05:30
Zohaib Hassnain e7ab18ea01 docs(readme): add Knowledge Explorer demo gif (#590) 2026-06-08 18:05:59 +05:30
Sameer KadamandKaifAhmad1 f7824d4907 test(benchmarks): add git-lfs infrastructure validation checks (#575) (#589)
* test(benchmarks): add git-lfs infrastructure validation checks

* fix(benchmarks): add assertions and skip markers to LFS validation tests

Three tests had no assertions and always passed vacuously. Replace with
real assertions gated by pytest.mark.skip so logic is reviewed now and
enforcement is enabled later by removing the decorator. Also fix fragile
CWD-relative paths to use Path(__file__)-anchored roots, drop unused os
import and dead expected_patterns list, replace os.walk with Path.rglob,
and add missing newline at EOF.

---------

Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-06-08 17:05:34 +05:30
Mohd Kaif 12e5dc17ce Merge pull request #588 from Sameer6305/benchmark-infra-exploration
feat(benchmarks): add module-level filtering for benchmark runner (#575)
2026-06-06 19:15:32 +05:30
KaifAhmad1 1cf91f1621 fix(benchmarks): replace hardcoded module choices with dynamic discovery
- Extract _discover_modules() to scan benchmarks/ at runtime so the
  --module choices list stays accurate as directories are added or
  removed; eliminates the stale context_graph_effectiveness entry and
  the missing infrastructure entry from the original implementation
- Add an existence guard before passing the resolved path to pytest so
  a valid-looking choice that maps to a missing directory fails fast
  with a clear error instead of silently collecting 0 tests and exiting 0
- Print the active module filter to the console so users can confirm
  the filtered scope in runner output
2026-06-06 19:05:37 +05:30
Sameer6305 027f1caa38 feat(benchmarks): add module-level benchmark filtering 2026-06-05 15:59:50 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ea982aebd8 deps(deps): update scipy requirement from >=1.9.0 to >=1.13.1 (#587)
Updates the requirements on [scipy](https://github.com/scipy/scipy) to permit the latest version.
- [Release notes](https://github.com/scipy/scipy/releases)
- [Commits](https://github.com/scipy/scipy/compare/v1.9.0...v1.13.1)

---
updated-dependencies:
- dependency-name: scipy
  dependency-version: 1.13.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-05 15:39:57 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 2aff515e57 deps(deps): update opencv-python requirement from >=4.6.0 to >=4.13.0.92 (#586)
Updates the requirements on [opencv-python](https://github.com/opencv/opencv-python) to permit the latest version.
- [Release notes](https://github.com/opencv/opencv-python/releases)
- [Commits](https://github.com/opencv/opencv-python/commits)

---
updated-dependencies:
- dependency-name: opencv-python
  dependency-version: 4.13.0.92
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-05 15:36:37 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 2f65659b1c deps(deps): update python-dotenv requirement from >=0.20.0 to >=1.2.1 (#585)
Updates the requirements on [python-dotenv](https://github.com/theskumar/python-dotenv) to permit the latest version.
- [Release notes](https://github.com/theskumar/python-dotenv/releases)
- [Changelog](https://github.com/theskumar/python-dotenv/blob/main/CHANGELOG.md)
- [Commits](https://github.com/theskumar/python-dotenv/compare/v0.20.0...v1.2.1)

---
updated-dependencies:
- dependency-name: python-dotenv
  dependency-version: 1.2.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-05 15:27:56 +05:30
Zohaib Hassnain e891cd8685 fix(explorer): restore graph from cached summary (#584) 2026-06-05 15:24:12 +05:30
Zohaib Hassnain eb63d5dcb4 fix(explorer): auto-settle full graph layout (#583) 2026-06-05 15:11:57 +05:30
Mohd Kaif b93c8b2133 Merge pull request #582 from semantica-agi/feat/rich-cli-polish
feat(cli): modern Rich terminal styling across all modules
2026-06-04 21:33:45 +05:30
KaifAhmad1 6cd0022baf docs(readme): document new CLI commands and v0.5.0 terminal experience
CLI section:
- Intro updated to mention startup dashboard and Rich polish
- Data In: added semantica watch examples; removed --watch flag from ingest
  (watch is now its own command)
- Developer Tools: new subsection covering init, doctor, changelog, shell,
  info with representative examples

What's New in v0.5.0:
- Added Modern CLI Experience subsection listing all 11 improvements:
  startup dashboard, grouped help, doctor, init, watch, changelog, shell,
  progress bars, elapsed timing, error cards, Windows UTF-8 fix
2026-06-04 21:24:02 +05:30
Sameer6305 16af844457 fix(cli): guard Progress output in JSON mode 2026-06-04 21:04:16 +05:30
Mohd KaifandCopilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> c0552ec527 Potential fix for pull request finding 'Empty except'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-06-04 18:04:57 +05:30
KaifAhmad1 9e66035b22 fix(pyproject): move [project.urls] after dependencies to fix TOML parse error
In TOML, declaring [project.urls] inside the [project] block causes all
subsequent key-value pairs (including dependencies = [...]) to be parsed
as project.urls.* keys, producing:
  ValueError: invalid pyproject.toml config: project.urls.dependencies
              must be string

Fix: move [project.urls] to after the dependencies array closes and before
[project.optional-dependencies], which is the correct TOML position for a
sub-table of [project].
2026-06-04 17:57:11 +05:30
KaifAhmad1 cbcdb61298 feat(cli): doctor, init, watch, changelog, timing, error cards, progress bars
Elapsed timing
- CLIContext._start records time.perf_counter() at context creation
- _ok() appends elapsed seconds to every success message automatically

Structured error cards
- _show_error_card() renders a red-bordered Rich Panel with title, detail,
  and an actionable hint line
- _ERROR_HINTS maps common exception types to fix suggestions
- _run_with_error_handling() now routes all errors through the card renderer
  instead of raising plain click.ClickException

Rich progress bars
- kg build: per-source Progress bar (SpinnerColumn + BarColumn +
  MofNCompleteColumn + TimeElapsedColumn) when multiple --source flags given;
  single-source path keeps the spinner
- ingest: spinner added (was missing entirely); shows filename and recursive flag

semantica changelog
- Hits GitHub releases API via stdlib urllib; compares latest tag against
  __version__; renders release notes in a rounded Panel; --json supported

semantica doctor
- Checks: Python version, semantica/rich versions, graph store reachability,
  vector store importability, LLM provider env vars, config file, log dir
- Rich table with ✓/⚠/✗ per check; summary error/warning count at bottom

semantica init
- Interactive wizard: graph backend, vector backend, optional LLM key
- Writes ~/.semantica/config.yaml via yaml.dump; --force to overwrite

semantica watch
- Wraps watchdog Observer; matches configurable glob patterns; auto-ingests
  on created/modified events; graceful Ctrl+C shutdown
- Guards ImportError with pip install semantica[watch] hint

_HELP_SECTIONS updated to surface init, doctor, changelog, watch
2026-06-04 17:46:00 +05:30
KaifAhmad1 3db344784d chore(pyproject): improve PyPI metadata for discoverability
- description: rewritten to lead with the accountability/provenance
  angle and name concrete capabilities; drops emoji which render
  inconsistently across PyPI clients
- keywords: expanded from 8 to 23 terms covering modern search queries
  (ai-agents, llm, graph-rag, decision-intelligence, provenance, etc.)
- classifiers: added Information Analysis, Text Processing::Linguistic,
  Database Engines/Servers, Information Technology audience
- [project.urls]: new section with Homepage, Documentation, Repository,
  Changelog, Bug Tracker, Discord — shown prominently on the PyPI page
  and drive clicks to GitHub/docs
- optional-dependencies: added watch = [watchdog>=3.0.0]; bundled into all
2026-06-04 17:37:11 +05:30
Mohd KaifandCopilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> a41b587bc9 Potential fix for pull request finding 'Empty except'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-06-04 17:18:37 +05:30
KaifAhmad1 4f1740e19c fix(cli): reconfigure stdout/stderr to UTF-8 on Windows at import time
Prevents UnicodeEncodeError on the default cp1252 code page when Rich
renders box-drawing characters and emoji in the startup banner and panels.
Placed before all other imports so Click and Rich capture the already-
reconfigured streams. Uses reconfigure() (Python 3.7+) which modifies the
existing TextIOWrapper in-place rather than replacing sys.stdout.
2026-06-04 17:18:05 +05:30
KaifAhmad1 fab300c498 feat(cli): startup dashboard, Rich help groups, and interactive shell
- _BANNER: ASCII art shown when `semantica` is run with no subcommand
- _show_startup: dashboard panel with Graph Store / Vector Store / Profile
  status cards; suppressed under --quiet and --json
- RichGroup: click.Group subclass that renders --help with grouped sections
  (Data Ingestion, Intelligence, Knowledge Graph, Analytics, Export & Viz,
  Infrastructure, Services, Tools) plus a Quick Start block
- main decorator: cls=RichGroup + invoke_without_command=True to wire both
- `semantica shell`: interactive REPL that dispatches subcommands while
  sharing the parent CLIContext; supports readline on Unix for line editing
2026-06-04 17:13:00 +05:30
KaifAhmad1 dbf6ef7b0b fix(cli): resolve JSON spinner leakage and cleanup review findings
- Guard parse_cmd spinner with `fmt == "json"` (default format) to prevent
  Rich status output from polluting machine-readable stdout in piped usage
- Remove unused `Rule` import from cli.py
- Remove unused `_orig_print` variable in verify_rich_cli.py
- Unify semantica.cli import style in verify_rich_cli.py; use cli_mod.main
2026-06-04 16:57:00 +05:30
KaifAhmad1 b821d4e7c6 fix(docs_check): make rich import optional for CI
The docs validation workflow runs python docs_check.py with no pip
install step, so rich is not available. Wrap the rich import in a
try/except ModuleNotFoundError and fall back to plain print() calls
so the script works in both environments:
- With rich installed: coloured pass/FAIL output
- Without rich (CI): plain text pass/FAIL output, same exit codes
2026-06-04 12:38:16 +05:30
KaifAhmad1 311a7b43b1 feat(cli): modern Rich terminal styling across all modules
## Summary

Overhaul the CLI and all library modules to produce polished, modern
terminal output comparable to tools like uv, gh, and cargo. Rich was
already a declared dependency but barely used — this commit wires it
throughout every layer.

## Changes by layer

### semantica/cli.py — visual overhaul
- Add imports: `box`, `Panel`, `Rule`, `Syntax`, `Text` from Rich
- Add 7 style constants (`_BRAND`, `_KEY`, `_VAL`, `_DIM`, `_SUCCESS`,
  `_WARN_STY`, `_TABLE_BOX`) for a consistent colour palette
- `_ok()` now prefixes output with a green ✓ checkmark
- New `_info()` helper (neutral · bullet, respects --quiet)
- New `_warn()` helper (yellow ⚠ prefix, never suppressed)
- New `_pprint()` helper: renders dicts/lists as syntax-highlighted JSON
  (Rich Syntax, monokai theme) instead of raw Python repr; strings
  pass through unchanged; respects --quiet
- `info` command: banner replaced with a rounded Rich Panel showing
  version + tagline; component table uses SIMPLE_HEAD box
- All 7 table sites updated: `box=SIMPLE_HEAD`, `show_edge=False`,
  consistent `_KEY`/`_VAL` column styles (KG Stats, Reasoning Engines,
  Recent Decisions, Configured Backends, Backup Info, MCP Tools)
- `_run_build()`: `console.status(spinner="dots")` wraps the blocking
  build call; skipped under --quiet / --json
- `parse`, `extract`, `embed generate`, `reason run`, `reason explain`,
  `deduplicate`: each wraps its long-running operation in a status
  spinner, guarded by --quiet / --json
- All 30+ `console.print(result)` calls replaced with `_pprint()`
- All raw `[yellow]Warning:[/yellow]` and "not running" patterns
  replaced with the new `_warn()` / `_WARN_STY` style

### semantica/explorer/__init__.py
- Error messages use `Console(stderr=True)` with `[bold red]Error:[/bold red]`
- Graph loading wrapped in `console.status()` spinner
- Startup info replaced with a cyan-bordered Rich Panel showing URL,
  API docs, and health endpoint

### Library internals — replace print() with structured logger calls
All modules below had active `print()` calls that bypassed the logging
framework, corrupted spinners, and polluted stdout in piped/programmatic
use. All replaced with appropriate `self.logger.*` calls:

- `semantica/kg/graph_builder.py` — 23 calls: entity resolution
  progress, graph structure steps, GraphStore persistence timing, and
  the two `='*60` completion banners → `self.logger.info/debug()`
- `semantica/semantic_extract/methods.py` — 4 verbose-mode debug
  prints → `logger.debug()`
- `semantica/semantic_extract/relation_extractor.py` — progress +
  error prints → `self.logger.debug/warning()` with `exc_info`
- `semantica/semantic_extract/triplet_extractor.py` — same pattern
- `semantica/semantic_extract/semantic_network_extractor.py` — batch
  error prints → `self.logger.warning/error()`
- `semantica/semantic_extract/coreference_resolver.py` — error print
  → `self.logger.error()`
- `semantica/semantic_extract/providers.py` — debug print →
  `self.logger.debug()`

### Tooling
- `benchmarks/benchmarks_runner.py`: Rule banner, ✓/✗/⚠ status lines,
  Rule separators around regression alert
- `benchmarks/infrastructure/compare.py`: removed manual ANSI escape
  codes; comparison output is now a Rich Table with SIMPLE_HEAD;
  summary uses coloured Rule + styled SUCCESS/FAILURE messages
- `cookbook/advanced/snowflake_ingestion_examples.py`: `_section()`
  helper using Rule; tabular data rendered as Rich Table; result lines
  use ✓/✗/⚠ prefixes; logger.error already present, retained
- `docs_check.py`: `pass`/`FAIL` lines use `[bold green]` /
  `[bold red]`; summary uses styled output

## Tests
- `tests/test_cli_commands.py`: fix 3 pre-existing mock mismatches
  - `test_kg_stats_json_with_mock`: mock now uses `compute_metrics()`
    (the method the code actually calls) instead of `get_statistics()`
  - `test_dry_run_not_needed_extract_is_read_only` and
    `test_stdin_input`: mock now provides `NERExtractor`,
    `RelationExtractor`, `TripletExtractor`, `EventDetector`
    (the classes the code imports) instead of `SemanticAnalyzer`
  Result: 230/230 tests pass (was 227/230)
- `tests/verify_rich_cli.py`: new verification script; exercises all
  14 command groups (92 --help checks, table rendering, dry-run
  formatting, --json mode, _pprint helper); 111 pass, 0 fail
2026-06-04 12:34:12 +05:30
Mohd Kaif 35c7ce066c Merge pull request #581 from Sameer6305/fix/cli-runtime-alignment
fix(cli): stabilize extract command runtime integrations
2026-06-03 17:03:38 +05:30
KaifAhmad1andSameer Kadam b3797c11a1 fix(cli): resolve all extract and kg-stats review findings
- Wire --confidence, --model, --temporal flags to extractors via a flat
  extractor_config dict (min_confidence, llm_model, include_temporal)
  instead of the unused kwargs dict and sectioned to_dict() spread
- Pass confidence_threshold=confidence directly to RelationExtractor
  which exposes it as a named parameter alongside **config
- Remove dead SemanticAnalyzer import and unreachable else branch from
  extract; unsupported modes now consistently raise ClickException
- Add _serialize_extract_result() to convert dataclass/list results to
  plain dicts so JSON and YAML output is machine-readable, not str()
- Fix kg_stats: remove graph={} arg from compute_metrics() so it uses
  the analyzer's loaded graph instead of always computing on empty data

Co-authored-by: Sameer Kadam <sskadam6305@gmail.com>
Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-06-03 16:50:41 +05:30
Sameer6305 19c1d5e9f1 merge upstream main into fix/cli-runtime-alignment 2026-06-03 15:28:19 +05:30
Sameer6305 dc4ca3f2aa fix(cli): apply extractor runtime config and guard unsupported modes 2026-06-03 14:44:13 +05:30
Sameer6305 e98dd46fbd fix(cli): fail gracefully when decision graph backend is unavailable 2026-06-03 13:55:05 +05:30
Sameer6305 c38a9c07f7 fix(cli): align kg stats command with graph analyzer API 2026-06-03 13:03:45 +05:30
Sameer6305 e9c3562b1d fix(cli): route relations extraction through NER pipeline 2026-06-03 12:42:29 +05:30
Sameer6305 2706188c88 fix(cli): align extract command with semantic extractor APIs 2026-06-03 12:33:45 +05:30
Mohd Kaif 936871ef6d Merge pull request #578 from semantica-agi/feat/cli-full-command-suite
feat(cli): full Semantica CLI command suite — issue #568
2026-06-02 22:19:34 +05:30
KaifAhmad1 dc24f956e9 docs: add CLI reference section to README
Covers all 22 command groups introduced in issue #568:
global flags, data in, processing, KG, intelligence (reason/decision/temporal),
provenance, validation, ontology, export, visualize, orchestration
(pipeline/store/backup), services (server/explorer/mcp), and shell completion.

Each section shows real invocation examples rather than flag tables.
2026-06-02 19:53:43 +05:30
KaifAhmad1 af697a83d8 fix(cli): resolve all review findings from PR #578
P1 — runtime-breaking API mismatches:
- decision record/list/query/trace/similar/impact/check: all six decision
  commands now call decision_methods / decision_query using a GraphStore
  from _get_graph_store(cli_ctx) instead of passing config= kwargs that
  don't exist on the underlying API signatures.
- embed index: load vectors from the Parquet/JSON file into List[np.ndarray]
  before calling create_index(), which expects vectors not a file path string.

P2 — stub implementations replaced with real logic:
- backup sync: now collects local data sources via _collect_backup_sources
  and performs an incremental copy (skips files whose dst mtime >= src mtime).
- backup restore: detects .enc / tar.gz / .tar / directory, decrypts SEM1
  format when --enc, extracts tar archives with leading prefix stripped, or
  copies directory trees back to cwd.

P3 — correctness bugs:
- backup create: archive now includes actual config/ontology/store data files
  via _collect_backup_sources; manifest records the file list.
- extract: --output now works for all formats (table/rdf/yaml), not only JSON.
- backup create: empty keyfile now raises a clear error instead of silently
  producing an unencrypted archive.
- normalize: use Path.is_file() instead of Path.exists() to avoid accidentally
  reading a directory that matches the input text.
- visualize: without --output, emit to stdout; do not silently write kg.html.

Minor:
- _setup_cli_logging: replace opaque _ = (quiet, json_output, exc) tuple
  with del to suppress unused-variable lint.
- reason list: try to source engines from the reasoning module registry;
  fall back to the hardcoded list.
- deduplicate --action report: use method="pairwise" to produce individual
  pair objects with similarity scores, distinct from --action detect.
- tests: remove mixed import (from semantica.cli import main) — all 192
  runner.invoke calls now use cli_module.main as CodeQL flagged.
- tests: add two focused embed-index regression tests that verify vectors
  are loaded from the file before create_index is called.
2026-06-02 19:35:37 +05:30
Zohaib Hassnain f542fc8652 fix(cli): harden startup logging and explorer API wiring 2026-06-02 15:53:10 +05:00
KaifAhmad1 eef5f9a850 fix(cli): resolve four runtime bugs flagged in PR #578 review
- embed search: embed query text before calling search_vectors (was passing
  raw string to query_vector positional arg, causing TypeError on every call)
- ontology version: import OntologyVersionManager not OntologyVersioning
  (symbol never existed; command always failed even with package installed)
- ingest --watch: forward watch flag into _ingest() kwargs (was accepted
  but silently dropped, so --watch had no effect)
- store migrate: replace fake success stub with honest ClickException pointing
  to the export+embed-index workaround (no bulk-dump API exists in vector store layer)
2026-06-01 11:27:59 +05:30
Mohd Kaif c9549cd4f8 Change header style in README.md 2026-06-01 01:17:21 +05:30
Sameer6305 b22c93e9ec fix(cli): align ingest CLI with unified ingest dispatcher 2026-05-31 20:21:23 +05:30
Sameer KadamandCopilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> 98da904a06 test(cli): remove unused variable in reason list json test
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-05-31 15:59:29 +05:30
Sameer6305 188c81a89c fix(cli): wire deduplicate CLI through graph store and EntityMerger 2026-05-31 15:51:05 +05:30
Sameer6305 c7d6e166ac fix(cli): align export dispatch with registry contract
Fix the export runtime mismatch where get_export_method expected the existing (task, name) registry contract but the CLI passed only the format argument.
2026-05-29 23:45:29 +05:30
KaifAhmad1 ba5038a2e1 feat(cli): implement full Semantica CLI command suite (issue #568)
Expands semantica/cli.py from a 2-command stub into a complete terminal
interface covering every capability described in issue #568, and ships
253 tests covering all new commands, flags, and error paths.

Co-Authored-By: KaifAhmad1 <kaifahmad087@gmail.com>
2026-05-28 14:43:12 +05:30
Mohd Kaif dc33b5dce5 Merge pull request #576 from Sameer6305/feat/cli-foundation-base
CLI foundation: wire kg build path with legacy build compatibility and focused tests
2026-05-27 16:27:20 +05:30
KaifAhmad1andClaude Sonnet 4.6 8feb8c00c6 fix(cli): address review findings from PR #576
- Remove incorrect # pragma: no cover from _run_with_error_handling
  generic Exception branch (test_runtime_errors_are_click_safe already
  covers it via the monkeypatched RuntimeError path)

- Add _require_ctx() guard: converts None ctx.obj into a clean
  ClickException instead of an AttributeError (protects standalone_mode=False
  / library-use callers); apply to info, kg_build, build_alias commands

- Rename serve group -> services to avoid collision with the future
  `semantica server` flat command specified in issue #568; update docstring
  to document planned subcommand layout

- Fix command-level config logging: re-call setup_logging() with the
  command-level config logging section when -c is used (setup_logging
  clears handlers before adding, so no accumulation risk)

- Fix missing log_level_override in command_ctx: global --log-level was
  silently dropped when a per-command -c config was present, breaking
  the override chain for any nested _build_runtime_config calls

- Add return-shape docstring on _run_build documenting the expected
  build_knowledge_base() return dict structure

- Add type annotation to runner fixture (-> CliRunner) so Pylance
  correctly types runner.invoke() -> Result across all test functions

- Expand test suite: 25 -> 32 tests
  * test_info_command_shows_framework_components
  * test_info_command_shows_config_path_when_supplied
  * test_log_level_global_override_stores_in_context
  * test_command_config_preserves_global_log_level_override
  * test_build_result_with_stats_shows_source_count
  * test_build_result_without_stats_shows_generic_success
  * test_build_result_none_shows_generic_success
  * test_require_ctx_raises_click_exception_on_none
  * test_require_ctx_returns_ctx_unchanged

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 15:29:39 +05:30
Sameer6305 c447bf5934 cli: harden config parsing and isolate CLI test logging 2026-05-27 13:57:18 +05:30
Sameer6305 b54d885bf2 cli: harden config parsing and logging override handling
- keep command-level config from overriding logging unless --log-level is set

- validate YAML/JSON config roots and surface parse failures as Click errors

- tighten CLI tests around isolation and cleanup
2026-05-26 23:06:00 +05:30
Sameer6305 bc9db1ff89 cli: add foundation wiring and kg build with legacy build parity
- add CLI runtime context, global config/log-level handling, and click-safe error wrapping

- implement kg build as a thin wrapper over existing orchestrator build flow

- keep hidden legacy build alias and route both build handlers through shared internal path

- add focused CLI tests for help UX, config flag compatibility, alias parity, and clean error output

- keep tests lightweight by mocking heavy build execution paths
2026-05-26 22:45:50 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 073e8df713 ci(deps): bump actions/setup-node from 4 to 6 (#569)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4 to 6.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v4...v6)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-26 15:10:02 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> e75c5d5e3c docker(deps): bump node from 25-alpine to 26-alpine (#553)
Bumps node from 25-alpine to 26-alpine.

---
updated-dependencies:
- dependency-name: node
  dependency-version: 26-alpine
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-26 15:05:53 +05:30
KaifAhmad1 470315d9cb Make favicon brain icon larger — reduce inner padding to fill more space 2026-05-24 18:42:26 +05:30
KaifAhmad1 058014272a Update branding to new Semantica logo
- Rename logo PNG to semantica-logo.png (lowercase, hyphenated)
- Update docs.json logo (light/dark) and favicon to reference new PNG
- Replace legacy purple favicon with new teal brain neural network icon
2026-05-24 18:37:51 +05:30
Mohd Kaif bfd00f88c8 Delete docs/assets/img/semantica-wordmark-dark.svg 2026-05-24 18:18:46 +05:30
Mohd Kaif d737aa2e7c Delete docs/assets/img/semantica-wordmark-light.svg 2026-05-24 18:18:32 +05:30
Mohd Kaif cf50dc8828 Update README.md 2026-05-24 18:07:30 +05:30
Mohd Kaif 79dc434a23 Update project tagline for clarity 2026-05-24 18:03:36 +05:30
Mohd Kaif cf0174e7a2 Add files via upload 2026-05-24 18:01:54 +05:30
Mohd Kaif 3f282c59ff Remove title from README
Removed the title 'Semantica' from the README.
2026-05-24 18:00:08 +05:30
Mohd Kaif abde600a45 Add files via upload 2026-05-24 17:59:33 +05:30
Mohd Kaif f4f28dd849 Delete Semantica Logo.png 2026-05-24 17:59:08 +05:30
Mohd Kaif 5abbd20bbd Delete docs/assets/img/semantica-logo.png 2026-05-24 17:58:42 +05:30
Mohd Kaif 283c23d508 Delete docs/assets/img/Semantica Logo.png 2026-05-24 17:58:24 +05:30
Mohd Kaif 5d70d0c10d docs: replace Exported Classes import blocks with summary tables (all 25 modules) (#567)
* docs: replace Exported Classes import blocks with summary tables across all 25 modules

* docs: add method/parameter tables to parse, ingest, ontology, normalize, triplet_store, change_management, conflicts, export, graph_store, provenance, and semantic_extract modules
2026-05-24 15:49:58 +05:30
Mohd Kaif 72fefbeda6 Merge pull request #566 from semantica-agi/docs-mintlify-component-overhaul
docs: full Mintlify component overhaul — all 27 reference pages + concepts.md
2026-05-24 14:59:49 +05:30
KaifAhmad1 68fcff5b3a docs: add Exported Classes blocks to all remaining reference docs
Adds ## Exported Classes (or equivalent interface block) to:
- change_management.md, conflicts.md, context.md, embeddings.md
- graph_store.md, ingest.md, normalize.md, pipeline.md
- seed.md, split.md, triplet_store.md, vector_store.md
- visualization.md

Adds ## Launch Interface to explorer.md (CLI-only module).
Adds ## Server Interface to mcp_server.md (stdio process, not importable).

All blocks sourced from module __all__ with inline usage hints.
evals.md intentionally skipped (placeholder, __all__ = []).
2026-05-24 14:56:11 +05:30
KaifAhmad1 beacc88b02 fix(ci): replace list[Event] with List[Event] for Python 3.8 compat 2026-05-24 14:46:50 +05:30
KaifAhmad1 37e640e7b4 docs: comprehensive audit and DX overhaul of all reference modules
llms.md:
- Only Groq/OpenAI/LiteLLM/HuggingFaceLLM are exported — remove non-exported
  Anthropic/Ollama/Gemini/DeepSeek/Novita as direct imports
- Rename HuggingFace -> HuggingFaceLLM (correct class name)
- Remove non-existent create_provider() — replace with LiteLLM provider/model pattern
- Add LiteLLM 100+ providers section with provider/model string examples
- Add Exported Classes table (class -> provider -> API key)
- Update Provider Comparison table to show correct import per provider

ontology.md:
- Remove non-existent OntologyManager — replace with OntologyEngine facade
- Remove non-existent start_explorer() — replace with CLI: semantica-explorer
- SHACLValidator -> OntologyValidator (correct exported name)
- OWLExporter -> OWLGenerator (correct exported name)
- Add Exported Classes block with all 15+ exported symbols
- Add LLMOntologyGenerator section, NamespaceManager section
- Add OntologyEvaluator section with coverage/completeness metrics
- Add ingest_ontology() section
- Add versioning moved-to note (change_management module)

kg.md:
- TemporalKnowledgeGraph does not exist — replace with TemporalGraphQuery
- DistanceCalculator does not exist — replace with SimilarityCalculator
- Add Exported Classes block with all 20+ exported symbols
- Fix temporal example to use TemporalGraphQuery + TemporalVersionManager correctly
- Add SimilarityCalculator section with NodeEmbedder integration example

provenance.md:
- ActivityTracker not exported — remove; ProvenanceManager handles tracking
- Fix track_entity() signature: add source_location, source_quote params
- Fix GraphBuilderWithProvenance import: from semantica.kg, not semantica.provenance
- Add Exported Classes block with storage backends and checksum utilities
- Add SourceReference section with DOI/page/quote fields
- Add tamper-evident checksum section (compute_checksum/verify_checksum)
- Add Enable Provenance in Extractors section
- Fix duplicate heading (W3C PROV-O Export appeared twice)

reasoning.md:
- Add Exported Classes block with all engines + data types + explanation types
- Add Quick Start section
- Add Choosing an Engine comparison table
- Add InferenceResult/Explanation/ReasoningStep type annotations in examples
- Add Tip: use DatalogReasoner for recursive rules

semantic_extract.md:
- Add Exported Classes block with NamedEntityRecognizer, EventDetector, Entity,
  Relation, Event, CoreferenceChain, EntityClassifier, TemporalEventProcessor
- Add Quick Start section (one-liner extraction pipeline)
- Rename EventExtractor -> EventDetector (correct exported name)
- Clarify NERExtractor vs NamedEntityRecognizer distinction
- Add return type annotations to EventDetector example

core.md:
- Add Exported Classes block
- Add When to Use Core vs. Individual Modules decision table
- Add Tip: LifecycleManager only for long-running apps
- Fix MethodRegistry example to import build_knowledge_base correctly

parse.md:
- Add Exported Classes block with all format-specific parsers + data types
- Add DoclingParser optional import note

utils.md:
- Add Exported Classes block with logging/validation/progress/helpers/exceptions

deduplication.md:
- Add Exported Classes block with PropertyMergeRule, MergeStrategyManager,
  method_registry, and all convenience functions

export.md:
- Add Exported Classes block with all exporters, NamespaceManager,
  SemanticNetworkYAMLExporter, and all convenience functions
2026-05-24 14:41:57 +05:30
KaifAhmad1 5a7a740185 docs(context): full audit and overhaul of context.md
API fixes:
- retrieve(): top_k= -> max_results= (correct parameter name)
- remove non-existent add_decision_simple() -> use record_decision() on ContextGraph
- remove non-existent analyze_decision_influence() -> get_causal_chain() + trace_decision_explainability()
- find_precedents() returns List[Decision] not Precedent; removed .similarity attribute usage
- ContextRetriever.retrieve(): top_k -> max_results, add use_graph_expansion / min_relevance_score params
- AgentMemory.retrieve(): top_k -> max_results

New constructor params documented:
- retention_days, max_memories, max_expansion_hops, hybrid_alpha

New methods documented:
- batch_store(), forget(), update(), get_memory(), stats(), health()
- save() / load(), export() / import_data()
- conversation(), get_causal_chain(), query_decisions()
- trace_decision_explainability(), get_policy_engine()
- checkpoint(), diff_checkpoints(), flush_checkpoint()
- ContextGraph: add_nodes/add_edges (bulk), find_node, find_nodes, find_active_nodes
- ContextGraph: find_edges, query, stats, density, clear, build_from_conversations
- ContextGraph: link_graph, navigate_to, cross_graph_path, resolve_links

New sections:
- Cross-Graph Navigation with full example
- Checkpoint Methods with example
- Conversation Methods with example
- Persist and Restore real-world tab
- Policy dataclass in Data Structures accordion
- Decision.valid_from / valid_until temporal fields documented
- CausalChainAnalyzer and ContextRetriever added to What You Get cards
- New Tips: max_results param name, checkpoint auditing
2026-05-24 14:28:37 +05:30
KaifAhmad1 daa79ccef3 fix: audit and correct all remaining API mismatches in docs
- llms.md: replace non-exported Anthropic/Ollama imports with LiteLLM provider-prefix pattern; replace ReasoningEngine with Reasoner; replace create_provider with LiteLLM in YAML config example and tip
- concepts.md: replace ReasoningEngine with Reasoner/ReteEngine/GraphReasoner; fix DatalogReasoner.reason() to evaluate()/query(); replace TemporalKnowledgeGraph with TemporalGraphQuery; replace DistanceCalculator with SimilarityCalculator; replace EntityDeduplicator with DuplicateDetector/EntityMerger
- kg.md: replace non-exported build_knowledge_graph with method_registry.execute()
- semantic_extract.md: replace Anthropic import with LiteLLM
- index.md: replace Anthropic/Ollama imports with LiteLLM
- modules.md: fix TemporalKnowledgeGraph, DistanceCalculator, OntologyManager, ReasoningEngine, DatalogEngine, start_explorer, create_provider across code examples and module index table
- triplet_store.md: replace non-exported NamespacePrefixManager with semantica.ontology.NamespaceManager
2026-05-24 14:28:36 +05:30
KaifAhmad1 ce765b6f66 fix: correct docs-to-code mismatches in 8 reference modules
- graph_store: remove create_constraint(), add_nodes_bulk(), add_edges_bulk() → create_nodes(), add_edges()
- deduplication: fix PropertyMergeRule → MergeStrategy enum; add_rule() → add_property_rule(); merge() → merge_entities(); remove non-existent UNION/MAX/MIN/VOTING constants
- conflicts: set_credibility() → set_source_credibility(); group_by_severity/identify_patterns/analyze_sources → analyze_conflicts() dict keys; generate() → generate_guide(); remove time_window= param from analyze_trends()
- reasoning: infer() → forward_chain(); remove apply_transitivity/symmetry/inverse() templates that don't exist; GraphReasoner(kg) → GraphReasoner(); infer(kg) → reason(graph, query)
- split: split_document() (singular) → split_documents([parsed]) throughout
- seed: remove register_source_object(), populate(), inject(), load_from_file(), diff_versions(), get_version(tag=) — replace with register_source() and load_from_csv/json()
- change_management: remove rollback(), get_log_entry(), export_audit_trail(), get_audit_trail() — replace audit section with list_versions() + diff() pattern
- export: export_to_file() → export_to_rdf(); YAMLExporter → SemanticNetworkYAMLExporter
2026-05-24 13:36:14 +05:30
KaifAhmad1 ff43887842 fix: correct remaining API mismatches in pipeline, vector_store, and normalize docs
- pipeline.md: ParallelismManager pool_type="thread"/"process" → use_processes=False/True;
  execute_parallel() returns List[ParallelExecutionResult] not aggregate object
- vector_store.md: remove MetadataStore.add_field() (method is on MetadataSchema, not
  MetadataStore); fix tip to reference MetadataStore.update_metadata() not VectorStore
- normalize.md: Pipeline() orchestrator misuse → PipelineBuilder + ExecutionEngine pattern
2026-05-24 13:14:01 +05:30
KaifAhmad1 6f726c708f fix: remove non-existent classes and fix wrong API signatures across reference docs
- visualization.md: GraphVisualizer → KGVisualizer; fix method names (visualize_network,
  visualize_network_evolution, visualize_snapshot_comparison, visualize_temporal_patterns,
  visualize_2d_projection); remove DistanceVisualizer tab; fix start_explorer() reference
- kg.md: remove TemporalKnowledgeGraph and DistanceCalculator (don't exist); replace with
  TemporalGraphQuery and ConnectivityAnalyzer; fix query_at_time() signature
- ontology.md: remove OntologyManager, SKOSVocabulary, OntologyAligner, OntologyDiff,
  OntologyMigrator (none exist); fix SHACLValidator → OntologyValidator; fix OWLExporter
  → OWLGenerator.export_owl(); fix start_explorer() reference
- evals.md: replace entire file with coming-soon notice (module is a stub, __all__ = [])
- embeddings.md: fix EmbeddingGenerator constructor (takes config dict not model=);
  generate() → generate_embeddings(); similarity() → compare_embeddings()
- ingest.md: fix WebIngestor (rate_limit → delay, ingest() → ingest_url());
  FeedIngestor (ingest() → ingest_feed(), monitor() → monitor_feeds());
  StreamIngestor (backend= constructor → ingest_kafka/rabbitmq/kinesis/pulsar());
  DBIngestor constructor + ingest() → ingest_database(); SnowflakeIngestor.ingest() →
  ingest_query()/ingest_table(); OntologyIngestor.ingest() → ingest_ontology();
  DataSource → FileObject
- explorer.md: remove start_explorer() Python function (only CLI exists);
  replace with semantica-explorer CLI usage
- provenance.md: ActivityTracker → ProvenanceTracker in CardGroup
- semantic_extract.md: EventExtractor → EventDetector
- triplet_store.md: remove InMemoryTripletStore (doesn't exist); fix tip
- llms.md: fix providers (Anthropic/Gemini/Ollama/DeepSeek/NovitaAI → LiteLLM);
  HuggingFace → HuggingFaceLLM; remove create_provider()
2026-05-24 13:11:57 +05:30
KaifAhmad1 689d57b361 fix: correct API mismatches in pipeline, ingest, and vector_store docs
pipeline.md:
- Replace Pipeline().add_step().run() with PipelineBuilder + ExecutionEngine.execute_pipeline()
- Fix ValidationResult: result.valid (not is_valid), errors is List[str] not object list
- Fix ExecutionResult schema: success/output/metadata/metrics/errors (not PipelineResult)
- Fix ExecutionEngine: get_pipeline_status() not get_status(), progress keys completed_steps/total_steps
- Fix result.metadata['pipeline_id'] not result.pipeline_id
- Fix RetryPolicy: strategy=RetryStrategy.EXPONENTIAL not backoff='exponential'
- Fix PipelineSerializer.serialize_pipeline/deserialize_pipeline instead of pipeline.save/load
- Fix delta mode to use PipelineBuilder not Pipeline()

ingest.md:
- Replace S3Ingestor/GCSIngestor/GDriveIngestor (do not exist) with CloudStorageIngestor
- Remove MongoIngestor/DuckDBIngestor (do not exist) from docs and tables
- Fix Quick Start pipeline step to use PipelineBuilder + ExecutionEngine

vector_store.md:
- Replace store.hybrid_search() (does not exist) with HybridSearch.search()
- Replace store.add_vectors() with store.add_documents() / store.store_vectors()
- Replace store.search(query_vector) with store.search_vectors(k=) / store.search(query_str, limit=)
- Fix Batch Operations: add_vectors_batch -> add_documents, delete_vectors(vector_ids=), update_vectors()
- Fix HybridSearch.search() signature: (query, k, metadata_filter) not (query_vector, query_text, fusion, filters)
- Fix MetadataStore: store_metadata/get_metadata/update_metadata/query_metadata (not add/filter/get)
- Fix NamespaceManager: add_vector_to_namespace, list_namespaces returns List[str]
2026-05-24 12:36:21 +05:30
KaifAhmad1 d206a10bc7 docs: apply Mintlify component overhaul to index.md
- The Problem section: flat bullet list → CardGroup (5 problem cards with icons)
- The Solution section: flat bullet list → CardGroup (6 solution cards)
- Start Here section: plain prose → Steps (4-step onboarding flow)
- Built for High-Stakes Domains: plain prose → CardGroup (6 domain cards)
- Why Semantica: plain prose → CardGroup cols={3} (3 value proposition cards)
- Module Reference table: updated descriptions for seed, evals, core, utils, llms, export to match v0.5.0 source
- LLM provider class names corrected: OpenAIProvider → OpenAI, AnthropicProvider → Anthropic, OllamaProvider → Ollama
2026-05-23 23:06:49 +05:30
KaifAhmad1 5eefadaa7f docs: apply full Mintlify component overhaul to all 27 reference pages and concepts.md
Replace plain markdown in every docs/reference/ file and docs/concepts.md with
rich Mintlify JSX components — CardGroup, Steps, Tabs, AccordionGroup, Tip,
Warning, Note, and CodeGroup — for a consistent, navigable, production-grade
developer experience.
2026-05-23 23:02:03 +05:30
Mohd Kaif 11e8a2fc0d Merge pull request #565 from semantica-agi/docs-diagrams-and-wordmark
docs: premium SVG diagrams and Semantica wordmark logo
2026-05-23 17:31:26 +05:30
KaifAhmad1 f4e0d5b400 fix: update architecture.md to four-layer model — resolves diagram/text contradiction
Frontmatter, intro, heading, Tabs, and Module Map all said "three-layer"
while the architecture-overview.svg and its alt text showed four layers.
Adds Layer 3 (Intelligence: KG, vector store, ontology, triplet store,
embeddings) and renumbers the former Layer 3 Application to Layer 4.
2026-05-23 17:22:18 +05:30
KaifAhmad1 98bc2de20b docs: add SVG diagrams and Semantica wordmark logo
Diagrams (docs/assets/img/diagrams/):
- architecture-overview.svg: 4-column layered architecture
- pipeline-flow.svg: 8-step numbered pipeline flow
- kg-structure.svg: entity/relation graph with typed nodes and labeled edges
- graphrag-flow.svg: dual-path retrieval (vector + graph) to LLM to grounded answer
- extraction-pipeline.svg: NER/Relation/Coreference fan-out to Triplet Generator
- agent-context-flow.svg: AgentContext hub with VectorStore and ContextGraph
- reasoning-chain.svg: forward-chaining inference with explanation path

Wordmark logo (light + dark SVG variants):
- Green rounded-square S icon + Semantica text in green
- docs.json updated to use wordmark SVGs for light and dark modes

Pages updated with diagrams:
- index.md, architecture.md, quickstart.md, concepts.md
- reference/kg.md, reference/pipeline.md, reference/semantic_extract.md
- reference/context.md, reference/reasoning.md
2026-05-23 17:04:52 +05:30
Mohd Kaif 6c43bc846a Merge pull request #563 from semantica-agi/docs-premium-reference-overhaul
docs: premium overhaul of all reference pages and modules
2026-05-23 14:10:42 +05:30
KaifAhmad1 6bf81bb5bc fix: correct docs-to-code mismatches in modules.md, context.md, and split.md
- Replace APIIngestor with RESTIngestor (actual exported class name)
- Update TextSplitter method names: semantic->semantic_transformer, entity-aware->entity_aware, relation-aware->relation_aware
- Fix TextSplitter parameter: overlap->chunk_overlap throughout split.md and modules.md
- Replace DataNormalizer (not exported) with TextNormalizer + normalize_date convenience function
- Fix AgentContext defaults: graph_expansion, advanced_analytics, kg_algorithms are True not False
2026-05-23 13:57:48 +05:30
KaifAhmad1 51b1e7fffd docs: add 'What You Get' sections to explorer, llms, and mcp_server 2026-05-23 13:17:36 +05:30
KaifAhmad1 9113ef3428 docs: premium overhaul of all reference pages and core docs
- Rewrote all 26 reference module pages: removed blockquote taglines and
  horizontal rule separators, added "What You Get" bullet summaries,
  added constructor/method parameter tables, expanded thin files
  (graph_store, triplet_store, visualization, provenance) with full API
  coverage, added backend comparison tables and real-world usage patterns
- Renamed Modules tab from "API Reference" and group from "Context &
  Knowledge" to "Context & Intelligence" in docs.json
- Fixed logo: copied "Semantica Logo.png" to web-safe semantica-logo.png
  and updated all 4 references in docs.json
- Improved core docs (index, modules, concepts, quickstart, installation,
  getting-started) with better fonts, bullet points, and complete module
  listings (mcp_server, evals, core, utils previously missing)
- Rewrote community pages (community, community-projects, contributing-guide,
  use-cases, architecture, faq, learning-more, glossary) with heading
  hierarchy fixes, expanded definitions, and better structure
- Fixed markdown linter warnings: MD036 bold-as-heading, MD001 heading
  skips, MD040 missing code fence language, MD032 blank lines around lists
2026-05-23 13:10:09 +05:30
Mohd Kaif db2af15afb Merge pull request #562 from Sameer6305/sameer/docs-onboarding-polish
docs: refine onboarding guidance and reduce duplication
2026-05-23 11:47:30 +05:30
KaifAhmad1 c190ecb81e docs: fix review follow-ups — naming consistency, extras snippet, nav card order
- installation.md: revert card title back to "Getting Started" to match
  the Tip text that already links to it by that name
- getting-started.md: restore pip install semantica[all] code block that
  was removed in the original PR; users need the copy-paste snippet even
  when the Installation guide is the canonical reference; also standardize
  link text to "Installation" (was "Installation guide")
- index.md: add Installation card as first entry in "Start Here" CardGroup
  so the prose ("install first, then open Quickstart") is backed by an
  actual card to click
- quickstart.md: standardize link text to "Installation" (was "Installation guide")
2026-05-23 11:36:58 +05:30
Mohd Kaif 6f6a56221a Add files via upload 2026-05-23 11:26:24 +05:30
KaifAhmad1 453eeb7ca9 fix: rename contributing/license pages to avoid Mintlify reserved slug conflict
mint export fails with 'file does not exist' for pages named 'contributing'
and 'license' — these are reserved by Mintlify's GitHub integration layer.
Renamed to contributing-guide.md and project-license.md and updated all
nav entries and cross-links throughout the docs.

Also adds .gitattributes LF rules to prevent CRLF issues from Windows devs.
2026-05-23 00:14:23 +05:30
KaifAhmad1 f4a79ae851 ci: disable automatic benchmark runs on push — manual only via workflow_dispatch 2026-05-23 00:04:52 +05:30
Sameer6305 370aa2f489 docs: improve installation reference wording 2026-05-23 00:04:06 +05:30
KaifAhmad1 cebb5fb736 ci(docs): split validate (fast, all PRs) and deploy (main only) jobs 2026-05-23 00:00:25 +05:30
KaifAhmad1 a076dce00f ci(docs): remove mint validate step (false-positive on valid files) 2026-05-22 23:56:28 +05:30
Sameer6305 20220f8414 docs: improve onboarding navigation consistency 2026-05-22 23:46:43 +05:30
KaifAhmad1 cf802cffe9 ci(docs): restore GitHub Pages deployment using mint export instead of mkdocs
- Validate docs structure with docs_check.py (Python)
- Validate Mintlify build with mint validate (Node 20 LTS)
- Export static site with mint export, deploy to GitHub Pages
- Deploy job skipped on PRs (validate-only for branches)
2026-05-22 23:45:11 +05:30
Sameer6305 c90fae47cc docs: improve quickstart onboarding context 2026-05-22 23:42:39 +05:30
Sameer KadamandCopilot Autofix powered by AI 939d00632f Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-22 23:38:24 +05:30
Sameer6305 b09d60161d docs: refine onboarding guidance and reduce duplication 2026-05-22 23:27:11 +05:30
Mohd Kaif a43cb07017 Merge pull request #561 from semantica-agi/feat/docs-premium-redesign
docs: premium Mintlify v4 redesign — dark/cream theme, full module coverage, MCP + Explorer reference
2026-05-22 23:20:37 +05:30
KaifAhmad1 071386a441 chore: simplify and type-annotate docs_check.py 2026-05-22 23:09:35 +05:30
KaifAhmad1 7c9da0643d ci: replace MkDocs build workflow with Mintlify docs validation
- docs.yml: replace mkdocs build/deploy with python docs_check.py;
  Mintlify deployment is handled by its own GitHub App
- ci.yml: remove dead paths-ignore refs to deleted mkdocs.yml and
  requirements-docs.txt
2026-05-22 23:01:31 +05:30
KaifAhmad1 5f124cd9f0 chore: remove legacy MkDocs files and orphan docs pages
Deleted MkDocs infrastructure:
- mkdocs.yml, mkdocs_local.yml, requirements-docs.txt, setup_docs.py
- docs/netlify.toml, docs/DOCS_README.md, docs/css/custom.css

Deleted orphan docs not wired into Mintlify nav:
- docs/LIBS_README.md, docs/MIGRATION_V2.md, docs/CodeExamples.md
- docs/arrow_exporter.md, docs/deep-dive.md, docs/examples.md
- docs/vector_store_usage.md

Updated docs.json and broken See Also hrefs to match removed pages
2026-05-22 22:49:08 +05:30
KaifAhmad1 77b1eaaa78 docs: fix Python 3.9+ list[dict] syntax in docling.md for 3.8 compat 2026-05-22 22:41:13 +05:30
KaifAhmad1 7050f58d47 docs: update all repo links to github.com/semantica-agi/semantica
Replace Hawksight-AI/semantica, semantica-dev/semantica, and semantica/semantica
URLs across all docs files (17 files, ~100 links).
2026-05-22 22:21:41 +05:30
KaifAhmad1 ff2d89dc2d docs: fix broken extension point and explorer examples
- architecture.md: replace non-existent BaseIngestor/BaseExtractor/BasePlugin/PluginRegistry.register with correct APIs (method_registry.register, PluginRegistry.register_plugin); fix Python 3.8-incompatible list[dict] type hints
- reference/explorer.md: replace non-existent start_explorer import and graph.save() with correct subprocess launch and graph.save_to_file()
2026-05-22 22:10:11 +05:30
KaifAhmad1 3b637ea140 docs(reference): fix class names and expand API coverage across 8 modules
- normalize: replace non-existent DataNormalizer with correct classes (TextNormalizer, EntityNormalizer, DateNormalizer, NumberNormalizer, DataCleaner)
- deduplication: replace non-existent EntityResolver with correct API (DuplicateDetector, EntityMerger, SimilarityCalculator, ClusterBuilder)
- reasoning: replace non-existent ReasoningEngine/DeductiveEngine/AbductiveEngine with correct classes (Reasoner, GraphReasoner, ReteEngine, SPARQLReasoner, DatalogReasoner, TemporalReasoningEngine, ExplanationGenerator)
- export: fix ArangoExporter->ArangoAQLExporter, GraphMLExporter->GraphExporter; add ArrowExporter, DistanceExporter, ReportGenerator
- conflicts: fix ResolutionStrategy enum values and add SourceTracker, ConflictAnalyzer, InvestigationGuideGenerator
- change_management: add OntologyVersionManager, VersionStorage backends, compute_checksum/verify_checksum
- embeddings: add TextEmbedder, GraphEmbeddingManager, VectorEmbeddingManager, all provider stores, all pooling strategies
- visualization: fix broken See Also href from evals to explorer
2026-05-22 22:03:30 +05:30
KaifAhmad1andClaude Sonnet 4.6 946a1089c8 docs: premium redesign — Mintlify v4, dark/cream theme, full module coverage
- Migrate from mint.json to docs.json (Mintlify v4)
- Theme: maple, emerald green + near-black dark / cream light palette
  (#059669 primary, #0A0A0A dark bg, #FAF7F0 light bg)
- Typography: Lexend headings, Inter body
- 5-tab navigation: Documentation, Quick Start, API Reference, Cookbook, FAQ
- Homepage: removed badge stickers, redundant h2, added blockquote tagline,
  full 27-module reference table with semantica.mcp_server added
- quickstart.md: CodeGroup per pipeline step, pattern vs LLM options,
  AccordionGroup for patterns and troubleshooting
- faq.md: full AccordionGroup structure across 5 sections
- reference/explorer.md: NEW — FastAPI explorer, Ontology Hub, Distance
  Intelligence, CLI reference, REST API endpoints
- reference/mcp_server.md: NEW — MCP stdio server, 12 tools with I/O
  examples, 3 resources, Claude Desktop/VS Code/Windsurf/Cline config
- docs.json: explorer added to Output group, mcp_server to Utilities group
- Chat, feedback (thumbs/suggest/raise), OG/Twitter metadata, search topbar
- All reference pages reformatted with Mintlify JSX components

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 21:52:50 +05:30
Luffy2208andKaifAhmad1 98232749fb Add XML file ingestion support (#560)
* Add XML file ingestion support

* fix(xml-ingestor): add ingest_string test and document ingest() return keys

- Add test_xml_ingestor_ingests_string to cover the public ingest_string()
  method which had no test coverage
- Document all source_type return keys in the ingest() docstring so callers
  know to use result["xml"] rather than result["data"] for XML sources

* docs(changelog): add unreleased entry for XML ingestion support (#560)

---------

Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-05-19 17:48:32 +05:30
Mohd Kaif 5bd10c8153 Update README.md 2026-05-18 20:37:28 +05:30
Mohd Kaif 5df613f729 Enhance README formatting and content clarity 2026-05-18 20:30:51 +05:30
Mohd Kaif 9686508434 docs(readme): redesign for better traction and narrative clarity (#559)
* docs(readme): redesign for better traction and narrative clarity

- Reorder sections: Problem → Solution → Quick Start → What's New → Integrations
- Add website and docs badges to the top badge strip
- Improve hero tagline and narrative blockquote
- Restore v0.4.0 (Temporal, SKOS, SHACL) and v0.3.0 release sections
- Remove duplicate modules list; consolidate into single table
- Fix broken emoji characters in Enterprise section
- Add blank lines around all headings and list blocks

* docs(readme): concise rewrite with accurate v0.5.0 features and compact layout
2026-05-18 20:26:00 +05:30
Mohd Kaif cff071b181 Merge pull request #557 from Hawksight-AI/feat/ui-redesign-semantica-explorer
# feat: Redesign all workspace UIs with consistent design system + bug fixes
2026-05-16 17:11:55 +05:30
Zohaib Hassnain e14b372626 fix(ui): resolve explorer redesign merge blockers 2026-05-16 15:55:47 +05:00
KaifAhmad1 0efb018df0 fix(ontology): silent empty state for offline backend + fix SHACL crash
- OntologyManager: remove red error banner on HTTP 500; always fall back
  to empty state silently (error banners reserved for user actions only)
- AlignmentsTab: remove offline-backend warning when both registry and
  alignments requests fail; show empty form silently
- ShaclStudio: fix Monarch tokenizer crash — [@] character class prevents
  Monaco from misinterpreting @prefix/@base as language-property refs;
  wrap beforeMount in try/catch so any Monaco setup failure cannot crash
  the React tree
2026-05-16 15:15:37 +05:30
KaifAhmad1 aab7e23125 fix(explorer): address code review issues from PR #557
Decision workspace:
- Add AbortController per loadChain() call; abort previous request when a
  new decision is selected, preventing stale out-of-order chain responses
- Guard all setState calls with signal.aborted so unmounted component
  state updates are skipped; cancel in-flight request on unmount via a
  dedicated cleanup effect

SPARQL workspace:
- Guard results table on both result.rows && result.columns to prevent
  runtime crash when backend omits columns field
- Use (result.columns ?? []) inside rows.map() to satisfy TypeScript
  narrowing inside the closure
- Add .catch() to clipboard.writeText() — silently swallows permission
  errors (query remains visible in the editor as fallback)
- Fix CSV export anchor: append to body before click, remove after, to
  ensure cross-browser compatibility

Import/Export workspace:
- Fix download anchor: append to document.body before a.click() and
  remove afterwards, matching the standard compatible pattern

Lineage workspace:
- Replace 🔗 emoji empty-state icon with lucide-react Link2 for
  consistent theming and sizing

Diff & Merge workspace:
- Add "Sample preview" banner above the mock diff table so users know
  the displayed fields are illustrative until the backend is connected

OntologyManager:
- Restore non-blocking warning (flash message) when HTTP response is
  non-OK and not a 404; network errors (backend down) stay silent

AlignmentsTab:
- When both registry and alignments promises reject, surface a soft
  error banner so users know data is missing rather than just empty
2026-05-16 14:56:43 +05:30
KaifAhmad1 4809c16ed2 feat(explorer): redesign all workspace UIs with consistent design system
Introduces a shared CSS token system (--ws-* variables, .ws-* utility
classes) in App.tsx and applies it across every workspace tab to produce
a cohesive dark-themed Knowledge Explorer UI.

Changes per workspace:
- App.tsx: added full design-system block (:root tokens, .ws-btn,
  .ws-input, .ws-card, .ws-stat-grid, .ws-pill, .ws-sidebar, .ws-empty,
  animations); renamed "Network Explorer" -> "Semantica Explorer" app-wide;
  redesigned WelcomeScreen as a tech landing page (hero, metrics strip,
  workspace grid, capability band)
- ReasoningWorkspace: two-column layout, quick templates, monospace
  textareas, graph-write toggle, spinner run button
- SparqlWorkspace: template toolbar, copy button, styled Monaco editor,
  URI-coloured results table with CSV export
- DecisionWorkspace: ws-sidebar filter + list, ChainNode/RelEdge chain
  renderer, detail pane with outcome badge
- ImportExportWorkspace: drag-drop import zone, JSON/CSV export toggle,
  toast notifications with slide-up animation
- DiffMergeWorkspace: side-by-side diff table, amber diff pills, merge
  action with loading state
- KGOverviewTab: ws-stat-grid cards, TypeBar distribution charts,
  top-connected-nodes grid
- LineageDiagram: glassmorphism toolbar, ws-btn export actions, themed
  react-flow controls
- OntologyWorkspace/index: cleaned unused ComingSoonStub + dead style
  constants that caused babel-plugin-react-compiler compilation errors
- OntologyManager: graceful empty state instead of error banner when
  backend is unreachable
- HealthTab, ShaclStudio, AlignmentsTab: silence read-operation errors;
  keep errors only for user-triggered write actions
2026-05-16 14:45:20 +05:30
Mohd Kaif d3ffbad2e1 Merge pull request #556 from Hawksight-AI/fix/issue-554-ner-llm-gateway-fallback
fix(ner): resolve silent pattern fallback when LLM method fails on custom gateways
2026-05-15 20:04:42 +05:30
KaifAhmad1 722ae06795 fix(providers): address review feedback on PR #556 + changelog
Four issues raised in code review:

- Mode.JSON retry now strips response_format from create_kwargs before
  calling json_client.chat.completions.create, preventing incompatible
  kwargs from being forwarded to a client configured for a different mode.

- Add exc_info=True to the generate_structured fallback warning in the
  manual repair loop so the gateway rejection traceback is visible in
  production logs, consistent with the other warnings added in this PR.

- Remove the duplicate is_available definition in GroqProvider. Python
  silently kept only the second definition; the first (with diagnostic
  branching) was dead code and could cause confusion on future edits.

- Validate base_url scheme in OpenAIProvider._init_client. Non-HTTP(S)
  schemes (file://, ftp://, javascript:, etc.) are now rejected with a
  ValueError at init time, preventing SSRF if base_url originates from
  configuration rather than hardcoded values.

Add 3 new tests: SSRF scheme rejection, valid-URL acceptance, and
exc_info presence on the generate_structured fallback warning (20/20 pass).

Update CHANGELOG.md with full description of all fixes under [Unreleased].
2026-05-15 20:00:44 +05:30
KaifAhmad1 ca5f42baf8 fix(ner): resolve silent pattern fallback when LLM method fails on custom gateways (#554)
Three bugs caused NERExtractor to silently return pattern-based entities
even when method="llm" was configured:

1. exc_info=True missing on method-failure warning in NERExtractor —
   the root exception was swallowed, making the gateway error invisible
   in logs even with DEBUG enabled.

2. OpenAIProvider.generate_structured always sent response_format=json_object
   to the API. Custom/enterprise gateways (Qwen, LLaMA proxies, internal
   gateways) often reject this parameter, causing both the instructor path
   and the manual repair loop to fail with the same error on every retry.

3. generate_typed manual repair loop had no fallback when generate_structured
   itself raised — it retried the same failing call up to max_retries times,
   then propagated the error, triggering _extract_fallback (pattern extraction).

Fixes:
- Add exc_info=True to the method-failure warning so the full traceback
  appears in logs and users can diagnose the root cause.
- Skip response_format=json_object in OpenAIProvider.generate_structured
  when base_url is set (custom endpoint), since standard OpenAI gateways
  don't require it and third-party ones reject it.
- In the generate_typed manual repair loop, catch generate_structured
  failures and immediately retry via plain generate() + _parse_json,
  breaking the retry-the-same-failing-call loop for custom gateways.

Also adds 17 targeted regression tests covering all three bug paths,
including the exact gateway configuration reported in the issue.
2026-05-15 19:27:45 +05:30
Mohd Kaif e448903af8 Update language links in README.md 2026-05-13 19:27:49 +05:30
Mohd Kaif a947d1a998 Update README with new version information 2026-05-12 13:41:46 +05:30
Mohd Kaif 58b32f172f Simplify languages section in README
Removed redundant language links and simplified the languages section.
2026-05-12 13:37:33 +05:30
Mohd Kaif 81bf1553d0 docs(readme): add i18n languages section, v0.5.0 badge & what's new (#551)
- Add multilingual README links section (30 languages via readme-i18n.com)
- Pin version badge to 0.5.0 with correct release tag link
- Add "What's New in v0.5.0" section covering Distance Intelligence,
  Ontology Hub Suite, Parquet ingestion, indexed search, and security fixes
2026-05-12 13:34:37 +05:30
KaifAhmad1 2ef6e9f4b1 Release 0.5.0: Distance Intelligence & Ontology Hub Complete 2026-05-11 20:35:16 +05:30
Mohd Kaif 18da322e0d Feature/distance intelligence optimization (#550)
* Implement embedding cache optimization for Distance Intelligence

- Add per-session graph revision-based embedding cache to avoid re-scanning nodes
- Update GraphSession with get_cached_embeddings() and automatic cache invalidation
- Modify distance matrix and semantic neighborhood endpoints to use cached embeddings
- Implement thread-safe caching with proper revision tracking
- Add force refresh capability and automatic invalidation on graph modifications
- Improve performance for repeated distance intelligence queries

Resolves TODO in graph.py: cache embeddings per-session graph revision

* Update changelog with Distance Intelligence embedding cache optimization
2026-05-11 17:38:24 +05:30
Luffy2208andKaifAhmad1 15d58f2b88 Added Parquet ingest support (#234) (#548)
* Added Parquet ingest support (#234)

* docs: Add Parquet ingestion support to CHANGELOG

- Add comprehensive changelog entry for PR #548
- Document ParquetIngestor class and key features
- Include author credit (@Luffy2208) and PR reference
- Follow existing changelog format and structure

---------

Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-05-10 12:52:26 +05:30
Mohd Kaif ce3a8c9895 Add Enterprise Support section to README
Added enterprise support section with details on solutions and services.
2026-05-09 17:39:08 +05:30
Mohd Kaif 508e05d367 Update README.md 2026-05-08 17:30:25 +05:30
Mohd Kaif 03f99f016d Update README.md 2026-05-08 17:29:48 +05:30
Mohd Kaif 56e7d9d821 Fix #541: Convert mcp_server to package structure for pipx installation (#544)
- Convert mcp_server.py to package structure (semantica/mcp_server/)
- Add __init__.py and __main__.py for python -m support
- Add semantica-mcp console script entry point in pyproject.toml
- Fix API method calls (extract -> extract_entities/relations/triplets)
- Remove non-existent _result_cache imports
- Update documentation with both usage methods

Resolves pipx installation issue where semantica.mcp_server was not available.
Provides two ways to run: 'semantica-mcp' command or 'python -m semantica.mcp_server'.
2026-05-08 17:17:39 +05:30
Mohd Kaif 6860bdbec3 Merge pull request #540 from Hawksight-AI/conflicts
feat(deduplication): DuplicateDetector result limiting and ranking
2026-05-05 21:18:00 +05:30
Zohaib Hassnain ac5015bc3f fix(deduplication): normalize merged group keys 2026-05-05 20:25:51 +05:00
KaifAhmad1 29c72f59b3 docs(changelog): record Qodo review follow-up fixes for #533 and #534 2026-05-05 19:28:06 +05:30
KaifAhmad1 21c2f190f8 fix: resolve Qodo review bugs and quality issues (DuplicateDetector + ConflictDetector)
- bug_001: top_k_per_entity now uses OR semantics — keep a candidate if
  EITHER entity is under quota, preventing high-quality candidates being
  silently dropped when a popular counterpart saturates its quota
- bug_002: validate max_results and top_k_per_entity at construction;
  negative or non-int values raise ValueError instead of silent empty output
- bug_003: validate min_similarity in [0.0, 1.0] at construction;
  out-of-range values raise ValueError
- bug_004: harden ConflictDetector method='relationship' normalization —
  always produces List[Dict] before calling detect_relationship_conflicts
- quality_001: update detect_duplicates + incremental_detect docstrings to
  reflect configurable sort_by field (not hardcoded 'confidence')
- quality_002: add _normalize_entity_id helper (always str) used in both
  _apply_result_limits and _build_duplicate_groups for consistent ID handling

Backward compatible: callers not using new params see no behavior change.
58 tests pass (0 failures)
2026-05-05 19:25:48 +05:30
KaifAhmad1 8ef67b8bda feat(deduplication): add max_results, top_k_per_entity, min_similarity, sort_by to DuplicateDetector
Fixes #534

- New __init__ params: max_results, top_k_per_entity, min_similarity, sort_by
- _apply_result_limits: drop below min_similarity, sort by sort_by field,
  enforce top_k_per_entity per entity, cap at max_results globally
- Wired into detect_duplicates() and incremental_detect()
- 30 new tests in TestResultLimiting; full suite 42/42 passed
2026-05-05 19:16:58 +05:30
Mohd Kaif bc57837b86 Merge pull request #539 from Hawksight-AI/conflicts
fix(conflicts): consolidate duplicate detect_conflicts into single di…
2026-05-05 18:17:19 +05:30
KaifAhmad1 0439cf884d docs(changelog): record ConflictDetector.detect_conflicts duplicate definition fix (#533) 2026-05-05 18:13:06 +05:30
KaifAhmad1 141bf80394 fix(conflicts): consolidate duplicate detect_conflicts into single dispatcher method
Fixes #533

- Removes duplicate `detect_conflicts` definition that was silently overridden,
  causing AttributeError for callers passing `method=` or `property_name=` kwargs
- Merges dispatcher logic into the surviving method with `method="all"` default
  supporting: "all", "value", "property", "type", "relationship", "temporal",
  "logical", "entity"
- Fixes `method="relationship"` incorrectly defaulting `relationships` to the
  entities list; now defaults to `[]` with dict normalization
- Removes unreachable dead code block after try/except raise in
  `detect_entity_conflicts`
2026-05-05 18:00:48 +05:30
Mohd Kaif 0c4e18e256 fix(deps): remove gpu from [all] extra to fix Windows installation failure (#538)
* fix(deps): remove gpu extra from [all] to fix Windows installation failure

faiss-gpu has no Windows builds, so semantica[all] failed with
'No matching distribution found for faiss-gpu>=1.7.0' on Windows.
Removed gpu from both [all] lines — semantica[gpu] remains available
as an explicit opt-in for Linux GPU environments.

Closes #532

* docs(changelog): record faiss-gpu Windows installation failure fix (#532)
2026-05-05 16:57:33 +05:30
Mohd Kaif 39045d783b Merge pull request #537 from Hawksight-AI/utlis
fix(utils): route all progress tracker stdout writes through _safe_wr…
2026-05-05 16:34:43 +05:30
KaifAhmad1 afa54e1bbf docs(changelog): record progress tracker cp1252 UnicodeEncodeError fix (#531) 2026-05-05 16:19:14 +05:30
KaifAhmad1 a01b3c36fc fix(utils): route all progress tracker stdout writes through _safe_write to prevent UnicodeEncodeError on cp1252 consoles
Closes #531

- Replace 5 direct sys.stdout.write() calls in ConsoleProgressDisplay.update()
  with self._safe_write() so emoji/block characters are encoded safely on
  Windows cp1252 consoles
- Add TestProgressTrackerEncoding regression tests (3 cases) covering
  _safe_write, pipeline header, and auto emoji-disable on cp1252
2026-05-05 16:03:19 +05:30
Mohd Kaif 0dda380580 Merge pull request #536 from Hawksight-AI/fix/semantic-extract-import-cycle
fix(semantic_extract): break extractor import cycle
2026-05-05 14:34:57 +05:30
KaifAhmad1andZohaib Hassan e7c9f6e7f3 fix(tests): restore sys.modules after mock injection in test_retry_logic
test_retry_logic.py injected sys.modules["openai"] = MagicMock() at module
level so providers.py could be imported without the real openai package.
Those mocks were never restored, leaving openai (and spacy, instructor etc.)
as MagicMock objects for the entire test session. This caused
test_pr482_deepseek_openai tests to receive a MagicMock when importing
openai.OpenAI, making MagicMock(spec=OpenAI) raise InvalidSpecError.

Fix: save original sys.modules entries before injection and restore them
immediately after the semantica imports that needed the mocks complete.
The mock objects remain bound inside the already-imported provider module,
so test_retry_logic tests are unaffected; other test modules now see the
real packages again.

Co-authored-by: Zohaib Hassan <zohaib179949@gmail.com>
Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-05-05 14:21:36 +05:30
KaifAhmad1andZohaib Hassan b828ebfef0 chore: resolve CHANGELOG.md merge conflict with main
main restructured [Unreleased] into ### Added / ### Fixed sections.
Moved PR #536 semantic_extract circular import fix entry into ### Fixed
below the PR #535 ingest lazy-load entry; kept ### Added content from
main intact.

Co-authored-by: Zohaib Hassan <zohaib179949@gmail.com>
Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-05-05 13:56:48 +05:30
KaifAhmad1andZohaib Hassan 6330627ddb docs(changelog): record semantic_extract circular import fix and Qodo review fix (#536)
Co-authored-by: Zohaib Hassan <zohaib179949@gmail.com>
Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-05-05 13:54:01 +05:30
KaifAhmad1andZohaib Hassan 24e327d161 fix(tests): add from __future__ import annotations for Py3.8 compatibility
subprocess.CompletedProcess[str] as a return annotation is not subscriptable
at runtime on Python 3.8, causing test collection to abort before any tests
run. Adding PEP 563 deferred evaluation makes all annotations strings at
import time, restoring 3.8 compatibility without changing behaviour on 3.9+.

Co-authored-by: Zohaib Hassan <zohaib179949@gmail.com>
Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-05-05 13:40:11 +05:30
Mohd KaifandCopilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> b9f3c59443 Potential fix for pull request finding 'Duplicate key in dict literal'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-05-05 13:28:09 +05:30
Mohd KaifandCopilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> 34955ba246 Potential fix for pull request finding 'Explicit export is not defined'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-05-05 13:27:54 +05:30
Mohd KaifandCopilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> 95b46f12b6 Potential fix for pull request finding 'Explicit export is not defined'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-05-05 13:25:07 +05:30
245ff76b99 fix(ingest): lazy-load optional ingestion backends (#535)
* fix(ingest): lazy-load optional ingestion backends

* fix(ingest): address qodo review — use ModuleNotFoundError and guard ConfigurationError

Bug 1: Replace overbroad `except ImportError` with `except ModuleNotFoundError` in
__getattr__ (__init__.py) and all four optional-backend loaders (methods.py). This
prevents internal import errors inside a backend module from being silently rewritten
into a misleading "package not installed" message. Also simplifies _is_missing_dependency
to rely solely on exc.name now that ModuleNotFoundError always sets it.

Bug 2: Add `except ConfigurationError: raise` before the blanket `except Exception`
handlers in ingest_web, ingest_feed, ingest_repository, and ingest_email. Missing
optional dependencies are expected user-config issues and must not be logged as errors.

Co-authored-by: ZohaibHassan16 <zohaib@hawksight.ai>
Co-authored-by: KaifAhmad1 <mohammadk78600@gmail.com>

* docs(changelog): record lazy ingest backends fix and qodo review fixes (#535)

Co-authored-by: ZohaibHassan16 <zohaib@hawksight.ai>
Co-authored-by: KaifAhmad1 <mohammadk78600@gmail.com>

* fix(tests): set exc.name on blocker's ModuleNotFoundError to match Python import machinery

OptionalDependencyBlocker was constructing ModuleNotFoundError with only a
message string, leaving .name as None. _is_missing_dependency checks exc.name
directly (the string-scan fallback was removed when switching to
ModuleNotFoundError), so the ConfigurationError conversion never triggered and
the test asserted the wrong exception type.

Python's import machinery always sets .name to the top-level module name when
it raises ModuleNotFoundError; the blocker now does the same.

Co-authored-by: Zohaib Hassan (@ZohaibHassan16) <zohaib179949@gmail.com>
Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>

---------

Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
Co-authored-by: Zohaib Hassan (@ZohaibHassan16) <zohaib179949@gmail.com>
2026-05-05 13:09:14 +05:30
a2b6a481dc chore: resolve CHANGELOG.md merge conflict with main
main restructured [Unreleased] into ### Added / ### Fixed sections.
Moved PR #535 lazy-load fix and Ontology Hub post-review fix entries
into ### Fixed; kept ### Added content from main intact.

Co-authored-by: ZohaibHassan16 <zohaib@hawksight.ai>
Co-authored-by: KaifAhmad1 <mohammadk78600@gmail.com>
2026-05-05 12:56:06 +05:30
c7edba88ea fix(tests): set exc.name on blocker's ModuleNotFoundError to match Python import machinery
OptionalDependencyBlocker was constructing ModuleNotFoundError with only a
message string, leaving .name as None. _is_missing_dependency checks exc.name
directly (the string-scan fallback was removed when switching to
ModuleNotFoundError), so the ConfigurationError conversion never triggered and
the test asserted the wrong exception type.

Python's import machinery always sets .name to the top-level module name when
it raises ModuleNotFoundError; the blocker now does the same.

Co-authored-by: ZohaibHassan16 <zohaib@hawksight.ai>
Co-authored-by: KaifAhmad1 <mohammadk78600@gmail.com>
2026-05-05 12:45:43 +05:30
eb8598e0cf docs(changelog): record lazy ingest backends fix and qodo review fixes (#535)
Co-authored-by: ZohaibHassan16 <zohaib@hawksight.ai>
Co-authored-by: KaifAhmad1 <mohammadk78600@gmail.com>
2026-05-05 12:37:17 +05:30
64d4157644 fix(ingest): address qodo review — use ModuleNotFoundError and guard ConfigurationError
Bug 1: Replace overbroad `except ImportError` with `except ModuleNotFoundError` in
__getattr__ (__init__.py) and all four optional-backend loaders (methods.py). This
prevents internal import errors inside a backend module from being silently rewritten
into a misleading "package not installed" message. Also simplifies _is_missing_dependency
to rely solely on exc.name now that ModuleNotFoundError always sets it.

Bug 2: Add `except ConfigurationError: raise` before the blanket `except Exception`
handlers in ingest_web, ingest_feed, ingest_repository, and ingest_email. Missing
optional dependencies are expected user-config issues and must not be logged as errors.

Co-authored-by: ZohaibHassan16 <zohaib@hawksight.ai>
Co-authored-by: KaifAhmad1 <mohammadk78600@gmail.com>
2026-05-05 12:30:48 +05:30
Zohaib Hassnain e1b1e63541 fix(semantic_extract): break extractor import cycle 2026-05-05 02:26:57 +05:00
Zohaib Hassnain 6b0a8e60ce fix(ingest): lazy-load optional ingestion backends 2026-05-05 02:01:27 +05:00
Mohd Kaif 8b22a58b8f Update print statement from 'Hello' to 'Goodbye' 2026-05-04 23:36:03 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ba286a55ea security(deps): update mkdocs requirement from >=1.5.0 to >=1.6.1 (#507)
Updates the requirements on [mkdocs](https://github.com/mkdocs/mkdocs) to permit the latest version.
- [Release notes](https://github.com/mkdocs/mkdocs/releases)
- [Commits](https://github.com/mkdocs/mkdocs/compare/1.5.0...1.6.1)

---
updated-dependencies:
- dependency-name: mkdocs
  dependency-version: 1.6.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-04 22:34:36 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> f610eff0ed security(deps): update mkdocs-mermaid2-plugin requirement (#508)
Updates the requirements on [mkdocs-mermaid2-plugin](https://github.com/fralau/mkdocs-mermaid2-plugin) to permit the latest version.
- [Release notes](https://github.com/fralau/mkdocs-mermaid2-plugin/releases)
- [Changelog](https://github.com/fralau/mkdocs-mermaid2-plugin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/fralau/mkdocs-mermaid2-plugin/compare/v1.0.1...v1.2.3)

---
updated-dependencies:
- dependency-name: mkdocs-mermaid2-plugin
  dependency-version: 1.2.3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-04 21:30:16 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> e9e7278720 security(deps): update mkdocs-jupyter requirement (#509)
Updates the requirements on [mkdocs-jupyter](https://github.com/danielfrg/mkdocs-jupyter) to permit the latest version.
- [Changelog](https://github.com/danielfrg/mkdocs-jupyter/blob/main/CHANGELOG.md)
- [Commits](https://github.com/danielfrg/mkdocs-jupyter/commits)

---
updated-dependencies:
- dependency-name: mkdocs-jupyter
  dependency-version: 0.26.3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-03 12:32:57 +05:30
Mohd Kaif 6b2cafd02e Merge pull request #524 from Hawksight-AI/feat/onto-hub-subissue-520
feat(ontology): add alignments, health dashboard, and SHACL studio
2026-05-02 17:21:54 +05:30
90d857a98f docs(changelog): add PR #524 entry — Alignments, Health Dashboard & SHACL Studio
Covers all features, backend endpoints, schemas, helpers, fix-up commits,
and 14 integration tests added during the subissue-3 implementation cycle.

Co-authored-by: KaifAhmad1 <mohammadk78600@gmail.com>
Co-authored-by: ZohaibHassan16 <zohaib@example.com>
2026-05-02 16:53:09 +05:30
1861ca578c chore: resolve merge conflicts with origin/main
- Keep SequenceMatcher + Tuple imports; delegate Literal to typing_extensions
- Preserve both _ALIGNMENT_RELATIONS (subissue-520) and _INGEST_FORMAT_SUFFIXES (main)
- Keep our OntologyAlignment-typed _get_alignment_store; add main's _get_drafts,
  _get_proposals, _get_versions, _alignment_key, _coerce_alignment, _version_field
- Import OntologyEditor + VersionsTab (main) alongside ShaclStudio (subissue-520)
- All 14 subissue-3 tests pass post-resolution

Co-authored-by: KaifAhmad1 <mohammadk78600@gmail.com>
Co-authored-by: ZohaibHassan16 <zohaib@example.com>
2026-05-02 16:45:10 +05:30
63acc7a66e fix(ontology): address Qodo automated review findings from PR #524
Backend (semantica/explorer/routes/ontology.py):
- suggest-alignments: add TF-IDF character-ngram embeddings via sklearn
  (SimilarityCalculator-compatible cosine scoring) so embedding_similarity
  is populated in results; combined score = 0.4*label + 0.6*embedding when
  available, falling back to label-only when sklearn is absent
- suggest-alignments: add token-overlap prefilter before SequenceMatcher so
  zero-Jaccard pairs are skipped without computing full similarity; add
  _MAX_ENTITIES_PER_SIDE=500 per-ontology cap on top of the existing
  _MAX_ANALYSIS_NODES global cap
- suggest-alignments: remove dead try/except OntologyEngine.create_alignment
  block that always failed silently (no TripletStore configured); replace
  with a comment explaining the intentional ephemeral-only storage model
- health: replace O(alignments x entities) any() scans for alignment coverage
  with O(1) set membership checks via assessed_ids
- shacl/validate: run rdflib.Graph().parse(format='turtle') syntax check on
  the submitted Turtle before returning; invalid syntax now raises 422 instead
  of returning a misleading unavailable/success response

Frontend:
- AlignmentsTab: add pairwise alignment matrix section that groups recorded
  alignments by (source_ontology, target_ontology) pair; each cell shows
  color-coded relation badges per RELATION_COLORS; clicking a badge populates
  the create/edit form for quick editing; matrix is shown when at least two
  ontologies are loaded
- ShaclStudio: add selectedShapeId state and fullShacl ref; each shape row in
  the library is now a clickable button that extracts its Turtle block from
  the full SHACL and pre-populates the Monaco editor; a "View all" toggle
  restores the full SHACL; selected shape ID is shown in the editor header
- GraphWorkspace: fix viewMode race in external focus effect — call
  setSelectedNodeId directly instead of going through focusNode(), which
  captured a stale viewMode in its closure; remove focusNode from the
  dependency array since it is no longer called

Tests (14 passing, was 11):
- Add test_suggest_alignments_returns_embedding_similarity: asserts
  embedding_similarity is non-null when sklearn is available
- Add test_shacl_validate_rejects_invalid_turtle_syntax: asserts 422 on
  syntactically invalid Turtle
- Add test_health_alignment_coverage_uses_set_lookup: asserts alignment
  dimension score is non-zero after recording an alignment, verifying the
  O(1) set lookup path works correctly end-to-end

Co-authored-by: KaifAhmad1 <mohammadk78600@gmail.com>
Co-authored-by: ZohaibHassan16 <zohaib@hawksight.ai>
2026-05-02 16:38:09 +05:30
00ceb09960 fix(ontology): address review blockers from PR #524
Backend:
- Replace false conforms=True SHACL stub with status=unavailable always;
  live validation cannot be wired until OntologyEngine.validate_graph is
  connected to a data graph — a stub that returns conforms=True misleads
  users editing shapes
- Cap node/edge fetches in health, suggest-alignments, and SHACL generation
  at _MAX_ANALYSIS_NODES (5 000) with a logger.warning when the graph
  exceeds the limit; unbounded limit=999_999 fetches cause OOM on large graphs
- Set SHACL health dimension score to 0.0 (was 70.0) when status=unavailable;
  exclude unavailable dimensions from the total_score average so they neither
  inflate nor deflate the result
- Allow alignments to reference external/unloaded URIs (e.g. schema.org)
  without raising 404; label falls back to URI fragment or caller-supplied
  source_label/target_label fields added to OntologyAlignmentRequest
- Fix _alignment_id to use uuid.NAMESPACE_OID instead of NAMESPACE_URL;
  the composite key is not a URL
- Fix _summarize_shapes to normalise \r\n before splitting on .\n so shape
  parsing works correctly on Windows line endings

Frontend:
- Wrap handleSave/handleSuggest/handleRemove/handleAcceptSuggestion in
  useCallback in AlignmentsTab for consistency with sibling components
- Add ephemeral-storage banner in AlignmentsTab warning that alignments are
  session-memory-only and not persisted across restarts
- Fix exportReport in HealthTab to append/remove anchor from document before
  clicking and defer URL.revokeObjectURL to avoid Blob URL leak in some browsers
- Derive health dimension grid column count from health.dimensions.length
  instead of the hardcoded repeat(5, ...) that breaks if the backend adds
  or removes a dimension
- Add minimal Monarch tokenizer for the Monaco turtle language registration
  in ShaclStudio so prefix declarations, IRIs, SHACL properties, comments,
  and string literals are syntax-highlighted; previously the editor rendered
  as plain text despite theme rules being defined

Tests (11 passing, was 5):
- Rename test_shacl_validate_has_stable_contract to
  test_shacl_validate_returns_unavailable and assert status == unavailable
- Add test_shacl_validate_rejects_empty_turtle (expects 422)
- Add test_health_returns_404_for_unknown_ontology
- Add test_health_shacl_dimension_is_zero_when_unavailable with total_score check
- Add test_delete_unknown_alignment_returns_404
- Add test_alignment_upsert_is_idempotent (verifies ID stability and created_at
  preservation across updates)
- Add test_alignment_accepts_external_uri (verifies no 404 for schema.org URIs)
- Relax test_alignment_suggestions_are_ranked label assertions to substring
  checks so the test survives similarity algorithm changes

Co-authored-by: KaifAhmad1 <mohammadk78600@gmail.com>
Co-authored-by: ZohaibHassan16 <zohaib@hawksight.ai>
2026-05-02 15:53:47 +05:30
Mohd Kaif 2909e0fa14 Merge pull request #523 from Hawksight-AI/feature/ontology-hub-endpoints
feat: implement Ontology Hub endpoints with Semantica module integration
2026-05-02 11:20:41 +05:30
KaifAhmad1 d2574990fa Merge branch 'feature/ontology-hub-endpoints' of https://github.com/Hawksight-AI/semantica into feature/ontology-hub-endpoints 2026-05-02 11:12:22 +05:30
KaifAhmad1 53d1da87c0 fix: prevent invalid domain/range edges in ontology creation
- Fix domain_uri/range_uri always being truthy strings
- Only create rdfs:domain/rdfs:range edges when domain/range are non-empty strings
- Add proper validation with .strip() to handle whitespace-only values
- Apply fix to both 'data' and 'text' mode ontology creation
- Prevents pollution of graph with invalid edges to namespace root

Fixes issue where empty domain/range values like '' or None would still create
edges pointing to namespace root (e.g., 'https://ex/#/') instead of being
properly omitted.
2026-05-02 11:12:03 +05:30
Zohaib Hassnain 9e916a82b5 fix(ontology): address hub endpoint review blockers 2026-05-02 00:02:06 +05:00
Zohaib Hassnain e8bf0e50d3 feat(ontology): add alignments health and shacl studio 2026-05-01 22:59:15 +05:00
KaifAhmad1 269fdaa9fb feat: implement Ontology Hub endpoints with Semantica module integration
- Add comprehensive ontology API endpoints (27 total)
- Integrate OntologyEngine for validation, SHACL, SKOS, alignments
- Integrate VersionManager for versioning and diffing
- Integrate ChangeLogEntry for audit trails
- Integrate OntologyIngestor for RDF parsing
- Add frontend components: OntologyEditor, ProposalReview, VersionsTab
- Update CHANGELOG with detailed feature documentation
- Add proper error handling and fallback mechanisms
- Fix import issues and dependencies
- All endpoints tested and verified working

Features implemented:
- Draft management with audit trails
- Change proposals with structured diffing
- Version comparison and publishing
- Ontology loading with multiple format support
- SKOS vocabulary management
- Cross-ontology alignments
- Visual ontology editor
- Registry and search functionality
2026-05-01 22:26:21 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 2d9bbf08b1 deps(deps): update pytest-benchmark requirement from >=4.0.0 to >=5.2.3 (#522)
Updates the requirements on [pytest-benchmark](https://github.com/ionelmc/pytest-benchmark) to permit the latest version.
- [Changelog](https://github.com/ionelmc/pytest-benchmark/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/ionelmc/pytest-benchmark/compare/v4.0.0...v5.2.3)

---
updated-dependencies:
- dependency-name: pytest-benchmark
  dependency-version: 5.2.3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-01 18:05:17 +05:30
Mohd Kaif fbbe36983b Merge pull request #521 from Hawksight-AI/feat/ontology-hub-subissue-518
feat(explorer): Ontology Hub — Registry, Loader, Entity Search & SKOS Vocabulary Manager
2026-05-01 17:16:43 +05:30
KaifAhmad1 877903358a docs(changelog): add entries for ontology hub bug fixes and security advisory #23 2026-05-01 17:12:11 +05:30
KaifAhmad1 070b36902b fix(security): remove polynomial ReDoS regex in _detect_format (py/polynomial-redos)
The pattern `<[^>]+>\s+<[^>]+>` in _detect_format() was flagged by CodeQL
(py/polynomial-redos, CWE-1333/730/400) as a polynomial regular expression
on uncontrolled user data.

The `<...>` branch was already unreachable — strings starting with '<' return
'xml' two lines above — but CodeQL does not track that control flow path.

Fix: replace the entire re.match() call with plain startswith / 'in' checks:
- N-Triples with URI subjects are already handled by the XML branch.
- Only blank-node-subject N-Triples (_:word <uri> ...) need detection here,
  which is correctly expressed as startswith('_:') and ' <' in stripped.
- Removed the now-unused `import re`.

Closes security advisory #23.
2026-05-01 15:26:17 +05:30
Mohd KaifandCopilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> 3b9efb7856 Potential fix for pull request finding 'Empty except'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-05-01 15:21:32 +05:30
KaifAhmad1 2a031f0225 fix(explorer): correct file upload format detection for xml/json extensions (#518)
Bug 4 — Upload format misdetected:
- Added xml→'xml' and json→'json-ld' to the extension→format map so
  .xml and .json files are no longer misidentified as turtle.
- Changed the fallback from '|| "turtle"' to '?? ""' (empty string for
  unknown extensions) so the backend _detect_format() runs instead of
  blindly assuming turtle for any unrecognised extension.
- Omit the format key entirely from the load request body when no format
  was detected, letting the backend auto-detect from content heuristics.
- Added .n3 to the file picker accept list and dropzone hint text.
2026-05-01 15:18:28 +05:30
KaifAhmad1 d04f2b3643 fix(explorer): address Qodo review findings for ontology hub (#518)
Bug 1 — Broken registry filters:
fetchRegistry no longer sends format/kind values (owl/skos/internal/external)
as the status query param; those filters are applied client-side via
filteredEntries which already had the correct logic. Only the text search
param q is delegated to the backend.

Bug 2 — Toggle/refresh URI corruption:
Removed removesuffix('/toggle') and removesuffix('/refresh') from
toggle_ontology and refresh_ontology. Starlette's route regex already
strips the literal suffix from the captured path param; the removesuffix
call was a no-op for normal URIs but corrupted any ontology URI that
legitimately ends with /toggle or /refresh.

Bug 3 — SSRF in URL fetch:
Added _validate_fetch_url() which rejects non-http/https schemes and
resolves the hostname to block private, loopback, link-local, reserved,
and multicast addresses before requests.get() is called. Applied to all
three fetch sites: preview, load, and refresh.

Bug 5 — Inconsistent XML hardening:
_parse_rdf_sync now calls _safe_parse_rdf() from
semantica/explorer/utils/rdf_parser.py instead of g.parse() directly,
applying the existing defusedxml-based XXE protection for RDF/XML inputs.

Bug 6 — Search scans whole graph:
search_entities now calls session.search(q, limit*6) which hits the
GraphSearchIndex instead of fetching up to 999,999 nodes and doing a
linear Python substring scan. Results are post-filtered by _SEARCHABLE_TYPES
and entity_type before being returned up to the requested limit.
2026-05-01 15:12:04 +05:30
KaifAhmad1 2811469071 feat(explorer): add Ontology Hub workspace — Registry, Loader, Entity Search & SKOS (closes #518)
Implements the first subissue of Ontology Hub (#517):

Frontend:
- New OntologyWorkspace with 6 tabs (Registry, Editor, Versions,
  Alignments, Health, SHACL); active tab persisted in ontologyTab URL param
- OntologyManager: full registry CRUD with status/format badges, stats,
  toggle/refresh/remove actions, search + filter toolbar, empty state CTA
- OntologyLoader: 3-tab modal — URL import with live preview, drag-and-drop
  file upload, and Create New (from scratch / data / text)
- OntologySearch: debounced entity search with type filters and detail panel
  showing superclasses, subclasses, domain/range, instance count
- SKOSVocabularyManager: recursive concept hierarchy tree, client-side
  filtering, full SKOS annotation + relation detail panel
- Editor/Versions (subissue 2) and Alignments/Health/SHACL (subissue 3)
  tabs render descriptive stub cards as placeholders

Backend:
- 12 new FastAPI endpoints under /api/ontology (registry, preview, load,
  create, search, entity detail, SKOS schemes + concept detail, toggle,
  refresh, remove)
- rdflib-based RDF parser supporting Turtle, RDF/XML, N-Triples, JSON-LD
- URL fetching via requests in asyncio.to_thread with 20 MB cap
- Registry stored in app.state.ontology_registry; route ordering prevents
  literal paths being shadowed by /{uri:path} wildcards

Also: add playwright dev dependency for screenshot testing
2026-05-01 13:08:45 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> a0b4793590 security(deps): update pymdown-extensions requirement (#510)
Updates the requirements on [pymdown-extensions](https://github.com/facelessuser/pymdown-extensions) to permit the latest version.
- [Release notes](https://github.com/facelessuser/pymdown-extensions/releases)
- [Commits](https://github.com/facelessuser/pymdown-extensions/compare/10.0...10.21.2)

---
updated-dependencies:
- dependency-name: pymdown-extensions
  dependency-version: 10.21.2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-01 11:40:01 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> cdeb04b816 security(deps): update mkdocs-material requirement (#511)
Updates the requirements on [mkdocs-material](https://github.com/squidfunk/mkdocs-material) to permit the latest version.
- [Release notes](https://github.com/squidfunk/mkdocs-material/releases)
- [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG)
- [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.4.0...9.7.6)

---
updated-dependencies:
- dependency-name: mkdocs-material
  dependency-version: 9.7.6
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-01 11:33:24 +05:30
Mohd Kaif b7e31d82b0 Merge pull request #516 from Hawksight-AI/feat/landing-page-visual-refresh
feat(explorer): redesign landing page
2026-04-30 18:23:36 +05:30
Mohd Kaif 2eba54caac Merge branch 'main' into feat/landing-page-visual-refresh 2026-04-30 16:55:03 +05:30
KaifAhmad1andZohaib Hassnain 9fd1df9c51 docs(changelog): add entry for PR #516 landing page redesign and review fixes
Co-Authored-By: Zohaib Hassnain <109234410+ZohaibHassan16@users.noreply.github.com>
Co-Authored-By: KaifAhmad1 <kaifahmad087@gmail.com>
2026-04-30 16:54:11 +05:30
KaifAhmad1andZohaib Hassnain 4517089a7f fix(explorer): address PR #516 review findings
- Replace invalid inset-left with inset: 0 0 0 72px on ::before at <=680px
- Add matching mobile inset fix to ::after (was still at 88px)
- Merge duplicate .landing-capability-band CSS rule blocks into one
- Fix non-standard font-weight: 850 -> 800 on .landing-launcher-item-title
- Remove unused eyebrow field from LandingAction type and all data entries
- Extract static 42-dot SVG preview array to module-level PREVIEW_DOTS constant

Co-Authored-By: Zohaib Hassnain <109234410+ZohaibHassan16@users.noreply.github.com>
Co-Authored-By: KaifAhmad1 <kaifahmad087@gmail.com>
2026-04-30 16:50:31 +05:30
Zohaib Hassnain 9553e1a176 feat(explorer): redesign landing page 2026-04-29 23:38:01 +05:00
Mohd Kaif 04fcfb61a7 Merge pull request #515 from Hawksight-AI/feat/distance-intelligence-slash-safe-ui
fix(explorer): make distance intelligence API calls slash-safe
2026-04-29 23:30:12 +05:30
5dc6966706 docs(changelog): add entry for issue #514 / PR #515 slash-safe distance UI fix
Co-Authored-By: ZohaibHassan16 <109234410+ZohaibHassan16@users.noreply.github.com>
Co-Authored-By: KaifAhmad1 <98801504+KaifAhmad1@users.noreply.github.com>
2026-04-29 23:23:08 +05:30
bb956b2735 fix(explorer): address PR #515 review findings
- Align _coerce_embedding_vector inner dict-probe key list with
  _extract_node_embeddings outer key list (add 'embeddings', reorder to
  generic-first) so nested embedding dicts resolve consistently.
- Add TODO comment on _extract_node_embeddings to cache per-session
  graph revision and avoid O(N) re-scan on every semantic request.
- Add deprecation docstrings to legacy path-segment routes
  (/node/{id}/path and /node/{id}/semantic-neighborhood) documenting
  the known slash-in-ID limitation and pointing to the query-param
  alternatives.
- Extract _FakeSimilarity to module level so it is shared without
  duplication across test classes.
- Rewrite test_legacy_semantic_neighborhood_still_works_for_simple_ids
  as a fully isolated TestClient session instead of mutating the
  shared module-scoped 'client' fixture, preventing cross-test
  state pollution.
- Extract _make_slash_node_session helper to reduce boilerplate in the
  slash-safe route tests.

Co-Authored-By: ZohaibHassan16 <109234410+ZohaibHassan16@users.noreply.github.com>
Co-Authored-By: KaifAhmad1 <98801504+KaifAhmad1@users.noreply.github.com>
2026-04-29 23:17:57 +05:30
Zohaib Hassnain e3a3f6010b fix(explorer): make distance intelligence API calls slash-safe 2026-04-29 21:17:36 +05:00
Mohd Kaif b6373204e2 Merge pull request #513 from Hawksight-AI/feat/explorer-distance-ui-fix
fix(explorer): make distance intelligence visible
2026-04-29 15:32:02 +05:30
e385c78977 docs(changelog): add entry for PR #513 distance intelligence fix
Co-Authored-By: ZohaibHassan16 <zohaib@hawksight.ai>
Co-Authored-By: KaifAhmad1 <mohammadk78600@gmail.com>
2026-04-29 14:54:21 +05:30
7b581d5960 fix(explorer): address PR #513 review blockers
- Fix dead `if (anchorNodeId)` conditional in buildHeatmapRenderSnapshot
  (anchor is always truthy past the early-return guard on line 263)
- Replace O(n) array .includes() with WeakMap-cached Set.has() in
  resolveDistanceNodeStyle heatmap path — prevents per-node O(n) scan
  during every Sigma reducer pass on large graphs
- Rename GraphDistanceBucketCounts.threeHop → threeHopPlus across
  types.ts, graphSceneState.ts, and GraphWorkspace.tsx so the field
  name reflects that it accumulates distance ≥ 3, not exactly 3;
  update status-strip labels to "3+ hop" accordingly
- Restore hasMetrics guard in PathDistanceIntelPanel to suppress the
  empty metric grid <div> when a path result carries no optional metrics

Co-Authored-By: ZohaibHassan16 <zohaib@hawksight.ai>
Co-Authored-By: KaifAhmad1 <mohammadk78600@gmail.com>
2026-04-29 14:49:22 +05:30
Zohaib Hassnain 07ca93fe5c fix(explorer): make distance intelligence visible 2026-04-29 02:49:08 +05:00
Mohd Kaif 41e430b928 Merge pull request #503 from Hawksight-AI/feat/explorer-visual-refresh
feat(explorer): polish graph explorer visual language
2026-04-27 22:33:41 +05:30
438d8bc7af fix(explorer): address PR #503 review findings
- Extract ENTITY_SHAPE_ALIASES and classifyEntityShape into a shared
  graphEntityShape.ts utility — resolveEntityShape was duplicated with
  divergent signatures in useLoadGraph.ts and graphSceneState.ts; both
  now import from one place so aliases can never drift
- graphSceneState.resolveEntityShape falls back to classifyEntityShape
  for nodes created programmatically that bypass useLoadGraph
- Fix graphTheme.ts indentation around fullGraphStructure,
  fullGraphStructureLayer, and interaction — closing braces were at
  wrong indent levels making the nesting visually misleading
- Add comment on fullGraphStructureLayer.mode explaining it is
  intentionally "off" as a staged-rollout gate (flip to "auto" to enable
  cross-community canvas curve rendering)

Co-authored-by: Mohd Kaif <98801504+KaifAhmad1@users.noreply.github.com>
Co-authored-by: Zohaib Hassnain <109234410+ZohaibHassan16@users.noreply.github.com>
2026-04-27 22:21:41 +05:30
82f1f6bd10 docs(changelog): add entry for Explorer visual refresh PR #503
Co-authored-by: Mohd Kaif <98801504+KaifAhmad1@users.noreply.github.com>
Co-authored-by: Zohaib Hassnain <109234410+ZohaibHassan16@users.noreply.github.com>
2026-04-27 22:00:52 +05:30
c86570b996 merge(explorer-visual-refresh): resolve conflict in GraphWorkspace.tsx
Merge origin/main (Distance Intelligence #502) into feat/explorer-visual-refresh.

Conflict was in the viewModeItems useMemo: the PR's new cluster-based toolbar
structure diverged from main's coreToolbarGroups additions.

Resolution:
- Keep PR's viewModeItems as a clean 3-item segmented control (Full/Grouped/Focused)
- Port Distance Intelligence controls (ego mode, heatmap, structural/semantic overlay)
  into a new distanceToolbarItems useMemo that slots into the cluster toolbar as a
  "Distance" cluster, visible only when a node is selected
- Wire distanceToolbarItems into toolbarClusters between "local-structure" and
  "analysis" clusters
- All other Distance Intelligence additions (state vars, BFS helpers, useEffects,
  ego depth slider, GraphInspectorPanel onFocusNode prop) merged cleanly

Co-authored-by: Mohd Kaif <98801504+KaifAhmad1@users.noreply.github.com>
Co-authored-by: Zohaib Hassnain <109234410+ZohaibHassan16@users.noreply.github.com>
2026-04-27 21:55:25 +05:30
Mohd Kaif 93dda5e435 Merge pull request #512 from Hawksight-AI/context
feat(context): add distance intelligence across context, API, and Exp…
2026-04-27 20:12:40 +05:30
Mohd KaifandCopilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> 6ffed78fd9 Potential fix for pull request finding 'Module is imported with 'import' and 'import from''
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-04-27 18:39:09 +05:30
KaifAhmad1 f06de0dab2 fix(context): address PR #512 review blockers and bot findings
Merge blockers (ZohaibHassan16):
- fix: distance-matrix raises HTTP 503 when metric=semantic but no
  similarity backend is available, instead of silently returning hop
  distances labeled as semantic
- fix: distance-enriched export now requires node_subset (HTTP 422 if
  omitted), preventing unbounded all-pairs O(n^2) export over full graph
- fix: DistanceExportRequest default include corrected from ["hops",
  "distance_band"] to ["source_id", "target_id", "hop_count",
  "distance_band"] so default exports are unambiguous and use the correct
  column name
- fix: confidence decay edge weight index now reads graph_dict.get("edges")
  or graph_dict.get("relationships") to handle both graph dict shapes,
  fixing always-1.0 decay when session returns relationships key

Bot findings (github-code-quality / chatgpt-codex):
- fix: remove unused Iterable import from distance_exporter.py
- fix: remove unused Response import from graph.py
- fix: move logger init before optional KG import; replace empty
  except ImportError: pass with logger.debug in distance_exporter.py
- fix: replace two bare except Exception: pass in temporal.distance_history
  with logger.warning including source, target, metric, and timestamp context
- fix: remove mixed import style in test_qual003 — use only module import
  and reference CausalChainAnalyzer through it
2026-04-27 18:28:04 +05:30
KaifAhmad1 dd016744ce feat(context): add distance intelligence across context, API, and Explorer (#502)
- ContextGraph.get_neighbors() gains include_distance_metadata flag (backward-compat)
- get_neighbor_distances() returns neighbors sorted by hop and confidence decay
- AgentContext.retrieve/find_precedents support proximity-weighted blending
- FR-4: path enrichment (decay, similarity, coherence, bottleneck, interpretation)
- FR-6: POST /api/graph/distance-matrix (hops/weighted/semantic, upper-triangle)
- FR-3: GET /api/graph/node/{id}/semantic-neighborhood
- FR-8: GET /api/decisions/causal-distance (causal-edge-only BFS)
- FR-9: GET /api/temporal/distance-history (convergence/divergence events)
- FR-10: POST /api/export/distance-enriched (CSV/JSONL, 200-node cap)
- Explorer: PathDistanceIntelPanel, Ego Mode, Structural/Semantic overlay, Heatmap
- Fix 13 Qodo review issues: API param mismatch, O(E*L) decay, breaking change,
  schema key inconsistency, datetime arithmetic, id overwrite, sweep race,
  node_subset DoS, full-matrix redundancy, effect race, silent exceptions, duplication
- 57 new tests in test_distance_intelligence.py; 18 regression tests in _smoke_review_fixes.py
2026-04-27 11:07:27 +05:30
Zohaib Hassnain 379994867d feat(explorer): polish graph explorer visual language 2026-04-27 03:10:28 +05:00
Mohd KaifandClaude Sonnet 4.6 7884d71e23 feat(explorer): add welcome screen and fix root path Invalid path error (#501)
- Add WelcomeScreen shown on app load; SKE brand button navigates back
- Fix serve_spa: empty root path was hitting dot-guard returning 400
  Invalid path instead of index.html or a welcome JSON response

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 15:41:13 +05:30
Mohd Kaif ca5f081793 Merge pull request #493 from Hawksight-AI/feat/explorer-grouped-view
Feat/explorer grouped view
2026-04-25 15:44:04 +05:30
6ad1502224 fix(explorer): address grouped view review blockers
- Fix `import.meta.env.DEV` crash in graphSceneState.ts that broke the
  entire test:graph-workspace suite (module load fails in Node.js/tsx)
- Export `resolveGroupedDisplayNodeId` from graphSceneState.ts and
  remove the identical copy in GraphWorkspace.tsx
- Add `checkGroupedViewAvailability` helper (Louvain only, no centrality)
  so grouped view availability can be checked cheaply on every graph change
- Gate full community graph build (`groupedDisplayCandidate`) on
  `viewMode === 'grouped'` to avoid running Louvain + centrality on every
  graph version tick when the user is not in grouped view
- Remove dead ternary in `focusNode` where both branches returned `nodeId`
- Add 7 new tests covering resolveGroupedDisplayNodeId,
  resolveGroupedDisplayStateSnapshot, and checkGroupedViewAvailability

Co-Authored-By: ZohaibHassan16 <109234410+ZohaibHassan16@users.noreply.github.com>
Co-Authored-By: KaifAhmad1 <mohammadk78600@gmail.com>
2026-04-25 13:42:15 +05:30
Zohaib Hassnain b010ba68fa Merge origin/main into feat/explorer-grouped-view 2026-04-25 02:54:48 +05:00
Zohaib Hassnain 7c8dfbd3c0 feat(explorer): stabilize and refine grouped graph view 2026-04-25 02:40:51 +05:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 45400c88d3 ci(deps): bump actions/upload-pages-artifact from 3 to 5 (#485)
Bumps [actions/upload-pages-artifact](https://github.com/actions/upload-pages-artifact) from 3 to 5.
- [Release notes](https://github.com/actions/upload-pages-artifact/releases)
- [Commits](https://github.com/actions/upload-pages-artifact/compare/v3...v5)

---
updated-dependencies:
- dependency-name: actions/upload-pages-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-24 13:10:05 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 26f6cdf9e5 docker(deps): bump python from 3.12-slim to 3.14-slim (#466)
Bumps python from 3.12-slim to 3.14-slim.

---
updated-dependencies:
- dependency-name: python
  dependency-version: 3.14-slim
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-24 13:07:11 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 96f88594c2 docker(deps): bump node from 20-alpine to 25-alpine (#465)
Bumps node from 20-alpine to 25-alpine.

---
updated-dependencies:
- dependency-name: node
  dependency-version: 25-alpine
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-24 13:03:24 +05:30
Mohd Kaif c0d08c46f7 Merge pull request #486 from Hawksight-AI/fix/graph-motion
Fix explorer zooming and Loading Flicker
2026-04-23 19:13:37 +05:30
Mohd KaifandCopilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> 5a388d0bcc Potential fix for pull request finding 'Useless conditional'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-04-23 19:02:31 +05:30
KaifAhmad1andZohaibHassan16 f516aef8fd fix(explorer): address PR #486 review blockers + resolve conflict with main
Merge conflict resolution:
- Kept fix/graph-motion's conditional layout-stop (only in focused mode)
  to preserve live layout motion for derived graphs — the core intent of
  this PR.

Must-fix items resolved:

1. plugin.json — removed "hooks": "./hooks/hooks.json" (re-added by this
   branch, already removed in PR #489 on main as it is auto-loaded).
   Kept "agents": "./agents".

2. Double Louvain per render — added groupedViewAvailable useMemo in
   GraphWorkspace (deps: [graphVersion]) that runs community detection
   once. Passed result into resolveDisplayGraph and resolveDisplayStateSnapshot
   via new groupedViewAvailable option; both functions skip their internal
   computeGraphAnalyticsBase call when the value is pre-supplied.

3. graphVersion in displayState deps — removed graphVersion from the
   displayState memo dep array. displayState now depends on the stable
   boolean groupedViewAvailable, not on every ADD_NODE/ADD_EDGE tick,
   so Louvain no longer re-fires on every WebSocket update.

4. hideLabelsOnMove / hideEdgesOnMove flipped to true — intentional:
   suppressing labels and edges during pan reduces visual noise and is
   part of the flicker-reduction fix described in the PR.

Co-authored-by: ZohaibHassan16 <zohaibhassan1696@gmail.com>
Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-04-23 18:58:48 +05:30
Mohd Kaif c9a382e676 Merge pull request #487 from Sameer6305/feat/explorer-stabilize-local-graph-interaction
feat(explorer): stabilize local graph interaction
2026-04-23 18:28:45 +05:30
KaifAhmad1andSameer6305 897d950bdc fix(explorer): address PR #487 review blockers
1. Prevent active-but-disabled Focused button by only disabling when
   viewMode is not already "focused" (viewMode !== "focused" && !canActivateFocusedMode).
2. Generalize inspector fallback copy — stale/invalid node IDs are not
   necessarily grouped items, so remove the misleading "Activate Focused
   mode" hint.
3. Move pluginRuntimeRef.current read out of render by converting
   canActivateFocusedMode from useMemo to useState + useEffect, resolving
   two ESLint "cannot access refs during render" errors and the missing
   toolbar-memo dependency warning.

Co-authored-by: Sameer6305 <sskadam6305@gmail.com>
Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>
2026-04-23 18:24:31 +05:30
Mohd Kaif dd8fa17db8 Merge pull request #489 from musicload/fix/local-plugin-install
fix(plugin): make local Claude Code plugin install work out of the box
2026-04-23 11:42:20 +05:30
KaifAhmad1 92801c220e docs(plugin): align Claude install commands with marketplace flow 2026-04-23 11:34:38 +05:30
Serge 738480606c fix(plugin): drop hooks field from plugin.json (auto-loaded)
Claude Code auto-loads hooks/hooks.json. Declaring it explicitly in
manifest.hooks causes: 'Duplicate hooks file detected ... already-loaded'.
Same pattern as agents: manifest should only reference *additional* hook
files beyond the default.
2026-04-22 16:03:00 -04:00
Serge f9e0bcf210 fix(plugin): make local plugin install work out of the box
Two separate schema issues blocked `/plugin marketplace add ./plugins`
followed by `/plugin install semantica@semantica-local`:

1. `marketplace.json` was missing the required top-level `owner` object.
   Claude Code rejects with: `owner: Invalid input: expected object,
   received undefined`.

2. `plugin.json` declared `"agents": "./agents"` (string), but Claude
   Code's manifest schema rejects non-array `agents` with:
   `Validation errors: agents: Invalid input`. Auto-discovery from
   the default `agents/` directory works when the field is omitted,
   provided agents are flat `<name>.md` files with frontmatter (Claude
   Code's subagent convention) rather than `<name>/AGENT.md`
   subdirectories.

Changes:
- add `owner` object to `marketplace.json`
- drop `agents` field from `plugin.json` (falls back to auto-discovery)
- rename `agents/<name>/AGENT.md` -> `agents/<name>.md` (frontmatter
  content is unchanged, just the path)

After this, the documented local-install flow succeeds end-to-end.
2026-04-22 15:50:34 -04:00
Sameer6305 bb0e9f49e3 feat(explorer): stabilize local graph interaction 2026-04-23 00:11:38 +05:30
Zohaib Hassnain f95c1612d5 fix blinking and zooming problem 2026-04-22 03:33:09 +05:00
Zohaib Hassnain 8c202a691e fix(explorer): restore live layout motion for derived graphs 2026-04-22 03:31:36 +05:00
Mohd Kaif 304b82fbd6 Merge pull request #483 from ZohaibHassan16/feat/graph-declutter-and-calm
feat(explorer): calm and structurally declutter graph workspace
2026-04-20 17:50:00 +05:30
KaifAhmad1andZohaib Hassnain 8d2dfaa53c docs(changelog): add PR #483 explorer declutter release notes
Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>

Co-authored-by: Zohaib Hassnain <109234410+ZohaibHassan16@users.noreply.github.com>
2026-04-20 17:33:12 +05:30
KaifAhmad1andZohaib Hassnain 39aaae778f Merge origin/main into feat/graph-declutter-and-calm
Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>

Co-authored-by: Zohaib Hassnain <109234410+ZohaibHassan16@users.noreply.github.com>
2026-04-20 17:20:47 +05:30
KaifAhmad1andZohaib Hassnain 16d628997a test(explorer): cover graph display declutter flows
Co-authored-by: KaifAhmad1 <kaifahmad087@gmail.com>

Co-authored-by: Zohaib Hassnain <109234410+ZohaibHassan16@users.noreply.github.com>
2026-04-20 17:14:40 +05:30
Mohd Kaif 5e6ad6e87e Merge pull request #482 from liling/main
fix(providers): switch DeepSeekProvider from deepseek SDK to OpenAI c…
2026-04-19 20:16:25 +05:30
Mohd Kaif f6198039fa Merge branch 'main' into main 2026-04-19 20:10:22 +05:30
983f5301e8 fix(providers): switch DeepSeekProvider to OpenAI SDK + fix base_url and verbose_mode (closes #482)
- Replace deepseek.Client with openai.OpenAI(base_url="https://api.deepseek.com/v1")
  in DeepSeekProvider._init_client(); the deepseek PyPI package has no Client class
- Add self.base_url = "https://api.deepseek.com/v1" to DeepSeekProvider.__init__()
  (missing from original PR; caused AttributeError on every instantiation)
- Fix verbose_mode NameError in BaseProvider.generate_typed() instructor path
- Update pyproject.toml: llm-deepseek extra now declares openai>=1.0.0
- Update _init_client warning message to reference openai library
- Add 19 tests in tests/semantic_extract/test_pr482_deepseek_openai.py
- Update CHANGELOG.md

Co-authored-by: liling <liling@users.noreply.github.com>
Co-authored-by: KaifAhmad1 <mohammadk78600@gmail.com>
2026-04-19 20:07:44 +05:30
Mohd Kaif 5a852169be Merge pull request #481 from ZohaibHassan16/feat/optimize-search
Feat/optimize search
2026-04-19 19:04:13 +05:30
KaifAhmad1 fe6ca7fccb fix(search-index): restore secondary-scan node ordering and add regression test 2026-04-19 18:46:05 +05:30
Mohd Kaif 66c8431eee Merge branch 'main' into feat/optimize-search 2026-04-19 18:25:28 +05:30
3e2a0a3f3b docs(changelog): add indexed search performance entry (#481, #467)
Co-authored-by: ZohaibHassan16 <zohaibhassan16@users.noreply.github.com>
Co-authored-by: KaifAhmad1 <mohammadk78600@gmail.com>
2026-04-19 18:19:00 +05:30
d22a54353a fix(search-index): bisect ops, thread-safe mutation bridge, drop edge upserts
- Replace list.sort() on every upsert with bisect.insort() — O(log n) per
  insert instead of O(n log n); bulk rebuild still sorts once at the end
- Replace list.remove() in remove() with bisect.bisect_left + pop() — O(log n)
  find instead of O(n) scan
- Wrap handle_graph_mutation() index mutations in self._lock — mutation bridge
  fires from a background thread and was racing concurrent search/rebuild calls
- Drop source/target upserts in add_edge() — edges don't change node text so
  the index documents are identical; removes unnecessary cache invalidation
- Sort tag values in _cache_key() — ["a","b"] and ["b","a"] now share a cache
  entry since _passes_filters() uses set intersection (order-independent)
- Restore @app.get("/") root handler missing from this branch vs main

Co-authored-by: ZohaibHassan16 <zohaibhassan16@users.noreply.github.com>
Co-authored-by: KaifAhmad1 <mohammadk78600@gmail.com>
2026-04-19 18:07:42 +05:30
Mohd Kaif f165679c11 Merge pull request #480 from Sameer6305/fix/provenance-ego-graph
fix(provenance): include upstream ancestors + add direction classific…
2026-04-19 17:50:03 +05:30
17460edca9 docs(changelog): add provenance upstream traversal fix entry (#480, #470)
Co-authored-by: Sameer6305 <sameer6305@users.noreply.github.com>
Co-authored-by: KaifAhmad1 <mohammadk78600@gmail.com>
2026-04-19 17:36:58 +05:30
7e815920ac fix(provenance): resolve merge conflicts, fix session API, move schemas
- Resolve all merge conflict markers in provenance.py, app.py, .gitignore
- Revert broken session.get_nodes()/get_edges() to session.graph.nodes/edges
- Keep undirected=True ego_graph fix for upstream ancestor traversal
- Add direction field to ProvenanceEdge (upstream/downstream/lateral)
- Group lineage edges in _render_markdown by direction section
- Move ProvenanceNode/ProvenanceEdge/ProvenanceResponse to schemas.py
- Restore complete router import set in app.py (sparql, vocabulary, etc.)

Co-authored-by: Sameer6305 <sameer6305@users.noreply.github.com>
Co-authored-by: KaifAhmad1 <mohammadk78600@gmail.com>
2026-04-19 17:30:03 +05:30
Zohaib Hassnain 7f93eb7104 feat(explorer): calm and structurally declutter graph workspace 2026-04-19 01:13:37 +05:00
Ling Li eec3e8804a fix(providers): add missing verbose_mode assignment in generate_typed 2026-04-19 00:12:25 +08:00
Ling Li 9cb6073568 fix(providers): switch DeepSeekProvider from deepseek SDK to OpenAI client
DeepSeek API is compatible with OpenAI, use the openai SDK instead of
the unmaintained deepseek SDK for better compatibility.
2026-04-18 23:21:49 +08:00
Zohaib Hassnain 073c48882c chore 2 2026-04-17 21:24:45 +05:00
Zohaib Hassnain be86d1b5db chore: remove local benchmark helper 2026-04-17 21:23:51 +05:00
Zohaib Hassnain 6f93f429c4 perf(explorer): add indexed search for large graphs 2026-04-17 21:22:12 +05:00
Mohd KaifandCopilot bc683e7a34 Update semantica/explorer/routes/provenance.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-17 20:48:57 +05:30
Mohd KaifandCopilot cda5310949 Update semantica/explorer/routes/provenance.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-17 20:48:45 +05:30
Mohd KaifandCopilot 17f88ca600 Update semantica/explorer/routes/provenance.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-17 20:47:10 +05:30
Sameer6305 658de23357 fix: resolve merge conflicts with upstream main 2026-04-17 19:56:41 +05:30
Sameer6305 66e8964d22 fix(provenance): include upstream ancestors + add direction classification and markdown grouping 2026-04-17 19:33:12 +05:30
Mohd KaifandClaude Sonnet 4.6 892ff4b4a7 fix(export): fix OWLExporter Turtle invalid syntax and silent data-property omission (#478) (#479)
- Add _ttl_block() helper to accumulate all predicate-object pairs before
  writing, producing a single valid Turtle subject block terminated by one
  period — eliminates the bug where rdfs:subClassOf / domain / range were
  appended after a closed '.' block
- Add missing data_properties loop to _export_owl_turtle so
  owl:DatatypeProperty declarations are no longer silently dropped
- Add _escape_ttl_str() to escape quotes, backslashes, newlines, carriage
  returns, and tabs inside Turtle string literals (rdfs:label, rdfs:comment,
  owl:versionInfo)
- Unify optional-field null checks to consistent x = prop.get(); if x: pattern
- Add 43 tests in tests/export/test_owl_exporter.py covering syntax validity,
  data properties, string escaping, null handling, and header output
- Update CHANGELOG.md with [Unreleased] entry

Closes #478

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 15:08:59 +05:30
Mohd Kaif a88300d74f Merge pull request #477 from Hawksight-AI/feat/node-distance-semantics-472
feat(explorer): add node distance semantics to PathResponse (#472)
2026-04-16 19:46:42 +05:30
KaifAhmad1 390152c78c feat(explorer): add node distance semantics to PathResponse (#472)
- Extend PathResponse with hop_count (len(path)-1) and distance_band
  ("direct"|"near"|"mid-range"|"distant") as first-class API fields
- Add classify_path_distance() to semantica/utils/helpers.py as the
  single source of truth for hop-count thresholds; both the route and
  the visualizer import from it, eliminating duplicate threshold logic
- Populate hop_count and distance_band in find_path route via
  classify_path_distance(); remove local _classify_distance() copy
- Add highlight_path: list[str] param to KGVisualizer.visualize_network;
  path edges rendered as a distance-aware orange trace (opacity and
  stroke width scale from direct→distant: 1.0/4px to 0.35/1.5px)
- Fix bidirectional edge lookup: only forward pairs (A→B) along the
  path are added to path_edge_set; reverse back-edges in directed
  graphs are no longer incorrectly highlighted
- Add logger.warning when highlight_path contains node IDs absent from
  the layout position map, surfacing silent no-op mismatches
- Extend frontend PathResponse type in GraphInspectorPanel.tsx and
  GraphWorkspaceShell.tsx with hop_count: number and distance_band
  literal union to match the updated API contract
- Add 10 new tests: 2 API-level and 8 unit tests covering all four
  band boundaries (0, 1, 2, 3, 4, 6, 7, 20 hops); 104 explorer tests
  pass, 0 failures introduced
- Update CHANGELOG.md
2026-04-16 17:59:50 +05:30
Mohd Kaif 17602812f9 Merge pull request #476 from Hawksight-AI/feat/bidirectional-path-finding-469
feat(explorer): Bidirectional Path Finding in Knowledge Explorer
2026-04-16 15:29:47 +05:30
KaifAhmad1andClaude Sonnet 4.6 523b02083f feat(explorer): add bidirectional path finding with directed=false param (#469)
- PathFinder.bfs_shortest_path() and dijkstra_shortest_path() gain a
  directed: bool = True parameter. When False, a temporary undirected
  view (graph.to_undirected()) is used for traversal only; the original
  directed edges are preserved and returned in the response.
- _make_undirected_view() helper added to PathFinder; falls back safely
  for non-NetworkX graph types.
- GET /api/graph/node/{id}/path exposes ?directed=false query param.
- PathResponse gains a directed: bool field echoing the mode used.
- Route now returns 404 on empty path (previously returned 200 with
  path: []).
- 21 new tests: 12 unit (TestBidirectionalPathFinding) + 9 API-level
  (TestBidirectionalPathRoute). All 120 tests pass.
- CHANGELOG updated under [Unreleased].

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 15:20:12 +05:30
Mohd Kaif 952a4530f5 Merge pull request #474 from Hawksight-AI/kg
feat(kg): Native `KnowledgeGraph` Support in `KGVisualizer`
2026-04-16 12:26:19 +05:30
KaifAhmad1 2ce5067aa3 docs(changelog): add entry for #471 native KnowledgeGraph support in KGVisualizer 2026-04-16 12:18:38 +05:30
KaifAhmad1 d056e47ab7 feat(kg): add KnowledgeGraph dataclass and native KGVisualizer support (#471)
- Add semantica/kg/knowledge_graph.py with KnowledgeGraph dataclass
  (entities, relationships, metadata) plus __len__ and __bool__ helpers
- Export KnowledgeGraph from semantica/kg/__init__.py
- Add KGVisualizer._convert_knowledge_graph() for explicit, non-mutating
  conversion from KnowledgeGraph to internal dict format
- Route isinstance(graph, KnowledgeGraph) through _convert_knowledge_graph
  inside _normalize_graph so all five visualize_* entry points accept
  KnowledgeGraph directly without any manual conversion
- Add TestFormalKnowledgeGraphType (15 tests)

Closes #471
2026-04-16 12:15:59 +05:30
Mohd KaifandClaude Sonnet 4.6 8eafd2d024 fix(explorer): replace KeyError/ValueError with HTTPException across all routes, fix temporal pattern method, add SPA root handler (#463)
- routes/graph.py: raise HTTPException(404) for missing nodes; wrap path_finder call in try/except → HTTPException(404)
- routes/decisions.py: raise HTTPException(404) on chain, compliance, precedents sub-routes
- routes/annotations.py: raise HTTPException(404) on create (missing node) and delete (missing annotation)
- routes/enrich.py: raise HTTPException(404/503/422) for missing nodes, unavailable services, and extraction errors
- routes/temporal.py: fix TemporalPatternDetector method name detect_patterns → detect_temporal_patterns
- explorer/app.py: root / now serves built index.html when available, falls back to minimal HTML shell; add HTMLResponse import
- tests/explorer/test_explorer_api.py: test_extract accepts 503 (spacy/transformers not installed is a valid service state)

All 45 explorer API integration tests pass.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 00:19:19 +05:30
Mohd Kaif 7ba93f6772 Add initialization file for Claude 2026-04-14 23:25:20 +05:30
Mohd Kaif d466203761 Add initialization file for Claude skills 2026-04-14 23:24:39 +05:30
Mohd Kaif 730dea7911 Add initialization comment to semantica file 2026-04-14 23:24:00 +05:30
Mohd KaifandClaude Sonnet 4.6 47764c3033 Utils Explorer Welcome Message, Version Bump & Plugin README Overhaul (#462)
* Clarify plugin README install and usage steps

* feat(explorer): add welcome message to root endpoint and bump version to 0.4.0

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs(plugins): update all plugin READMEs for v0.4.0 with full platform list

- Rewrite main community guide with platform table (8 plugins), skills/agents
  inventory, Knowledge Explorer section, and per-platform install steps
- Add v0.4.0 badge and Knowledge Explorer section to VS Code, Cline,
  Continue, Windsurf, and OpenClaw READMEs
- Fix inconsistent tool count (12 → 17) across all READMEs
- Bump Python requirement from 3.8+ to 3.10+ across all plugins

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: add PR description for utils → main

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: remove PR_DESCRIPTION.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 20:32:04 +05:30
Mohd KaifandClaude Sonnet 4.6 055d2fd98d docs: reorganise README integrations and agentic frameworks sections (#461)
- Remove duplicate integrations table from bottom of README
- Move Agentic Frameworks section to top alongside AI tools table
- Show only Agno as supported; list LangChain, LangGraph, CrewAI, LlamaIndex, AutoGen, OpenAI Agents SDK, Google ADK as coming soon
- Add logo icons for all agentic frameworks matching existing plugin style

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 15:08:18 +05:30
Mohd Kaif ce66681715 Delete RELEASE_NOTES.md 2026-04-14 14:11:59 +05:30
Mohd Kaif 655b553262 Delete STRATEGIES_SUMMARY.md 2026-04-14 14:11:37 +05:30
Mohd KaifandClaude Sonnet 4.6 60bf8ec75e feat(integrations): add OpenClaw plugin and integration module (#460)
- Add integrations/openclaw/ with OpenClawKGTool (REST) and
  OpenClawMCPConfig (mcporter.json generator)
- Add plugins/.openclaw-plugin/ bundle (plugin.json, marketplace.json,
  README) with MCP + native tool support
- Add OpenClaw badge to README header
- Reorganize "Works With Every AI Tool" table into labeled groups:
  Native Plugin Bundle, MCP Server + Plugin, MCP Server, REST API

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 12:27:25 +05:30
Mohd Kaif a1478af9c4 Merge pull request #453 from Hawksight-AI/explorer
feat(explorer): add Semantica Knowledge Explorer UI with full feature…
2026-04-14 11:43:57 +05:30
Mohd Kaif ee4d6a9188 Update CHANGELOG with recent changes and fixes
Updated CHANGELOG to reflect recent fixes and security enhancements, including improvements to KGVisualizer and vulnerability fixes.
2026-04-14 11:21:27 +05:30
Mohd Kaif 2d00257ae5 Merge pull request #459 from Hawksight-AI/visualization
fix(visualization): Accept KnowledgeGraph objects in all `visualize_*` methods
2026-04-14 11:18:41 +05:30
KaifAhmad1andClaude Sonnet 4.6 e78ad7f819 fix(visualization): accept KnowledgeGraph objects in all visualize_* methods (closes #458)
KGVisualizer.visualize_network() (and sibling methods) only accepted a raw
dict. Passing a KnowledgeGraph object — the natural output of
GraphBuilder.build() — silently returned without rendering.

Added _normalize_graph() which duck-types the input: dicts pass through
unchanged; any object exposing .entities / .relationships attributes is
converted to the canonical dict form; anything else raises a clear
ProcessingError naming the offending type.

_normalize_graph() is called as the first statement in visualize_network(),
visualize_communities(), visualize_centrality(), visualize_entity_types(),
and visualize_relationship_matrix().

Also adds 21 tests in tests/visualization/test_kg_visualizer_normalize_graph.py
covering the helper directly, the end-to-end regression for #458, and
a guard that every public method routes through _normalize_graph.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 11:04:25 +05:30
Mohd KaifandClaude Sonnet 4.6 fdb347fe8a feat(cookbook): add Datalog-style reasoning end-to-end notebook (#457)
End-to-end example using DatalogReasoner, GraphBuilder, ContextGraph,
GraphAnalyzer, ExplanationGenerator, DatalogFact, and DatalogRule.
Covers ancestor query, KG dependency analysis, RBAC policy, and org hierarchy.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 21:43:04 +05:30
Mohd KaifandCopilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> 1cc2f6b93a Potential fix for pull request finding 'Wrong number of arguments in a class instantiation'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-04-13 17:52:23 +05:30
Mohd KaifandCopilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> 9e26d96b3c Potential fix for pull request finding 'Wrong number of arguments in a call'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-04-13 17:46:50 +05:30
Mohd KaifandCopilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> 7267425eb5 Potential fix for pull request finding 'Wrong number of arguments in a call'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-04-13 17:46:34 +05:30
Mohd KaifandCopilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> d9cf7b0088 Potential fix for pull request finding 'Unused global variable'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-04-13 17:46:14 +05:30
Mohd KaifandCopilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> 09666806da Potential fix for pull request finding 'Empty except'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-04-13 17:45:55 +05:30
Mohd KaifandCopilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> 9daddd8186 Potential fix for pull request finding 'Empty except'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-04-13 17:45:40 +05:30
Mohd KaifandCopilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> dc8d7ddb03 Potential fix for pull request finding 'Empty except'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-04-13 17:44:44 +05:30
Mohd KaifandCopilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> b34634c8b5 Potential fix for pull request finding 'Wrong number of arguments in a call'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-04-13 17:44:28 +05:30
Mohd KaifandCopilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> ee93c4bbe1 Potential fix for pull request finding 'Wrong name for an argument in a class instantiation'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-04-13 17:44:13 +05:30
Mohd KaifandCopilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> e4425818e4 Potential fix for pull request finding 'Empty except'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-04-13 17:43:56 +05:30
KaifAhmad1andClaude Sonnet 4.6 7b31304e1e feat(mcp): add modular MCP server package at repo root
Adds a fully self-contained `mcp/` package that exposes Semantica as a
Model Context Protocol server over stdio (JSON-RPC 2.0).

17 tools across 5 domains:
- Extraction: extract_entities, extract_relations, extract_all
- Decision intelligence: record_decision, query_decisions, find_precedents,
  get_causal_chain, analyze_decision_impact
- Knowledge graph: add_entity, add_relationship, search_graph,
  get_graph_summary, get_graph_analytics
- Reasoning: run_reasoning, abductive_reasoning
- Export & provenance: export_graph (JSON/CSV/GraphML/Parquet/RDF), get_provenance

4 resources: semantica://graph/summary, semantica://decisions/list,
semantica://schema/info, semantica://ontology/schema

Package layout:
  mcp/__init__.py + __main__.py  — entry points (python -m mcp)
  mcp/server.py                  — SemanticaMCPServer + stdio event loop
  mcp/session.py                 — lazy ContextGraph singleton
  mcp/schemas.py                 — JSON Schema for all 17 tool inputs
  mcp/tools/{extraction,decisions,graph,reasoning,export}.py
  mcp/resources/registry.py      — URI → handler map
  mcp/README.md                  — per-tool setup (Claude Code, Cursor, Windsurf,
                                   Cline, Continue, VS Code, Amazon Q)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 17:38:23 +05:30
KaifAhmad1andClaude Sonnet 4.6 ab93ec3e8f feat(plugins): add MCP server + 4 new plugin bundles (Windsurf, Cline, Continue, VS Code)
MCP Server (semantica/mcp_server.py):
- Full stdio-based MCP server compatible with Claude Desktop, Windsurf,
  Cline, Continue, VS Code, Roo Code, and any MCP-aware tool
- 12 tools: extract_entities, extract_relations, record_decision,
  query_decisions, find_precedents, get_causal_chain, add_entity,
  add_relationship, run_reasoning, get_graph_analytics, export_graph,
  get_graph_summary
- 3 resources: semantica://graph/summary, semantica://decisions/list,
  semantica://schema/info
- Lazy graph session with optional SEMANTICA_KG_PATH env var
- JSON-RPC 2.0 over stdin/stdout; run with: python -m semantica.mcp_server

New plugin bundles (each: plugin.json + marketplace.json + README.md):
- plugins/.windsurf-plugin/ — Windsurf MCP config + 17 skills + 3 agents
- plugins/.cline-plugin/    — Cline MCP config + 17 skills + 3 agents
- plugins/.continue-plugin/ — Continue MCP config + 17 skills + 3 agents
- plugins/.vscode-plugin/   — VS Code MCP config + 17 skills + 3 agents

Updated plugins/.claude-plugin/README.md:
- Platform support table expanded to 9 tools
- Full MCP server section: per-tool config snippets for Claude Desktop,
  Windsurf, Cline, Continue, VS Code; tool/resource reference tables;
  environment variables

Updated README.md:
- Hero line updated to mention MCP server
- Visual grid: Windsurf/VS Code/Cline/Continue → 'MCP server + plugin';
  Claude Desktop → 'MCP server'
- Plugin Bundles section: expanded table listing all 7 bundles with dirs
- New MCP Server section with quick-start snippet and tool/resource list
- Detailed integrations table: corrected connection types and config paths

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 16:59:45 +05:30
KaifAhmad1andClaude Sonnet 4.6 f2eb3e1608 docs(readme): accurate plugin/integration/API docs based on actual code
Tools grid:
- Claude Code/Cursor/Codex: 'Native plugin' (plugins/ dirs exist in repo)
- All other tools: 'REST API' (no MCP server impl in codebase — Semantica
  has an MCP CLIENT for ingesting from MCP servers, not an MCP server)
- Codex CLI added back (has real plugin bundle at plugins/.codex-plugin/)

Plugin Bundles section:
- Full table of all 17 skills with descriptions matching SKILL.md files
- Full table of all 3 agents (kg-assistant, decision-advisor, explainability)
- Hooks entry referencing plugins/hooks/hooks.json

MCP Client section:
- Correct framing: MCPClient in semantica/ingest/mcp_client.py pulls
  data FROM MCP servers into KG (not an MCP server itself)
- Code snippet + supported schemes

REST API Server section:
- Lists all 10 route modules from semantica/explorer/routes/ with paths
- WebSocket /ws endpoint
- Health check

Agno integration section:
- Expanded to table showing all 5 actual files in integrations/agno/
  with class names and descriptions matching source code

AI Coding Tools table:
- Corrected connection types and setup notes to match actual code

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 16:51:34 +05:30
KaifAhmad1andClaude Sonnet 4.6 898a660ca7 docs(readme): add explicit integrations table for all 16 AI tools + expand sections
- Add 'AI Coding Tools & IDEs' table under Integrations listing every
  tool from the visual grid with connection type and setup note:
  Claude Code, Cursor, Windsurf, Claude Desktop, VS Code, GitHub
  Copilot, Cline, Roo Code, Continue, Goose, Kilo Code, Aider,
  Amazon Q, Zed, Claude SDK, REST API (109 endpoints)
- Add Neo4j to Graph Databases list (was in modules but missing here)
- Add Email and Repository ingestors to Data Sources
- Expand LLM Providers: add Groq, HuggingFace, Ollama entries

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 16:25:02 +05:30
KaifAhmad1andClaude Sonnet 4.6 fb1e6a6d6e docs(readme): revise tools grid with accurate popular integrations
AI tools grid (removed Gemini CLI, Codex CLI; added VS Code, GitHub
Copilot, Continue, Amazon Q, Zed — all confirmed MCP-supporting tools
with significant user bases in 2026):
Row 1: Claude Code, Cursor, Windsurf, Claude Desktop, VS Code,
        GitHub Copilot, Cline, Roo Code
Row 2: Continue, Goose, Kilo Code, Aider, Amazon Q, Zed,
        Claude SDK, Any agent REST API

Agentic frameworks grid (added LangGraph and OpenAI Agents SDK, expanded
to 8 entries): Agno, LangChain, LangGraph, LlamaIndex, AutoGen, CrewAI,
OpenAI Agents SDK, Google ADK

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 16:11:00 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 8d0dce13c5 ci(deps): bump softprops/action-gh-release from 1 to 3 (#455)
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 1 to 3.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](https://github.com/softprops/action-gh-release/compare/v1...v3)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-version: '3'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-13 15:58:50 +05:30
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 61c8435bd7 ci(deps): bump actions/github-script from 8 to 9 (#454)
Bumps [actions/github-script](https://github.com/actions/github-script) from 8 to 9.
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/v8...v9)

---
updated-dependencies:
- dependency-name: actions/github-script
  dependency-version: '9'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-13 15:21:15 +05:30
KaifAhmad1andClaude Sonnet 4.6 0d7b9ca1df docs(readme): add Semantica Knowledge Explorer section to main README
- New '🖥️ Semantica Knowledge Explorer' section placed after Plugins,
  with a workspace-tab table (Graph, Timeline, Decisions, Registry,
  Entity Resolution, KG Overview, Ontology), a 4-line quick-start
  snippet, requirements line, and a pointer to explorer/README.md
- Added explorer/ row to the detailed Modules table with a link
- Added explorer/ bullet to the condensed Modules list

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 14:59:21 +05:30
KaifAhmad1andClaude Sonnet 4.6 aca15d3694 docs(explorer): replace default Vite README with full local setup guide
Covers requirements (Node 18+/Python 3.8+), backend start command,
npm install, dev server, all 6 workspace tabs, available npm scripts,
API/WebSocket proxy table, production build, troubleshooting steps,
and tech stack summary.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 14:30:27 +05:30
KaifAhmad1andClaude Sonnet 4.6 670027fd22 fix(explorer): resolve 3 code-review bugs in GraphWorkspace, DecisionWorkspace, and index.css
- GraphWorkspace: set isRunningPredictions=true before link-prediction fetch
  and false in finally block; pass isRunningPredictions prop to
  LazyGraphInspectorPanel so the inspector button disables and shows a
  spinner during the request (was declared but never wired — broke
  noUnusedLocals TypeScript build)

- DecisionWorkspace: add AbortController to the /api/decisions useEffect
  so the fetch is cancelled on unmount; add per-call AbortController to
  handleSelectDecision for /api/decisions/:id/chain; add res.ok guards
  before .json() on both fetches; encodeURIComponent on decision_id to
  prevent path-injection edge cases

- index.css: add missing @keyframes skeleton-pulse rule (0%/100% opacity
  0.45, 50% opacity 0.85) — KGOverviewTab skeletonBarStyle referenced
  this animation but it was never defined, leaving skeleton bars static

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 14:16:59 +05:30
KaifAhmad1andClaude Sonnet 4.6 3ea1283626 feat(explorer): add Semantica Knowledge Explorer UI with full feature set
## Folder & Project
- Renamed `semantica-explorer/` → `explorer/` (cleaner path)
- Browser tab title: `Semantica Knowledge Explorer`
- Brand pill: `SEM` → `SKE` (tooltip: Semantica Knowledge Explorer)
- Nav rail label: `Explore` → `Knowledge Explorer`
- package.json name: `semantica-knowledge-explorer`
- Downgraded Vite 8 → Vite 5 for Node v20.17.0 compatibility

## App Shell
- Dynamic per-workspace kicker labels replacing static "Workspace" pill:
  Graph Studio · Vocabulary Browser · Reasoning Engine · SPARQL Query ·
  Decision Intelligence · Knowledge Audit · Graph Governance

## Enrich Workspace — 2 new tabs
### Entity Resolution tab
- Similarity threshold slider (0.50–0.99)
- Run Dedup Scan → POST /api/enrich/dedup
- Flagged pairs list with colour-coded score bars (red/amber/green)
- Expandable inline diff: primary vs duplicate side-by-side
- One-click Merge → POST /api/enrich/merge with logEvent dispatch
- Dismiss per pair; Clear all button
- Merge history sidebar pulled live from Registry store

### Registry tab (Document Registry)
- Live chronological audit log of all KG mutations in-session
- Colour-coded op-type badges: IMPORT · MERGE · ADD NODE · ADD EDGE ·
  INFER · DELETE · EXPORT · VOCAB
- Filter pills to narrow by operation type
- Expandable JSON detail rows per entry
- Clear log button
- Entirely client-side via registryStore (no backend needed)

## Manage Workspace — 2 new tabs
### KG Overview tab
- Stats chips: total nodes, edges, graph density
- Node type breakdown bar chart (up to 8 types, colour-coded)
- Edge type breakdown bar chart from /api/graph/stats
- Top-10 most connected nodes ranked by degree
- Skeleton loading states + Refresh button

### Ontology Summary tab
- Read-only SKOS scheme tree (scheme → top concepts → narrower)
- Concept detail panel: labels, notation, description, narrower nav
- "Open Full Browser" button deep-links to Vocabulary Browser tab

## Decision Workspace polish
- CausalFlowDiagram: vertical node cards connected by relationship pills
- Outcome badges: colour-coded (green=approved, red=rejected, amber=deferred)
- Live filter input across decision ID, category, and outcome
- Animated skeleton loading while list fetches

## Graph Inspector polish
- PathFlowViz: clickable node chips connected by edge-type labels;
  clicking a chip focuses that node in the canvas
- Link Prediction button shows spinner while computing
- Empty states for path trace and candidate links sections

## Registry dispatch — WebSocket
- ADD_NODE events → logEvent("add-node", …) in GraphWorkspace WS handler
- ADD_EDGE events → logEvent("add-edge", …) in GraphWorkspace WS handler
- Import, Export, Merge already dispatched logEvent on API response

## Graph visibility overhaul
### Edge colours (were nearly transparent, now clearly visible)
- edgeBackbone:    rgba(…, 0.04)  → rgba(…, 0.38)
- edgeStructure:   rgba(…, 0.009) → rgba(…, 0.28)
- edgeInspection:  rgba(…, 0.026) → rgba(…, 0.48)
- Muted edges:     0.009–0.02    → 0.12–0.26
- Focus edges:     0.16          → 0.42

### Edge sizes
- default minSize: 0.18 → 0.9 (always at least 1 pixel wide)
- path minSize:    1.8  → 2.4
- inactive/muted:  hide:true → hide:false (dimmed not hidden)

### Node sizes
- default sizeMultiplier: 0.72 → 0.92
- default minSize:        0.68 → 3.5 (visible at all zoom levels)
- overview nodeScale:     0.66 → 0.88
- nodeTintMix (colour):   0.03 → 0.14
- nodeCoreMix (brightness): 0.52 → 0.72

### Label budget
- overview:   10  → 28 labels
- structure:  36  → 60 labels
- inspection: 80  → 120 labels

### Sigma settings
- renderEdgeLabels:        false → true  (relationship type on every edge)
- edgeLabelSize:           —    → 10
- labelRenderedSizeThreshold: 4 → 2
- labelDensity:            0.86 → 1.1
- hideLabelsOnMove:        true → false (labels stay visible while panning)
- hideEdgesOnMove:         true → false (edges stay visible while panning)
- minCameraRatio:          —    → 0.04 (prevents zooming inside a node)
- maxCameraRatio:          —    → 8    (graph stays visible when zoomed out)

### Zoom controls
- Added Zoom In (+) and Zoom Out (−) buttons to graph toolbar
- Smooth animated zoom via camera.animatedZoom / animatedUnzoom (200ms)
- Mouse scroll wheel clamped between minCameraRatio and maxCameraRatio

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 13:40:40 +05:30
Mohd Kaif b313604bde Merge pull request #452 from Hawksight-AI/security-enhancement
Security Enhancement — Fix 12 Vulnerabilities (CRITICAL → LOW)
2026-04-12 16:08:51 +05:30
Mohd KaifandCopilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> 5e6df93f64 Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression'
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2026-04-12 15:56:15 +05:30
Mohd KaifandCopilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> 920c0e55d5 Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression'
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2026-04-12 15:34:50 +05:30
Mohd KaifandCopilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> 7de2a2eb5e Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression'
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2026-04-12 14:53:52 +05:30
KaifAhmad1andClaude Sonnet 4.6 ce60acb294 docs(changelog): add security-enhancement PR entries to [Unreleased]
Documents all 12 vulnerability fixes (CRITICAL→LOW), 4 post-review bug
fixes, and CodeQL infrastructure changes under [Unreleased] following
the existing Keep a Changelog format.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 14:40:12 +05:30
KaifAhmad1andClaude Sonnet 4.6 4acdefd4b8 fix: address 4 post-review bugs from security-enhancement PR
fix(agent_memory): implement MemoryItem.to_dict() / from_dict() for safe JSON
  persistence — timestamps serialised via isoformat(), embeddings dropped (not
  JSON-safe, regenerated on demand); save() and load() now round-trip correctly
  without TypeError or AttributeError (Bug #1)

fix(sparql): add asyncio.Semaphore(_SPARQL_MAX_CONCURRENT=4) around graph.query
  so timed-out threads cannot exhaust the default ThreadPoolExecutor; add
  `truncated: bool` field to SparqlResponse so callers know when the 5 000-row
  cap was hit (Bug #2)

fix(export_import): trim _ALLOWED_IMPORT_EXTENSIONS to {.json, .csv} — the only
  formats the handler actually parses; removes .graphml/.gexf/.ttl/.rdf that
  passed the allowlist check but hit a hard 422 inside the handler (Bug #3)

fix(codeql): remove blanket rule-ID auto-dismiss job; replace with a commented
  template for pinning specific alert numbers — prevents future real alerts of
  the same rule being silently suppressed (Bug #4)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 14:35:30 +05:30
Mohd KaifandCopilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> a16cb9c468 Potential fix for pull request finding 'Unused import'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-04-12 14:17:55 +05:30
Mohd KaifandCopilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> 1bdaad9c59 Potential fix for pull request finding 'Unused import'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-04-12 14:14:20 +05:30
Mohd KaifandCopilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> db00a3d1ad Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression'
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2026-04-12 14:14:04 +05:30
KaifAhmad1andClaude Sonnet 4.6 d8b8ae634b security: fix 12 vulnerabilities across CRITICAL→LOW severity
Closes CodeQL alerts #12, #13, #14, #15, #16, #17, #18

CRITICAL
- fix(media_parser): replace eval() with fractions.Fraction for fps parsing (CWE-95)
- fix(agent_memory): replace pickle serialization with JSON to prevent RCE (CWE-502)

HIGH
- fix(snowflake_ingestor): parameterize LIMIT/OFFSET, validate ORDER BY with regex,
  reject semicolons in WHERE to prevent SQL injection (CWE-89)
- fix(rdf_parser): add defusedxml XXE protection for RDF/XML format parsing (CWE-611)
- fix(server): add CORSMiddleware, security response headers middleware
  (X-Content-Type-Options, X-Frame-Options, X-XSS-Protection, Referrer-Policy,
  Permissions-Policy, HSTS), and global error handler (CWE-346, CWE-200)
- fix(explorer/app): narrow CORS to specific methods/headers, redact exception
  messages in HTTP error handlers, enforce 64 KB WebSocket message size cap (CWE-346)

MEDIUM
- fix(graph): replace free-text algorithm param with _PathAlgorithm enum (CWE-20)
- fix(vocabulary): validate uploaded file extensions against allowlist (CWE-434)
- fix(llm_extraction): json.dumps() all user content in LLM prompts to block
  prompt-injection attacks (CWE-1336)
- fix(pipeline_validator): replace __import__("collections") with proper import (CWE-95)

LOW
- fix(sparql): cap results at 5 000 rows and enforce 30-second query timeout (CWE-400)
- fix(export_import): validate file extension + enforce 50 MB upload limit (CWE-434)

CodeQL / scanning
- feat(codeql): add .github/codeql/codeql-config.yml to exclude generated
  cookbook HTML bundles (Plotly + MapLibre) from JS scanning
- feat(codeql): extend dismiss-fixed-alerts job with all new rule IDs
  (py/path-injection, py/polynomial-redos, js/incomplete-url-substring-sanitization,
  js/insecure-randomness, js/prototype-pollution-utility)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 13:41:31 +05:30
Mohd Kaif cdb26aab3b Merge pull request #420 from ZohaibHassan16/feat/explorer-vocab-ui
feat(explorer): add initial UI for SKOS Vocabulary Workspace
2026-04-11 20:56:42 +05:30
KaifAhmad1andClaude Sonnet 4.6 f4db4469ba chore: untrack remaining generated Vite bundles from git
semantica/static/ is already in .gitignore but the 19 newly-hashed
build artifacts introduced by the main merge were still tracked.
Runs git rm --cached to complete the untracking so future frontend
builds do not create dirty working-tree diffs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 20:11:38 +05:30
KaifAhmad1andClaude Sonnet 4.6 98453cab5d docs(changelog): add PR #420 explorer blocker and security fix entries
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 19:54:21 +05:30
KaifAhmad1andClaude Sonnet 4.6 1fde71768d fix(explorer): resolve blockers and significant issues from PR #420 review
Blockers fixed:
- Rename DockerFile → Dockerfile (case-sensitive fix for Linux CI/Docker)
- Fix Docker CMD: semantica.server:app → semantica.explorer.app:app
- Add module-level app = create_app() so uvicorn can reference the ASGI app
- Remove pre-built static assets from git; add semantica/static/ to .gitignore

Security / correctness fixes:
- Fix CORS default from "*" to localhost:5173 (explicit env var still overrides)
- Add guard to get_ws_manager() — returns 503 instead of AttributeError when unset
- Restrict SPARQL endpoint to read-only query types (SELECT/ASK/CONSTRUCT/DESCRIBE)
- Add 10 MB upload size limit to vocabulary import route
- Add JSON-LD format auto-detection (.jsonld / .json-ld / .json) in vocabulary import

Code quality fixes:
- Replace O(N) annotation scan in create_annotation with O(1) get_annotation() lookup
- Add get_annotation(ann_id) method to GraphSession
- Add self-loop guard in batchMergeEdges (graph has allowSelfLoops: false)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 19:13:40 +05:30
Mohd Kaif e00bdfe8d4 Merge branch 'main' into feat/explorer-vocab-ui 2026-04-11 18:01:45 +05:30
Mohd Kaif 9e31e8d746 Merge pull request #451 from Hawksight-AI/triplet-store
fix(triplet-store): resolve entity/class/property IRIs against ontolo…
2026-04-11 17:09:39 +05:30
KaifAhmad1 9d680d4369 docs(changelog): add TripletStore namespace IRI resolution and regression fix entries for PR #447 2026-04-11 17:04:08 +05:30
KaifAhmad1 9d0744e20e fix(triplet-store): coerce non-string IDs and guard known vocabulary prefixes in _resolve_iri
Bug 1 — non-string IDs crash store():
_resolve_iri() called .startswith() directly on local, causing AttributeError
when upstream graph builders emit integer entity/relationship IDs. Fixed by
coercing local to str() at entry; None/empty returns a safe urn: sentinel.

Bug 2 — prefixed W3C terms mis-resolved under base_uri:
Values like 'owl:Thing' and 'xsd:date' were not recognised as absolute IRIs
and with base_uri set were rewritten to e.g. https://example.com/owl:Thing,
corrupting standard OWL/XSD IRIs in stored triples. Fixed by adding a known-
prefix expansion table (xsd/rdf/rdfs/owl/skos/semantica) that is checked before
base_uri is applied, matching the same prefix map already used in blazegraph_store.

Added 5 regression tests covering both bugs: integer IDs with/without base_uri,
owl:Thing domain/range, xsd:date range, and rdfs:/skos: parent class expansion.
2026-04-11 16:51:39 +05:30
KaifAhmad1 0b52b715dc fix(triplet-store): resolve entity/class/property IRIs against ontology namespace base_uri (Fixes #447)
store() was minting urn:entity:, urn:class:, and urn:property: URIs for every
bare local name, even when the ontology carried a namespace.base_uri. This made
instance data and ontology class data irreconcilable in SPARQL joins.

- Extract base_uri from ontology.namespace.base_uri (or ontology.uri as fallback)
- Introduce _resolve_iri(local, kind) closure that appends the local name to
  base_uri when present, keeping urn: fallback only when no base URI is known
- Apply _resolve_iri consistently for entity URIs, entity types, relationship
  predicates, ontology class URIs, parent class URIs, property URIs, and
  property domain/range URIs
- Explicit entity.uri values are never overridden
- Added 9 regression tests in TestTripletStoreOntologyNamespace covering all
  IRI expansion paths, urn: fallback, explicit URI passthrough, top-level uri
  key fallback, and trailing-slash safety
2026-04-11 15:47:42 +05:30
Mohd Kaif 745927d674 Merge pull request #450 from Hawksight-AI/triplet-store
Fix Blazegraph literal serialization in bulk loader (Fixes #448)
2026-04-11 15:28:50 +05:30
KaifAhmad1 af401c8566 docs(changelog): add Blazegraph literal serialization and SPARQL injection fix entries for PR #448 2026-04-11 15:21:42 +05:30
KaifAhmad1 2e2dae558f fix(blazegraph): expand prefixed datatypes and validate lang/datatype metadata
- Added _resolve_datatype_iri() to expand known prefixes (xsd/rdf/rdfs/owl/skos)
  to full IRIs instead of blindly wrapping in <...>, fixing invalid SPARQL like
  <xsd:integer>
- Validated language tags against RFC 5646 regex to prevent SPARQL injection
  via metadata["lang"] values containing whitespace or punctuation
- Validated datatype IRIs for whitespace/special characters before interpolation
- Extended test suite from 7 to 15 cases covering prefix expansion, injection
  rejection, and all accepted input forms
2026-04-11 15:16:15 +05:30
KaifAhmad1 3a1a798107 Fix Blazegraph literal serialization in bulk loader (Fixes #448) 2026-04-11 14:58:51 +05:30
Mohd Kaif a4b17dd72b Merge pull request #449 from Hawksight-AI/ontology
fix(ontology): preserve user-facing schema fields in OWL generation\n…
2026-04-11 14:09:09 +05:30
Zohaib Hassnain dfd7785cc1 feat: overhaul graph explorer visuals and loading flow 2026-04-11 03:19:28 +05:00
Zohaib Hassnain 6e6b190da1 perf(explore): split GraphWorkspace into lazy subchunks 2026-04-09 14:22:15 +05:00
Zohaib Hassnain e03ba5685c feat(graph): add opt-in exploration effects panel 2026-04-09 13:59:44 +05:00
Zohaib Hassnain 1af17f3398 feat: productized explorer workspace 2026-04-09 03:17:12 +05:00
Zohaib Hassnain c964e11d38 feat(graph): add rich element rendering system 2026-04-09 02:42:40 +05:00
Zohaib Hassnain 8829aa5ce2 feat(graph): add plugin host for graph tools 2026-04-09 02:21:35 +05:00
Zohaib Hassnain 102274c668 refactor(graph): add typed theme system and first-class behavior modules 2026-04-09 01:30:33 +05:00
Zohaib Hassnain 38b766298e feat(explorer): harden knowledge explorer backend and frontend, polish dashboard UX 2026-04-07 02:04:17 +05:00
Zohaib Hassnain c0f106dc1e feat(explorer): implement Phase 4 & 5 : Temporal Engine and Power-User Suite
Phase 4: Time Travel & Decisions
- Integrated Temporal Scrubber (TimelinePanel.tsx) with high-speed WebGL filtering.
- Implemented Decision Tree Viewer with recursive causal chain visualization.

Phase 5: Power-User Tools
- Built SPARQL Engine with Monaco Editor UI and rdflib backend integration.
- Implemented PROV-O Lineage swimlanes using React Flow with custom layout math.
- Developed side-by-side Entity Diff/Merge tool with Amber-highlighting.
- Expanded Import/Export suite for robust JSON/CSV dataset ingestion.
- Refactored temporal routes for delta-only ID snapshots.
2026-04-05 15:05:53 -07:00
ZohaibHassan16 98a2cf9490 feat(ui): complete graph visualization overhaul
This commit transforms the raw 150k-element graph into a high-performance, exploratory UI:

- Implemented Universal Sizing (logarithmic scale based on node degree) and a Procedural Color Mapper (string hashing) to automatically size and colorize categorical data.
- Built the 'Focus Mode' engine using Sigma reducers. Hovering or clicking a node instantly isolates it and its 1-hop neighbors while muting the canvas, eliminating visual noise.
- Applied an enterprise-grade visual style, featuring deep radial background gradients, structural grid overlays, and a sliding glassmorphism metadata HUD.
- Shifted from DOM-bound state mutations to direct WebGL render pipelines to maintain visual performance.
2026-04-03 00:20:52 +05:00
ZohaibHassan16 9203c2d684 feat(ui): complete phase 2 massive graph rendering and api alignment 2026-04-01 23:51:42 +05:00
ZohaibHassan16 719063e781 Merge branch 'fix/cg-pagination' into feat/explorer-vocab-ui 2026-04-01 12:18:08 +05:00
ZohaibHassan16 60c00fb5c2 feat(explorer): implement phase 1: single-server deployment and dockerization 2026-03-31 13:47:49 +05:00
ZohaibHassan16 1b277dcdd7 feat(ui): wire TanStack query, update UI types, and configure Vite proxy 2026-03-31 04:43:25 +05:00
ZohaibHassan16 3065f3c00e feat(explorer): add initial UI for SKOS Vocabulary Workspace 2026-03-31 02:24:10 +05:00
758 changed files with 154065 additions and 303531 deletions
+19
View File
@@ -0,0 +1,19 @@
# Checkov configuration.
# Cloud Run false-positives (CKV_K8S_21/28/30) are suppressed via per-file
# inline checkov:skip comments in deploy/gcp/cloudrun-service.yaml rather than
# globally here, so future real Kubernetes manifests are not silently exempted.
#
# The knowledge-explorer Helm chart's unconditional templates (service.yaml,
# deployment.yaml, configmap.yaml) set metadata.namespace to .Release.Namespace,
# which is only bound at `helm install`/`helm template` time. Checkov's helm
# framework renders the chart without a namespace override, so it always
# resolves to "default" and trips CKV_K8S_21 even though the chart is
# namespace-agnostic by design. Suppressed via metadata annotations
# (checkov.io/skip1 / runterrascan.io/skip) on each resource's metadata.annotations,
# as both Checkov and Terrascan require K8s/Helm resource-level annotations
# rather than file-header comments.
# deployment.yaml additionally suppresses AC_K8S_0080 and CKV_K8S_31 (seccomp) via
# metadata.annotations on both the Deployment resource and the pod template:
# the seccomp profile is set correctly in values.yaml and only resolves once
# Helm actually renders `toYaml`, which static template scanning does not do.
skip-check: []
+1
View File
@@ -0,0 +1 @@
# Initialization
+1
View File
@@ -0,0 +1 @@
# Intialization
+1
View File
@@ -0,0 +1 @@
# Initialization
+103
View File
@@ -0,0 +1,103 @@
# Start with a tiny Docker context and opt in only files used by Dockerfile.
*
!Dockerfile
!.dockerignore
!pyproject.toml
!README.md
!LICENSE
!MANIFEST.in
!semantica/
!semantica/**
!integrations/
!integrations/**
!explorer/
!explorer/**
# VCS, local config, and secrets.
.git
.git/**
.github
.github/**
.claude
.claude/**
.codex
.codex/**
.agents
.agents/**
.env
.env.*
*.env
# Python build/test/cache artifacts.
__pycache__
**/__pycache__
*.py[cod]
.pytest_cache
.pytest_cache/**
.mypy_cache
.mypy_cache/**
.ruff_cache
.ruff_cache/**
.tox
.tox/**
.venv
.venv/**
venv
venv/**
coverage
coverage/**
htmlcov
htmlcov/**
*.egg-info
*.egg-info/**
build
build/**
dist
dist/**
# Frontend dependency/build artifacts.
node_modules
node_modules/**
explorer/node_modules
explorer/node_modules/**
explorer/dist
explorer/dist/**
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Local outputs and large generated samples.
logs
logs/**
*.log
*.tmp
*.bak
*.backup
tests
tests/**
explorer/tests
explorer/tests/**
docs
docs/**
site
site/**
.mkdocs_cache
.mkdocs_cache/**
cookbook
cookbook/**
examples
examples/**
demo_assets
demo_assets/**
demo_out
demo_out/**
demo_out_*
demo_out_*/**
outputs
outputs/**
pytest-cache-files-*
pytest-cache-files-*/**
test_data
test_data/**
sample_data
sample_data/**
+8
View File
@@ -1,3 +1,11 @@
# Line endings — force LF so Mintlify/Linux CI parses frontmatter correctly
* text=auto eol=lf
*.md text eol=lf
*.json text eol=lf
*.yml text eol=lf
*.yaml text eol=lf
*.py text eol=lf
# Linguist documentation and generated files
# This ensures GitHub language statistics reflect the core Python code
+11
View File
@@ -0,0 +1,11 @@
name: "Semantica CodeQL Config"
# Exclude auto-generated notebook exports and bundled third-party JS.
# Files in cookbook/**/*.html are self-contained Plotly/MapLibre bundles
# produced by Jupyter nbconvert — they embed minified third-party libraries
# (Plotly, MapLibre GL JS) whose internal patterns trigger false-positive JS
# alerts (js/incomplete-url-substring-sanitization, js/insecure-randomness,
# js/prototype-pollution-utility). These are not application code.
paths-ignore:
- "cookbook/**/*.html"
- "cookbook/**/*.js"
+7
View File
@@ -70,6 +70,13 @@ updates:
- "dependencies"
- "github-actions"
- "ci"
# All our actions are SHA-pinned with a "# vX" comment; Dependabot
# resolves the new tag's SHA and updates both the pin and the comment
# together, so this stays the source of truth (no separate script needed).
groups:
github-actions:
patterns:
- "*"
# Optional dependencies (separate schedule for stability)
- package-ecosystem: "pip"
+2
View File
@@ -1,3 +1,5 @@
> **Before you submit:** make sure you followed the [issue workflow in CONTRIBUTING.md](https://github.com/semantica-agi/semantica/blob/main/CONTRIBUTING.md#-working-on-an-existing-issue) — wait for the issue to be assigned to you before opening a PR, to avoid duplicate work.
## Description
<!-- Provide a clear description of your changes -->
+70
View File
@@ -0,0 +1,70 @@
#!/usr/bin/env bash
# Verifies that every third-party GitHub Action referenced in
# .github/workflows/*.yml and .github/workflows/*.yaml is pinned to a full
# commit SHA (not a mutable tag
# or branch), and that any pin's trailing "# vX" comment still matches what
# that tag resolves to today.
#
# Fails closed on purpose:
# - a `uses:` line pinned to anything other than a 40-hex-char SHA is a
# hard failure, not a skip - this is what stops a newly-added mutable
# tag (e.g. `uses: some/action@v1`) from slipping past unnoticed.
# - a tag that can't be resolved via the GitHub API (rate limit, deleted
# tag, typo) is also a hard failure rather than a warning - an
# unverifiable pin is exactly the failure mode this check exists to
# catch, so it must not pass silently.
set -uo pipefail
fail=0
checked=0
# Pattern for a third-party uses: line — stored in a variable so bash's
# [[ =~ ]] parser never sees literal \" or \' escapes, which cause a
# "syntax error in conditional expression: unexpected token )" at runtime.
# Semantics: optional leading quote, owner/repo, optional subpath, @ref,
# optional trailing quote; quote chars excluded from the ref capture group.
USES_PATTERN='uses:[[:space:]]+["'"'"']?([A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+)(/[^[:space:]@"'"'"']+)?@([^[:space:]"'"'"']+)["'"'"']?'
while IFS=: read -r file lineno content; do
# Local composite actions (./x) and Docker image refs (docker://...) use a
# different pinning mechanism and aren't in scope here.
[[ "$content" =~ uses:\ +\./ ]] && continue
[[ "$content" =~ uses:\ +docker:// ]] && continue
if [[ "$content" =~ $USES_PATTERN ]]; then
repo="${BASH_REMATCH[1]}"
ref="${BASH_REMATCH[3]}"
checked=$((checked + 1))
if [[ ! "$ref" =~ ^[0-9a-fA-F]{40}$ ]]; then
echo "::error file=$file,line=$lineno::$repo is pinned to '$ref', not a full commit SHA. Mutable tags/branches can be silently re-pointed (see the LiteLLM/Trivy 2026 incident) - pin to a commit SHA instead."
fail=1
continue
fi
sha="$ref"
if [[ "$content" =~ \#[[:space:]]*([^[:space:]]+)[[:space:]]*$ ]]; then
tag="${BASH_REMATCH[1]}"
else
echo "::warning file=$file,line=$lineno::$repo@$sha has no trailing '# vX' comment recording which tag it corresponds to - add one for auditability."
continue
fi
resolved=$(gh api "repos/$repo/commits/$tag" --jq '.sha' 2>/dev/null)
if [[ -z "$resolved" ]]; then
echo "::error file=$file,line=$lineno::Could not resolve '$repo@$tag' via the GitHub API (rate limit, deleted tag, or typo). Treating as unverifiable = failure."
fail=1
continue
fi
if [[ "$resolved" != "$sha" ]]; then
echo "::error file=$file,line=$lineno::$repo is pinned to $sha but tag '$tag' now resolves to $resolved. Update the pin or the comment."
fail=1
else
echo "OK $repo@$tag -> $sha ($file:$lineno)"
fi
fi
done < <(grep -rHn "uses:" .github/workflows/*.yml .github/workflows/*.yaml 2>/dev/null)
echo "Checked $checked action reference(s)."
exit $fail
+5 -12
View File
@@ -1,13 +1,6 @@
name: Semantica Performance Suite
on:
push:
branches: [main]
paths-ignore:
- 'docs/**'
- 'mkdocs.yml'
- 'requirements-docs.txt'
- '**/*.md'
workflow_dispatch:
permissions:
@@ -20,14 +13,14 @@ jobs:
steps:
- name: Checkout Code
uses: actions/checkout@v4
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
fetch-depth: 0
- name: Set up Python 3.12
uses: actions/setup-python@v5
- name: Set up Python 3.11
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
with:
python-version: "3.12"
python-version: "3.11"
cache: 'pip'
- name: Install Dependencies
@@ -50,7 +43,7 @@ jobs:
# pytest-benchmark --storage file://benchmarks/results --benchmark-compare
- name: Upload Benchmark Results
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
if: always()
with:
name: benchmark-report-${{ github.run_id }}
+62 -7
View File
@@ -1,28 +1,83 @@
name: CI
permissions:
contents: read
on:
push:
branches: [main]
paths-ignore:
- 'docs/**'
- 'mkdocs.yml'
- 'requirements-docs.txt'
- 'docs_check.py'
- '**/*.md'
pull_request:
branches: [main]
paths-ignore:
- 'docs/**'
- 'mkdocs.yml'
- 'requirements-docs.txt'
- 'docs_check.py'
- '**/*.md'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
with:
python-version: '3.11'
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: explorer/package-lock.json
- name: Install Explorer frontend dependencies
working-directory: explorer
run: npm ci
- name: Test Explorer frontend
working-directory: explorer
run: |
npm run test:graph-store
npm run test:graph-workspace
npm run test:plugin-registry
- name: Build Explorer frontend
working-directory: explorer
run: npm run build
- name: Install pinned Python dependencies
run: |
pip install -r requirements-ci.txt
- name: Verify requirements-ci.txt is up to date
run: |
pip install uv==0.12.1
# Re-resolve with the committed file as a constraint: upstream package
# releases must NOT fail CI (deps only change when pyproject.toml
# changes intentionally). Compare only version lines (pkg==ver),
# ignoring the -c constraint comments and the `\` line continuations
# that --generate-hashes emits.
uv pip compile pyproject.toml --python-version 3.11 --extra all \
--constraint requirements-ci.txt -o /tmp/requirements-ci-check.txt
diff \
<(grep -E '^[a-zA-Z0-9._-]+==' requirements-ci.txt | sed 's/ \\$//') \
<(grep -E '^[a-zA-Z0-9._-]+==' /tmp/requirements-ci-check.txt)
- run: pip install build
- run: python -m build
# wheel is build-time only (not in requirements-ci.txt) — install the
# same pinned version [build-system] declares so --no-isolation works.
- run: pip install wheel==0.48.0
- name: Build package (no isolation — pinned deps)
run: python -m build --no-isolation
- name: Verify Explorer frontend is packaged
run: |
python - <<'PY'
import zipfile
from pathlib import Path
wheels = list(Path("dist").glob("*.whl"))
assert wheels, "No wheel was built"
with zipfile.ZipFile(wheels[0]) as wheel:
names = set(wheel.namelist())
assert "semantica/static/index.html" in names, "Explorer index.html missing from wheel"
assert any(name.startswith("semantica/static/assets/") for name in names), "Explorer assets missing from wheel"
print("Explorer frontend is packaged")
PY
+52 -41
View File
@@ -20,19 +20,49 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
# The CodeQL bundle download (github/codeql-action/init's "Setup CodeQL
# tools" step) streams a ~1GB tarball from GitHub's release CDN and
# does not retry on a transient connection reset (ECONNRESET) itself
# (github/codeql-action, unresolved as of v4 / CLI 2.26.1: the HTTP
# error is retryable but isn't retried internally). Since a `uses:`
# step can't be wrapped by a shell-level retry action, attempt init
# up to 3 times; each retry is a fresh download attempt with no
# meaningful state carried over from a failed attempt.
- name: Initialize CodeQL (attempt 1)
id: codeql-init-1
uses: github/codeql-action/init@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4
continue-on-error: true
with:
languages: python
queries: security-and-quality
config-file: .github/codeql/codeql-config.yml
- name: Initialize CodeQL (attempt 2)
id: codeql-init-2
if: steps.codeql-init-1.outcome == 'failure'
uses: github/codeql-action/init@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4
continue-on-error: true
with:
languages: python
queries: security-and-quality
config-file: .github/codeql/codeql-config.yml
- name: Initialize CodeQL (attempt 3)
id: codeql-init-3
if: steps.codeql-init-2.outcome == 'failure'
uses: github/codeql-action/init@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4
with:
languages: python
queries: security-and-quality
config-file: .github/codeql/codeql-config.yml
- name: Autobuild
uses: github/codeql-action/autobuild@v4
uses: github/codeql-action/autobuild@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
uses: github/codeql-action/analyze@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4
with:
category: "/language:python"
upload: false
@@ -42,45 +72,26 @@ jobs:
# Uploads results only when Default Setup is not active.
# If Default Setup is still enabled, this step skips gracefully
# instead of failing the workflow with HTTP 409.
uses: github/codeql-action/upload-sarif@v4
uses: github/codeql-action/upload-sarif@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4
with:
sarif_file: ${{ steps.codeql.outputs.sarif-output }}
category: "/language:python"
wait-for-processing: true
continue-on-error: true
dismiss-fixed-alerts:
name: Dismiss Fixed Security Alerts
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
steps:
- name: Dismiss resolved CodeQL alerts via API
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
run: |
FIXED_PATTERNS=(
"py/clear-text-logging-sensitive-data"
"py/incomplete-url-substring-sanitization"
"actions/missing-workflow-permissions"
)
# Fetch all open code scanning alerts
ALERTS=$(gh api repos/$REPO/code-scanning/alerts \
--jq '.[] | {number: .number, rule: .rule.id, state: .state}' \
-X GET -f state=open -f per_page=100)
for PATTERN in "${FIXED_PATTERNS[@]}"; do
ALERT_NUMS=$(echo "$ALERTS" | jq -r \
"select(.rule == \"$PATTERN\") | .number")
for NUM in $ALERT_NUMS; do
echo "Dismissing alert #$NUM ($PATTERN) — fixed in security-enhancement PR"
gh api repos/$REPO/code-scanning/alerts/$NUM \
-X PATCH \
-f state=dismissed \
-f dismissed_reason="won't fix" \
-f dismissed_comment="Fixed in PR security-enhancement: code changes remove the vulnerability. Dismissing because Default Setup prevents Advanced Setup SARIF upload." \
&& echo " ✓ Alert #$NUM dismissed" \
|| echo " ⚠ Could not dismiss alert #$NUM (may already be closed)"
done
done
# NOTE: Auto-dismissal by rule-id is intentionally removed.
# Dismissing every alert that matches a rule ID would silently suppress
# future real vulnerabilities of the same type. The alerts below were
# individually triaged and dismissed manually in the security-enhancement
# PR (alerts #12#18). New alerts must be reviewed and dismissed by hand,
# or will auto-close when the underlying code no longer triggers them.
#
# If you need to dismiss a specific known-safe alert, pin its alert NUMBER
# here and remove it once CodeQL stops reporting it naturally. Example:
#
# PINNED_ALERT_NUMBERS=(12 13 14 15 16 17 18)
# for NUM in "${PINNED_ALERT_NUMBERS[@]}"; do
# gh api repos/$REPO/code-scanning/alerts/$NUM \
# -X PATCH -f state=dismissed -f dismissed_reason="false positive" \
# -f dismissed_comment="<reason>"
# done
+88
View File
@@ -0,0 +1,88 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
#
# Microsoft Security DevOps (MSDO) is a command line application which integrates static analysis tools into the development cycle.
# MSDO installs, configures and runs the latest versions of static analysis tools
# (including, but not limited to, SDL/security and compliance tools).
#
# The Microsoft Security DevOps action is currently in beta and runs on the windows-latest queue,
# as well as Windows self hosted agents. ubuntu-latest support coming soon.
#
# For more information about the action , check out https://github.com/microsoft/security-devops-action
#
# Please note this workflow do not integrate your GitHub Org with Microsoft Defender For DevOps. You have to create an integration
# and provide permission before this can report data back to azure.
# Read the official documentation here : https://learn.microsoft.com/en-us/azure/defender-for-cloud/quickstart-onboard-github
name: "Microsoft Defender For Devops"
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
schedule:
- cron: '43 17 * * 6'
permissions:
contents: read
security-events: write
jobs:
MSDO:
# currently only windows-latest is supported
runs-on: windows-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6
with:
dotnet-version: |
5.0.x
6.0.x
- name: Run Microsoft Security DevOps
uses: microsoft/security-devops-action@08976cb623803b1b36d7112d4ff9f59eae704de0 # v1.12.0
id: msdo
with:
# checkov is intentionally excluded from this MSDO step.
# MSDO 0.215.0's guardian.cmd wrapper treats checkov's exit code 1
# (emitted whenever any violation is found, even below the active severity
# threshold) as a fatal "tool error" and breaks the build even when
# "Active results: 0" and "Found no breaking results." The .checkov.yaml
# soft-fail setting is never read by the guardian wrapper.
# IaC security scanning continues below in this same MSDO job identity.
# That preserves the existing GitHub code-scanning configuration while
# avoiding the guardian.cmd/checkov exit-code bug in the MSDO wrapper.
tools: eslint,templateanalyzer,terrascan
- name: Upload results to Security tab
uses: github/codeql-action/upload-sarif@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4
with:
sarif_file: ${{ steps.msdo.outputs.sarifFile }}
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
with:
python-version: "3.12"
- name: Install Checkov
run: python -m pip install checkov==3.3.1
- name: Run Checkov
shell: pwsh
env:
PYTHONUTF8: "1"
run: |
New-Item -ItemType Directory -Force reports | Out-Null
checkov --directory . --framework kubernetes helm dockerfile github_actions secrets bicep arm --soft-fail --output sarif --output-file-path reports/checkov.sarif
if (-not (Test-Path reports/checkov.sarif)) {
$sarif = Get-ChildItem -Path reports -Recurse -Filter *.sarif | Select-Object -First 1
if ($null -eq $sarif) { throw "Checkov did not produce a SARIF file" }
Copy-Item $sarif.FullName reports/checkov.sarif
}
- name: Upload Checkov results to Security tab
uses: github/codeql-action/upload-sarif@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4
if: always()
with:
sarif_file: reports/checkov.sarif
+34 -46
View File
@@ -1,80 +1,68 @@
name: Build and Deploy Documentation
# This workflow builds the documentation site and deploys it to GitHub Pages
# It runs when changes are pushed to the 'docs' folder on the main branch
on:
push:
branches: [main]
paths:
- 'docs/**'
- 'mkdocs.yml'
- 'requirements-docs.txt'
- 'docs_check.py'
- 'CHANGELOG.md'
- 'RELEASE.md'
release:
types: [published]
pull_request:
branches: [main]
paths:
- 'docs/**'
- 'docs_check.py'
workflow_dispatch:
# Permissions needed to deploy to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Prevent concurrent deployments
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
build:
name: Build Documentation
validate:
name: Validate Documentation
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
with:
python-version: '3.11'
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
with:
node-version: '20'
- run: python docs_check.py
- name: Install documentation dependencies
deploy:
name: Build and Deploy to GitHub Pages
if: github.event_name != 'pull_request'
runs-on: ubuntu-latest
needs: validate
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
with:
node-version: '20'
- name: Export static site
run: |
python -m pip install --upgrade pip
pip install -r requirements-docs.txt
cd docs
npx mintlify export --output ../export.zip
cd ..
unzip -q export.zip -d site
- name: Build documentation
# Builds the static site using MkDocs
run: mkdocs build --strict
- uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6
- name: Check for broken links
# Optional: checks if any links in the docs are broken
run: |
pip install linkchecker || echo "Skipping link check"
if [ -d "site" ]; then
linkchecker site/ --check-extern || echo "Link check completed"
fi
continue-on-error: true
- name: Setup Pages
uses: actions/configure-pages@v6
continue-on-error: true
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
- uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5
with:
path: ./site
deploy:
name: Deploy to GitHub Pages
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v5
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5
+55 -7
View File
@@ -5,21 +5,69 @@ on:
tags: ['v*']
permissions:
contents: write
id-token: write
contents: read
jobs:
release:
runs-on: ubuntu-latest
environment: pypi
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
permissions:
contents: write # for the GitHub Release
id-token: write # for PyPI Trusted Publishing (OIDC) and attestation signing
attestations: write # for SLSA build provenance
# If you add another job to this workflow, give it its own explicit
# `permissions:` block rather than relying on the workflow-level default
# above (contents: read) - do not widen the workflow-level default.
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
with:
python-version: '3.11'
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: explorer/package-lock.json
- name: Build Explorer frontend
working-directory: explorer
run: |
npm ci
npm run build
# Install the pinned dependency set (with hashes) so the sdist/wheel
# build runs against the same versions CI tests against.
- name: Install pinned build dependencies
run: pip install -r requirements-ci.txt
- run: pip install build
- run: python -m build
- uses: softprops/action-gh-release@v1
# wheel is build-time only (not in requirements-ci.txt) — install the
# same pinned version [build-system] declares so --no-isolation works.
- run: pip install wheel==0.48.0
- name: Build package (no isolation — pinned deps)
run: python -m build --no-isolation
- name: Verify Explorer frontend is packaged
run: |
python - <<'PY'
import zipfile
from pathlib import Path
wheels = list(Path("dist").glob("*.whl"))
assert wheels, "No wheel was built"
with zipfile.ZipFile(wheels[0]) as wheel:
names = set(wheel.namelist())
assert "semantica/static/index.html" in names, "Explorer index.html missing from wheel"
assert any(name.startswith("semantica/static/assets/") for name in names), "Explorer assets missing from wheel"
print("Explorer frontend is packaged")
PY
- name: Attest build provenance
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4
with:
subject-path: 'dist/*'
- uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3
with:
files: dist/*
- uses: pypa/gh-action-pypi-publish@release/v1
- uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
+124 -70
View File
@@ -18,6 +18,9 @@ on:
- 'requirements-docs.txt'
- '**/*.md'
permissions:
contents: read
jobs:
security-scan:
runs-on: ubuntu-latest
@@ -25,35 +28,71 @@ jobs:
contents: read
security-events: write
actions: read
# Needed for the "Comment PR with Security Results" step below. Safe on
# pull_request (not pull_request_target): GitHub always forces a
# read-only token for PRs from forks regardless of this permission.
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- name: Set up Python
uses: actions/setup-python@v4
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
with:
python-version: '3.11'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
# Install the pinned dependency set FIRST so Safety scans Semantica's
# exact CI/release dependency tree (requirements-ci.txt is generated
# from pyproject.toml extras, so this covers the project's real deps).
pip install -r requirements-ci.txt
# Tooling AFTER the pinned set: installing safety/bandit/semgrep/jq
# first lets the pinned requirements overwrite their transitive deps
# (e.g. rich), which breaks the safety CLI at runtime.
pip install safety bandit semgrep jq
- name: Run Safety Check (Package Vulnerabilities)
run: |
safety check --json --output safety-report.json || true
# NOTE: Safety 3.x repurposed --output to select a console format
# (json/text/screen/...), not a file path. Writing JSON to a file
# now requires --save-json; the previous `--output safety-report.json`
# usage was silently invalid and never produced a report.
safety check --save-json safety-report.json || true
# Guard 1: fail loudly if Safety exited before writing a report at all
# (network error, API auth failure, tool crash). Without this check a
# missing or empty file causes jq to fall back to "0", making a broken
# scanner indistinguishable from a clean scan.
if [ ! -s safety-report.json ]; then
echo "::error::Safety scan produced no report (safety-report.json is missing or empty). Treating as failure — check for network errors, API auth failures, or Safety crashes in the logs above."
exit 1
fi
echo "Checking for package vulnerabilities..."
# Count vulnerabilities safely
VULNS=$(safety check --json --output /dev/stdout 2>/dev/null | jq '.vulnerabilities | length' 2>/dev/null || echo "0")
# No || echo "0" fallback: if jq fails (malformed JSON, missing key,
# vulnerabilities:null) VULNS will be empty or "null" so guard 2 below
# catches it rather than silently treating the broken report as zero.
VULNS=$(jq '.vulnerabilities | length' safety-report.json 2>/dev/null)
# Guard 2: ensure VULNS is a non-negative integer before the -gt
# comparison. "null" (missing/null key) or "" (jq parse failure) would
# cause bash's -gt to throw an arithmetic error and fall through to the
# success branch — the same silent-pass bug as a missing file.
if ! [[ "$VULNS" =~ ^[0-9]+$ ]]; then
echo "::error::Safety report exists but 'vulnerabilities' is missing or non-numeric (got: '${VULNS}'). The report may be malformed or Safety may have written an error-only JSON. Treating as failure."
exit 1
fi
if [ "$VULNS" -gt 0 ]; then
echo "❌ Security vulnerabilities found: $VULNS"
echo "CI will fail to prevent merging of vulnerable dependencies"
echo ""
echo "Vulnerability details:"
safety check || true
jq -r '.vulnerabilities[] | "- \(.package_name)==\(.analyzed_version): \(.vulnerability_id) (\(.CVE // "no CVE assigned"))"' safety-report.json || true
exit 1
else
echo "✅ No security vulnerabilities found"
@@ -96,9 +135,10 @@ jobs:
fi
- name: Upload Security Reports
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: security-reports
retention-days: 14
path: |
safety-report.json
bandit-report.json
@@ -106,77 +146,91 @@ jobs:
- name: Comment PR with Security Results
if: github.event_name == 'pull_request'
uses: actions/github-script@v8
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
with:
script: |
const fs = require('fs');
// Read safety report
let safetyResults = '';
try {
const safetyData = JSON.parse(fs.readFileSync('safety-report.json', 'utf8'));
if (safetyData.vulnerabilities && safetyData.vulnerabilities.length > 0) {
safetyResults = `## Safety Vulnerabilities Found\\n`;
safetyData.vulnerabilities.forEach(vuln => {
safetyResults += `- **${vuln.package}**: ${vuln.advisory}\\n`;
});
} else {
safetyResults = '## No Safety Vulnerabilities Found\\n';
// Renders one tool's findings as a section. `items` is already
// the list of pre-formatted "- `thing` in `where`" strings; this
// just handles the found/not-found/report-missing framing and
// collapses long lists into a <details> block so the comment
// doesn't turn into a wall of text.
function renderSection(title, reportPath, parse) {
let data;
try {
data = JSON.parse(fs.readFileSync(reportPath, 'utf8'));
} catch (e) {
return [
`### ${title}`,
`⚠️ No report found at \`${reportPath}\` — the scan may have failed before producing output. Check the job logs.`,
].join('\n');
}
} catch (e) {
safetyResults = '## Safety scan completed\\n';
}
// Read bandit report
let banditResults = '';
try {
const banditData = JSON.parse(fs.readFileSync('bandit-report.json', 'utf8'));
if (banditData.results && banditData.results.length > 0) {
const highIssues = banditData.results.filter(issue => issue.issue_severity === 'HIGH');
if (highIssues.length > 0) {
banditResults = `## High Severity Security Issues Found\\n`;
highIssues.forEach(issue => {
banditResults += `- **${issue.test_name}**: ${issue.filename}:${issue.line_number}\\n`;
});
} else {
banditResults = '## No High Severity Security Issues Found\\n';
}
} else {
banditResults = '## No Bandit Issues Found\\n';
const items = parse(data);
if (items.length === 0) {
return [`### ${title}`, `✅ No findings.`].join('\n');
}
} catch (e) {
banditResults = '## Bandit scan completed\\n';
}
// Read semgrep report
let semgrepResults = '';
try {
const semgrepData = JSON.parse(fs.readFileSync('semgrep-report.json', 'utf8'));
if (semgrepData.results && semgrepData.results.length > 0) {
semgrepResults = `## Security Patterns Found\\n`;
semgrepData.results.slice(0, 10).forEach(issue => {
semgrepResults += `- **${issue.rule_id}**: ${issue.path}\\n`;
});
if (semgrepData.results.length > 10) {
semgrepResults += `- ... and ${semgrepData.results.length - 10} more\\n`;
}
const lines = [`### ${title}`, `Found **${items.length}**.`, ''];
const shown = items.slice(0, 15);
if (items.length > 15) {
lines.push('<details>', '<summary>Show all findings</summary>', '');
lines.push(...items);
lines.push('', '</details>');
} else {
semgrepResults = '## No Security Patterns Found\\n';
lines.push(...shown);
}
} catch (e) {
semgrepResults = '## Semgrep scan completed\\n';
return lines.join('\n');
}
// Create summary comment
const comment = `# 🔒 Security Scan Results\\n\\n${safetyResults}\\n\\n${banditResults}\\n\\n${semgrepResults}\\n\\n---\\n\\n*This security scan runs automatically on source-code PRs and bi-weekly (skipped for doc/markdown-only changes).*\\n\\n📊 **Security Policy**: CI fails on vulnerabilities and HIGH severity issues.`;
// Post comment with error handling
const safetySection = renderSection(
'Safety — dependency vulnerabilities',
'safety-report.json',
(data) => (data.vulnerabilities || []).map(
(v) => `- \`${v.package_name}==${v.analyzed_version}\`: ${v.vulnerability_id}` +
(v.CVE ? ` (${v.CVE})` : '') + ` — ${v.advisory || 'no advisory text'}`
)
);
const banditSection = renderSection(
'Bandit — HIGH-severity code issues',
'bandit-report.json',
(data) => (data.results || [])
.filter((issue) => issue.issue_severity === 'HIGH')
.map((issue) => `- \`${issue.test_name}\` in \`${issue.filename}:${issue.line_number}\``)
);
const semgrepSection = renderSection(
'Semgrep — static analysis patterns',
'semgrep-report.json',
(data) => (data.results || []).map(
(issue) => `- \`${issue.check_id}\` in \`${issue.path}:${issue.start?.line ?? '?'}\``
)
);
const comment = [
'# 🔒 Security Scan Results',
'',
safetySection,
'',
banditSection,
'',
semgrepSection,
'',
'---',
'',
'*This security scan runs automatically on source-code PRs and bi-weekly (skipped for doc/markdown-only changes).*',
'',
'📊 **Security Policy**: CI fails on Safety vulnerabilities and Bandit HIGH-severity findings. Semgrep findings above are informational and do not block merge.',
].join('\n');
try {
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: comment
body: comment,
});
console.log('✅ Security comment posted successfully');
} catch (error) {
+25 -4
View File
@@ -4,6 +4,12 @@ on:
schedule:
- cron: '0 0 * * 1'
workflow_dispatch:
pull_request:
branches: [main]
paths:
- 'pyproject.toml'
- 'requirements-ci.txt'
- '.github/workflows/security.yml'
permissions:
contents: read
@@ -12,10 +18,25 @@ jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
with:
python-version: '3.11'
# Upgrade first: actions/setup-python's baked-in setuptools has been
# behind known-vulnerable floors before (e.g. PYSEC-2026-3447 /
# setuptools 75.1.0), so don't trust the preinstalled one.
- run: python -m pip install --upgrade pip setuptools
# Audit the pinned dependency set (requirements-ci.txt is compiled from
# pyproject.toml with --extra all — the same coverage as the [all]
# extra, minus the Linux-only gpu set — so this keeps scan parity with
# CI/release builds without a time-dependent resolution). This is the
# fix for PYSEC-2024-38 (#869): the bare-env job never had fastapi or
# python-multipart installed to look at.
- run: pip install -r requirements-ci.txt
# PR runs gate on findings, since they're scoped to actual
# pyproject.toml changes under review. The schedule/workflow_dispatch
# runs stay non-blocking until a full pass over pre-existing findings
# across the whole [all] tree has been done.
- run: pip install pip-audit
- run: pip-audit
continue-on-error: true
- run: pip-audit -r requirements-ci.txt
continue-on-error: ${{ github.event_name != 'pull_request' }}
+28
View File
@@ -0,0 +1,28 @@
name: Verify Action Pins
on:
pull_request:
paths:
- '.github/workflows/**'
- '.github/scripts/verify-action-pins.sh'
push:
branches: [main]
paths:
- '.github/workflows/**'
- '.github/scripts/verify-action-pins.sh'
schedule:
- cron: '0 3 * * 1' # weekly, in case an upstream tag is deliberately moved
workflow_dispatch:
permissions:
contents: read
jobs:
verify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- name: Verify pinned action SHAs match their tag comments
env:
GH_TOKEN: ${{ github.token }}
run: bash .github/scripts/verify-action-pins.sh
BIN
View File
Binary file not shown.
+108
View File
@@ -0,0 +1,108 @@
# Semantica — Architecture
Complete data flow from every source type to every final output, and the decision intelligence lifecycle.
---
## Full Data Pipeline
Every source, every processing step, every final artifact — in one diagram.
```mermaid
flowchart TD
%% ── SOURCES ──────────────────────────────────────────────────────
subgraph SRC["🗂️ Sources (semantica.ingest)"]
direction LR
F["📄 Files\nPDF · DOCX · PPTX · HTML\nTXT · CSV · JSON · Excel · XML"]
W["🌐 Web\nPages · RSS/Atom Feeds\nPublic REST APIs"]
DB["🗃️ Databases\nPostgreSQL · MySQL · SQLite\nOracle · DuckDB · MongoDB"]
CL["☁️ Cloud\nSnowflake · Google Drive\nElasticsearch · HuggingFace"]
RT["⚡ Streams\nKafka · RabbitMQ\nAWS Kinesis · Pulsar"]
DV["🛠️ Dev\nGit Repos · Email IMAP/POP3\nMCP Resources · Parquet · Pandas"]
end
%% ── INGEST ───────────────────────────────────────────────────────
F --> FI["FileIngestor"]
W --> WI["WebIngestor"]
DB --> DI["DBIngestor"]
CL --> PI["ParquetIngestor\nSnowflakeIngestor"]
RT --> SI["StreamIngestor"]
DV --> RI["RepoIngestor\nEmailIngestor · MCPIngestor"]
FI & WI & DI & PI & SI & RI --> RAW[/"📦 Raw Documents"/]
%% ── PARSE ────────────────────────────────────────────────────────
RAW --> PRS["🔍 Parse (semantica.parse)\nDocumentParser · StructuredDataParser\nCodeParser · WebParser · EmailParser"]
PRS --> NRM["🧹 Normalize (semantica.normalize)\nTextNormalizer · EntityNormalizer\nDateNormalizer · NumberNormalizer · DataCleaner"]
NRM --> SPL["✂️ Split (semantica.split)\nentity_aware · relation_aware\ngraph_based · ontology_aware · hierarchical"]
%% ── EXTRACT ──────────────────────────────────────────────────────
SPL --> EXT["🔬 Extract (semantica.semantic_extract)\nNamedEntityRecognizer · RelationExtractor\nEventDetector · TripletExtractor · CoreferenceResolver"]
EXT --> CFT["⚠️ Conflict Detection (semantica.conflicts)\nConflictDetector · ConflictResolver · SourceTracker"]
CFT --> DDP["🔁 Deduplication (semantica.deduplication)\nDuplicateDetector · EntityMerger"]
DDP --> KGB["🕸️ KG Construction (semantica.kg)\nGraphBuilder · EntityResolver\nBiTemporalFact · TemporalGraphQuery"]
KGB --> KG[/"🗺️ Knowledge Graph\nnodes · edges · temporal facts · provenance"/]
%% ── INTELLIGENCE LAYER ───────────────────────────────────────────
KG --> ONT["Ontology (semantica.ontology)\nOntologyGenerator · OntologyValidator\nOWL · SHACL · SKOS"]
KG --> RSN["Reasoning (semantica.reasoning)\nReteEngine · DatalogReasoner\nSPARQLReasoner · ExplanationGenerator"]
KG --> PRV["Provenance (semantica.provenance)\nProvenanceManager · W3C PROV-O"]
KG --> CTX["Context & Decisions (semantica.context)\nContextGraph · AgentContext\nDecisionRecorder · CausalChainAnalyzer · PolicyEngine"]
ONT & RSN & PRV & CTX --> EKG[/"🗃️ Enriched KG\n+ ontology · inferences · provenance · decisions"/]
%% ── STORAGE ──────────────────────────────────────────────────────
EKG --> VS["Vector Store (semantica.vector_store)\nFAISS · Qdrant · Weaviate · Milvus · Pinecone · PgVector\nHybrid Search · RRF Fusion"]
EKG --> GS["Graph Store (semantica.graph_store)\nNeo4j · FalkorDB · Apache AGE · Amazon Neptune"]
%% ── OUTPUTS ──────────────────────────────────────────────────────
VS & GS --> EXP["📦 Export (semantica.export)\nRDF Turtle · JSON-LD · N-Triples · OWL · SHACL\nParquet · Cypher · ArangoDB AQL · GraphML · CSV · HTML"]
VS & GS --> VIZ["📊 Visualize (semantica.visualization)\nKGVisualizer · OntologyVisualizer\nEmbeddingVisualizer · TemporalVisualizer"]
EKG --> SVC["🔌 Services\nREST API 100+ endpoints · MCP Server 10+ tools\nCLI 50+ commands · Knowledge Explorer"]
```
---
## Decision Intelligence Lifecycle
```mermaid
flowchart LR
subgraph RECORD["1️⃣ Record"]
R1["record_decision()\ncategory · scenario\nreasoning · outcome\nconfidence · metadata"]
end
subgraph LINK["2️⃣ Link"]
L1["add_causal_relationship()\ntriggers · enables\ncauses · precedes"]
end
subgraph QUERY["3️⃣ Query"]
Q1["find_similar_decisions()\nSemantic precedent search"]
Q2["trace_decision_chain()\nFull causal ancestry"]
Q3["analyze_decision_impact()\nDownstream influence map"]
end
subgraph GOVERN["4️⃣ Govern"]
G1["check_decision_rules()\nPolicy evaluation\nCompliance gate"]
end
subgraph AUDIT["5️⃣ Audit Export"]
A1["W3C PROV-O · CSV · JSON\nRegulator-ready audit trail"]
end
RECORD -->|decision_id| LINK
LINK -->|causal graph| QUERY
QUERY -->|results| GOVERN
GOVERN -->|signed-off decisions| AUDIT
```
---
*→ [README](README.md) · [Docs](https://docs.getsemantica.ai/) · [Cookbook](https://github.com/semantica-agi/semantica/tree/main/cookbook)*
> Note: `Docs` and `Cookbook` are external resources maintained outside this file and may change over time. If a link is unavailable, refer to the repository `README.md` and in-repo documentation as canonical fallbacks.
+1226 -2035
View File
File diff suppressed because it is too large Load Diff
+84 -13
View File
@@ -2,20 +2,58 @@
Thank you for your interest in contributing! Every contribution, no matter how small, is valuable. 🎉
**Give us a Star** • 🍴 **[Fork Semantica](https://github.com/Hawksight-AI/semantica/fork)** • 💬 **Join our [Discord](https://discord.gg/sV34vps5hH)**
**Give us a Star** • 🍴 **[Fork Semantica](https://github.com/semantica-agi/semantica/fork)** • 💬 **Join our [Discord](https://discord.gg/sV34vps5hH)**
> **New to contributing?** Start with a [`good first issue`](https://github.com/Hawksight-AI/semantica/labels/good%20first%20issue) or join our [Discord](https://discord.gg/sV34vps5hH) community.
> **New to contributing?** Start with a [`good first issue`](https://github.com/semantica-agi/semantica/labels/good%20first%20issue) or join our [Discord](https://discord.gg/sV34vps5hH) community.
---
## 🚀 Quick Start
1. Find a [`good first issue`](https://github.com/Hawksight-AI/semantica/labels/good%20first%20issue)
2. [Fork Semantica](https://github.com/Hawksight-AI/semantica/fork) & clone the repository
1. Find a [`good first issue`](https://github.com/semantica-agi/semantica/labels/good%20first%20issue)
2. [Fork Semantica](https://github.com/semantica-agi/semantica/fork) & clone the repository
3. Make your changes
4. Submit a pull request!
**Need help?** Join [Discord](https://discord.gg/sV34vps5hH) or [GitHub Discussions](https://github.com/Hawksight-AI/semantica/discussions)
**Need help?** Join [Discord](https://discord.gg/sV34vps5hH) or [GitHub Discussions](https://github.com/semantica-agi/semantica/discussions)
---
## 🗂️ Working on an Existing Issue
If you want to work on an open GitHub issue, please follow these steps to keep things coordinated and avoid duplicate effort:
1. **Check the issue.** Look at the issue's assignees and recent comments. If someone is already actively working on it, consider a different issue or ask in the comments whether help is welcome.
2. **Comment if you'd like the issue reserved.** Leaving a comment like *"I'd like to take this on"* is the fastest way to get assigned, but it isn't required — maintainers can also assign an issue directly to a contributor (e.g., based on recent activity in the repo) without waiting for a comment first.
3. **Wait for assignment.** A maintainer will assign the issue when appropriate, whether or not a comment was left. Please wait for this before investing significant time in implementation, as priorities and approaches can shift.
4. **Create a branch and implement.** Once assigned, fork the repository (if you haven't already), create a dedicated branch, and begin your work.
```bash
git checkout -b fix/short-description # or feature/short-description
```
5. **Open a focused PR and link the issue.** When you're ready, open a pull request and reference the issue in the description (e.g., `Closes #123`). Keep the PR scoped to the work described in the issue.
> **Why this matters:** Assignment (with or without a comment) helps maintainers track who is working on what and prevent two contributors from solving the same problem independently. It also gives you a chance to align on the expected approach before writing code.
Not sure where to start? Try a [`good first issue`](https://github.com/semantica-agi/semantica/labels/good%20first%20issue) or ask in [Discord](https://discord.gg/sV34vps5hH).
---
## 🔀 Duplicate PRs & Issue Priority
When more than one pull request targets the same issue, maintainers triage using this order of priority. These rules decide between PRs that are otherwise following the [assignment workflow above](#-working-on-an-existing-issue) — opening a PR before being assigned doesn't grant priority on its own, and an unassigned PR can still be closed as a duplicate once someone else is assigned to the issue.
1. **Contributor-raised issue with an existing PR.** If the person who opened the issue has also opened a PR for it, that PR is prioritized (they still need to be assigned before it's merged).
2. **Maintainer-raised issue with a claim comment.** If we opened the issue and someone has commented asking to work on it, we assign it to them and check their PR before picking up any other PR for the same issue.
3. **No prior assignment or comment.** If multiple PRs exist and no one was assigned or claimed the issue first, priority goes to whichever contributor has the most consistent activity in the repo over the last 60 days (e.g., merged PRs, substantive reviews, or issue triage participation) — not just PR volume.
4. **Late duplicate PRs.** If a PR is opened after another contributor has already been assigned to the issue, we close the duplicate early rather than let it sit open, and point the author to another open issue (or ask them to check `main` for newly opened ones). This avoids contributors spending time updating a PR that won't be merged.
5. **Overlapping scope.** If a PR covers multiple issues, or there's genuine overlap between competing PRs, maintainers discuss it on [Discord](https://discord.gg/sV34vps5hH) before deciding rather than resolving it unilaterally.
**Why this matters:** it keeps triage predictable, avoids wasted contributor effort on PRs that won't merge, and helps retain active contributors.
---
@@ -78,7 +116,7 @@ Thank you for your interest in contributing! Every contribution, no matter how s
**What:** Report bugs you find
**How:** Use the [bug report template](https://github.com/Hawksight-AI/semantica/issues/new?template=bug_report.md)
**How:** Use the [bug report template](https://github.com/semantica-agi/semantica/issues/new?template=bug_report.md)
**Include:** Description, steps to reproduce, expected vs actual behavior, environment details
@@ -88,7 +126,7 @@ Thank you for your interest in contributing! Every contribution, no matter how s
**What:** Suggest new features or improvements
**How:** Use the [feature request template](https://github.com/Hawksight-AI/semantica/issues/new?template=feature_request.md)
**How:** Use the [feature request template](https://github.com/semantica-agi/semantica/issues/new?template=feature_request.md)
**Include:** Problem statement, proposed solution, use cases
@@ -108,7 +146,7 @@ Thank you for your interest in contributing! Every contribution, no matter how s
**What:** Help others in the community
**Where:** [Discord](https://discord.gg/sV34vps5hH), [GitHub Discussions](https://github.com/Hawksight-AI/semantica/discussions)
**Where:** [Discord](https://discord.gg/sV34vps5hH), [GitHub Discussions](https://github.com/semantica-agi/semantica/discussions)
**Examples:** Answer questions, review PRs, share your projects
@@ -135,12 +173,12 @@ Thank you for your interest in contributing! Every contribution, no matter how s
### 1. Fork & Clone
First, [fork Semantica](https://github.com/Hawksight-AI/semantica/fork) on GitHub, then:
First, [fork Semantica](https://github.com/semantica-agi/semantica/fork) on GitHub, then:
```bash
git clone https://github.com/your-username/semantica.git
cd semantica
git remote add upstream https://github.com/Hawksight-AI/semantica.git
git remote add upstream https://github.com/semantica-agi/semantica.git
```
### 2. Set Up Environment
@@ -157,6 +195,39 @@ pip install -e ".[dev]"
pre-commit install
```
### Pinned CI dependencies
`requirements-ci.txt` pins every transitive dependency at exact versions so CI,
security scans, and release builds install the same packages every run (the
Python equivalent of `explorer/package-lock.json` + `npm ci`). It is a
**separate build environment**: every package carries a SHA-256 hash
(`--generate-hashes`), so installs are reproducible and supply-chain safe —
never install into your local dev environment from it.
Regenerate it after changing `pyproject.toml` dependencies:
```bash
pip install uv==0.12.1
uv pip compile pyproject.toml --python-version 3.11 --extra all --generate-hashes -o requirements-ci.txt
```
The `all` extra is the repo's cross-platform dependency set (GPU extras like
`faiss-gpu`/`cupy` are excluded and installed separately on Linux — see
`pyproject.toml`). Keep the pinned `uv` version in sync with CI so regeneration
is deterministic.
CI's staleness check re-resolves with the committed lockfile as a constraint
and compares version lines only: upstream package releases never fail CI —
the lockfile changes only when `pyproject.toml` changes intentionally.
CI fails if `requirements-ci.txt` is stale relative to `pyproject.toml`
(the version-line comparison detects new/removed/changed dependencies).
Build-system pins: `[build-system].requires` is pinned to exact versions
(`setuptools==84.0.0`, `wheel==0.48.0`) and release builds run
`python -m build --no-isolation` against the lockfile — no unpinned
build-time isolation anywhere.
### 3. Create Branch
```bash
@@ -327,8 +398,8 @@ result = instance.method()
## 🆘 Getting Help
- 💬 [Discord](https://discord.gg/sV34vps5hH) - Real-time chat
- 💭 [GitHub Discussions](https://github.com/Hawksight-AI/semantica/discussions) - Q&A
- 🐛 [GitHub Issues](https://github.com/Hawksight-AI/semantica/issues) - Bug reports
- 💭 [GitHub Discussions](https://github.com/semantica-agi/semantica/discussions) - Q&A
- 🐛 [GitHub Issues](https://github.com/semantica-agi/semantica/issues) - Bug reports
**Before asking:** Check existing documentation, search issues/discussions, review cookbook examples
@@ -363,4 +434,4 @@ This project follows a [Code of Conduct](CODE_OF_CONDUCT.md). Be respectful and
Every contribution matters - whether it's a single line of code, a typo fix, a helpful answer, or a bug report. We appreciate you! 🙏
**Give us a Star** • 🍴 **[Fork Semantica](https://github.com/Hawksight-AI/semantica/fork)** • 💬 **Join our [Discord](https://discord.gg/sV34vps5hH)**
**Give us a Star** • 🍴 **[Fork Semantica](https://github.com/semantica-agi/semantica/fork)** • 💬 **Join our [Discord](https://discord.gg/sV34vps5hH)**
+1 -1
View File
@@ -101,7 +101,7 @@ When using the all-contributors bot, use these codes:
- `infra` - Infrastructure
- `maintenance` - Maintenance
See [all-contributors specification](https://allcontributors.org/docs/en/emoji-key) for complete list.
See [all-contributors specification](https://github.com/all-contributors/all-contributors#emoji-key) for complete list.
---
+40
View File
@@ -0,0 +1,40 @@
# syntax=docker/dockerfile:1
FROM node:26-alpine AS frontend-builder
WORKDIR /app
COPY explorer/package*.json ./explorer/
WORKDIR /app/explorer
RUN npm ci
COPY explorer/ ./
RUN mkdir -p /app/semantica && npm run build
FROM python:3.14-slim AS runtime
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
FALKORDB_HOST=falkordb \
FALKORDB_PORT=6379 \
ALLOWED_ORIGINS=http://localhost:8000,http://127.0.0.1:8000
WORKDIR /app
RUN groupadd --system semantica \
&& useradd --system --gid semantica --home-dir /app --shell /usr/sbin/nologin semantica
COPY pyproject.toml README.md LICENSE MANIFEST.in ./
COPY semantica/ ./semantica/
COPY integrations/ ./integrations/
COPY --from=frontend-builder /app/semantica/static ./semantica/static
RUN pip install --no-cache-dir ".[explorer]" \
&& chown -R semantica:semantica /app
USER semantica
EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
CMD python -c "import json, urllib.request; data=json.load(urllib.request.urlopen('http://127.0.0.1:8000/api/health', timeout=3)); raise SystemExit(0 if data.get('status') == 'ok' else 1)"
CMD ["python", "-m", "uvicorn", "semantica.explorer.app:app", "--host", "0.0.0.0", "--port", "8000"]
+1
View File
@@ -0,0 +1 @@
recursive-include semantica/static *
+1459 -602
View File
File diff suppressed because it is too large Load Diff
+150 -246
View File
@@ -1,282 +1,186 @@
# Semantica v0.3.0 Release Notes
# Semantica 0.5.0 Release Notes
**Released:** 2026-03-10
**PyPI:** `pip install semantica`
**Tag:** [v0.3.0](https://github.com/Hawksight-AI/semantica/releases/tag/v0.3.0)
**Classification:** Production/Stable
## 🎉 Major Release: Distance Intelligence & Ontology Hub Complete
> First stable, full public release of Semantica. Covers everything shipped across three release stages: 0.3.0-alpha (2026-02-19), 0.3.0-beta (2026-03-07), and 0.3.0 stable (2026-03-10).
**Release Date:** May 11, 2026
**Version:** 0.5.0
---
## Contributors
## 🚀 **MAJOR HIGHLIGHTS**
| Contributor | Role |
|------------|------|
| [@KaifAhmad1](https://github.com/KaifAhmad1) | Lead maintainer — context graph, decision intelligence, KG algorithms, semantic extraction, pipeline, provenance, bug fixes, release management |
| [@ZohaibHassan16](https://github.com/ZohaibHassan16) | Deduplication v2 suite (candidate generation, two-stage scoring, semantic dedup), incremental/delta processing, benchmark suite |
| [@Sameer6305](https://github.com/Sameer6305) | Apache AGE backend, PgVector store, Snowflake connector, Apache Arrow export |
| [@tibisabau](https://github.com/tibisabau) | ArangoDB AQL export, Apache Parquet export |
| [@d4ndr4d3](https://github.com/d4ndr4d3) | ResourceScheduler deadlock fix |
### **Distance Intelligence Framework** (PR #502, @KaifAhmad1)
- **Embedding Cache Optimization**: Per-session graph revision-based caching for 10x+ performance improvement
- **Advanced UI Features**: Ego mode, overlays, heatmap, and path inspector
- **Semantic Neighborhood Search**: Context-aware similarity with proximity metrics
- **Distance Matrix API**: N×N semantic distance calculations with caching
### **Complete Ontology Hub Suite** (PR #517, @KaifAhmad1 @ZohaibHassan16)
- **Alignments Tab** (PR #524): Cross-ontology alignment authoring with ML suggestions
- **Health Dashboard** (PR #524): Quality scoring across 5 dimensions with issue tracking
- **SHACL Studio** (PR #524): Interactive shape generation and validation
- **Visual Editor** (PR #519): Canvas-based ontology authoring without hand-coding
- **Registry & Search** (PR #518): Comprehensive ontology management and discovery
### **Security Hardening** (Security Enhancement PR, @KaifAhmad1)
- **12 Critical Vulnerabilities Fixed**: Eval injection, XXE, SQL injection, and more
- **SSRF Protection**: Comprehensive URL validation and hostname resolution
- **Input Validation**: Enhanced file upload restrictions and format detection
- **CORS & Headers**: Proper security headers and WebSocket protection
---
## v0.3.0 — Stable (2026-03-10)
## 📊 **BY THE NUMBERS**
### Context Graph Feature Completeness
**Temporal Validity Windows** (by @KaifAhmad1)
Nodes and edges now carry first-class `valid_from` / `valid_until` ISO datetime fields. These are stored directly on `ContextNode` and `ContextEdge` dataclasses — not in metadata — and survive full serialisation round-trips through `save_to_file()` / `load_from_file()` and `to_dict()` / `from_dict()`.
- `ContextNode.is_active(at_time=None)` and `ContextEdge.is_active(at_time=None)` — returns `True` if the node/edge is live at the given time (defaults to now). Handles both tz-aware and tz-naive datetime inputs correctly.
- `ContextGraph.find_active_nodes(node_type=None, at_time=None)` — filters the entire graph and returns only nodes within their validity window.
- `add_node(valid_from=..., valid_until=...)` and `add_edge(valid_from=..., valid_until=...)` — pass validity fields directly in the call signature.
- Bug fix: `is_active()` previously crashed with `TypeError` when passed a tz-aware `datetime` (e.g. `datetime.now(timezone.utc)`). Fixed by normalising all inputs to tz-naive UTC via a new `_parse_iso_dt()` helper.
- Bug fix: validity fields were silently lost in `add_nodes()`, `add_edges()`, `to_dict()`, and `from_dict()`. All four paths now correctly preserve and restore them.
**Weighted Multi-Hop BFS** (by @KaifAhmad1)
`ContextGraph.get_neighbors(node_id, hops=1, relationship_types=None, min_weight=0.0)` now accepts a `min_weight` threshold. Any edge with weight below the threshold is skipped during BFS traversal, allowing callers to confine multi-hop queries to high-confidence causal links. Default `0.0` is fully backward-compatible.
**Cross-Graph Navigation** (by @KaifAhmad1)
Separate `ContextGraph` instances can now be linked and navigated between — hierarchically, like separate knowledge domains that reference each other.
- `link_graph(other_graph, source_node_id, target_node_id, link_type="CROSS_GRAPH") -> str` — creates a navigable bridge and returns a `link_id`. Records a dedicated `"cross_graph_link"` typed marker node internally (not a phantom `"entity"`) and a marker edge.
- `navigate_to(link_id) -> (other_graph, target_node_id)` — jumps to the target graph and entry node for a given link.
- `graph_id` field — each `ContextGraph` now carries a stable UUID so instances can identify each other across save/load.
- `save_to_file()` — now writes a `links` section alongside nodes and edges, containing `link_id`, `source_node_id`, `target_node_id`, and `other_graph_id` for every cross-graph link.
- `load_from_file()` — restores `graph_id` and populates `_unresolved_links` from the `links` section.
- `resolve_links(registry: Dict[str, ContextGraph]) -> int` — reconnects unresolved links post-load. Pass `{graph_id: graph_instance}` for each linked graph; returns the count of successfully resolved links. `navigate_to()` raises a clear `KeyError` with a `resolve_links()` hint if called before resolution.
- Bug fix: the previous implementation auto-created the synthetic marker target as an `"entity"` node (phantom pollution). Fixed by explicitly pre-creating a `"cross_graph_link"` typed `ContextNode` before the marker edge.
- 14 new tests in `tests/context/test_cross_graph_navigation.py` covering all scenarios including full save/load round-trips with partial registry resolution.
**Other Fixes** (by @KaifAhmad1)
- `PipelineBuilder.add_step()` return type annotation corrected from `"PipelineBuilder"` to `"PipelineStep"` — the implementation was already correct; only the annotation and docstring were stale.
- `test_hybrid_search_performance` timing computation fixed — now accumulates a true `search_times` list instead of reusing the last loop iteration's `start_time`; threshold relaxed to `< 5.0s` for real `sentence-transformers` (384-dim) latency on development machines.
**Test Coverage Added**
- 14 cross-graph navigation tests (`tests/context/test_cross_graph_navigation.py`)
- **Total: 335 context tests, 886+ tests across all modules — 0 failures**
- **12 Major Features** ✅ Tested & Verified
- **16 Ontology Hub API Endpoints** ✅ Production Ready
- **57 New Distance Intelligence Tests** ✅ All Passing
- **32 Parquet Ingestion Tests** ✅ All Passing
- **12 Security Vulnerabilities** ✅ All Patched
- **100% Test Coverage** ✅ Core Features Verified
---
## v0.3.0-beta — Beta (2026-03-07)
## 🔧 **NEW FEATURES**
### Semantic Extraction Fixes
### **Performance & Architecture**
- **Distance Intelligence Embedding Cache** (PR #502, @KaifAhmad1): Thread-safe per-session caching with automatic invalidation
- **Parquet File Ingestion** (PR #548, @Luffy2208): PyArrow backend with column selection and partition support
- **Indexed Search** (PR #481, @ZohaibHassan16): O(log n) search for large graphs (118k nodes: 24ms → 0.004ms)
**Multi-Founder LLM Extraction & Reasoner Inference Fix** (PR #354, by @KaifAhmad1)
### **Ontology Hub Suite**
- **Cross-ontology Alignments** (PR #524, @KaifAhmad1 @ZohaibHassan16): ML-powered suggestions with confidence scoring
- **Quality Health Dashboard** (PR #524, @KaifAhmad1 @ZohaibHassan16): 5-dimension scoring with actionable issue tracking
- **SHACL Studio** (PR #524, @KaifAhmad1 @ZohaibHassan16): Interactive shape authoring with Monaco editor
- **Visual Ontology Editor** (PR #519, @KaifAhmad1): Drag-and-drop ontology construction
- **16 Backend Endpoints** (PRs #518, #519, #524, @KaifAhmad1 @ZohaibHassan16): Complete CRUD and analysis capabilities
- `_parse_relation_result` in `methods.py` — unmatched subjects/objects now produce a synthetic `UNKNOWN` entity instead of silently dropping the relation. All co-founders returned by the LLM are preserved in the output.
- Duplicate relation fix — an orphaned legacy block that appended every relation twice has been removed.
- `extraction_method` parameter added — typed extraction paths now correctly record `"llm_typed"` in relation metadata instead of `"llm"`.
- `_match_pattern` in `reasoner.py` rewritten — splits patterns on `?var` placeholders first, then escapes only literal segments. Pre-bound variables resolve to exact literals, repeated variables use backreferences, non-greedy `.+?` prevents over-consumption of separators.
- Added `tests/reasoning/test_reasoner.py` (4 tests) and `tests/semantic_extract/test_relation_extractor.py` (6 tests).
### **UI & User Experience**
- **Distance Intelligence UI** (PR #502, @KaifAhmad1 @ZohaibHassan16): Ego mode, overlays, heatmap, path inspector
- **Explorer Redesign** (PR #516, @ZohaibHassan16): Modern hero section with live metrics
- **Graph Workspace Declutter** (PR #483, @ZohaibHassan16): Improved visualization for dense graphs
- **Bidirectional Path Finding** (PR #469, @KaifAhmad1): Undirected traversal support
**TTL Export Alias Fix** (PR #355, by @KaifAhmad1)
### **Platform Compatibility**
- **Windows Installation Fixes** (PR #532, @KaifAhmad1): Removed faiss-gpu from [all], Unicode console support
- **Cross-platform Dependencies** (PR #527, @ZohaibHassan16): Proper optional dependency management
- **MCP Server Package Structure** (PR #541, @KaifAhmad1): Fixed pipx installation issues
- `RDFExporter` now accepts `"ttl"`, `"nt"`, `"xml"`, `"rdf"`, and `"json-ld"` as format aliases in `export_to_rdf()`. Aliases resolve before format validation — zero public API changes.
- Added `tests/export/test_rdf_exporter.py` (8 tests).
### Incremental / Delta Processing
**Native Delta Computation** (PR #349, by @ZohaibHassan16, reviewed and fixed by @KaifAhmad1)
- Native SPARQL-based diff between graph snapshots — only changed triples flow through the pipeline.
- `delta_mode` configuration in `PipelineBuilder` for near-real-time workloads.
- Version snapshot management with graph URI tracking and metadata storage.
- `prune_versions()` for automatic snapshot retention cleanup.
- Bug fixes: corrected SPARQL variable order, fixed class references, resolved duplicate dictionary keys.
### Deduplication v2
**Candidate Generation v2** (PR #338, by @ZohaibHassan16)
- New opt-in strategies: `blocking_v2` and `hybrid_v2`, replacing O(N²) pair enumeration.
- Multi-key blocking with normalised token prefixes, type-aware keys, and optional phonetic (Soundex) blocking.
- Deterministic `max_candidates_per_entity` budgeting with stable sorting.
- **63.6% faster** in worst-case scenarios (0.259s → 0.094s for 100 entities).
**Two-Stage Scoring Prefilter** (PR #339, by @ZohaibHassan16)
- Fast gates for type mismatch, name-length ratio, and token overlap eliminate expensive semantic scoring for obvious non-matches.
- Configurable thresholds: `min_length_ratio`, `min_token_overlap_ratio`, `required_shared_token`.
- **1825% faster** batch processing with prefilter enabled (`prefilter_enabled=False` by default).
**Semantic Relationship Deduplication v2** (PR #340, by @ZohaibHassan16, fixes by @KaifAhmad1)
- Canonicalisation engine with predicate synonym mapping (`works_for``employed_by`).
- O(1) hash matching for exact canonical signatures.
- Weighted scoring: 60% predicate + 40% object composition with explainable `semantic_match_score`.
- **6.98x faster** than legacy mode (83ms vs 579ms).
- `dedup_triplets()` infinite recursion bug fixed; function is now a first-class API in `methods.py`.
**Deduplication v2 Migration Guide** (PR #344, by @ZohaibHassan16, fixes by @KaifAhmad1)
- Comprehensive `MIGRATION_V2.md` documenting all v2 strategies with code examples.
- Full backward compatibility maintained — legacy mode remains the default.
### Export Formats
**ArangoDB AQL Export** (PR #342, by @tibisabau)
- Full AQL INSERT statement generation for vertices and edges.
- Configurable collection names with validation and sanitisation; batch processing (default: 1000).
- `export_arango()` convenience function; `.aql` auto-detection in the unified exporter.
- 17 tests, 100% pass rate.
**Apache Parquet Export** (PR #343, by @tibisabau)
- Columnar storage format with configurable compression: snappy, gzip, brotli, zstd, lz4, none.
- Explicit Apache Arrow schemas with type safety; field normalisation for varied naming conventions.
- `export_parquet()` convenience function; `.parquet` auto-detection.
- Analytics-ready for pandas, Spark, Snowflake, BigQuery, Databricks.
- 25 tests, 100% pass rate.
### Bug Fixes & Test Suite Stabilisation
**Test Suite Fixes** (by @KaifAhmad1)
Context module:
- `retrieve_decision_precedents` — gated entity extraction on `use_hybrid_search=True` correctly.
- `_extract_entities_from_query` — now uses `word[0].isupper()` to capture camelCase identifiers like `CreditCard`.
- Added missing `expand_context` (BFS traversal) and `_get_decision_query` methods.
- Fixed `hybrid_retrieval`, `dynamic_context_traversal`, and `multi_hop_context_assembly` for correct single-pass BFS.
- Fixed `_retrieve_from_vector` fallback to `result["metadata"]["content"]` to prevent empty content and negative re-ranking scores.
KG module:
- `calculate_pagerank` — added `alpha`/`max_iter` aliases; return format changed to `{"centrality": scores, "rankings": sorted_list}`.
- `community_detector._to_networkx` — now returns a NetworkX graph directly when one is passed (previously lost all edges).
- Added 9 domain-specific tracking methods to `AlgorithmTrackerWithProvenance`.
- Created `provenance_tracker.py` with `ProvenanceTracker` (`track_entity`, `get_all_sources`, `clear`).
Pipeline module:
- Retry loop fixed — now correctly iterates to `max_retries`.
- `RecoveryAction` dataclass and `handle_failure(error, policy, retry_count)` added with LINEAR, EXPONENTIAL, and FIXED strategies.
- `add_step()` fixed to return the created `PipelineStep`.
- `validate` added as alias for `validate_pipeline` in `PipelineValidator`.
Other:
- Fixed `NameError` for missing `Type` import in `utils/helpers.py`.
- Vector store performance threshold relaxed (100ms → 500ms per decision for development machines).
- Windows cp1252 encoding fix in test files (emoji → ASCII).
- `ProvenanceTracker` added to `semantica/kg/__init__.py` exports.
**Results: ~840 tests passing, 36 skipped (external services), 0 failed**
### **Algorithm Enhancements**
- **DuplicateDetector Result Limiting** (PR #534, @KaifAhmad1): Ranking, sorting, and incremental detection features
- **ConflictDetector Parameter Handling** (PR #533, @KaifAhmad1): Method parameter validation and error handling
---
## v0.3.0-alpha — Alpha (2026-02-19)
## 🛡️ **SECURITY IMPROVEMENTS** (Security Enhancement PR, @KaifAhmad1)
### Context & Decision Intelligence
### **Critical Fixes**
- **Eval Injection** (CWE-95): Replaced with `fractions.Fraction` in media parser
- **Pickle Deserialization** (CWE-502): Switched to JSON with migration support
- **SQL Injection** (CWE-89): Parameterized queries and input validation
- **XXE Protection** (CWE-611): `defusedxml` hardening for all RDF parsing
**Context Engineering Enhancement** (PR #307, by @KaifAhmad1)
### **Web Security**
- **SSRF Protection**: URL validation with hostname resolution
- **CORS Hardening**: Narrowed origins and WebSocket limits
- **Security Headers**: HSTS, X-Content-Type-Options, X-Frame-Options
- **Path Traversal**: `Path.resolve().relative_to()` protection
The foundational 0.3.0 feature — complete overhaul of the context module for production-grade decision intelligence:
- Full decision lifecycle: `record_decision()``add_decision()``add_causal_relationship()``trace_decision_chain()``analyze_decision_impact()``analyze_decision_influence()``find_similar_decisions()`
- `AgentContext` unified wrapper with granular feature flags: `decision_tracking`, `kg_algorithms`, `graph_expansion`; methods: `store()`, `retrieve()`, `get_conversation_history()`, `get_statistics()`, `capture_cross_system_inputs()`
- `AgentMemory` with working, conversation, and long-term memory tiers
- `PolicyEngine` with versioned policy nodes, compliance checking (`check_decision_rules()`), and `PolicyException` model
- Hybrid precedent search combining vector, structural, and category similarity with configurable weights
- Decision influence analysis via centrality measures and causal chain tracking
- GraphStore validation preventing runtime failures; secure logging
- 9 critical bug fixes across logging, security, audit trails, API compatibility, Cypher queries, centrality access, validation
**Context Decision Tracking Fixes** (PR #315, by @KaifAhmad1)
- Fixed empty/None decision ID handling in `add_decision()`
- Fixed None metadata handling preventing `TypeError`
- Fixed causal chain depth logic and node exclusion
- Fixed nonexistent node handling in `add_causal_relationship()`
- Fixed precedent search direction in `find_precedents()`
- Added missing `properties` field in `to_dict()`; added `from_dict()` method
- Fixed UUID generation across all decision models
- All 71 context tests passing
### Knowledge Graph Algorithms
**Improved Graph Algorithms** (PR #292, by @KaifAhmad1)
- 30+ graph algorithms across 7 categories
- Node embeddings: Node2Vec, DeepWalk, Word2Vec via `NodeEmbedder`
- Similarity: cosine, Euclidean, Manhattan, Correlation via `SimilarityCalculator`
- Path finding: Dijkstra, A*, BFS, K-shortest paths via `PathFinder`
- Link prediction: preferential attachment, Jaccard, Adamic-Adar via `LinkPredictor`
- Centrality: degree, betweenness, closeness, PageRank via `CentralityAnalyzer`
- Community detection: Louvain, Leiden, label propagation via `CommunityDetector`
- Connectivity: components, bridges, density via `ConnectivityAnalyzer`
- `GraphBuilderWithProvenance` and `AlgorithmTrackerWithProvenance` with full execution metadata
**Improved Vector Store for Decision Tracking** (PR #293, by @KaifAhmad1)
- `DecisionEmbeddingPipeline` with semantic and structural embeddings
- `HybridSimilarityCalculator` with configurable weights (semantic: 0.7, structural: 0.3)
- `ContextRetriever` with multi-hop reasoning
- Convenience API: `quick_decision()`, `find_precedents()`, `explain()`, `similar_to()`, `batch_decisions()`, `filter_decisions()`
- 34+ tests; performance: 0.028s per decision, 0.031s search, ~0.8KB memory per decision
### Graph Database Backends
**Apache AGE Backend Security Fixes** (PR #311, by @Sameer6305, fixes by @KaifAhmad1)
- `AgeStore` class with `GraphStore` API compatibility (openCypher via SQL on PostgreSQL)
- SQL injection vulnerabilities fixed with input validation
- psycopg2-binary dependency and migration guide added
- Fixed parameter replacement and test mock leakage
**PgVector Store Support** (PR #303, by @Sameer6305, @KaifAhmad1)
- Native PostgreSQL vector storage using the pgvector extension
- Multiple distance metrics: cosine, L2/Euclidean, inner product
- HNSW and IVFFlat indexing for approximate nearest neighbour search
- JSONB metadata storage with flexible filtering; batch operations
- Connection pooling with psycopg3/psycopg2 fallback
- SQL injection protection via `psycopg_sql.SQL()`; idempotent index and table management
- 36+ tests with Docker integration
### Infrastructure
**ResourceScheduler Deadlock Fix** (PR #299, #301, by @d4ndr4d3, @KaifAhmad1)
- Replaced `threading.Lock()` with `threading.RLock()` to fix nested lock acquisition deadlock in `allocate_resources()`
- Added `ValidationError` when no resources can be allocated
- Progress tracking updates moved outside lock scope
- 6 regression tests for deadlock prevention
**Security Configuration** (by @KaifAhmad1)
- Dependabot bi-weekly security updates with manual review
- Automated security scans (Bandit, Safety, Semgrep) on schedule
- Security-critical package grouping; zero auto-merge policy
### **Input Validation**
- **File Upload Restrictions**: Extension allowlist and size limits
- **SPARQL Limits**: Row caps, timeouts, and concurrency controls
- **ReDoS Prevention**: Eliminated polynomial regex patterns
---
## Summary by the Numbers
## 🔍 **QUALITY ASSURANCE**
| Metric | Value |
|--------|-------|
| Total tests passing | **886+** |
| Test failures | **0** |
| Context tests | 335 |
| KG tests | ~430 |
| Semantic extraction tests | 70 (9 skipped — external LLM APIs) |
| Reasoning tests | 19 |
| Real-world scenario tests | 85 |
| PyPI classifier | Production/Stable |
| Python support | 3.8 3.12 |
### **Testing Coverage**
- **Distance Intelligence**: 57 new tests, 100% passing
- **Parquet Ingestion**: 32 tests, comprehensive coverage
- **Security Fixes**: 14 vulnerability-specific tests
- **UI Components**: All major features verified
- **Platform Tests**: Windows, Linux compatibility confirmed
### **Performance Benchmarks**
- **Embedding Cache**: 10x+ improvement in repeated requests
- **Search Performance**: 6,000x faster for large graphs
- **Memory Efficiency**: Lazy loading and optional dependencies
- **Concurrent Operations**: Thread-safe caching with locks
---
## Upgrade
## 🔄 **BREAKING CHANGES**
### **Dependencies**
- **Windows Users**: `faiss-gpu` removed from `[all]` - install `[gpu]` explicitly if needed
- **Optional Dependencies**: Now lazy-loaded to improve import performance
### **API Changes**
- **ConflictDetector**: Fixed duplicate method definitions with proper parameter handling
- **DuplicateDetector**: New result limiting and ranking options
---
## 📚 **DOCUMENTATION**
- **Comprehensive Changelog**: Detailed feature descriptions and credits
- **API Documentation**: All new endpoints documented
- **Security Advisory**: Complete vulnerability disclosure and fixes
- **Migration Guide**: Breaking changes and upgrade instructions
---
## 🙏 **CREDITS**
**Core Contributors:**
- **@KaifAhmad1** - Distance Intelligence (PR #502), Security Hardening, Ontology Hub (PRs #517, #518, #519, #524), Windows Fixes (PR #532), ConflictDetector (PR #533), Testing & Release Preparation
- **@ZohaibHassan16** - Ontology Hub UI (PRs #516, #518, #519, #524), Graph Explorer (PRs #420, #481, #483, #503), Semantic Extract (PR #536), Lazy Loading (PR #535)
- **@Luffy2208** - Parquet Ingestion Support (PR #548)
- **@liling** - DeepSeek Provider Integration (PR #482)
- **@Sameer6305** - Provenance Traversal Fixes (PR #480), Named Graph Support
**Special Thanks:**
- Security research team for vulnerability disclosures
- Community testers and feedback providers
- Documentation contributors and reviewers
---
## 🚀 **INSTALLATION**
```bash
pip install --upgrade semantica
# Standard installation
pip install semantica==0.5.0
# With all optional dependencies (cross-platform)
pip install "semantica[all]==0.5.0"
# With GPU acceleration (Linux only)
pip install "semantica[gpu]==0.5.0"
# With Parquet support
pip install "semantica[ingest-parquet]==0.5.0"
```
No breaking changes. All new parameters have safe defaults and all new methods are additive.
---
See [CHANGELOG.md](CHANGELOG.md) for the full line-by-line diff.
## 📈 **WHAT'S NEXT FOR 0.5.0**
The 0.5.0 release establishes Semantica as a production-ready framework for:
- **Enterprise Knowledge Engineering** with comprehensive ontology management
- **Advanced Analytics** through distance intelligence and semantic search
- **Security-First Design** with comprehensive vulnerability protection
- **Cross-Platform Compatibility** supporting diverse deployment environments
**Immediate next steps for 0.5.0:**
- PyPI package publication and distribution
- Docker image updates with new features
- Documentation website deployment with updated guides
- Community outreach and feature announcements
- Integration testing across different deployment scenarios
---
**🎯 Semantica 0.5.0: Production-Ready Knowledge Engineering Platform**
+99 -4
View File
@@ -24,7 +24,7 @@ Security vulnerabilities should be reported privately to prevent potential explo
### 2. Report Security Issue
Create a [GitHub Security Advisory](https://github.com/Hawksight-AI/semantica/security/advisories/new) or contact us through [GitHub Issues](https://github.com/Hawksight-AI/semantica/issues) with "[SECURITY]" prefix.
Create a [GitHub Security Advisory](https://github.com/semantica-agi/semantica/security/advisories/new) or contact us via the security email listed in `SUPPORT.md`.
Include the following information:
@@ -37,7 +37,7 @@ Include the following information:
### 3. Response Timeline
- **Initial Response**: Within 48 hours
- **Initial Response**: Within 24 hours for critical issues; within 48 hours for non-critical issues
- **Status Update**: Within 7 days
- **Resolution**: Depends on severity and complexity
@@ -112,6 +112,101 @@ We regularly update dependencies to address security vulnerabilities. However, y
- Be cautious with external API calls
- Implement proper authentication and authorization
## CI/CD Supply-Chain Security
Semantica's build and release pipeline is explicitly hardened against
CI/CD supply-chain attacks — the class of attack behind the March 2026
LiteLLM/Trivy incident, where a compromised third-party Action with a
**mutable tag** was used to steal a long-lived publishing token, after which
malicious packages were pushed straight to PyPI without ever touching the
source repository. Every control below maps directly to closing one step of
that attack chain.
### Immutable build inputs
- **Risk**: a tag (`@v4`, `@release/v1`) is re-pointed by a compromised upstream maintainer or account, silently changing what every consumer's CI runs.
**Control**: every third-party GitHub Action in every workflow is pinned to a full 40-character commit SHA, with the human-readable tag kept only as a trailing comment (e.g. `actions/checkout@3d3c42e... # v7`).
- **Risk**: a SHA pin drifts out of sync with its own comment over time, or is mistyped.
**Control**: `verify-action-pins.yml` fails closed on any `uses:` reference that isn't a full commit SHA (catching a newly added mutable tag, not just auditing existing pins), resolves every pinned tag via the GitHub API on each workflow change, on every push to `main`, and weekly, and fails if the SHA no longer matches the tag it claims to be — an API lookup that can't be resolved is treated as a failure, not a silent skip.
- **Risk**: manually re-pinning ~15 actions across 8 workflow files on every upstream release is error-prone.
**Control**: Dependabot (`github-actions` ecosystem) opens a grouped PR that bumps the SHA *and* the tag comment together whenever an action releases — pins never require hand-editing.
### Publishing pipeline (highest-privilege path)
- **Risk**: a long-lived `PYPI_TOKEN` sitting in repo/org secrets is exfiltrated by any compromised step.
**Control**: PyPI publishing uses Trusted Publishing (OIDC) (`id-token: write`) — there is no long-lived PyPI credential anywhere in this repository to steal.
- **Risk**: a compromised CI run publishes to PyPI with no human in the loop.
**Control**: the publish job runs only inside a protected `pypi` GitHub Environment with a required human reviewer — every release needs manual approval in the Actions UI before it runs.
- **Risk**: the release job could be triggered from an arbitrary branch/ref.
**Control**: the `pypi` environment's deployment-branch policy is restricted to `v*` tags only.
- **Risk**: a scanner or unrelated job inherits publish-level credentials.
**Control**: `release.yml` sets `permissions: contents: read` at the workflow level; `contents: write` / `id-token: write` / `attestations: write` are granted only to the release job, never workflow-wide.
- **Risk**: two tag pushes race through the publish pipeline simultaneously.
**Control**: `concurrency: group: release-${{ github.ref }}` serializes releases per tag.
- **Risk**: a consumer can't verify a wheel on PyPI actually came from this repo's CI.
**Control**: SLSA build provenance is attested for every release via `actions/attest-build-provenance`, producing a signed, verifiable record of the exact commit and workflow run that produced the artifact (checkable with `gh attestation verify`).
### Repository controls
- **Risk**: unreviewed or force-pushed changes land on `main`.
**Control**: `main` requires 1 approving PR review (stale approvals dismissed on new pushes), resolved conversations, and blocks force-pushes and branch deletion.
- **Risk**: a PR merges without its security/CI checks passing.
**Control**: merges require the `build`, `Analyze Python` (CodeQL), and `security-scan` checks to pass, in strict mode (checks must be re-run against the latest `main`).
- **Risk**: a compromised scanner job reaches secrets or write access.
**Control**: scanning jobs (`CodeQL`, `security-scan.yml`, `security.yml`, `defender-for-devops.yml`) run with read-only, least-privilege permissions (typically `contents: read` + `security-events: write` only) and never share a job, environment, or secret scope with the publish job.
- **Risk**: secrets are committed accidentally.
**Control**: GitHub secret scanning and push protection are both enabled at the repository level, rejecting pushes that contain recognizable credential patterns before they land in history.
## Automated Security Scanning
Every scan below runs continuously in CI, not just at release time:
- **CodeQL** (`security-and-quality` query pack) — Python source: injection, unsafe deserialization, and other code-level vulnerability classes. Runs in `codeql.yml` on every push/PR to `main` and weekly.
- **Bandit** — Python-specific security anti-patterns (hardcoded secrets, unsafe `eval`/`pickle`, weak crypto, etc.); CI fails on any HIGH-severity finding. Runs in `security-scan.yml` on every push/PR to `main` and twice weekly.
- **Semgrep** (`p/security` ruleset) — cross-language static-analysis security patterns. Runs in `security-scan.yml` on every push/PR to `main` and twice weekly.
- **Safety** — known CVEs in Semantica's own installed dependencies, including optional LLM-provider extras such as LiteLLM; CI fails on any match. Runs in `security-scan.yml` on every push/PR to `main` and twice weekly.
- **pip-audit** — independent, PyPA-maintained vulnerability database cross-check against installed dependencies (Safety and pip-audit use different advisory sources, so both run). Runs in `security.yml` weekly.
- **Microsoft Defender for DevOps** (`eslint`, `templateanalyzer`, `terrascan`) — JavaScript/TypeScript lint-security rules and infrastructure-as-code misconfigurations. Runs in `defender-for-devops.yml` on every push/PR to `main` and weekly.
- **Checkov** — Kubernetes, Helm, Dockerfile, GitHub Actions, and secrets-pattern IaC scanning; results upload to the same Security tab as CodeQL. Runs in `defender-for-devops.yml` on every push/PR to `main` and weekly.
- **GitGuardian** — secret-detection check on every pull request, installed as a GitHub App integration (not a repo-local workflow). Runs on every PR.
- **GitHub secret scanning + push protection** — blocks known credential patterns before they're pushed, and continuously scans existing history. Platform-level, continuous.
- **Dependabot** — version/security PRs for Python, Docker, and GitHub Actions dependencies, grouped where relevant to reduce review noise. Configured in `.github/dependabot.yml`, runs weekly for security-relevant packages and monthly for docs dependencies.
- **`verify-action-pins.yml`** — enforces that every Action reference is a full commit SHA (failing on a newly introduced mutable tag) and confirms each SHA still matches the tag it claims to be. Runs on every workflow change, every push to `main`, and weekly.
All SARIF-producing scanners (CodeQL, Checkov, Microsoft Defender) publish
findings to the repository's **Security → Code scanning alerts** tab, giving
a single audit trail across tools rather than scattered per-tool reports.
### Adopting this posture in a fork or downstream deployment
Teams standing up their own instance of Semantica, or forking it for an
internal/regulated deployment, can reuse this posture directly:
1. Keep Dependabot's `github-actions` ecosystem entry — it is what keeps
SHA pins current without manual maintenance.
2. Re-run `verify-action-pins.yml` after re-pointing the repository's Actions
at your own mirrors, if you do so.
3. If you publish your own PyPI package from a fork, configure your own
Trusted Publishing trust relationship on PyPI (Trusted Publishing is
scoped to a specific `owner/repo` + workflow filename) and your own
protected environment with your own required reviewers — these are not
transferable from this repository.
4. Branch protection, environment protection, and repository secret
scanning are repository *settings*, not workflow files — cloning or
forking the repo does **not** copy them. They must be re-applied via
the GitHub UI or API on the new repository.
5. GitHub secret scanning and push protection are repository settings that
don't carry over to a fork either — re-enable both under the new
repository's Security settings, not just Dependabot.
6. GitGuardian runs as a GitHub App installation scoped to this specific
repository, not a workflow file — a fork gets no secret-detection
coverage from it until the app is installed separately on the new repo.
7. CodeQL's `upload-sarif` step in `codeql.yml` only runs meaningfully if
Default Setup is *not* already enabled for the repository (it's designed
to skip gracefully otherwise) — check whether Default Setup or Advanced
Setup is active on the new repository and adjust expectations for where
CodeQL findings show up accordingly.
## Dependency Security Policy
### Regular Updates
@@ -156,8 +251,8 @@ We appreciate responsible disclosure. Security researchers who help us improve t
For security-related questions or concerns:
- **GitHub Issues**: [Create an issue](https://github.com/Hawksight-AI/semantica/issues) with "[SECURITY]" prefix
- **GitHub Security Advisories**: [Report vulnerability](https://github.com/Hawksight-AI/semantica/security/advisories/new)
- **Private Reporting**: Please do not report vulnerabilities in public issues.
- **GitHub Security Advisories**: [Report vulnerability](https://github.com/semantica-agi/semantica/security/advisories/new)
## Additional Resources
-105
View File
@@ -1,105 +0,0 @@
# Deduplication & Conflict Resolution Strategies Summary
## Quick Reference by Use Case
| Use Case | Deduplication Method | Merge Strategy | Conflict Detection | Conflict Resolution |
|----------|---------------------|----------------|-------------------|---------------------|
| **Finance** |
| `01_Financial_Data_Integration_MCP` | `DuplicateDetector` (incremental) | `keep_highest_confidence` | `temporal` | `most_recent` |
| `02_Fraud_Detection` | `ClusterBuilder` (graph_based) | `merge_all` | `logical` | `expert_review` |
| **Biomedical** |
| `01_Drug_Discovery_Pipeline` | `EntityResolver` (semantic) | - | `relationship` | `voting` |
| `02_Genomic_Variant_Analysis` | `DuplicateDetector` (group) | `keep_most_complete` | `value` | `credibility_weighted` |
| **Cybersecurity** |
| `01_Real_Time_Anomaly_Detection` | `DuplicateDetector` (pairwise) | `keep_first` | `entity` | `first_seen` |
| `02_Threat_Intelligence_Hybrid_RAG` | `EntityResolver` (exact) | - | `type` | `highest_confidence` |
| **Blockchain** |
| `01_DeFi_Protocol_Intelligence` | `DuplicateDetector` (group) | `keep_last` | `relationship` | `voting` |
| `02_Transaction_Network_Analysis` | `ClusterBuilder` (hierarchical) | `keep_most_complete` | `temporal` | `most_recent` |
| **Intelligence** |
| `01_Criminal_Network_Analysis` | `EntityResolver` (fuzzy) | - | `value` | `credibility_weighted` |
| `02_Intelligence_Analysis_Orchestrator_Worker` | `DuplicateDetector` (batch) | `merge_all` | `entity` | `voting` |
| **Renewable Energy** |
| `01_Energy_Market_Analysis` | `DuplicateDetector` (pairwise) | `keep_highest_confidence` | `temporal` | `most_recent` |
| **Supply Chain** |
| `01_Supply_Chain_Data_Integration` | `DuplicateDetector` (incremental) | `keep_most_complete` | `value` | `credibility_weighted` |
---
## Strategy Rationale by Domain
### Finance
- **Financial Data Integration**: Incremental for streaming data; most_recent for time-sensitive financial data
- **Fraud Detection**: Graph-based clustering for fraud groups; expert_review for fraud assessment
### Biomedical
- **Drug Discovery**: Semantic matching for drug compounds; voting for research source aggregation
- **Genomic Variants**: Group method for related variants; credibility weighting for research sources
### Cybersecurity
- **Real-Time Anomaly**: Pairwise for real-time streams; keep_first for first detection priority
- **Threat Intelligence**: Exact matching for IOCs; highest_confidence for threat classification
### Blockchain
- **DeFi Protocols**: Group method for related protocols; keep_last for latest protocol info
- **Transaction Networks**: Hierarchical clustering for nested groups; temporal for time-sensitive data
### Intelligence
- **Criminal Networks**: Fuzzy matching for intelligence data; credibility weighting for intelligence sources
- **Intelligence Analysis**: Batch for multi-source integration; merge_all to combine all intelligence sources
### Renewable Energy
- **Energy Markets**: Pairwise for real-time market data; most_recent for time-sensitive energy data
### Supply Chain
- **Supply Chain Integration**: Incremental for continuous updates; credibility weighting for supply chain sources
---
## Method Distribution
### Deduplication Methods (9 total)
- `pairwise`: 2 notebooks (real-time processing)
- `batch`: 3 notebooks (large datasets)
- `incremental`: 2 notebooks (streaming/continuous)
- `group`: 2 notebooks (related entities)
- `graph_based` (ClusterBuilder): 2 notebooks (interconnected entities)
- `hierarchical` (ClusterBuilder): 1 notebook (nested groups)
- `exact` (EntityResolver): 1 notebook (exact matching)
- `semantic` (EntityResolver): 2 notebooks (semantic similarity)
- `fuzzy` (EntityResolver): 1 notebook (fuzzy matching)
### Merge Strategies (5 total)
- `keep_first`: 1 notebook (first detection priority)
- `keep_last`: 1 notebook (latest information)
- `keep_most_complete`: 5 notebooks (preserve all details)
- `keep_highest_confidence`: 2 notebooks (most reliable data)
- `merge_all`: 3 notebooks (combine all information)
### Conflict Detection Methods (6 total)
- `value`: 4 notebooks (property value conflicts)
- `type`: 2 notebooks (type/classification conflicts)
- `entity`: 2 notebooks (entity-wide conflicts)
- `relationship`: 3 notebooks (relationship conflicts)
- `temporal`: 3 notebooks (time-sensitive conflicts)
- `logical`: 2 notebooks (logical inconsistencies)
### Conflict Resolution Strategies (6 total)
- `voting`: 5 notebooks (majority vote)
- `credibility_weighted`: 4 notebooks (source credibility)
- `most_recent`: 3 notebooks (latest data)
- `first_seen`: 1 notebook (first detection)
- `highest_confidence`: 2 notebooks (most confident)
- `expert_review`: 1 notebook (manual review)
---
## Key Patterns
1. **Real-Time Systems**: Use `pairwise` + `keep_first` + `first_seen`
2. **Time-Sensitive Data**: Use `temporal` + `most_recent`
3. **Multi-Source Integration**: Use `batch` + `merge_all` + `voting`
4. **Medical/Research**: Use `credibility_weighted` for authoritative sources
5. **Fraud/Security**: Use `graph_based` + `logical` + `expert_review`
6. **Exact Matching Required**: Use `exact` strategy (IOCs, identifiers)
+8 -8
View File
@@ -20,8 +20,8 @@ Start with our comprehensive documentation:
**Best for**: General questions, feature discussions, and getting help
- [Ask a question](https://github.com/Hawksight-AI/semantica/discussions/new?category=q-a)
- [Browse discussions](https://github.com/Hawksight-AI/semantica/discussions)
- [Ask a question](https://github.com/semantica-agi/semantica/discussions/new?category=q-a)
- [Browse discussions](https://github.com/semantica-agi/semantica/discussions)
#### Discord
@@ -33,8 +33,8 @@ Start with our comprehensive documentation:
**Best for**: Bug reports and feature requests
- [Report a bug](https://github.com/Hawksight-AI/semantica/issues/new?template=bug_report.md)
- [Request a feature](https://github.com/Hawksight-AI/semantica/issues/new?template=feature_request.md)
- [Report a bug](https://github.com/semantica-agi/semantica/issues/new?template=bug_report.md)
- [Request a feature](https://github.com/semantica-agi/semantica/issues/new?template=feature_request.md)
### Before Asking
@@ -47,7 +47,7 @@ Start with our comprehensive documentation:
### Bug Reports
Use our [bug report template](https://github.com/Hawksight-AI/semantica/issues/new?template=bug_report.md) to report bugs.
Use our [bug report template](https://github.com/semantica-agi/semantica/issues/new?template=bug_report.md) to report bugs.
Include:
- Clear description of the bug
@@ -58,7 +58,7 @@ Include:
### Feature Requests
Use our [feature request template](https://github.com/Hawksight-AI/semantica/issues/new?template=feature_request.md) to suggest features.
Use our [feature request template](https://github.com/semantica-agi/semantica/issues/new?template=feature_request.md) to suggest features.
Include:
- Problem statement
@@ -71,7 +71,7 @@ Include:
**Do NOT** create a public issue for security vulnerabilities.
Instead:
- Email: semantica-dev@users.noreply.github.com
- Email: kaif@getsemantica.ai
- Subject: [SECURITY] Brief description
- See [Security Policy](SECURITY.md) for details
@@ -79,7 +79,7 @@ Instead:
For enterprise support, custom development, or consulting:
- **Email**: semantica-dev@users.noreply.github.com
- **Email**: kaif@getsemantica.ai
- **Subject**: [ENTERPRISE] Your request
## Response Times
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 770 KiB

-75
View File
@@ -1,75 +0,0 @@
--- Python Standards ---
pycache/
*.py[cod]
*$py.class
*.so
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
--- Virtual Environments ---
.env
.venv
venv/
ENV/
--- Benchmarks & Results ---
Ignore all individual benchmark runs to avoid repository bloat
benchmarks/results/run_*.json
Ignore the .pytest_cache which can get quite large
.pytest_cache/
Ignore any temporary files created by benchmarks
benchmarks/input_layer/*.txt
--- IMPORTANT: Keep the Baseline ---
We want to track the 'gold standard' performance in Git
!benchmarks/results/baseline.json
--- IDEs & Editors ---
.idea/
.vscode/
*.swp
*.swo
.project
.pydevproject
.settings/
--- Jupyter Notebooks ---
.ipynb_checkpoints
--- OS Specific ---
.DS_Store
Thumbs.db
--- Project Specific ---
logs/
*.log
semantica.log
-343
View File
@@ -1,343 +0,0 @@
# Semantica Benchmark Suite Results
## Executive Summary
**Test Date**: February 7, 2026
**Total Benchmarks**: 138 passed, 1 skipped
**Test Duration**: 38 minutes 35 seconds
**Environment**: Windows 10, Intel i5-1135G7 @ 2.40GHz, Python 3.11.9
## Performance Overview
| Module | Tests | Performance Grade | Status |
|--------|-------|------------------|---------|
| Input Layer | 6 | 🟢 Excellent | All passed |
| Core Processing | 5 | 🟢 Excellent | All passed |
| Context Memory | 2 | 🟢 Excellent | All passed |
| Storage | 4 | 🟢 Excellent | All passed |
| Ontology | 4 | 🟢 Excellent | All passed |
| Export | 4 | 🟢 Excellent | All passed |
| Visualization | 3 | 🟢 Excellent | All passed |
| Quality Assurance | 2 | 🟢 Excellent | All passed |
| Output Orchestration | 2 | 🟢 Excellent | All passed |
| Context | 3 | 🟢 Excellent | All passed |
---
## 📊 Detailed Benchmark Results
### 🔄 Input Layer Benchmarks
**Purpose**: Test document parsing, data ingestion, and text processing performance
| Benchmark | Operations/sec | Mean Time (ms) | Min Time (ms) | Max Time (ms) | StdDev | Status |
|-----------|----------------|----------------|---------------|---------------|---------|---------|
| `test_json_parsing_throughput[1000]` | 27,365.2 | 36.54 | 35.62 | 40.13 | 0.99 | ✅ |
| `test_json_parsing_throughput[5000]` | 5,541.6 | 180.45 | 165.73 | 194.32 | 11.42 | ✅ |
| `test_csv_parsing_throughput[1000]` | 18,127.9 | 55.16 | 52.41 | 61.87 | 3.33 | ✅ |
| `test_html_scraping_speed[100]` | 2,437.8 | 410.20 | 346.30 | 6,736.50 | 89.27 | ✅ |
| `test_pdf_extraction_overhead[10]` | 9.36 | 106.84 | 11.63 | 91.87 | 62.48 | ✅ |
| `test_python_ast_parsing` | 3,142.6 | 318.21 | 291.96 | 347.90 | 35.67 | ✅ |
**Key Insights**:
- JSON parsing scales linearly (5K items processed in 180ms)
- HTML scraping shows high variance due to complexity
- PDF extraction optimized for batch processing
- AST parsing maintains sub-millisecond performance per operation
---
### ⚙️ Core Processing Benchmarks
**Purpose**: Test NER extraction, semantic analysis, and text processing algorithms
| Benchmark | Operations/sec | Mean Time (ms) | Min Time (ms) | Max Time (ms) | StdDev | Status |
|-----------|----------------|----------------|---------------|---------------|---------|---------|
| `test_ner_ml_wrapper_overhead` | 2,480.3 | 403.18 | - | - | - | ✅ |
| `test_ner_pattern_speed` | 1,440.1 | 694.42 | - | - | - | ✅ |
| `test_ner_batch_throughput` | 2.33 | 429.70 | - | - | - | ✅ |
| `test_similarity_calculation` | 3,142.6 | 318.21 | - | - | - | ✅ |
| `test_clustering_algorithm` | 39.1 | 25,558.38 | 6,113.80 | 42,058.84 | 42,058.84 | ✅ |
| `test_ner_ml_real_performance` | - | - | - | - | - | ⏭️ Skipped |
**Key Insights**:
- Pattern-based NER significantly outperforms ML approaches
- Semantic clustering is computationally intensive (25s mean time)
- Real spaCy ML test skipped due to mocked environment
- Batch processing provides good throughput
---
### 🧠 Context Memory Benchmarks
**Purpose**: Test graph operations, memory storage, and retrieval logic
| Benchmark | Operations/sec | Mean Time (ms) | Min Time (ms) | Max Time (ms) | StdDev | Status |
|-----------|----------------|----------------|---------------|---------------|---------|---------|
| `test_bfs_traversal_depth[1]` | 469.48 | 2.13 | 1.42 | 2.04 | 1.86 | ✅ |
| `test_bfs_traversal_depth[2]` | 419.46 | 2.38 | 2.04 | 2.38 | 0.89 | ✅ |
| `test_memory_storage_overhead` | 9.36 | 106.84 | 11.63 | 91.87 | 62.48 | ✅ |
| `test_short_term_pruning` | 9.23 | 108.36 | 91.87 | 108.36 | 20.76 | ✅ |
| `test_linking_operations` | 2,869.0 | 348.55 | 313.28 | 346.30 | 39.45 | ✅ |
| `test_retrieval_logic[False]` | 2,437.8 | 410.20 | 347.90 | 410.20 | 89.27 | ✅ |
| `test_retrieval_logic[True]` | 39.13 | 25,558.38 | 6,113.80 | 42,058.84 | 42,058.84 | ✅ |
**Key Insights**:
- BFS traversal scales linearly with graph depth
- Memory storage optimized for batch operations
- Retrieval pipeline maintains sub-millisecond performance for simple cases
- Complex retrieval (with context) significantly increases processing time
---
### 💾 Storage Layer Benchmarks
**Purpose**: Test vector stores, triplet storage, and graph database operations
| Benchmark | Operations/sec | Mean Time (ms) | Min Time (ms) | Max Time (ms) | StdDev | Status |
|-----------|----------------|----------------|---------------|---------------|---------|---------|
| `test_binary_raw_throughput` | 5.83 | 171.52 | 162.04 | 178.50 | 7.56 | ✅ |
| `test_numpy_compression_speed[1000]` | 2.47 | 404.81 | 387.07 | 393.72 | 11.55 | ✅ |
| `test_numpy_compression_speed[10000]` | 0.25 | 3,972.74 | 3,867.34 | 3,983.95 | 61.69 | ✅ |
| `test_json_vector_overhead` | 0.66 | 1,504.93 | 1,471.47 | 1,443.15 | 29.39 | ✅ |
| `test_triplet_conversion_overhead` | 87.71 | 11.40 | 5.51 | 157.91 | 21.54 | ✅ |
| `test_bulk_loader_logic` | 2.03 | 492.98 | 304.90 | 40,477.30 | 2,084.37 | ✅ |
**Key Insights**:
- Binary vector storage is 8x faster than JSON serialization
- Triplet conversion is highly optimized (11ms mean)
- Bulk loading shows high variance due to retry logic
- Vector compression scales linearly with data size
---
### 🏗️ Ontology Benchmarks
**Purpose**: Test ontology inference, serialization, and namespace management
| Benchmark | Operations/sec | Mean Time (ms) | Min Time (ms) | Max Time (ms) | StdDev | Status |
|-----------|----------------|----------------|---------------|---------------|---------|---------|
| `test_property_inference_scaling[size0]` | 1,440.1 | 694.42 | 637.90 | - | 65.09 | ✅ |
| `test_owl_xml_generation` | 516.92 | 1.93 | 1.02 | 1.93 | 1.42 | ✅ |
| `test_rdf_serialization_formats[turtle]` | 457.77 | 2.18 | 1.90 | 2.18 | 0.48 | ✅ |
| `test_rdf_serialization_formats[rdfxml]` | 357.26 | 2.80 | 2.23 | 2.80 | 0.79 | ✅ |
| `test_owl_serialization_formats[xml]` | 85.55 | 11.69 | 8.51 | 11.69 | 5.73 | ✅ |
| `test_owl_serialization_formats[turtle]` | 61.10 | 16.37 | 12.28 | 16.37 | 6.84 | ✅ |
**Key Insights**:
- RDF Turtle format is 2x faster than RDF/XML
- OWL serialization efficient for large ontologies
- Property inference is computationally intensive
- XML formats show higher overhead than Turtle
---
### 📤 Export Benchmarks
**Purpose**: Test data export and serialization performance
| Benchmark | Operations/sec | Mean Time (ms) | Min Time (ms) | Max Time (ms) | StdDev | Status |
|-----------|----------------|----------------|---------------|---------------|---------|---------|
| `test_json_parsing_throughput[1000]` | 27,365.2 | 36.54 | 35.62 | 40.13 | 0.99 | ✅ |
| `test_csv_entity_export` | 18,127.9 | 55.16 | 52.41 | 61.87 | 3.33 | ✅ |
| `test_json_parsing_throughput[5000]` | 5,541.6 | 180.45 | 165.73 | 194.32 | 11.42 | ✅ |
| `test_yaml_serialization_overhead` | 2.33 | 429.70 | 357.29 | 429.70 | 68.83 | ✅ |
| `test_graph_conversion_overhead[graphml]` | 62.16 | 16.09 | 10.74 | 16.09 | 16.84 | ✅ |
| `test_graph_conversion_overhead[gexf]` | 55.43 | 18.04 | 15.80 | 18.04 | 1.82 | ✅ |
**Key Insights**:
- JSON export maintains excellent performance across data sizes
- YAML serialization is slower but feature-rich
- GraphML format is slightly faster than GEXF
- Export performance scales linearly with data size
---
### 📈 Visualization Benchmarks
**Purpose**: Test graph visualization, analytics, and dashboard performance
| Benchmark | Operations/sec | Mean Time (ms) | Min Time (ms) | Max Time (ms) | StdDev | Status |
|-----------|----------------|----------------|---------------|---------------|---------|---------|
| `test_network_evolution_frames` | 0.21 | 4,871.40 | 3,958.10 | 4,871.40 | 931.20 | ✅ |
| `test_temporal_dashboard_assembly` | 0.11 | 9,209.90 | 3,327.40 | 9,209.90 | 5,644.20 | ✅ |
| `test_graph_conversion_overhead[graphml]` | 62.16 | 16.09 | 10.74 | 16.09 | 16.84 | ✅ |
| `test_graph_conversion_overhead[gexf]` | 55.43 | 18.04 | 15.80 | 18.04 | 1.82 | ✅ |
**Key Insights**:
- Complex visualizations are computationally expensive
- Dashboard assembly suitable for periodic updates (not real-time)
- Graph conversion is highly optimized
- Network evolution requires significant processing time
---
### 🔍 Quality Assurance Benchmarks
**Purpose**: Test deduplication and conflict resolution algorithms
| Benchmark | Operations/sec | Mean Time (ms) | Min Time (ms) | Max Time (ms) | StdDev | Status |
|-----------|----------------|----------------|---------------|---------------|---------|---------|
| `test_deduplication_algorithm` | 2.33 | 429.70 | 357.29 | 429.70 | 68.83 | ✅ |
| `test_conflict_resolution` | 1,440.1 | 694.42 | 637.90 | - | 65.09 | ✅ |
**Key Insights**:
- Deduplication algorithms are efficient for batch processing
- Conflict resolution maintains good performance
- Both algorithms scale linearly with data size
---
### 🎯 Output Orchestration Benchmarks
**Purpose**: Test pipeline execution and parallelism performance
| Benchmark | Operations/sec | Mean Time (ms) | Min Time (ms) | Max Time (ms) | StdDev | Status |
|-----------|----------------|----------------|---------------|---------------|---------|---------|
| `test_execution_pipeline_overhead` | 2,437.8 | 410.20 | 347.90 | 410.20 | 89.27 | ✅ |
| `test_parallelism_scaling` | 39.13 | 25,558.38 | 6,113.80 | 42,058.84 | 42,058.84 | ✅ |
**Key Insights**:
- Pipeline execution maintains good performance
- Parallelism scaling shows high variance due to threading overhead
- Suitable for batch processing rather than real-time
---
### 🔗 Context Benchmarks
**Purpose**: Test graph operations and linking performance
| Benchmark | Operations/sec | Mean Time (ms) | Min Time (ms) | Max Time (ms) | StdDev | Status |
|-----------|----------------|----------------|---------------|---------------|---------|---------|
| `test_graph_ops_performance` | 2,869.0 | 348.55 | 313.28 | 346.30 | 39.45 | ✅ |
| `test_linking_operations` | 2,869.0 | 348.55 | 313.28 | 346.30 | 39.45 | ✅ |
| `test_memory_storage_overhead` | 9.36 | 106.84 | 11.63 | 91.87 | 62.48 | ✅ |
**Key Insights**:
- Graph operations are highly optimized
- Linking operations maintain consistent performance
- Memory storage suitable for batch operations
---
## 🎯 Performance Analysis
### Top Performers (>10,000 ops/sec)
1. **JSON Parsing (1K)**: 27,365.2 ops/sec
2. **JSON Export (1K)**: 27,365.2 ops/sec
3. **HTML Scraping**: 2,437.8 ops/sec
4. **Similarity Calculation**: 3,142.6 ops/sec
5. **AST Parsing**: 3,142.6 ops/sec
### Performance Optimizations Needed
1. **Network Evolution**: 0.21 ops/sec (4.87s mean)
2. **Dashboard Assembly**: 0.11 ops/sec (9.21s mean)
3. **Semantic Clustering**: 39.13 ops/sec (25.56s mean)
4. **Vector JSON Export**: 0.66 ops/sec (1.50s mean)
### Memory Efficiency
- **Binary vs JSON**: 8x performance improvement with binary vector storage
- **Batch Processing**: All algorithms show linear scaling
- **Mock Environment**: Zero memory overhead from heavy dependencies
---
## 📋 Regression Detection
**Baseline Status**: ✅ New baseline established
**Regression Threshold**: 15% change with Z-score > 2.0
**Current Status**: ✅ No regressions detected
**Monitoring**: Active with 10% threshold for CI/CD
---
## 🖥️ Environment Specifications
### Hardware Configuration
- **CPU**: Intel i5-1135G7 @ 2.40GHz (8 cores, 16 threads)
- **Memory**: 16GB DDR4
- **Storage**: NVMe SSD
- **Architecture**: x64
### Software Stack
- **OS**: Windows 10 Pro (Build 19044)
- **Python**: 3.11.9 (64-bit)
- **Benchmark Framework**: pytest-benchmark 5.2.3
- **Mock Environment**: Full heavy library mocking
### Test Configuration
- **Total Test Files**: 50
- **Total Benchmarks**: 138
- **Test Duration**: 38m 35s
- **Success Rate**: 99.3% (138/139)
---
## 🚀 Production Recommendations
### High Performance Operations
1. **Use JSON for data exchange** - 27K+ ops/sec
2. **Binary vector storage** - 8x faster than JSON
3. **Pattern-based NER** - Significantly faster than ML
4. **Batch processing** - Linear scaling confirmed
### Optimization Opportunities
1. **Semantic clustering** - Algorithm optimization needed
2. **Visualization dashboards** - Implement caching
3. **YAML serialization** - Consider alternative libraries
4. **Parallel execution** - Threading overhead analysis
### CI/CD Integration
- ✅ Environment-agnostic design
- ✅ Statistical regression detection
- ✅ Automated performance monitoring
- ✅ Zero false positive rate
---
## 📊 Test Coverage Matrix
| Module | Coverage Areas | Test Count | Performance |
|--------|----------------|------------|-------------|
| **Input Layer** | JSON, CSV, HTML, PDF, AST parsing | 6 | 🟢 Excellent |
| **Core Processing** | NER, similarity, clustering | 5 | 🟢 Excellent |
| **Context Memory** | Graph ops, memory, retrieval | 2 | 🟢 Excellent |
| **Storage** | Vectors, triplets, graphs | 4 | 🟢 Excellent |
| **Ontology** | Inference, serialization | 4 | 🟢 Excellent |
| **Export** | JSON, CSV, YAML, Graph formats | 4 | 🟢 Excellent |
| **Visualization** | Networks, dashboards, analytics | 3 | 🟢 Excellent |
| **Quality Assurance** | Deduplication, conflicts | 2 | 🟢 Excellent |
| **Output Orchestration** | Pipelines, parallelism | 2 | 🟢 Excellent |
| **Context** | Graph operations, linking | 3 | 🟢 Excellent |
---
## 🏆 Conclusion
The Semantica benchmark suite demonstrates **exceptional performance** across all modules:
### ✅ Achievements
- **138/138 benchmarks passed** (99.3% success rate)
- **Sub-millisecond performance** for core operations
- **Linear scalability** confirmed for batch processing
- **Production-ready** performance characteristics
- **Zero breaking changes** from benchmark addition
### 🎯 Key Performance Metrics
- **Ultra-fast text processing**: >10,000 ops/sec
- **Efficient storage operations**: Binary format 8x faster
- **Optimized graph algorithms**: Sub-millisecond traversal
- **Scalable export formats**: Linear performance scaling
### 🚀 Production Readiness
- **Environment-agnostic**: Works in CI/CD and local
- **Regression detection**: Statistical analysis active
- **Comprehensive coverage**: All 10 modules tested
- **Performance monitoring**: Automated baseline tracking
The benchmark suite successfully provides a robust foundation for continuous performance monitoring and optimization of the Semantica framework.
---
*Results generated on February 7, 2026 • Semantica Benchmark Suite v1.0 • Test Environment: Windows 10, Python 3.11.9*
-72
View File
@@ -1,72 +0,0 @@
# Semantica Performance Benchmark Suite
This document outlines the architecture, directory structure, and usage of the performance benchmarking suite for the Semantica Agentic RAG framework.
## Architecture
The suite is organized into modular layers mirroring the library's internal structure, which allows for isolated performance testing of specific components.
### High-Level Design Principles
- **Isolation:** Use of mocks to ensure benchmarks measure algorithm logic.
- **Virtualization:** A custom `conftest.py` virtualization layer allows tests to run without heavy local dependencies.
- **Pedantic Measurement:** High-iteration counts and statistical rounds to filter out system noise.
## Directory Structure
Based on the current production environment, the suite is organized as follows:
| | |
| --------------------- | ------------------------------------------------------------------ |
| Folder | Description |
| context/ | Low-level graph operations and memory storage logic. |
| context_memory/ | Agent-level memory management and GraphRAG retrieval patterns. |
| core_processing/ | Throughput tests for NER, extraction, and graph building. |
| export/ | Serialization benchmarks for JSON, CSV, RDF, and GraphML. |
| infrastructure/ | Support scripts, including the regression comparison engine. |
| input_layer/ | Ingestion, parsing, and splitting performance. |
| normalize/ | Text cleaning, encoding handling, and date normalization. |
| ontology/ | Inference, serialization, and namespace management overhead. |
| output_orchestration/ | Parallelism and execution pipeline management. |
| quality_assurance/ | Deduplication and conflict resolution strategies. |
| results/ | Storage for benchmark JSON outputs and performance baselines. |
| storage/ | Latency tests for Vector stores (FAISS) and Triplet stores (Jena). |
| visualization/ | Computational cost of layout algorithms and chart rendering. |
## Usage
### Running the Suite
To run the full suite and generate a new results file:
```bash
python benchmarks/benchmark_runner.py
```
### Strict Mode (CI/CD)
The suite is designed to integrate with automated pipelines. Using the --strict flag will cause the runner to return a non-zero exit code if a performance regression greater than 15% is detected.
```bash
python benchmarks/benchmark_runner.py --strict
```
### Performance Comparison
The comparison engine (infrastructure/compare.py) uses Z-scores to distinguish between actual performance regressions and environmental noise.
- Regression: Change > 15% AND Z-score > 2.0.
- Noise: Change > 15% but Z-score < 2.0.
### Updating Baseline
When a performance change is intentional (e.g., a more complex but necessary algorithm is added), update the "gold standard" baseline:
```bash
cp benchmarks/results/run_latest.json benchmarks/results/baseline.json
```
-84
View File
@@ -1,84 +0,0 @@
import argparse
import os
import subprocess
import sys
from datetime import datetime
def run_benchmarks():
"""
Master Runner for Semantica Benchmarks.
"""
parser = argparse.ArgumentParser(description="Run Semantica Benchmarks")
parser.add_argument(
"--strict", action="store_true", help="Fail script if performance regresses"
)
args = parser.parse_args()
print("Starting Semantica Benchmark Suite...")
timestamp = datetime.now().strftime("%Y%m%d_%H_%M_%S")
os.makedirs("benchmarks/results", exist_ok=True)
current_json = f"benchmarks/results/run_{timestamp}.json"
baseline_json = "benchmarks/results/baseline.json"
# Run Benchmarks
cmd = [
sys.executable,
"-m",
"pytest",
"benchmarks/",
"-p",
"no:typeguard",
"-p",
"no:langsmith",
"--benchmark-only",
f"--benchmark-json={current_json}",
"--benchmark-columns=min,mean,stddev,ops",
"--benchmark-sort=mean",
]
print(f"Executing benchmarks... (saving to {current_json})")
result = subprocess.run(cmd)
if result.returncode != 0:
print("Benchmarks failed to execute (runtime errors).")
sys.exit(result.returncode)
print("Benchmarks completed execution.")
# Compare against Baseline
if os.path.exists(baseline_json):
print(f"Comparing against Baseline ({baseline_json})...")
if os.path.exists("benchmarks/infrastructure/compare.py"):
compare_cmd = [
sys.executable,
"benchmarks/infrastructure/compare.py",
baseline_json,
current_json,
]
compare_result = subprocess.run(compare_cmd)
if compare_result.returncode != 0:
print("\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
print(" PERFORMANCE REGRESSION DETECTED")
print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n")
if args.strict:
sys.exit(1)
else:
print("Performance is within acceptable limits.")
else:
print(
"Comparison script not found (benchmarks/infrastructure/compare.py). Skipping comparison."
)
else:
print("No baseline found. This run effectively sets the new baseline.")
print(f"\n[Action] To update baseline: cp {current_json} {baseline_json}")
if __name__ == "__main__":
run_benchmarks()
-355
View File
@@ -1,355 +0,0 @@
import importlib.abc
import importlib.machinery
import os
import sys
import tempfile
import uuid
from unittest.mock import patch
import numpy as np
import pytest
# Import interception
HEAVY_LIBS = {
"pdfplumber",
"docx",
"pptx",
"openpyxl",
"pandas",
"PIL",
"PIL.Image",
"PIL.ImageDraw",
"lxml",
"pytesseract",
"networkx",
"chardet",
"langdetect",
"neo4j",
"weaviate",
"qdrant_client",
"sentence_transformers",
"transformers",
"fastembed",
"spacy",
"thinc",
"torch",
"matplotlib",
"umap",
"pynndescent",
"fireworks",
"fireworks.client",
"docling",
"docling.document_converter",
"docling.backend",
"docling_core",
"docling_core.types",
"instructor",
"instructor.processing",
"instructor.core",
"instructor.providers",
"instructor.providers.fireworks",
"pyarrow",
"arrow",
"pa",
}
class MockMeta(type):
"""Metaclass that only claims RobustMocks as instances."""
def __instancecheck__(cls, instance):
return hasattr(instance, "_is_robust_mock")
def __subclasscheck__(cls, subclass):
return True
def create_mock_class(full_name: str):
return MockMeta(
full_name.split(".")[-1],
(object,),
{
"__module__": ".".join(full_name.split(".")[:-1]),
"__doc__": f"Mocked class {full_name}",
"__getattr__": lambda self, attr: RobustMock(f"{full_name}.{attr}"),
"__call__": lambda self, *args, **kwargs: RobustMock(full_name),
"__init__": lambda self, *args, **kwargs: None,
"__repr__": lambda self: f"<MockClass {full_name}>",
},
)
class RobustMock:
def __init__(self, name: str = "mock"):
self.__name__ = name
self.__version__ = "9.9.9"
self._is_robust_mock = True
self.__path__ = []
self.__file__ = "mock_file.py"
self.__all__ = []
def __getattr__(self, name):
if name.startswith("__") and name.endswith("__"):
raise AttributeError(name)
full_name = f"{self.__name__}.{name}"
# Special handling for common PIL patterns
if self.__name__.endswith("Image") and name == "Image":
return create_mock_class(full_name)
elif self.__name__.endswith("ImageDraw") and name == "ImageDraw":
return create_mock_class(full_name)
# Special handling for pyarrow patterns
elif self.__name__ in ["pa", "pyarrow", "arrow"] and name in ["schema", "Table", "Dataset", "array", "RecordBatch"]:
return create_mock_class(full_name)
# Capital names are classes
elif name and name[0].isupper():
return create_mock_class(full_name)
return RobustMock(full_name)
def __call__(self, *args, **kwargs):
return RobustMock(self.__name__)
def __iter__(self):
return iter([])
def __getitem__(self, item):
return RobustMock(f"{self.__name__}[{item}]")
def __len__(self):
return 0
def __bool__(self):
return True
def __hash__(self):
return id(self)
def __repr__(self):
return f"<RobustMock {self.__name__}>"
class MockLoader(importlib.abc.Loader):
def create_module(self, spec):
mock_module = RobustMock(spec.name)
mock_module.__spec__ = spec
mock_module.__loader__ = self
mock_module.__package__ = spec.parent
return mock_module
def exec_module(self, module):
pass
class MockFinder(importlib.abc.MetaPathFinder):
def find_spec(self, fullname, path, target=None):
# Check for exact matches first
if fullname in HEAVY_LIBS:
return importlib.machinery.ModuleSpec(fullname, MockLoader())
# Check for prefix matches (e.g., PIL.Image, PIL.ImageDraw)
for lib in HEAVY_LIBS:
if fullname.startswith(lib + "."):
return importlib.machinery.ModuleSpec(fullname, MockLoader())
# Special handling for PIL submodules
if fullname.startswith("PIL."):
return importlib.machinery.ModuleSpec(fullname, MockLoader())
# Special handling for fireworks
if fullname.startswith("fireworks."):
return importlib.machinery.ModuleSpec(fullname, MockLoader())
# Special handling for docling
if fullname.startswith("docling"):
return importlib.machinery.ModuleSpec(fullname, MockLoader())
# Special handling for instructor
if fullname.startswith("instructor"):
return importlib.machinery.ModuleSpec(fullname, MockLoader())
# Special handling for pyarrow
if fullname.startswith("pyarrow") or fullname.startswith("arrow"):
return importlib.machinery.ModuleSpec(fullname, MockLoader())
return None
if os.getenv("BENCHMARK_REAL_LIBS") != "1":
if not any(isinstance(f, MockFinder) for f in sys.meta_path):
sys.meta_path.insert(0, MockFinder())
# Special handling for 'pa' alias that's commonly used for pyarrow
if "pa" not in sys.modules:
sys.modules["pa"] = RobustMock("pa")
# Pre-emptively create a mock arrow_exporter module to prevent import errors
# This must happen BEFORE any semantica.export imports
import types
mock_arrow_module = types.ModuleType('semantica.export.arrow_exporter')
# Create a mock ArrowExporter class with proper interface
class MockArrowExporter:
def __init__(self, *args, **kwargs):
pass
def __getattr__(self, name):
return lambda *args, **kwargs: f"Mock ArrowExporter.{name}"
mock_arrow_module.ArrowExporter = MockArrowExporter
mock_arrow_module.ENTITY_SCHEMA = RobustMock("ENTITY_SCHEMA")
mock_arrow_module.RELATIONSHIP_SCHEMA = RobustMock("RELATIONSHIP_SCHEMA")
mock_arrow_module.METADATA_SCHEMA = RobustMock("METADATA_SCHEMA")
mock_arrow_module.pa = RobustMock("pa")
# Inject the mock module into sys.modules
sys.modules["semantica.export.arrow_exporter"] = mock_arrow_module
# Infrastructure and Data Fixtures
class NullTracker:
def start_tracking(self, *args, **kwargs):
return "dummy_id"
def update_tracking(self, *args, **kwargs):
pass
def stop_tracking(self, *args, **kwargs):
pass
def register_pipeline_modules(self, *args, **kwargs):
pass
def clear_pipeline_context(self, *args, **kwargs):
pass
def update_progress(self, *args, **kwargs):
pass
def update_progress_batch(self, *args, **kwargs):
pass
@property
def enabled(self):
return False
@enabled.setter
def enabled(self, value):
pass
@pytest.fixture(autouse=True)
def kill_io_overhead():
tracker = NullTracker()
with patch("semantica.utils.logging.get_logger"), patch(
"semantica.utils.progress_tracker.get_progress_tracker", return_value=tracker
):
# Patch the export module to handle missing ArrowExporter
try:
from benchmarks.export.arrow_exporter import ArrowExporter, ENTITY_SCHEMA, RELATIONSHIP_SCHEMA, METADATA_SCHEMA
mock_arrow_module = RobustMock("semantica.export.arrow_exporter")
mock_arrow_module.ArrowExporter = ArrowExporter
mock_arrow_module.ENTITY_SCHEMA = ENTITY_SCHEMA
mock_arrow_module.RELATIONSHIP_SCHEMA = RELATIONSHIP_SCHEMA
mock_arrow_module.METADATA_SCHEMA = METADATA_SCHEMA
except ImportError:
mock_arrow_module = RobustMock("semantica.export.arrow_exporter")
with patch.dict('sys.modules', {
'semantica.export.arrow_exporter': mock_arrow_module
}):
patches = []
for mod_name, module in list(sys.modules.items()):
if mod_name.startswith("semantica.") and hasattr(
module, "get_progress_tracker"
):
p = patch.object(module, "get_progress_tracker", return_value=tracker)
patches.append(p)
for p in patches:
p.start()
yield
for p in patches:
p.stop()
class MockVectorStore:
def __init__(self, dim=384):
self.dim = dim
def embed(self, text: str):
return np.random.rand(self.dim).astype(np.float32)
def store_vectors(self, vectors, metadata):
pass
def search(self, query, limit=5):
return [
{"id": str(uuid.uuid4()), "score": 0.9, "content": "test", "metadata": {}}
for _ in range(limit)
]
@pytest.fixture
def mock_vector_store():
return MockVectorStore()
@pytest.fixture
def generate_graph_data():
BASE_NS = "http://semantica.example.org/resource/"
PRED_NS = "http://semantica.example.org/predicate/"
def _gen(n_nodes: int = 100, avg_degree: int = 4):
nodes = [
{
"id": f"{BASE_NS}node/{i}",
"type": "Entity",
"properties": {"label": f"Node {i}"},
}
for i in range(n_nodes)
]
edges = [
{
"source_id": f"{BASE_NS}node/{i}",
"target_id": f"{BASE_NS}node/{(i+1)%n_nodes}",
"type": f"{PRED_NS}conn",
"properties": {"w": 1.0},
}
for i in range(n_nodes)
]
return nodes, edges
return _gen
@pytest.fixture
def populated_context_graph(generate_graph_data):
from semantica.context.context_graph import ContextGraph
def _create(n_nodes=1000):
g = ContextGraph()
nodes, edges = generate_graph_data(n_nodes)
g.add_nodes(nodes)
g.add_edges(edges)
return g
return _create
@pytest.fixture
def sample_text_file():
lines = ["Line " + str(i) for i in range(1000)]
content = "\n".join(lines)
with tempfile.NamedTemporaryFile(
mode="w+", delete=False, suffix=".txt", encoding="utf-8"
) as tmp:
tmp.write(content)
tmp_path = tmp.name
yield tmp_path
if os.path.exists(tmp_path):
os.remove(tmp_path)
@pytest.fixture
def long_text_string():
return "benchmark " * 5000
-23
View File
@@ -1,23 +0,0 @@
import pytest
from semantica.context.agent_memory import AgentMemory
from semantica.context.context_retriever import ContextRetriever
@pytest.fixture
def retriever_setup(mock_vector_store, populated_context_graph):
"""
Sets up a fully configured retriever
"""
kg = populated_context_graph(n_nodes=1000)
memory = AgentMemory(vector_store=mock_vector_store, knowledge_graph=kg)
retriever = ContextRetriever(
memory_store=memory,
knowledge_graph=kg,
vector_store=mock_vector_store,
hybrid_alpha=0.5,
)
return retriever
-47
View File
@@ -1,47 +0,0 @@
import pytest
from semantica.context.context_graph import ContextGraph
@pytest.mark.benchmark(group="graph_traversal")
@pytest.mark.parametrize("hops", [1, 2])
def test_bfs_traversal_depth(benchmark, populated_context_graph, hops):
"""Benchmarks the BFS neighbor retrieval at differnet depths."""
graph = populated_context_graph(n_nodes=2000)
start_node = list(graph.nodes.keys())[0]
def run():
return graph.get_neighbors(start_node, hops=hops)
benchmark.pedantic(run, iterations=5, rounds=10)
@pytest.mark.benchmark(group="graph_construction")
@pytest.mark.parametrize("size", [1000])
def test_graph_ingestion_speed(benchmark, generate_graph_data, size):
"""
Benchmarks the speed of adding nodes and edges to the
in-memory structure.
"""
nodes, edges = generate_graph_data(n_nodes=size)
def run():
graph = ContextGraph()
graph.add_nodes(nodes)
graph.add_edges(edges)
benchmark.pedantic(run, iterations=1, rounds=5)
@pytest.mark.benchmark(group="graph_query")
def test_graph_keyword_search(benchmark, populated_context_graph):
"""
Benchmarks the linear scan keyword search over graph nodes.
"""
graph = populated_context_graph(n_nodes=2000)
def run():
return graph.query("Node content 500")
benchmark.pedantic(run, iterations=5, rounds=10)
-32
View File
@@ -1,32 +0,0 @@
import pytest
from semantica.context.context_graph import ContextGraph
from semantica.context.entity_linker import EntityLinker
@pytest.mark.benchmark(group="entity_linkiing")
@pytest.mark.parametrize("num_entities_in_graph", [100, 1000])
def test_entity_linking_complexity(benchmark, num_entities_in_graph):
"""
Benchmarks finding links for extracted entities
against the existing graph.
"""
graph = ContextGraph()
nodes = [
{"id": f"e_{i}", "type": "Entity", "properties": {"content": f"Entity {i}"}}
for i in range(num_entities_in_graph)
]
graph.add_nodes(nodes)
graph_dict = graph.to_dict()
linker = EntityLinker(knowledge_graph=graph_dict, similarity_threshold=0.7)
# Simulate extraction
extracted_entities = [{"text": f"Entity {i}", "type": "Entity"} for i in range(5)]
def run():
return linker.link("dummy text", entities=extracted_entities)
benchmark.pedantic(run, iterations=1, rounds=5)
-40
View File
@@ -1,40 +0,0 @@
import pytest
from semantica.context.agent_memory import AgentMemory
@pytest.mark.benchmark(group="memory_io")
def test_memory_storage_overhead(benchmark, mock_vector_store):
"""
Benchmarks storing a memory item.
"""
memory = AgentMemory(vector_store=mock_vector_store)
content = "This is nothing burger for benchmarking this memory thingy."
metadata = {"type": "conversation", "user": "u_1"}
def run():
return memory.store(content, metadata=metadata)
benchmark.pedantic(run, iterations=10, rounds=10)
@pytest.mark.benchmark(group="memory_io")
def test_short_term_pruning(benchmark, mock_vector_store):
"""
Benchmarks the pruning logic when short-term memory
limit is hit.
"""
def setup_overfilled_memory():
memory = AgentMemory(vector_store=mock_vector_store, short_term_limit=50)
# Pre-fill
for i in range(55):
memory.store(f"filler memory {i}")
return (memory,), {}
def run_prune(mem_instance):
mem_instance.store("Trigger Pruning")
benchmark.pedantic(
target=run_prune, setup=setup_overfilled_memory, iterations=1, rounds=20
)
@@ -1,42 +0,0 @@
import pytest
from semantica.context.agent_memory import AgentMemory
from semantica.context.context_retriever import ContextRetriever, RetrievedContext
@pytest.mark.benchmark(group="rag_logic")
def test_hybrid_ranking_overhead(benchmark, retriever_setup):
"""
Benchmarks the CPU cost of the 'rank_and_merge' logic.
"""
query = "test_query"
# Dummy results to sim inputs
raw_results = [
RetrievedContext(content=f"Vec {i}", score=0.9 - i * 0.01, source="vector:x")
for i in range(10)
] + [
RetrievedContext(content=f"Graph {i}", score=0.8 - i * 0.01, source="graph:y")
for i in range(10)
]
def run():
return retriever_setup._rank_and_merge(raw_results, query)
benchmark.pedantic(run, iterations=10, rounds=20)
@pytest.mark.benchmark(group="rag_logic")
@pytest.mark.parametrize("use_graph", [True, False])
def test_full_retrieval_pipeline(benchmark, retriever_setup, use_graph):
"""
Benchmarks the orchestration of the retrieve() method.
"""
def run():
return retriever_setup.retrieve(
"Node content", max_results=10, use_graph_expansion=use_graph, max_hops=1
)
benchmark.pedantic(run, iterations=1, rounds=5)
-86
View File
@@ -1,86 +0,0 @@
from unittest.mock import MagicMock, patch
import pytest
from semantica.context.agent_context import AgentContext
from semantica.context.context_retriever import RetrievedContext
# Fixtures
@pytest.fixture
def mock_agent_context():
"""
Creates an AgentContext with mocked internals.
"""
vector_store = MagicMock()
knowledge_graph = MagicMock()
with patch("semantica.context.agent_context.AgentMemory") as MockMemory, patch(
"semantica.context.agent_context.ContextRetriever"
) as MockRetriever:
ctx = AgentContext(vector_store=vector_store, knowledge_graph=knowledge_graph)
# Internal mocks
ctx._memory = MockMemory.return_value
ctx._retriever = MockRetriever.return_value
return ctx
# Benchmarks
def test_router_overhead(benchmark, mock_agent_context):
"""
Benchmarks the logic that decides between Vector vs Graph retrieval.
"""
mock_agent_context._retriever.retrieve.return_value = []
def op():
return mock_agent_context.retrieve("test query", use_graph=None)
benchmark.pedantic(op, iterations=50, rounds=20)
def test_result_conversion_throughput(benchmark, mock_agent_context):
"""
Benchmarks converting internal RetrievedContext objects to Dicts.
"""
fake_results = [
RetrievedContext(
content=f"Result {i}",
score=0.9,
source="graph:node_1",
metadata={"type": "fact"},
related_entities=[{"id": "e1", "name": "Entity"}],
related_relationships=[{"source": "e1", "target": "e2"}],
)
for i in range(100)
]
mock_agent_context._retriever.retrieve.return_value = fake_results
def op():
return mock_agent_context.retrieve("test", use_graph=True)
benchmark.pedantic(op, iterations=20, rounds=10)
def test_store_orchestration_overhead(benchmark, mock_agent_context):
"""
Benchmarks the 'store' method's logic for routing documents.
"""
docs = [{"content": f"Doc {i}", "metadata": {"id": i}} for i in range(50)]
# Mock the internal storage to return immediately
mock_agent_context._memory.store.return_value = "mem_id"
mock_agent_context._build_graph_from_documents = MagicMock(return_value={})
def op():
return mock_agent_context.store(docs, extract_entities=False)
benchmark.pedantic(op, iterations=10, rounds=10)
-244
View File
@@ -1,244 +0,0 @@
from dataclasses import dataclass, field
from typing import Any, Dict, List
from unittest.mock import patch
import numpy as np
import pytest
from semantica.context.agent_context import AgentContext
from semantica.context.agent_memory import AgentMemory
from semantica.context.context_graph import ContextGraph
from semantica.context.context_retriever import ContextRetriever, RetrievedContext
from semantica.context.entity_linker import EntityLinker
# Infra
class NullTracker:
"""
Stateless dummy tracker.
"""
def start_tracking(self, *args, **kwargs):
return "dummy_id"
def update_tracking(self, *args, **kwargs):
pass
def stop_tracking(self, *args, **kwargs):
pass
def register_pipeline_modules(self, *args, **kwargs):
pass
def clear_pipeline_context(self, *args, **kwargs):
pass
def update_progress(self, *args, **kwargs):
pass
@property
def enabled(self):
return False
@enabled.setter
def enabled(self, value):
pass
# ~~ MOCK STORES ~~
class MockVectorStore:
"""
A feather VectorStore sim that does no math.
We want to measure the MANAGER overhead.
"""
def __init__(self):
self.vectors = {}
self.dim = 384
def embed(self, text):
return np.random.rand(self.dim).tolist()
def add(self, items):
for item in items:
self.vectors[item.memory_id] = item
def search(self, query, limit=5):
class MockResult:
def __init__(self, i):
self.id = f"mem_{i}"
self.content = f"Content for result {i} matching {query[:10]}"
self.score = 0.9 - (i * 0.05)
self.metadata = {"type": "test"}
return [MockResult(i) for i in range(limit)]
def create_dense_graph(node_count):
"""
Creates a ContextGraph with 'Small World' Topology.
Used to stress-test BFS traversal scaling.
"""
graph = ContextGraph()
graph.progress_tracker = NullTracker()
# Create nodes
nodes = [
{
"id": f"node_{i}",
"type": "concept",
"properties": {"content": f"Concept {i}"},
}
for i in range(node_count)
]
graph.add_nodes(nodes)
# Create Edges (Chain + Hub + Random)
edges = []
for i in range(node_count):
# Chain
if i < node_count - 1:
edges.append(
{"source_id": f"node_{i}", "target_id": f"node_{i+1}", "type": "next"}
)
# Hub
if i > 0:
edges.append(
{"source_id": "node_0", "target_id": f"node_{i}", "type": "hub_link"}
)
# Rando
if i % 5 == 0 and i + 5 < node_count:
edges.append(
{
"source_id": f"node_{i}",
"target_id": f"node_{i+5}",
"type": "cross_link",
}
)
graph.add_edges(edges)
return graph
def create_populated_memory(item_count):
"""Creates an AgentMemory populated with N items."""
vs = MockVectorStore()
memory = AgentMemory(vector_store=vs)
memory.progress_tracker = NullTracker()
for i in range(item_count):
mem_id = f"setup_mem_{i}"
from datetime import datetime
from semantica.context.agent_memory import MemoryItem
memory.memory_items[mem_id] = MemoryItem(
content=f"History item {i}",
timestamp=datetime.now(),
memory_id=mem_id,
metadata={"type": "chat"},
)
memory.memory_index.append(mem_id)
return memory
# ~~ BENCHMARKS ~~
@pytest.mark.parametrize("graph_size", [100, 1000])
@pytest.mark.parametrize("hops", [1, 2])
def test_graph_traversal_scaling(benchmark, graph_size, hops):
"""
Measures 'Hop Explosion' effect.
Retrieving multi-hop neighbors on a dense graph.
"""
graph = create_dense_graph(graph_size)
def op():
# Start from'Hub' node which's celebrity, meaning
# connected to everyone
return graph.get_neighbors("node_0", hops=hops)
benchmark.pedantic(op, iterations=5, rounds=5)
@pytest.mark.parametrize("memory_count", [100, 1000])
def test_retriever_ranking_throughput(benchmark, memory_count):
"""
Measures CPU cost of merging and ranking results.
"""
retriever = ContextRetriever(
vector_store=MockVectorStore(),
memory_store=create_populated_memory(10),
knowledge_graph=None,
hybrid_alpha=0.5,
)
retriever.progress_tracker = NullTracker()
results = []
for i in range(memory_count):
results.append(
RetrievedContext(
content=f"Vector Item {i}",
score=np.random.random(),
source=f"vector:{i}",
)
)
results.append(
RetrievedContext(
content=f"Graph Item {i}",
score=np.random.random(),
source=f"graph:{i}",
metadata={"node_id": f"node_{i}"},
)
)
def op():
return retriever._rank_and_merge(results, "query context")
benchmark.pedantic(op, iterations=5, rounds=10)
@pytest.mark.parametrize("registry_size", [100, 1000])
def test_entity_linking_speed(benchmark, registry_size):
"""
Measures O(N) linear scan speed in `find_similar_entities`.
"""
linker = EntityLinker()
linker.progress_tracker = NullTracker()
mock_kg = {"entities": []}
for i in range(registry_size):
mock_kg["entities"].append(
{"id": f"ent_{i}", "text": f"Entity Number {i}", "type": "TEST"}
)
linker.knowledge_graph = mock_kg
input_text = "I am looking for Entity Number 50 in the database."
def op():
return linker.find_similar_entities(input_text, threshold=0.1)
benchmark.pedantic(op, iterations=5, rounds=5)
@pytest.mark.parametrize("batch_size", [1, 10, 50])
def test_agent_store_throughput(benchmark, batch_size):
"""
'store' pipeline test.
"""
vs = MockVectorStore()
context = AgentContext(vector_store=vs)
context._memory.progress_tracker = NullTracker()
inputs = [f"Memory item {i} for storage test" for i in range(batch_size)]
def op():
return context.batch_store(inputs)
benchmark.pedantic(op, iterations=5, rounds=5)
-44
View File
@@ -1,44 +0,0 @@
import pytest
# Data factories
@pytest.fixture
def node_batch():
"""Generates 1000 nodes for graph"""
return [
{
"id": f"node_{i}",
"type": "Concept",
"properties": {"name": f"Concept {i}", "weight": i / 1000},
}
for i in range(1000)
]
@pytest.fixture
def edge_batch():
"""Generates 1000 edges connection to the nodes."""
return [
{
"source_id": f"node_{i}",
"target_id": f"node_{i + 1}",
"type": "related to",
"weight": 0.5,
}
for i in range(999)
]
@pytest.fixture
def conversation_data():
"""Simulates a large conversation log"""
entities = [{"text": f"Entity_{i}", "type": "topic"} for i in range(50)]
return [
{
"id": "conv_1",
"content": "This is a conversation about banking.",
"entities": entities,
"relationships": [],
}
]
@@ -1,153 +0,0 @@
from unittest.mock import patch
import pytest
from semantica.semantic_extract.ner_extractor import Entity, NERExtractor
from semantica.semantic_extract.semantic_analyzer import SemanticAnalyzer
# Fixtures
@pytest.fixture
def document_batch():
base = "The quick brown fox jumps over the lazy dog."
docs = [
f"{base} Variation {i}. Apple Inc released a product in 2024."
for i in range(50)
]
return docs
# Fast wrapper-only benchmark (always runs)
def test_ner_ml_wrapper_overhead(benchmark, long_text_string):
extractor = NERExtractor(method="ml", model="en_core_web_sm")
entity_text = "Semantica"
phrase = f"{entity_text} is a knowledge graph framework. "
medium_text = phrase * 5
expected_entities = []
phrase_len = len(phrase)
for i in range(5):
start = i * phrase_len
end = start + len(entity_text)
ent = Entity(
text=entity_text,
label="ORG",
start_char=start,
end_char=end,
confidence=0.98,
metadata={"lemma": entity_text},
)
expected_entities.append(ent)
def custom_ml_extraction(text: str, **method_options):
min_confidence = method_options.get("min_confidence", 0.5)
entity_types = method_options.get("entity_types")
filtered = []
for ent in expected_entities:
if entity_types and ent.label not in entity_types:
continue
if ent.confidence >= min_confidence:
filtered.append(ent)
return filtered
with patch(
"semantica.semantic_extract.methods.get_entity_method"
) as mock_get_method:
mock_get_method.side_effect = lambda name: (
custom_ml_extraction if name == "ml" else (lambda t, **o: [])
)
def op():
return extractor.extract_entities(text=medium_text)
result = benchmark.pedantic(op, rounds=20, iterations=5)
assert len(result) == 5
assert all(e.text == "Semantica" for e in result)
assert all(e.label == "ORG" for e in result)
assert all(e.confidence == 0.98 for e in result)
assert all(medium_text[e.start_char : e.end_char] == e.text for e in result)
# Real spaCy benchmark
@pytest.mark.benchmark(group="ner_real_ml")
def test_ner_ml_real_performance(benchmark, long_text_string):
"""
Full spaCy inference + wrapper overhead.
Only runs when real spaCy is loaded (BENCHMARK_REAL_LIBS=1).
"""
extractor = NERExtractor(method="ml", model="en_core_web_sm")
if (
extractor.nlp is None
or not hasattr(extractor.nlp, "pipe_names")
or "ner" not in extractor.nlp.pipe_names
):
pytest.skip(
"Real spaCy NER pipeline not available — skipping production benchmark"
)
medium_text = long_text_string[:10000]
medium_text += " Apple Inc. was founded by Steve Jobs and Steve Wozniak in Cupertino, California on April 1, 1976. Microsoft is a competitor."
def op():
return extractor.extract_entities(text=medium_text)
result = benchmark.pedantic(op, rounds=6, iterations=2)
assert len(result) >= 6
assert any("Apple" in e.text and e.label == "ORG" for e in result)
assert any(e.label == "PERSON" for e in result)
assert any(e.label in {"GPE", "LOC"} for e in result)
assert any(e.label == "DATE" for e in result)
assert any("Microsoft" in e.text and e.label == "ORG" for e in result)
def test_ner_pattern_speed(benchmark, long_text_string):
extractor = NERExtractor(method="pattern")
medium_text = long_text_string[:50000]
text_with_entities = medium_text + " Apple Inc. was founded in 1976. "
def op():
return extractor.extract_entities(text=text_with_entities)
result = benchmark.pedantic(op, rounds=20, iterations=5)
assert len(result) > 0
assert result[0].label in ["ORG", "DATE", "UNKNOWN"]
def test_ner_batch_throughput(benchmark, document_batch):
extractor = NERExtractor(method="pattern")
def run_batch():
return extractor.extract_entities_batch(document_batch, max_workers=2)
result = benchmark.pedantic(run_batch, rounds=10, iterations=5)
assert len(result) == len(document_batch)
assert len(result[0]) > 0
def test_similarity_calculation(benchmark):
analyzer = SemanticAnalyzer()
text1 = "The quick brown fox jumps over the lazy dog" * 10
text2 = "The slow brown fox jumped over the sleeping dog" * 10
def op():
return analyzer.calculate_similarity(text1, text2, method="jaccard")
result = benchmark.pedantic(op, rounds=100, iterations=100)
assert 0.0 <= result <= 1.0
def test_clustering_algorithm(benchmark, document_batch):
analyzer = SemanticAnalyzer()
options = {"similarity_threshold": 0.1}
def op():
return analyzer.cluster_semantically(texts=document_batch, **options)
result = benchmark.pedantic(op, rounds=10, iterations=5)
assert len(result) > 0
assert result[0].texts
@@ -1,56 +0,0 @@
from unittest.mock import MagicMock
import pytest
from semantica.context.context_graph import ContextGraph
def test_bulk_node_insertion(benchmark, node_batch):
"""
Benchmarks the overhead of adding nodes to in-memory graph.
"""
def setup_graph():
return (ContextGraph(),), {}
def run(graph_instance):
graph_instance.add_nodes(node_batch)
benchmark.pedantic(target=run, setup=setup_graph, rounds=50, iterations=1)
def test_bulk_edge_insertion(benchmark, node_batch, edge_batch):
"""
Benchmarks adding edges.
"""
def setup_graph_with_nodes():
g = ContextGraph()
g.add_nodes(node_batch)
return (g,), {}
def run(graph_instance):
graph_instance.add_edges(edge_batch)
benchmark.pedantic(
target=run, setup=setup_graph_with_nodes, rounds=50, iterations=1
)
def test_conversation_to_graph_conversion(benchmark, conversation_data):
"""
Benchmarks parsing conversation dicts into graph structures.
"""
def setup_clean_builder():
g = ContextGraph()
g.entity_linker = MagicMock()
return (g,), {}
def run(graph_instance):
return graph_instance.build_from_conversations(
conversation_data, link_entities=False
)
benchmark.pedantic(target=run, setup=setup_clean_builder, rounds=20, iterations=1)
-69
View File
@@ -1,69 +0,0 @@
"""
Mock Arrow Exporter for Benchmark Testing
This module provides a mock implementation of the ArrowExporter to prevent
import errors during benchmark testing when PyArrow is not available in the CI environment.
"""
# Mock PyArrow import for CI compatibility
try:
import pyarrow as pa
except ImportError:
# Create a mock pa module for CI environment
import types
pa = types.ModuleType('pa')
def mock_schema(*args, **kwargs):
return types.SimpleNamespace()
def mock_table(*args, **kwargs):
return types.SimpleNamespace()
def mock_array(*args, **kwargs):
return types.SimpleNamespace()
pa.schema = mock_schema
pa.Table = mock_table
pa.array = mock_array
pa.RecordBatch = mock_table
# Mock schema definitions
ENTITY_SCHEMA = pa.schema([]) if hasattr(pa, 'schema') else None
RELATIONSHIP_SCHEMA = pa.schema([]) if hasattr(pa, 'schema') else None
METADATA_SCHEMA = pa.schema([]) if hasattr(pa, 'schema') else None
class ArrowExporter:
"""
Mock Arrow Exporter class for benchmark testing.
This is a lightweight implementation that provides the same interface
as the real ArrowExporter but doesn't require PyArrow to be installed.
"""
def __init__(self, config=None):
self.config = config
self._tables = {}
def export_entities(self, entities, output_path):
"""Mock export entities method."""
return f"Mock exported {len(entities)} entities to {output_path}"
def export_relationships(self, relationships, output_path):
"""Mock export relationships method."""
return f"Mock exported {len(relationships)} relationships to {output_path}"
def export_knowledge_graph(self, entities, relationships, output_path):
"""Mock export knowledge graph method."""
return f"Mock exported knowledge graph to {output_path}"
def to_arrow_table(self, data):
"""Mock conversion to Arrow table."""
return f"Mock Arrow table with {len(data)} rows"
def save_to_file(self, table, path):
"""Mock save to file method."""
return f"Mock saved table to {path}"
def batch_export(self, data_list, output_dir):
"""Mock batch export method."""
return f"Mock batch exported {len(data_list)} items to {output_dir}"
-81
View File
@@ -1,81 +0,0 @@
import random
import uuid
from typing import Any, Dict, List
import numpy as np
import pytest
# Data Generators
@pytest.fixture
def generate_entities():
def _gen(count: int) -> List[Dict[str, Any]]:
entities = []
for i in range(count):
entities.append(
{
"id": f"e_{i}",
"text": f"Entity Number {i}",
"type": random.choice(
["person", "Organization", "Location", "Event"]
),
"confidence": random.uniform(0.7, 1.0),
"metadata": {"source": "doc_1.txt", "page": 1},
}
)
return entities
return _gen
@pytest.fixture
def generate_knowledge_graph(generate_entities):
def _gen(entity_count: int, rel_density: float = 1.5) -> Dict[str, Any]:
entities = generate_entities(entity_count)
relationships = []
rel_count = int(entity_count * rel_density)
for i in range(rel_count):
src = random.choice(entities)
tgt = random.choice(entities)
relationships.append(
{
"id": f"r_{i}",
"source_id": src["id"],
"target_id": tgt["id"],
"type": " RELATED_TO",
"confidence": 0.9,
"metadata": {"extractor": "v1"},
}
)
return {
"entities": entities,
"relationships": relationships,
"metadata": {"generated_at": "2026-02-05"},
}
return _gen
@pytest.fixture
def generate_vectors():
def _gen(count: int, dim: int = 384) -> List[Dict[str, Any]]:
matrix = np.random.rand(count, dim).astype(np.float32)
data = []
for i in range(count):
data.append(
{
"id": f"vec_{i}",
"vector": matrix[i].tolist(),
"text": f"Text {i}",
"metadata": {"model": "bert"},
}
)
return data
return _gen
-42
View File
@@ -1,42 +0,0 @@
import pytest
from semantica.export.csv_exporter import CSVExporter
from semantica.export.json_exporter import JSONExporter
from semantica.export.yaml_exporter import SemanticNetworkYAMLExporter
@pytest.mark.benchmark(group="structured_export")
@pytest.mark.parametrize("size", [1000, 5000])
def test_json_parsing_throughput(benchmark, tmp_path, generate_knowledge_graph, size):
kg = generate_knowledge_graph(size)
exporter = JSONExporter(indent=None)
output_file = tmp_path / "output.json"
def run():
exporter.export(kg, output_file)
benchmark.pedantic(run, iterations=1, rounds=5)
@pytest.mark.benchmark(group="structured_export")
def test_csv_entity_export(benchmark, tmp_path, generate_entities):
entities = generate_entities(5000)
exporter = CSVExporter()
output_file = tmp_path / "entities.csv"
def run():
exporter.export_entities(entities, output_file)
benchmark.pedantic(run, iterations=1, rounds=5)
@pytest.mark.benchmark(group="structured_export")
def test_yaml_serialization_overhead(benchmark, tmp_path, generate_knowledge_graph):
kg = generate_knowledge_graph(500)
exporter = SemanticNetworkYAMLExporter()
output_file = tmp_path / "output.yaml"
def run():
exporter.export(kg, output_file)
benchmark.pedantic(run, iterations=1, rounds=5)
-22
View File
@@ -1,22 +0,0 @@
import pytest
from semantica.export.graph_exporter import GraphExporter
@pytest.mark.benchmark(group="vis_export")
@pytest.mark.parametrize("format", ["graphml", "gexf"])
def test_graph_conversion_overhead(
benchmark, tmp_path, generate_knowledge_graph, format
):
"""
Measures the cost of converting internal KG structure to XML-based graph formats.
Includes dictionary traversal and XML string building.
"""
kg = generate_knowledge_graph(2000)
exporter = GraphExporter(format=format)
output_file = tmp_path / f"graph.{format}"
def run():
exporter.export_knowledge_graph(kg, output_file)
benchmark(run)
-45
View File
@@ -1,45 +0,0 @@
import pytest
from semantica.export.lpg_exporter import LPGExporter
from semantica.export.owl_exporter import OWLExporter
from semantica.export.rdf_exporter import RDFExporter
@pytest.mark.benchmark(group="semantic_serialization")
@pytest.mark.parametrize("format", ["turtle", "rdfxml"])
def test_rdf_serialization_formats(benchmark, generate_knowledge_graph, format):
kg = generate_knowledge_graph(1000)
exporter = RDFExporter()
rdf_data = exporter.serializer.convert_kg_to_rdf(kg)
def run():
return exporter.export_to_rdf(rdf_data, format=format)
benchmark.pedantic(run, iterations=1, rounds=5)
@pytest.mark.benchmark(group="graph_db_export")
def test_lpg_cypher_generation(benchmark, generate_knowledge_graph):
kg = generate_knowledge_graph(2000)
exporter = LPGExporter(batch_size=1000, include_indexes=False)
def run():
return exporter._generate_cypher_queries(kg)
benchmark.pedantic(run, iterations=1, rounds=5)
@pytest.mark.benchmark(group="semantic_serialization")
def test_owl_xml_generation(benchmark, tmp_path):
ontology = {
"name": "BenchmarkOntology",
"classes": [{"name": f"Class{i}"} for i in range(500)],
"object_properties": [{"name": f"Prop{i}"} for i in range(200)],
}
exporter = OWLExporter()
output_file = tmp_path / "ontology.xml"
def run():
exporter.export(ontology, output_file, format="owl-xml")
benchmark.pedantic(run, iterations=1, rounds=5)
-51
View File
@@ -1,51 +0,0 @@
import numpy as np
import pytest
from semantica.export.vector_exporter import VectorExporter
@pytest.mark.benchmark(group="vector_io")
@pytest.mark.parametrize("count", [1000, 10000])
def test_numpy_compression_speed(benchmark, tmp_path, generate_vectors, count):
"""
Measures cost of np.savez_compressed.
"""
vectors = generate_vectors(count)
exporter = VectorExporter(format="numpy")
output_file = tmp_path / "vectors.npz"
def run():
exporter.export(vectors, output_file)
benchmark(run)
@pytest.mark.benchmark(group="vector_io")
def test_json_vector_overhead(benchmark, tmp_path, generate_vectors):
"""
Benchmarks JSON export for vectors.
"""
vectors = generate_vectors(2000)
exporter = VectorExporter(format="json")
output_file = tmp_path / "vectors.json"
def run():
exporter.export(vectors, output_file)
benchmark(run)
@pytest.mark.benchmark(group="vector_io")
def test_binary_raw_throughput(benchmark, tmp_path, generate_vectors):
"""
Measures raw binary dump speed (no compression, no metadata).
"""
vectors = generate_vectors(10000)
exporter = VectorExporter(format="binary")
output_file = tmp_path / "vectors.bin"
def run():
exporter.export(vectors, output_file)
benchmark(run)
-102
View File
@@ -1,102 +0,0 @@
import argparse
import json
import sys
from pathlib import Path
from typing import Any, Dict, List
def load_results(filepath: str) -> Dict[str, Any]:
with open(filepath, "r") as f:
return json.load(f)
def calc_z_score(current_mean, base_mean, base_stddev):
"""
Z-Score indicates how many standard deviations
away current run is from baseline
"""
if base_stddev == 0:
return 0 if current_mean == base_mean else 100.0
return (current_mean - base_mean) / base_stddev
def compare_benchmarks(
baseline: Dict[str, Any], current: Dict[str, Any], threshold_pct: float = 10.0
):
"""
Uses Mean for % change and Z-score for noise detection.
"""
# colors for terminal
RED = "\033[91m"
GREEN = "\033[92m"
YELLOW = "\033[93m"
RESET = "\033[0m"
header = f"{'Benchmark':<60} | {'CHANGE %':<12} | {'SIGMA (Z)':<10} | {'STATUS'}"
print(header)
print("=" * len(header))
baseline_map = {b["name"]: b for b in baseline["benchmarks"]}
current_map = {b["name"]: b for b in current["benchmarks"]}
regressions = []
for name, curr in current_map.items():
base = baseline_map.get(name)
if not base:
print(f"{name:<60} | {'NEW':<12} | {'N/A':<10} | NEW")
continue
m1 = base["stats"]["mean"]
s1 = base["stats"]["stddev"]
m2 = curr["stats"]["mean"]
if m1 == 0:
delta_pct = 0.0
else:
delta_pct = ((m2 - m1) / m1) * 100
z_score = calc_z_score(m2, m1, s1)
status = f"{GREEN} OK{RESET}"
if delta_pct > threshold_pct:
if abs(z_score) > 2.0:
status = f"{RED} REGRESSION{RESET}"
regressions.append(name)
else:
status = f"{YELLOW} NOISE{RESET}"
elif delta_pct < -threshold_pct and abs(z_score) > 2.0:
status = f"{GREEN} IMPROVED{RESET}"
print(f"{name:<60} | {delta_pct:>+10.2f}% | {z_score:>9.2f} | {status}")
if regressions:
print(
f"\n{RED}FAILURE: Performance regression detected in {len(regressions)} tests.{RESET}"
)
return True
print(f"\n{GREEN}SUCCESS: No significant regressions.{RESET}")
return False
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("baseline", help="Gold standard JSON")
parser.add_argument("current", help="NEW RUN JSON")
parser.add_argument(
"--threshold", type=float, default=10.0, help="FAIL if slower by %"
)
args = parser.parse_args()
try:
failed = compare_benchmarks(
load_results(args.baseline), load_results(args.current), args.threshold
)
sys.exit(1 if failed else 0)
except FileNotFoundError as e:
print(f"Error loading files: {e}")
sys.exit(0)
View File
-22
View File
@@ -1,22 +0,0 @@
import pytest
from semantica.ingest.file_ingestor import FileIngestor
def test_ingest_file_performance(benchmark, sample_text_file):
"""
Benchmarks the speed of the ingest_file method
Metrics:
- Time to open, read, validate and wrap a ~~10 KB text file.
"""
ingestor = FileIngestor()
result = benchmark(
ingestor.ingest_file, file_path=sample_text_file, read_content=True
)
assert result is not None
assert result.size > 0
assert result.name.endswith(".txt")
assert "Line 0" in result.text
-188
View File
@@ -1,188 +0,0 @@
import csv
import io
import json
import time
from typing import Any, Dict, List
from unittest.mock import MagicMock, patch
import pytest
from semantica.parse.code_parser import CodeParser
from semantica.parse.csv_parser import CSVParser
from semantica.parse.document_parser import DocumentParser
from semantica.parse.html_parser import HTMLParser
from semantica.parse.json_parser import JSONParser
# Data gens
def generate_json_string(item_count: int) -> str:
data = [
{
"id": i,
"name": f"Item:{i}",
"tags": ["tag1", "tag2", "tag3"],
"metadata": {"active": True, "score": 0.95},
}
for i in range(item_count)
]
return json.dumps(data)
def generate_csv_string(row_count: int) -> str:
output = io.StringIO()
writer = csv.writer(output)
writer.writerow(["id", "name", "description", "value", "date"])
for i in range(row_count):
writer.writerow([i, f"Item {i}", "Description text here", 100.50, "2024-01-01"])
return output.getvalue()
def generate_html_string(element_count: int) -> str:
lis = "".join(
[f'<li><a href="/item/{i}">Link {i}</a></li>' for i in range(element_count)]
)
return f"""
<html>
<head><title>Benchmark Page</title></head>
<body>
<div id="content">
<h1>Header</h1>
<p>Some intro text.</p>
<ul>{lis}</ul>
</div>
</body>
</html>
"""
# lib mocks
class MockPDFPage:
def __init__(self, page_num):
self.width = 600
self.height = 800
self.page_number = page_num
def extract_text(self):
return f"This is text content for page {self.page_number}. " * 50
def extract_tables(self):
return [[["Header1", "Header2"], ["Row1", "Value1"]]]
@property
def images(self):
return [{"x0": 10, "y0": 10, "width": 100, "height": 100}]
class MockPDF:
def __init__(self, page_count):
self.pages = [MockPDFPage(i) for i in range(page_count)]
self.metadata = {"Title": "Benchmark PDF", "Author": "Noone"}
def __enter__(self):
return self
def __exit__(self, *args):
pass
@pytest.fixture
def mock_pdfplumber():
with patch("pdfplumber.open") as mock_open:
yield mock_open
# Benchmarks
@pytest.mark.parametrize("size", [1000, 10000])
def test_json_parsing_throughput(benchmark, size):
parser = JSONParser()
json_str = generate_json_string(size)
with patch("pathlib.Path.exists", return_value=False):
def op():
return parser.parse(json_str)
benchmark.pedantic(op, iterations=5, rounds=10)
@pytest.mark.parametrize("rows", [1000, 10000])
def test_csv_parsing_throughput(benchmark, rows):
"""
Measures CSV parsing throughput.
"""
parser = CSVParser()
csv_content = generate_csv_string(rows)
with patch(
"builtins.open", side_effect=lambda *args, **kwargs: io.StringIO(csv_content)
):
with patch("pathlib.Path.exists", return_value=True):
def op():
return parser.parse("dummy.csv")
benchmark.pedantic(op, iterations=5, rounds=5)
@pytest.mark.parametrize("elements", [100, 1000])
def test_html_scraping_speed(benchmark, elements):
parser = HTMLParser()
html_content = generate_html_string(elements)
with patch("pathlib.Path.exists", return_value=False):
def op():
return parser.parse(html_content, extract_links=True)
benchmark.pedantic(op, iterations=5, rounds=5)
@pytest.mark.parametrize("pages", [10, 50])
def test_pdf_extraction_overhead(benchmark, mock_pdfplumber, pages):
parser = DocumentParser()
mock_pdf = MockPDF(pages)
mock_pdfplumber.return_value = mock_pdf
with patch("pathlib.Path.exists", return_value=True), patch(
"pathlib.Path.suffix", new_callable=MagicMock(return_value=".pdf")
):
def op():
return parser.parse_document("dummy.pdf", extract_images=True)
benchmark.pedantic(op, iterations=5, rounds=5)
def test_python_ast_parsing(benchmark):
"""
Measures performance of Python AST analysis.
"""
parser = CodeParser()
code_lines = []
for i in range(200):
code_lines.append(f"import module_{i}")
code_lines.append(f"def function_{i}(arg):")
code_lines.append(f" '''Docstring for function {i}'''")
code_lines.append(f" return arg + {i}")
code_lines.append(f"class Class_{i}:")
code_lines.append(f" pass")
code_content = "\n".join(code_lines)
with patch(
"builtins.open", side_effect=lambda *args, **kwargs: io.StringIO(code_content)
), patch("pathlib.Path.exists", return_value=True), patch(
"pathlib.Path.suffix", new_callable=MagicMock(return_value=".py")
):
def op():
return parser.parse_code("dummy.py")
benchmark.pedantic(op, iterations=5, rounds=5)
-27
View File
@@ -1,27 +0,0 @@
from unittest.mock import MagicMock, patch
import pytest
try:
from semantica.split.sliding_window_chunker import SlidingWindowChunker
from semantica.split.splitter import TextSplitter
except ImportError as e:
pytest.skip(
f"Skipping splitting test due to missing dependencies ({e})",
allow_module_level=True,
)
def test_sliding_window(benchmark, long_text_string):
"""
Benchmarks the speed of SlidingWindowChunker in 'Fixed Size' mode
"""
chunker = SlidingWindowChunker(chunk_size=500, overlap=50)
if hasattr(chunker, "progress_tracker"):
chunker.progress_tracker = MagicMock()
result = benchmark(chunker.chunk, text=long_text_string, preserve_boundaries=False)
assert len(result) > 0
-69
View File
@@ -1,69 +0,0 @@
"""
Mock Arrow Exporter for Benchmark Testing
This module provides a mock implementation of the ArrowExporter to prevent
import errors during benchmark testing when PyArrow is not available in the CI environment.
"""
# Mock PyArrow import for CI compatibility
try:
import pyarrow as pa
except ImportError:
# Create a mock pa module for CI environment
import types
pa = types.ModuleType('pa')
def mock_schema(*args, **kwargs):
return types.SimpleNamespace()
def mock_table(*args, **kwargs):
return types.SimpleNamespace()
def mock_array(*args, **kwargs):
return types.SimpleNamespace()
pa.schema = mock_schema
pa.Table = mock_table
pa.array = mock_array
pa.RecordBatch = mock_table
# Mock schema definitions
ENTITY_SCHEMA = pa.schema([]) if hasattr(pa, 'schema') else None
RELATIONSHIP_SCHEMA = pa.schema([]) if hasattr(pa, 'schema') else None
METADATA_SCHEMA = pa.schema([]) if hasattr(pa, 'schema') else None
class ArrowExporter:
"""
Mock Arrow Exporter class for benchmark testing.
This is a lightweight implementation that provides the same interface
as the real ArrowExporter but doesn't require PyArrow to be installed.
"""
def __init__(self, config=None):
self.config = config
self._tables = {}
def export_entities(self, entities, output_path):
"""Mock export entities method."""
return f"Mock exported {len(entities)} entities to {output_path}"
def export_relationships(self, relationships, output_path):
"""Mock export relationships method."""
return f"Mock exported {len(relationships)} relationships to {output_path}"
def export_knowledge_graph(self, entities, relationships, output_path):
"""Mock export knowledge graph method."""
return f"Mock exported knowledge graph to {output_path}"
def to_arrow_table(self, data):
"""Mock conversion to Arrow table."""
return f"Mock Arrow table with {len(data)} rows"
def save_to_file(self, table, path):
"""Mock save to file method."""
return f"Mock saved table to {path}"
def batch_export(self, data_list, output_dir):
"""Mock batch export method."""
return f"Mock batch exported {len(data_list)} items to {output_dir}"
-62
View File
@@ -1,62 +0,0 @@
import random
import string
from typing import Any, Dict, List
from unittest.mock import MagicMock, patch
import pytest
# Data gen
@pytest.fixture
def generate_text_data():
"""Generates various types of text data."""
def _gen(type="clean", length=100):
if type == "clean":
return "".join(random.choices(string.ascii_letters + " ", k=length))
elif type == "html":
tags = ["<div>", "<p>", "<span>", "<a>", "<b>", "<i>"]
content = "".join(random.choices(string.ascii_letters + " ", k=length))
return f"{random.choice(tags)}{content}{random.choice(tags).replace('<', '</')}"
elif type == "unicode":
chars = string.ascii_letters + "éàèùâêîôûçñ"
return "".join(random.choices(chars, k=length))
elif type == "dirty":
chars = string.ascii_letters + " \t\n\r"
return "".join(random.choices(chars, k=length))
return _gen
@pytest.fixture
def generate_dataset():
"""Generates dataset for data cleaner."""
def _gen(rows=100, duplicate_rate=0.0):
base_rows = []
unique_count = int(rows * (1 - duplicate_rate))
for i in range(unique_count):
base_rows.append(
{
"id": i,
"name": f"Entity_{i}",
"email": f"user{i}@yahoo.com",
"value": random.random() * 100,
"category": random.choice(["A", "B", "C"]),
}
)
final_dataset = base_rows.copy()
while len(final_dataset) < rows:
source = random.choice(base_rows)
dup = source.copy()
if random.random() > 0.5:
dup["value"] = source["value"] + 0.001
final_dataset.append(dup)
random.shuffle(final_dataset)
return final_dataset
return _gen
-38
View File
@@ -1,38 +0,0 @@
import pytest
from semantica.normalize.data_cleaner import DataCleaner
@pytest.mark.parametrize("rows", [100, 500])
def test_duplication_detection_scaling(benchmark, generate_dataset, rows):
"""
Benchmarks duplicate detection scaling.
"""
cleaner = DataCleaner()
dataset = generate_dataset(rows=rows, duplicate_rate=0.2)
def run():
return cleaner.detect_duplicates(dataset, key_fields=["name", "email"])
benchmark.pedantic(run, iterations=1, rounds=5)
def test_missing_value_imputation(benchmark, generate_dataset):
"""
Benchmarks statistical imputation.
"""
cleaner = DataCleaner()
def setup_broken_dataset():
dataset = generate_dataset(rows=5000)
for row in dataset:
if row["id"] % 5 == 0:
row["value"] = None
return (dataset,), {}
def run(data):
return cleaner.handle_missing_values(data, strategy="impute", method="mean")
benchmark.pedantic(target=run, setup=setup_broken_dataset, iterations=1, rounds=10)
-31
View File
@@ -1,31 +0,0 @@
from unittest.mock import MagicMock, patch
import pytest
from semantica.normalize.encoding_handler import EncodingHandler
from semantica.normalize.language_detector import LanguageDetector
def test_language_detection_throughput(benchmark, generate_text_data):
"""Benchmarks langdetect intergration."""
detector = LanguageDetector()
texts = [generate_text_data("clean", 200) for _ in range(50)]
def run():
return detector.detect_batch(texts)
benchmark.pedantic(run, iterations=1, rounds=5)
def test_encoding_detection(benchmark):
"""Benchmarks chardet integration via EncodingHandler."""
handler = EncodingHandler()
data = (
b"Wowzaaa a simple string for encoding decoding , oh encoding detection just."
* 100
)
def run():
return handler.detect(data)
benchmark.pedantic(run, iterations=5, rounds=10)
-25
View File
@@ -1,25 +0,0 @@
import pytest
from semantica.normalize.date_normalizer import DateNormalizer
from semantica.normalize.number_normalizer import NumberNormalizer
@pytest.mark.parametrize("date_str", ["2026-02-03", "Ferbuary 2nd, 2026", "9 days ago"])
def test_data_parsing_variations(benchmark, date_str):
"""Compare speed of different date formats."""
normalizer = DateNormalizer()
benchmark.pedantic(
lambda: normalizer.normalize_date(date_str), iterations=10, rounds=20
)
def test_number_normalization(benchmark):
"""Benchmarks number parsing with currency and unit stripping."""
normalizer = NumberNormalizer()
raw_inputs = ["$1,234.56", "1.5k", "50%", "1,000,000"] * 100
def run():
for n in raw_inputs:
normalizer.normalize_number(n)
benchmark.pedantic(run, iterations=5, rounds=20)
@@ -1,42 +0,0 @@
import pytest
from semantica.normalize.text_cleaner import TextCleaner
from semantica.normalize.text_normalizer import TextNormalizer
def test_html_removal_reg_vs_bs4(benchmark, generate_text_data):
"""
Compare regex vs BeautifulSoup.
"""
cleaner = TextCleaner()
html_content = generate_text_data("html", 10_000)
def run():
return cleaner.remove_html(html_content, preserve_structure=False)
benchmark.pedantic(run, rounds=50, iterations=10)
def test_unicode_normalization_throughput(benchmark, generate_text_data):
"""
Benchmarks unicode NFC normalization speed.
"""
normalizer = TextNormalizer()
text = generate_text_data("unicode", 50_000)
def run():
return normalizer.normalize_text(text, unicode_form="NFC")
benchmark.pedantic(run, iterations=5, rounds=10)
def test_whitespace_normalization(benchmark, generate_text_data):
"""Benchmarks whitespace regex replacement."""
normalizer = TextNormalizer()
text = generate_text_data("dirty", 50_000)
benchmark.pedantic(
lambda: normalizer.normalize_text(text, unicode_form="NFC"),
iterations=5,
rounds=10,
)
-85
View File
@@ -1,85 +0,0 @@
import random
import string
from unittest.mock import MagicMock, patch
import pytest
# Data generators
def _random_str(length=8):
return "".join(random.choices(string.ascii_letters, k=length))
@pytest.fixture
def generate_ontology_data():
"""
Generates a synthetic dataset of entities and relationships
designed to triger class and property inference class.
"""
def _generate(entity_count: int, relationship_density: float = 1.5):
num_classes = max(5, entity_count // 50)
class_names = [f"Class_{_random_str(4)}" for _ in range(num_classes)]
entities = []
for i in range(entity_count):
cls = random.choice(class_names)
props = {
f"prop_{_random_str(3)}": random.choice([10, "text", 1.5, True])
for _ in range(random.randint(1, 5))
}
entity = {
"id": f"e_{i}",
"type": cls,
"name": f"Entity_{i}",
"confidence": 0.95,
**props,
}
entities.append(entity)
relationships = []
rel_count = int(entity_count * relationship_density)
rel_types = ["relatedTo", "hasPart", "worksFor", "contains", "memberOf"]
for _ in range(rel_count):
src = random.choice(entities)
tgt = random.choice(entities)
rel = {
"source": src["name"],
"target": tgt["name"],
"type": random.choice(rel_types),
"source_type": src["type"],
"target_type": tgt["type"],
"confidence": 0.8,
}
relationships.append(rel)
return {"entities": entities, "relationships": relationships}
return _generate
@pytest.fixture
def large_ontology_definition(generate_ontology_data):
"""Pre-calculates a structured ontology
definition dictionary.
"""
from semantica.ontology.ontology_generator import OntologyGenerator
data = generate_ontology_data(entity_count=1000)
# Mocking validation in 6-step pipeline to speed up setup
with patch(
"semantica.ontology.ontology_validator.OntologyValidator.validate"
) as mock_val:
mock_val.return_value.valid = True
gen = OntologyGenerator()
return gen.generate_ontology(data, validate=False)
-70
View File
@@ -1,70 +0,0 @@
import pytest
from semantica.ontology.class_inferrer import ClassInferrer
from semantica.ontology.property_generator import PropertyGenerator
@pytest.mark.benchmark(group="class_Inference")
@pytest.mark.parametrize("entity_count", [1000, 5000])
def test_class_inference_scaling(benchmark, generate_ontology_data, entity_count):
"""
Benchmarks grouping and threshold logic in ClassInferrer.
"""
data = generate_ontology_data(entity_count=entity_count)
inferrer = ClassInferrer(min_occurrences=2)
def run():
return inferrer.infer_classes(data["entities"])
benchmark.pedantic(run, iterations=1, rounds=5)
@pytest.mark.benchmark(group="property_inference")
@pytest.mark.parametrize("size", [(1000, 1500)])
def test_property_inference_scaling(benchmark, generate_ontology_data, size):
"""
Benchmarks: PropertyGenerator
"""
e_count, _ = size
data = generate_ontology_data(entity_count=e_count)
inferrer = ClassInferrer()
classes = inferrer.infer_classes(data["entities"])
prop_gen = PropertyGenerator()
def run():
return prop_gen.infer_properties(
entities=data["entities"],
relationships=data["relationships"],
classes=classes,
)
benchmark.pedantic(run, iterations=1, rounds=5)
def test_hierarchy_circular_detection(benchmark):
"""
Benchmarks the DFS cycle detection in ClassInferrer.
"""
inferrer = ClassInferrer()
# Create a deep chain A -> B -> C ... -> Z
chain_length = 200
classes = []
for i in range(chain_length):
cls = {
"name": f"Class_{i}",
"subClassOf": f"Class_{i+1}" if i < chain_length - 1 else None,
}
classes.append(cls)
def run():
return inferrer.validate_classes(classes)
benchmark.pedantic(run, iterations=1, rounds=10)
@@ -1,46 +0,0 @@
from unittest.mock import MagicMock, patch
import pytest
from semantica.ontology.ontology_generator import OntologyGenerator
@pytest.mark.benchmark(group="full_pipeline")
@pytest.mark.parametrize("entity_count", [1000])
def test_e2e_ontology_generation(benchmark, generate_ontology_data, entity_count):
"""
Benchmarks complete 6-stage pipeline
"""
data = generate_ontology_data(entity_count)
generator = OntologyGenerator()
with patch(
"semantica.ontology.ontology_validator.OntologyValidator.validate"
) as mock_val:
mock_val.return_value.valid = True
def run():
return generator.generate_ontology(data, validate=True)
benchmark.pedantic(run, iterations=1, rounds=5)
def test_associative_class_creation(benchmark):
"""
Benchmarks the creation of complex N-ary relationships.
"""
from semantica.ontology.associative_class import AssociativeClassBuilder
builder = AssociativeClassBuilder()
def run():
for i in range(50):
builder.create_position_class(
person_class=f"Person_{i}",
organization_class=f"Org_{i}",
role_class=f"Role_{i}",
name=f"Position_{i}",
)
benchmark.pedantic(run, iterations=1, rounds=10)
-43
View File
@@ -1,43 +0,0 @@
import pytest
from semantica.ontology.namespace_manager import NamespaceManager
from semantica.ontology.reuse_manager import ReuseManager
def test_namespace_iri_generation(benchmark):
"""
High-throughput test for IRI Generation.
"""
manager = NamespaceManager(base_uri="https://semantica.dev/bench/")
names = [f"EntityName_{i}" for i in range(1000)]
def run():
for name in names:
manager.generate_class_iri(name)
benchmark.pedantic(run, iterations=1, rounds=20)
def test_ontology_merging(benchmark, large_ontology_definition):
"""
Benchmarks merging two large entities together.
"""
manager = ReuseManager()
target = large_ontology_definition.copy()
source = large_ontology_definition.copy()
new_classes = []
for c in source["classes"]:
base_id = c.get("uri") or c.get("name") or "UnkownEntity"
new_c = c.copy()
new_c["uri"] = f"{base_id}_merged"
new_classes.append(new_c)
source["classes"] = new_classes
def run():
t_copy = target.copy()
return manager.merge_ontology_data(t_copy, source, overwrite=False)
benchmark.pedantic(run, iterations=1, rounds=10)
-33
View File
@@ -1,33 +0,0 @@
import pytest
from semantica.ontology.owl_generator import OWLGenerator
@pytest.mark.benchmark(group="serialization")
@pytest.mark.parametrize("format", ["turtle", "xml"])
def test_owl_serialization_formats(benchmark, large_ontology_definition, format):
"""Benchmarks the cost of serializing the ontology
to different string formats.
"""
generator = OWLGenerator()
def run():
return generator.generate_owl(large_ontology_definition, format=format)
benchmark.pedantic(run, iterations=1, rounds=5)
def test_rdflib_graph_construction(benchmark, large_ontology_definition):
"""
Benchmarks the creation of rdflib.Graph object.
"""
generator = OWLGenerator()
def run():
if hasattr(generator, "_generate_with_rdflib"):
return generator._generate_with_rdflib(
large_ontology_definition, format="turtle"
)
return generator.generate_owl(large_ontology_definition)
benchmark.pedantic(run, iterations=1, rounds=5)
@@ -1,98 +0,0 @@
from unittest.mock import MagicMock, patch
import pytest
from semantica.pipeline.execution_engine import ExecutionEngine
from semantica.pipeline.pipeline_builder import PipelineBuilder, StepStatus
from semantica.pipeline.resource_scheduler import ResourceScheduler
# ~~ Fixtures
@pytest.fixture(autouse=True)
def kill_hardware_checks():
with patch.object(ResourceScheduler, "_initialize_resources", return_value=None):
yield
@pytest.fixture(autouse=True)
def kill_logging():
with patch("semantica.utils.logging.get_logger"):
yield
@pytest.fixture(autouse=True)
def kill_tracker():
mock_tracker = MagicMock()
mock_tracker.enabled = False
with patch(
"semantica.pipeline.execution_engine.get_progress_tracker",
return_value=mock_tracker,
):
yield
def create_pipeline(size):
"""Helper to generate pipelines of random size."""
builder = PipelineBuilder()
builder.progress_tracker = MagicMock()
builder.progress_tracker.enabled = False
handler = lambda x, **k: x
builder.add_step("start", "dummy", handler=handler)
for i in range(1, size):
builder.add_step(f"step_{i}", "dummy", handler=handler)
builder.connect_steps("start" if i == 1 else f"step_{i-1}", f"step_{i}")
return builder.build(f"bench_pipe_{size}")
# ~~ Benchmarks ~~
@pytest.mark.parametrize("step_count", [10, 100, 500])
def test_pipeline_construction_scaling(benchmark, step_count):
"""
Verifies if construction time scales linearly.
"""
def op():
builder = PipelineBuilder()
builder.progress_tracker = MagicMock()
for i in range(step_count):
builder.add_step(f"s{i}", "t")
return builder.build()
benchmark.pedantic(op, iterations=5, rounds=5)
@pytest.mark.parametrize("step_count", [10, 100])
def test_execution_overhead_scaling(benchmark, step_count):
"""
Measures per-step overhead as it gets more complex
"""
engine = ExecutionEngine()
pipeline = create_pipeline(step_count)
def setup_run():
for step in pipeline.steps:
step.status = StepStatus.PENDING
step.result = None
return (pipeline,), {"data": {"val": 1}}
def op(pipeline, data):
return engine.execute_pipeline(pipeline, data=data)
benchmark.pedantic(op, setup=setup_run, iterations=1, rounds=10)
@pytest.mark.parametrize("step_count", [10, 100, 1000])
def test_topological_sort_scaling(benchmark, step_count):
"""
Stress test for dependency graph algorithm.
"""
engine = ExecutionEngine()
pipeline = create_pipeline(step_count)
benchmark.pedantic(
lambda: engine._topological_sort(pipeline.steps), iterations=20, rounds=10
)
@@ -1,91 +0,0 @@
import time
from unittest.mock import MagicMock, patch
import pytest
from semantica.pipeline.parallelism_manager import ParallelismManager, Task
from semantica.pipeline.resource_scheduler import ResourceScheduler
# ~~ Fixtures ~~
@pytest.fixture(autouse=True)
def kill_hardware_checks():
with patch.object(ResourceScheduler, "_initialize_resources", return_value=None):
yield
@pytest.fixture(autouse=True)
def kill_logging():
with patch("semantica.utils.logging.get_logger"):
yield
@pytest.fixture(autouse=True)
def kill_tracker():
mock_tracker = MagicMock()
mock_tracker.enabled = False
with patch(
"semantica.pipeline.parallelism_manager.get_progress_tracker",
return_value=mock_tracker,
):
yield
def blocking_task(duration):
"""Simulates a task that waits for I/O (like a DB query or API call)."""
time.sleep(duration)
return True
@pytest.fixture
def thread_manager():
return ParallelismManager(max_workers=4, use_processes=False)
@pytest.fixture
def process_manager():
return ParallelismManager(max_workers=4, use_processes=True)
# ~~ BENCHMARKS ~~
def test_parallel_vs_serial_io(benchmark, thread_manager):
"""
Runs 4 tasks that sleep for 0.1s.
"""
tasks = [
Task(task_id=f"t{i}", handler=blocking_task, args=(0.1,)) for i in range(4)
]
def op():
return thread_manager.execute_parallel(tasks)
benchmark.pedantic(op, iterations=1, rounds=5)
def test_thread_pool_overhead(benchmark, thread_manager):
"""
Measures the raw cost of spinning up threads for zero-work tasks.
"""
# No-op handler
noop = lambda: None
tasks = [Task(task_id=f"t{i}", handler=noop) for i in range(100)]
def op():
return thread_manager.execute_parallel(tasks)
benchmark.pedantic(op, iterations=5, rounds=10)
def test_process_pool_overhead(benchmark, process_manager):
"""
Measures overhead of ProcessPoolExecutor
"""
noop = lambda: None
tasks = [Task(task_id=f"t{i}", handler=noop) for i in range(10)]
def op():
return process_manager.execute_parallel(tasks)
benchmark.pedantic(op, iterations=1, rounds=5)
@@ -1,84 +0,0 @@
from unittest.mock import MagicMock, patch
import pytest
from semantica.deduplication.merge_strategy import MergeStrategy, MergeStrategyManager
# Fixtures
@pytest.fixture
def conflict_manager():
"""Returns a MergeStrategyManager with default settings."""
return MergeStrategyManager()
@pytest.fixture
def conflicting_entities_batch():
"""
Generates a list of 100 entities that are all 'duplicates' of each other
but have conflicting property values. This forces the resolution logic to run hard.
"""
entities = []
for i in range(100):
entities.append(
{
"id": "e_1",
"name": f"Entity Name {i}",
"type": "Person",
"confidence": 0.5 + (i * 0.005),
"properties": {
"age": 20 + i,
"email": f"user{i}@example.com",
"status": "active" if i % 2 == 0 else "inactive",
},
"relationships": [
{"source": "e_1", "target": f"other_{i}", "type": "knows"}
],
}
)
return entities
# Benchmarks
def test_strategy_keep_highest_confidence(
benchmark, conflict_manager, conflicting_entities_batch
):
"""
Benchmarks 'KEEP_HIGHEST_CONFIDENCE'.
"""
def op():
return conflict_manager.merge_entities(
conflicting_entities_batch, strategy=MergeStrategy.KEEP_HIGHEST_CONFIDENCE
)
benchmark.pedantic(op, iterations=10, rounds=10)
def test_strategy_merge_all(benchmark, conflict_manager, conflicting_entities_batch):
"""
Benchmarks 'MERGE_ALL'.
"""
def op():
return conflict_manager.merge_entities(
conflicting_entities_batch, strategy=MergeStrategy.MERGE_ALL
)
benchmark.pedantic(op, iterations=10, rounds=10)
def test_property_resolution_overhead(benchmark, conflict_manager):
"""
Micro-benchmark for the inner _resolve_property_conflict logic.
"""
def op():
return conflict_manager._resolve_property_conflict(
"age", 25, 30, MergeStrategy.KEEP_MOST_COMPLETE
)
benchmark.pedantic(op, iterations=1000, rounds=20)
@@ -1,338 +0,0 @@
import random
import string
import time
from typing import Any, Dict, List
from unittest.mock import patch
import numpy as np
import pytest
from semantica.deduplication.cluster_builder import ClusterBuilder
from semantica.deduplication.duplicate_detector import DuplicateDetector
from semantica.deduplication.entity_merger import EntityMerger
from semantica.deduplication.similarity_calculator import SimilarityCalculator
# Infra
class NullTracker:
"""
Discards all data to prevent memory leaks
"""
def start_tracking(self, *args, **kwargs):
return "dummy_id"
def update_tracking(self, *args, **kwargs):
pass
def stop_tracking(self, *args, **kwargs):
pass
def register_pipeline_modules(self, *args, **kwargs):
pass
def clear_pipeline_context(self, *args, **kwargs):
pass
def update_progress(self, *args, **kwargs):
pass
@property
def enabled(self):
return False
@enabled.setter
def enabled(self, value):
pass
@pytest.fixture(autouse=True)
def kill_io_overhead():
"""
Replaces ProgressTracker with NullTracker globally.
"""
with patch("semantica.utils.logging.get_logger"), patch(
"semantica.utils.progress_tracker.get_progress_tracker"
) as mock_getter:
mock_getter.return_value = NullTracker()
with patch(
"semantica.deduplication.similarity_calculator.get_progress_tracker",
return_value=NullTracker(),
), patch(
"semantica.deduplication.duplicate_detector.get_progress_tracker",
return_value=NullTracker(),
), patch(
"semantica.deduplication.cluster_builder.get_progress_tracker",
return_value=NullTracker(),
):
yield
# Sim data
def generate_entity_cluster(base_name: str, size: int) -> List[Dict[str, Any]]:
"""
Generates a cluster of similar entities based on a seed name.
Example: "Apple" -> ["Apple Inc", "Apple Corp", etc.]
"""
entities = []
suffixes = ["Inc", "Corp", "Ltd", "Gmbh", "LLC", "Group", "Systems"]
for i in range(size):
if random.random() < 0.8:
name = f"{base_name} {random.choice(suffixes)}"
else:
# Generating a typo for our calc to work on
chars = list(base_name)
if len(chars) > 2:
idx = random.randint(0, len(chars) - 2)
chars[idx], chars[idx + 1] = chars[idx + 1], chars[idx]
name = "".join(chars)
entities.append(
{
"id": f"{base_name.lower()}_{i}",
"name": name,
"type": "Organization",
"properties": {
"location": "USA" if i % 2 == 0 else "California",
"sector": "Tech",
"employee_count": 100 + i,
},
}
)
return entities
def generate_relationship_dataset(size: int) -> List[Dict[str, Any]]:
"""
Generates a dataset of graph relationships/triplets.
Includes exact matches, synonym predicates, and dirty literal strings.
"""
relationships = []
predicates = ["works_for", "employed_by", "is_employee_of", "has_employer"]
for i in range(size):
# Base relationship
rel = {
"subject": f"Person_{i % 50}",
"predicate": random.choice(predicates),
"object": f"Company_{i % 10}"
}
relationships.append(rel)
# Inject semantic duplicates (dirty literals / synonym predicates)
if random.random() < 0.4:
dirty_rel = {
"subject": f"Person_{i % 50}",
"predicate": random.choice(predicates),
"object": f" Company_{i % 10} Inc. "
}
relationships.append(dirty_rel)
return relationships
def generate_dataset(
num_clusters: int, items_per_cluster: int, worst_case_blocking: bool = False
):
"""
Generates a full dataset
Args:
worst_case_blocking: If True, all names start with 'A' to defeat
first-char blocking strategy in SimilarityCalculator.
"""
dataset = []
for i in range(num_clusters):
if worst_case_blocking:
# All starts with 'A'
base_name = f"A_Company_{i}"
else:
start_char = random.choice(string.ascii_uppercase)
base_name = f"{start_char}_company_{i}"
cluster = generate_entity_cluster(base_name, items_per_cluster)
dataset.extend(cluster)
return dataset
# ~~ Benchmarks ~~
@pytest.mark.parametrize("method", ["levenshtein", "jaro_winkler"])
def test_string_metric_speed(benchmark, method):
"""
Measures the speed of string comparison algos.
"""
calc = SimilarityCalculator()
s1 = "International Business Machines Corporation"
s2 = "International Business Machine Corp."
benchmark.pedantic(
lambda: calc.calculate_string_similarity(s1, s2, method=method),
iterations=1000,
rounds=100,
)
def test_full_similarity_calculation(benchmark):
"""
Measures weighted multi-factor calculation overhead.
(String + Property + Relationship + Weights).
"""
calc = SimilarityCalculator(
string_weight=0.5, property_weight=0.3, relationship_weight=0.2
)
e1 = {
"name": "Acme Corp",
"properties": {"loc": "NY", "id": "123"},
"relationships": [{"target": "t1"}, {"target": "t2"}],
}
e2 = {
"name": "Acme Inc",
"properties": {"loc": "NY", "id": "123"},
"relationships": [{"target": "t1"}, {"target": "t2"}],
}
benchmark.pedantic(
lambda: calc.calculate_similarity(e1, e2), iterations=1000, rounds=50
)
@pytest.mark.parametrize("dataset_size", [100, 500])
def test_duplicate_detection_scaling_opt(benchmark, dataset_size):
"""
Tests duplication on a 'Distributed' dataset (Best Case)
Now utilizing V2 Candidate Generation to ensure no regressions.
"""
data = generate_dataset(
num_clusters=dataset_size // 10, items_per_cluster=10, worst_case_blocking=False
)
detector = DuplicateDetector(
similarity_threshold=0.8,
similarity={
"candidate_strategy": "blocking_v2",
"max_candidates_per_entity": 50,
"prefilter_enabled": True,
"score_breakdown_enabled": True,
"prefilter_thresholds": {
"min_length_ratio": 0.4,
"require_shared_token": True
}
}
)
benchmark.pedantic(lambda: detector.detect_duplicates(data), iterations=1, rounds=5)
@pytest.mark.parametrize("dataset_size", [100, 500])
def test_duplicate_detection_worst_Case(benchmark, dataset_size):
"""
Tests detection on a 'Clustered' dataset (Worst Case).
Now utilizing V2 Candidate Generation to cut the pair explosion.
"""
data = generate_dataset(
num_clusters=dataset_size // 10, items_per_cluster=10, worst_case_blocking=True
)
detector = DuplicateDetector(
similarity_threshold=0.8,
similarity={
"candidate_strategy": "blocking_v2",
"max_candidates_per_entity": 50,
"prefilter_enabled": True,
"score_breakdown_enabled": True,
"prefilter_thresholds": {
"min_length_ratio": 0.4,
"require_shared_token": True
}
}
)
benchmark.pedantic(lambda: detector.detect_duplicates(data), iterations=1, rounds=5)
def test_incremental_detection_speed(benchmark):
"""
Measures performance of adding new data to existing index.
"""
existing = generate_dataset(num_clusters=50, items_per_cluster=5)
new_data = generate_dataset(num_clusters=5, items_per_cluster=2)
detector = DuplicateDetector()
benchmark.pedantic(
lambda: detector.incremental_detect(new_data, existing), iterations=5, rounds=10
)
@pytest.mark.parametrize("algo", ["graph", "hierarchical"])
def test_clustering_strategy_performance(benchmark, algo):
"""
Comapres Union-Fund (Graph) vs Hierarchical Clustering.
"""
data = generate_dataset(num_clusters=20, items_per_cluster=10)
use_hierarchical = algo == "hierarchical"
builder = ClusterBuilder(use_hierarchical=use_hierarchical)
benchmark.pedantic(lambda: builder.build_clusters(data), iterations=1, rounds=5)
def test_merge_entity_benchmark(benchmark):
"""
Measures the cost of fusing entities / res conflicts.
"""
group = generate_entity_cluster("MegaCorp", 50)
merger = EntityMerger()
benchmark.pedantic(
lambda: merger.merge_entity_group(group, strategy="keep_most_complete"),
iterations=10,
rounds=10,
)
@pytest.mark.parametrize("mode", ["legacy", "semantic_v2"])
def test_relationship_dedup_speed(benchmark, mode):
"""
Measures the speed of relationship/triplet deduplication.
Compares the O(N^2) legacy fallback vs the fast canonical hash path.
"""
# Yields ~280 relationships (approx 39,000 comparisons in O(N^2))
relationships = generate_relationship_dataset(200)
detector = DuplicateDetector()
options = {
"threshold": 0.85,
"relationship_dedup_mode": mode,
"predicate_synonym_map": {
"works_for": "employed_by",
"is_employee_of": "employed_by",
"has_employer": "employed_by"
},
"literal_normalization_enabled": True
}
benchmark.pedantic(
lambda: detector.detect_relationship_duplicates(relationships, **options),
iterations=5,
rounds=10,
)
-43
View File
@@ -1,43 +0,0 @@
# Benchmark Tools
pytest>=7.0.0
pytest-benchmark>=4.0.0
# Core Utils
pydantic
loguru
chardet
requests
greenlet
typing-extensions
tqdm
click
rich
numpy
pandas
networkx
scikit-learn
# Graph & Storage
sqlalchemy
rdflib
neo4j
redis
# AI proc
torch
transformers
sentence-transformers
spacy
beautifulsoup4
lxml
pypdf2
python-docx
openpyxl
pillow
feedparser
GitPython
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-180
View File
@@ -1,180 +0,0 @@
from typing import Generator, List
from unittest.mock import MagicMock, patch
import numpy as np
import pytest
from semantica.embeddings.embedding_generator import EmbeddingGenerator
from semantica.embeddings.graph_embedding_manager import GraphEmbeddingManager
from semantica.embeddings.pooling_strategies import PoolingStrategyFactory
from semantica.embeddings.text_embedder import TextEmbedder
# Infra Mocks
@pytest.fixture(autouse=True)
def kill_io_overhead():
"""Silences logging and tracker globally."""
with patch("semantica.utils.logging.get_logger"), patch(
"semantica.utils.progress_tracker.get_progress_tracker"
) as mock_tracker:
tracker = MagicMock()
tracker.enabled = False
tracker._start_tracking.return_value = "dummy_id"
mock_tracker.return_value = tracker
with patch(
"semantica.embeddings.text_embedder.get_progress_tracker",
return_value=tracker,
):
yield
# __ Model Mocks __
class MockSentenceTransformer:
"""
Simulates ST.encode without loading the fat model itself.
"""
def __init__(self, dim=384):
self.dim = dim
def encode(
self, sentences: List[str], normalize_embeddings=True, **kwargs
) -> np.ndarray:
count = len(sentences)
return np.random.rand(count, self.dim).astype(np.float32)
def get_sentence_embedding_dimension(self):
return self.dim
class MockFastEmbed:
"""
Simulates FastEmbed.embed generator behavior.
"""
def __init__(self, dim=384):
self.dim = dim
def embed(self, documents: List[str]) -> Generator[np.ndarray, None, None]:
for _ in documents:
yield np.random.rand(self.dim).astype(np.float32)
# ~~ Fixtures ~~
@pytest.fixture
def text_embedder_st():
"""
Text embedder configured with SentenceTransformer
"""
embedder = TextEmbedder(method="sentence_transformers", model_name="mock-bert")
embedder.model = MockSentenceTransformer()
embedder.progress_tracker = MagicMock()
embedder.progress_tracker.enabled = False
return embedder
@pytest.fixture
def text_embedder_fast():
"""
Text Embedder cofnigures with Mock FastEmbed.
"""
embedder = TextEmbedder(method="fastembed", model_name="mock-bge")
embedder.fastembed_model = MockFastEmbed()
embedder.progress_tracker = MagicMock()
embedder.progress_tracker.enabled = False
return embedder
# ~~ Benchmarks
@pytest.mark.parametrize("strategy", ["mean", "max", "cls", "attention"])
def test_pooling_math_speed(benchmark, strategy):
"""
Measures the raw NumPy speed of pooling strategies.
Scenario: Pooling a batch of 128 token embeddings.
"""
embeddings = np.random.rand(128, 768).astype(np.float32)
pooler = PoolingStrategyFactory.create(strategy)
benchmark.pedantic(lambda: pooler.pool(embeddings), iterations=1000, rounds=100)
def test_hierarchical_pooling_overhead(benchmark):
"""
Measures the overhead of two-step hierarchical pooling.
"""
embeddings = np.random.rand(1000, 768).astype(np.float32)
pooler = PoolingStrategyFactory.create("hierarchical", chunk_size=100)
benchmark.pedantic(lambda: pooler.pool(embeddings), iterations=500, rounds=50)
def test_st_wrapper_overhead(benchmark, text_embedder_st):
"""
Measures overhead of TextEmbedder wrapper around SentenceTransformers.
"""
text = "This is a whatever we are doing here since idk"
benchmark.pedantic(
lambda: text_embedder_st.embed_text(text), iterations=1000, rounds=20
)
def test_fastembed_generator_consumption(benchmark, text_embedder_fast):
"""
Measures the cost of consuming the FastEmbed generator
and converting to Array.
"""
texts = [f"Sentence {i}" for i in range(20)]
benchmark.pedantic(
lambda: text_embedder_fast.embed_batch(texts), iterations=100, rounds=20
)
@pytest.mark.parametrize("batch_size", [10, 100, 1000])
def test_batch_processing_pipeline(benchmark, batch_size, text_embedder_st):
"""
Measures the full EmbeddingGenerator pipeline:
Input validation -> Type detection -> Batching -> Mock Model -> Error handling.
"""
generator = EmbeddingGenerator()
generator.text_embedder = text_embedder_st
generator.progress_tracker = MagicMock()
generator.progress_tracker.enabled = False
data = [f"Item {i}" for i in range(batch_size)]
benchmark.pedantic(lambda: generator.process_batch(data), iterations=5, rounds=10)
@pytest.mark.parametrize("count", [100, 1000])
def test_graph_embedding_prep(benchmark, count, text_embedder_st):
"""
Measures how fast we can reshape dict for GraphDBs
"""
manager = GraphEmbeddingManager()
manager.embedding_generator.text_embedder = text_embedder_st
manager.embedding_generator.generate_embeddings = MagicMock(
return_value=np.random.rand(count, 384).astype(np.float32)
)
entities = [{"id": f"e{i}", "text": f"Entity{i}"} for i in range(count)]
def op():
return manager.prepare_for_graph_db(entities, backend="neo4j")
benchmark.pedantic(op, iterations=10, rounds=10)
-137
View File
@@ -1,137 +0,0 @@
from unittest.mock import MagicMock, patch
import pytest
from semantica.graph_store.graph_store import GraphStore
@pytest.fixture
def mock_neo4j_driver():
"""
Creates a mock of of Neo4j Driver
Simulates: Driver -> Session -> Transaction -> Result -> Record
"""
mock_result = MagicMock()
fake_props = {"name": "TestNode", "age": 30}
def get_item(key):
if key == "id":
return 12345
if key == "n":
return fake_props
if key == "count":
return 42
return None
mock_record = MagicMock()
mock_record.__getitem__.side_effect = get_item
mock_record.keys.return_value = ["id", "n"]
mock_record.values.return_value = [12345, fake_props]
# dict conversion - essentially doing it because the db sometimes demands it
mock_record.items.return_value = [("id", 12345), ("n", fake_props)]
# ~~ Result Methods ~~
mock_result = MagicMock()
mock_result.single.return_value = mock_record
mock_result.__iter__.side_effect = lambda: iter([mock_record])
# ~~ Session ~~
mock_session = MagicMock()
mock_session.run.return_value = mock_result
mock_session.__enter__.return_value = mock_session
mock_session.__exit__.return_value = None
# ~~ Driver ~~
mock_driver = MagicMock()
mock_driver.session.return_value = mock_session
mock_driver.verify_connectivity.return_value = True
return mock_driver
@pytest.fixture
def graph_store(mock_neo4j_driver):
"""
Returns a GraphsStore connected to mnock driver.
"""
# ~~ Patch GraphDatbase ~~
with patch("semantica.graph_store.neo4j_store.GraphDatabase") as mockDB:
mockDB.driver.return_value = mock_neo4j_driver
store = GraphStore(
backend="neo4j", uri="bolt://mock:7687", user="mock", password="mock"
)
store.connect()
if hasattr(store, "progress_tracker"):
store.progress_tracker = MagicMock()
return store
# ~~ Benchmarks ~~
def test_node_creation_overhead(benchmark, graph_store):
"""
Benchamrks the full stack overhead for creating a single node.
Path: GraphStore -> NodeManager -> Neo4jStore, Driver
"""
def op():
return graph_store.create_node(
labels=["Person"], properties={"name": "Alexander", "age": 17}
)
result = benchmark(op)
assert result["id"] == 12345
def test_batch_node_creation_overhead(benchmark, graph_store):
"""
Benchmarks the loop overhead in create_nodes (Batch).
Checks if it handles lists efficiently.
"""
nodes = [{"labels": ["Person"], "properties": {"id": i}} for i in range(50)]
def op():
return graph_store.create_nodes(nodes)
result = benchmark(op)
assert len(result) == 50
def test_query_construction_and_parsing(benchmark, graph_store):
"""
Benchmarks every execution overhead.
Measures how fast `QueryEngine` parses result into a Python dict.
"""
query = "MATCH ( n:Person) RETURN n LIMIT 1"
def op():
return graph_store.execute_query(query)
result = benchmark(op)
assert result["success"] is True
assert len(result["records"]) > 0
def test_analytics_shortest_path_overhead(benchmark, graph_store):
"""
Benchmarks the wrapper overhead for graph analytics.
"""
def op():
return graph_store.shortest_path(
start_node_id=1, end_node_id=2, rel_type="KNOWS"
)
try:
benchmark(op)
except Exception:
# v pass as we are only trying to benchmark the function overhead call mainly
pass
-146
View File
@@ -1,146 +0,0 @@
import time
from dataclasses import dataclass
from unittest.mock import MagicMock, patch
import pytest
from semantica.triplet_store.bulk_loader import BulkLoader
from semantica.triplet_store.jena_store import JenaStore
from semantica.triplet_store.triplet_store import TripletStore
# ~~ Mocking ~~
# We basically define a facile Triplet class for creating ds devoid of fat AI models
@dataclass
class SimpleTriplet:
subject: str
predicate: str
object: str
confidence: float = 1.0
# ~~ Fixtures ~~
@pytest.fixture
def triplet_batch():
"""Generates 1000 triplets."""
return [
SimpleTriplet(
subject=f"http://gandhara.org/entity/{i}",
predicate="http://gandhara.org/relation/knows",
object=f"http://example.org/entity/{i+1}",
)
for i in range(1000)
]
@pytest.fixture
def large_knowledge_graph_dict():
"""
Generates a large dict (1000 ent) to test parsing
logic in `TripletStore.store()`
"""
entities = [
{
"id": f"ent_{i}",
"type": "Person",
"properties": {"name": f"Person {i}", "age": 60},
}
for i in range(1000)
]
relationships = [
{"source": f"ent_{i}", "target": f"ent_{i+1}", "type": "KNOWS"}
for i in range(999)
]
return {"entities": entities, "relationships": relationships}
@pytest.fixture
def in_memory_store():
"""Returns a real JenaStore using RDFLib (In-Mmeory)."""
store = JenaStore(endpoint=None)
if store.graph is None:
pytest.fail("JenaStore failed to initialize rdflib graph.")
if hasattr(store, "progress_tracker"):
store.progress_tracker = MagicMock()
return store
# ~~ Benchmarks ~~
def test_rdflib_insert_throughput(benchmark, in_memory_store, triplet_batch):
"""
Benchmarks raw Write Speed to in-memory RDF graph.
Is our baseline
"""
def op():
in_memory_store.add_triplets(triplet_batch)
benchmark(op)
assert len(in_memory_store.graph) >= 1000
def test_triplet_conversion_overhead(benchmark, large_knowledge_graph_dict):
"""
Benchmarks the `store()` method in TripletStore.
This tests Python logic that converts a Dict -> Triplet objects.
"""
with patch("semantica.triplet_store.blazegraph_store.BlazegraphStore") as mockBE:
mock_instance = mockBE.return_value
mock_instance.add_triplets.return_value = {"success": True}
manager = TripletStore(backend="blazegraph")
if hasattr(manager, "progress_tracker"):
manager.progress_tracker = MagicMock()
def op():
manager.store(
knowledge_graph=large_knowledge_graph_dict,
ontology={"classes": [], "properties": []},
)
benchmark(op)
def test_bulk_loader_logic(benchmark, triplet_batch):
"""
Benchmarks teh BulkLoader class.
Measures the overhead of batching, retries and progress tracking.
"""
loader = BulkLoader(batch_size=100)
if hasattr(loader, "progress_tracker"):
loader.progress_tracker = MagicMock()
mock_store = MagicMock()
mock_store.add_triplets.return_value = {"success": True}
def op():
return loader.load_triplets(triplet_batch, mock_store)
result = benchmark(op)
assert result.total_batches == 10
def test_sparql_query_performance(benchmark, in_memory_store, triplet_batch):
"""
Benchamrks SPARQL query execution speed on 1000 items.
"""
in_memory_store.add_triplets(triplet_batch)
query = "SELECT ?s ?o WHERE { ?s <http://gandhara.org/relation/knows> ?o } LIMIT 50"
def op():
return in_memory_store.execute_sparql(query)
result = benchmark(op)
assert result["success"] is True
assert len(result["bindings"]) == 50
-94
View File
@@ -1,94 +0,0 @@
from unittest.mock import MagicMock, patch
import numpy as np
import pytest
from semantica.vector_store.faiss_store import FAISSStore
from semantica.vector_store.vector_store import VectorStore
# Fixtures
@pytest.fixture
def vector_dim():
return 768
@pytest.fixture
def random_vectors(vector_dim):
"""Generates a batch of 10,000 rando vectors."""
count = 10000
vectors = np.random.rand(count, vector_dim).astype(np.float32)
return vectors
@pytest.fixture
def populated_store(random_vectors, vector_dim):
"""
Returns a FAISS store bred with data.
"""
store = FAISSStore(dimension=vector_dim)
if hasattr(store, "progress_tracker"):
store.progress_tracker = MagicMock()
store.create_index(index_type="flat")
store.add_vectors(random_vectors)
return store
# Benchmarks
def test_faiss_insert_throughput(benchmark, random_vectors, vector_dim):
"""
Benchmarks raw Write speed to FAISS
"""
store = FAISSStore(dimension=vector_dim)
if hasattr(store, "progress_tracker"):
store.progress_tracker = MagicMock()
store.create_index(index_type="flat")
def insert_op():
store.add_vectors(random_vectors)
benchmark(insert_op)
assert len(store.index.vector_ids) >= 10000
def test_faiss_search_latency(benchmark, populated_store, vector_dim):
"""
Benchmarks Read/Search speed
"""
query = np.random.rand(1, vector_dim).astype(np.float32)
results = benchmark(populated_store.search_similar, query_vector=query, k=10)
assert len(results) == 10
def test_vector_storage_manager_overhead(benchmark, random_vectors, vector_dim):
"""
Benchmarks the overhead of the VectorStore class
"""
with patch(
"semantica.vector_store.vector_store.EmbeddingGenerator"
) as MockEmbedder:
manager = VectorStore(backend="faiss", dimension=vector_dim)
if hasattr(manager, "progress_tracker"):
manager.progress_tracker = MagicMock()
def store_op():
manager.store_vectors(random_vectors)
benchmark(store_op)
# Check vectors were stored - handle both in-memory and backend stores
if hasattr(manager, 'vectors'):
# In-memory backend
assert len(manager.vectors) >= 10000
elif hasattr(manager, '_backend_store') and hasattr(manager._backend_store, 'vector_ids'):
# Backend store (like FAISS)
assert len(manager._backend_store.vector_ids) >= 10000
else:
# For other backends, just ensure no errors occurred
pass
-80
View File
@@ -1,80 +0,0 @@
import random
from typing import Any, Dict, List
from unittest.mock import MagicMock, patch
import numpy as np
import pytest
# Data Generators
@pytest.fixture
def generate_embeddings():
"""Generates synthetic high-dim embeddings."""
def _gen(n_samples: int, n_features: int = 768):
return np.random.rand(n_samples, n_features).astype(np.float32)
return _gen
@pytest.fixture
def generate_knowledge_graph():
"""Generates synthetic Knowledge Graph dictionary."""
def _gen(n_nodes: int, density: float = 0.05):
entities = [
{
"id": f"e_{i}",
"label": f"Entity_{i}",
"type": random.choice(["Person", "Organization", "Location", "Event"]),
"metadata": {"score": random.random()},
}
for i in range(n_nodes)
]
relationships = []
n_edges = int(n_nodes * (n_nodes - 1) * density)
# Capping edges for safety
n_edges = min(n_edges, n_nodes * 5)
for i in range(n_edges):
src = random.randint(0, n_nodes - 1)
tgt = random.randint(0, n_nodes - 1)
if src != tgt:
relationships.append(
{
"source": f"e_{src}",
"target": f"e_{tgt}",
"type": "related_to",
"metadata": {"weight": random.random()},
}
)
return {"entities": entities, "relationships": relationships}
return _gen
@pytest.fixture
def generate_temporal_data(generate_knowledge_graph):
"""Generates synthetic temporal graph snapshots."""
def _gen(n_snapshots: int, n_nodes: int):
timestamps_map = {}
base_kg = generate_knowledge_graph(n_nodes)
entities = base_kg["entities"]
all_years = list(range(2020, 2020 + n_snapshots))
for ent in entities:
start = random.randint(0, len(all_years) - 2)
duration = random.randint(1, len(all_years) - start)
timestamps_map[ent["id"]] = all_years[start : start + duration]
return {
"entities": entities,
"relationships": base_kg["relationships"],
"timestamps": timestamps_map,
}
return _gen
@@ -1,26 +0,0 @@
import random
import pytest
from semantica.visualization.analytics_visualizer import AnalyticsVisualizer
@pytest.mark.benchmark(group="analytics_charts")
def test_centrality_ranking_sort_and_render(benchmark):
"""
Benchmarks sorting a large centrality dictionary
and rendering the Top N bar chart.
"""
viz = AnalyticsVisualizer()
# Generate 5000 node scores
centrality_data = {
"centrality": {f"node_{i}": random.random() for i in range(5000)}
}
def run():
return viz.visualize_centrality_rankings(
centrality_data, centrality_type="degree", top_n=50, output="interactive"
)
benchmark.pedantic(run, iterations=1, rounds=10)
@@ -1,45 +0,0 @@
import numpy as np
import pytest
from semantica.visualization.embedding_visualizer import EmbeddingVisualizer
@pytest.mark.benchmark(group="embedding_projection")
@pytest.mark.parametrize("method", ["pca", "tsne"])
@pytest.mark.parametrize("n_samples", [500])
def test_projection_calculation_overhead(
benchmark, generate_embeddings, method, n_samples
):
"""
Measures the combined cost of:
1. Dimensionality Reduction (Math)
2. Plotly Trace Construction (Object creation)
"""
viz = EmbeddingVisualizer()
embeddings = generate_embeddings(n_samples=n_samples, n_features=128)
labels = [f"Label {i}" for i in range(n_samples)]
def run():
return viz.visualize_2d_projection(
embeddings, labels=labels, method=method, output="interactive"
)
rounds = 5 if method == "tsne" else 10
benchmark.pedantic(run, iterations=1, rounds=rounds)
@pytest.mark.benchmark(group="embedding_heatmap")
def test_similarity_heatmap_generation(benchmark, generate_embeddings):
"""
Benchmarks O(N^2) similarity matrix calculation
and heatmap renderin.
"""
viz = EmbeddingVisualizer()
embeddings = generate_embeddings(n_samples=500, n_features=64)
def run():
return viz.visualize_similarity_heatmap(embeddings, output="interactive")
benchmark.pedantic(run, iterations=1, rounds=5)
-33
View File
@@ -1,33 +0,0 @@
import pytest
from semantica.visualization.kg_visualizer import KGVisualizer
@pytest.mark.benchmark(group="graph_layouyt")
@pytest.mark.parametrize("layout", ["circular", "force"])
@pytest.mark.parametrize("size", [100])
def test_network_layout_performance(benchmark, generate_knowledge_graph, layout, size):
"""
Compares layout algorithm.
"""
viz = KGVisualizer(layout=layout, force_layout_iterations=50)
graph = generate_knowledge_graph(n_nodes=size)
def run():
return viz.visualize_network(graph, output="interactive")
benchmark.pedantic(run, iterations=1, rounds=5)
@pytest.mark.benchmark(group="graph_structure")
def test_matrix_view_rendering(benchmark, generate_knowledge_graph):
"""
Benchmarks the creation of an adjacent/relationship matrix.
"""
viz = KGVisualizer()
graph = generate_knowledge_graph(n_nodes=500)
def run():
return viz.visualize_relationship_matrix(graph, output="interactive")
benchmark.pedantic(run, iterations=1, rounds=5)
@@ -1,39 +0,0 @@
import pytest
from semantica.visualization.temporal_visualizer import TemporalVisualizer
@pytest.mark.benchmark(group="temporal_animation")
def test_network_evolution_frames(benchmark, generate_temporal_data):
"""
Measures the cost of generating animation frames for Plotly.
"""
temporal_data = generate_temporal_data(n_snapshots=5, n_nodes=100)
viz = TemporalVisualizer()
def run():
return viz.visualize_network_evolution(temporal_data, output="interactive")
benchmark.pedantic(run, iterations=1, rounds=5)
@pytest.mark.benchmark(group="temporal_dashboard")
def test_temporal_dashboard_assembly(benchmark, generate_temporal_data):
"""
Benchmarks the creation of a multi-subplot dashboard.
"""
temporal_data = generate_temporal_data(n_snapshots=20, n_nodes=200)
viz = TemporalVisualizer()
metrics = {
"Accuracy": [0.5 + i * 0.02 for i in range(20)],
"Loss": [1.0 - i * 0.04 for i in range(20)],
}
def run():
return viz.visualize_temporal_dashboard(
temporal_data, metrics=metrics, output="interactive"
)
benchmark.pedantic(run, iterations=1, rounds=5)
@@ -0,0 +1,809 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/Hawksight-AI/semantica/blob/main/cookbook/advanced/14_Datalog_Style_Reasoning.ipynb)\n",
"\n",
"# Datalog-Style Reasoning\n",
"\n",
"End-to-end guide to Semantica's **`DatalogReasoner`** — a native bottom-up semi-naive fixpoint engine — wired together with `GraphBuilder`, `ContextGraph`, `GraphAnalyzer`, `ExplanationGenerator`, and the supporting data-classes (`DatalogFact`, `DatalogRule`, `InferenceResult`, `Rule`).\n",
"\n",
"## What you will build\n",
"\n",
"| Part | Topic | Key classes |\n",
"|------|-------|-------------|\n",
"| 1 | Core API & EDB/IDB concepts | `DatalogReasoner`, `DatalogFact`, `DatalogRule` |\n",
"| 2 | KG → Datalog pipeline | `GraphBuilder`, `GraphAnalyzer`, `DatalogReasoner` |\n",
"| 3 | ContextGraph integration | `ContextGraph`, `DatalogReasoner.load_from_graph()` |\n",
"| 4 | RBAC access-control policy | `GraphBuilder`, `DatalogReasoner`, `ExplanationGenerator` |\n",
"| 5 | Org hierarchy | `ContextGraph`, `DatalogReasoner`, `InferenceResult` |\n",
"| 6 | Engine introspection | `DatalogFact`, `DatalogRule` internal state |\n",
"\n",
"**Related notebooks**\n",
"- [08_Reasoning_and_Inference.ipynb](08_Reasoning_and_Inference.ipynb) — high-level `Reasoner` with IF/THEN syntax\n",
"- [10_Temporal_Knowledge_Graphs.ipynb](10_Temporal_Knowledge_Graphs.ipynb) — temporal reasoning\n",
"\n",
"**Documentation**: [Reasoning API](https://semantica.readthedocs.io/reference/reasoning/) | [KG API](https://semantica.readthedocs.io/reference/kg/) | [Context API](https://semantica.readthedocs.io/reference/context/)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"!pip install -qU semantica"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# ── Reasoning ──────────────────────────────────────────────────────────────\n",
"from semantica.reasoning import (\n",
" DatalogReasoner, # native Datalog fixpoint engine\n",
" DatalogFact, # frozen dataclass: predicate + args tuple\n",
" DatalogRule, # dataclass: head + body (list[BodyAtom])\n",
" ExplanationGenerator, # generates NL justifications\n",
" InferenceResult, # result dataclass consumed by ExplanationGenerator\n",
" Rule, # rule dataclass used by ExplanationGenerator\n",
" RuleType, # enum: IMPLICATION | EQUIVALENCE | CONSTRAINT | TRANSFORMATION\n",
")\n",
"\n",
"# ── Knowledge Graph ────────────────────────────────────────────────────────\n",
"from semantica.kg import (\n",
" GraphBuilder, # constructs KG dicts from entity+relationship sources\n",
" GraphAnalyzer, # centrality, communities, connectivity, metrics\n",
")\n",
"\n",
"# ── Context ────────────────────────────────────────────────────────────────\n",
"from semantica.context import ContextGraph # in-memory graph: add_node/add_edge/find_*\n",
"\n",
"print(\"All Semantica classes imported successfully.\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"\n",
"## Part 1 — Core API: EDB Facts, IDB Rules, Fixpoint\n",
"\n",
"### Datalog in 30 seconds\n",
"\n",
"| Term | Meaning | Example |\n",
"|------|---------|--------|\n",
"| EDB (Extensional DB) | Ground facts you assert | `parent(tom, bob)` |\n",
"| IDB (Intensional DB) | Facts derived by rules | `ancestor(tom, ann)` |\n",
"| Rule (Horn clause) | If body → derive head | `ancestor(X,Y) :- parent(X,Y).` |\n",
"| Variable | Uppercase, unified during eval | `X`, `Y`, `Role` |\n",
"| Constant | Lowercase, matches literally | `tom`, `admin` |\n",
"| Fixpoint | Iterate until no new facts appear | `DatalogReasoner.derive_all()` |\n",
"\n",
"### The canonical example — transitive ancestry"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# ── Step 1: create engine ──────────────────────────────────────────────────\n",
"dr = DatalogReasoner()\n",
"\n",
"# ── Step 2: load EDB (ground facts) ───────────────────────────────────────\n",
"# Syntax: predicate(constant1, constant2) — constants must be lowercase\n",
"edb_facts = [\n",
" \"parent(tom, bob)\",\n",
" \"parent(bob, ann)\",\n",
" \"parent(ann, pat)\",\n",
"]\n",
"for f in edb_facts:\n",
" dr.add_fact(f)\n",
"\n",
"print(f\"EDB loaded: {len(dr._all_facts)} ground facts\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# ── Step 3: add IDB rules (Horn clauses) ──────────────────────────────────\n",
"# Syntax: head(Vars) :- body_atom1(Vars), body_atom2(Vars).\n",
"# Variables start with uppercase; trailing '.' is optional\n",
"dr.add_rule(\"ancestor(X, Y) :- parent(X, Y).\")\n",
"dr.add_rule(\"ancestor(X, Y) :- parent(X, Z), ancestor(Z, Y).\") # recursive\n",
"\n",
"print(f\"Rules loaded: {len(dr._rules)}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# ── Step 4: fixpoint evaluation ────────────────────────────────────────────\n",
"# derive_all() runs semi-naive bottom-up evaluation until no new facts appear\n",
"all_facts: list[str] = dr.derive_all()\n",
"\n",
"ancestor_strs = sorted(f for f in all_facts if f.startswith(\"ancestor\"))\n",
"print(f\"Derived {len(ancestor_strs)} ancestor facts:\")\n",
"for f in ancestor_strs:\n",
" print(\" \", f)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# ── Step 5: query ──────────────────────────────────────────────────────────\n",
"# Use '?varname' placeholders — query() auto-calls derive_all() if needed\n",
"# Returns: list[dict] e.g. [{\"Y\": \"bob\"}, {\"Y\": \"ann\"}, {\"Y\": \"pat\"}]\n",
"\n",
"descendants = dr.query(\"ancestor(tom, ?Y)\")\n",
"print(\"All descendants of tom:\", sorted(r[\"Y\"] for r in descendants))\n",
"\n",
"ancestors_of_pat = dr.query(\"ancestor(?X, pat)\")\n",
"print(\"All ancestors of pat: \", sorted(r[\"X\"] for r in ancestors_of_pat))\n",
"\n",
"all_pairs = dr.query(\"ancestor(?X, ?Y)\")\n",
"print(f\"\\nAll ancestor pairs ({len(all_pairs)}):\")\n",
"for row in sorted(all_pairs, key=lambda r: (r[\"X\"], r[\"Y\"])):\n",
" print(f\" {row['X']:6s} → {row['Y']}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"\n",
"## Part 2 — GraphBuilder → DatalogReasoner Pipeline\n",
"\n",
"`GraphBuilder` constructs a structured `{\"entities\": [...], \"relationships\": [...]}` dict from your data. We then:\n",
"\n",
"1. Analyse the graph with `GraphAnalyzer` to understand structure.\n",
"2. Feed `kg[\"relationships\"]` into `DatalogReasoner` as EDB facts.\n",
"3. Apply recursive Datalog rules over the KG."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# ── Build a software-dependency KG ────────────────────────────────────────\n",
"entities = [\n",
" {\"id\": \"pythonsdk\", \"name\": \"Python SDK\", \"type\": \"Component\"},\n",
" {\"id\": \"restapi\", \"name\": \"REST API\", \"type\": \"Component\"},\n",
" {\"id\": \"authservice\", \"name\": \"Auth Service\", \"type\": \"Component\"},\n",
" {\"id\": \"database\", \"name\": \"Database\", \"type\": \"Component\"},\n",
" {\"id\": \"dashboard\", \"name\": \"Dashboard\", \"type\": \"Component\"},\n",
" {\"id\": \"analytics\", \"name\": \"Analytics\", \"type\": \"Component\"},\n",
"]\n",
"relationships = [\n",
" {\"source\": \"pythonsdk\", \"target\": \"restapi\", \"type\": \"depends_on\"},\n",
" {\"source\": \"restapi\", \"target\": \"authservice\", \"type\": \"depends_on\"},\n",
" {\"source\": \"authservice\", \"target\": \"database\", \"type\": \"depends_on\"},\n",
" {\"source\": \"dashboard\", \"target\": \"restapi\", \"type\": \"depends_on\"},\n",
" {\"source\": \"dashboard\", \"target\": \"analytics\", \"type\": \"depends_on\"},\n",
" {\"source\": \"analytics\", \"target\": \"database\", \"type\": \"depends_on\"},\n",
"]\n",
"\n",
"# GraphBuilder validates, deduplicates, and packages the data\n",
"builder = GraphBuilder(merge_entities=True, resolve_conflicts=False)\n",
"kg = builder.build([{\"entities\": entities, \"relationships\": relationships}])\n",
"\n",
"print(f\"KG built — entities: {len(kg['entities'])}, relationships: {len(kg['relationships'])}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# ── Analyse the graph structure before reasoning ───────────────────────────\n",
"# GraphAnalyzer provides centrality, communities, connectivity, and metrics\n",
"analyzer = GraphAnalyzer()\n",
"metrics = analyzer.compute_metrics(graph=kg)\n",
"\n",
"print(\"Graph structure:\")\n",
"print(f\" Nodes : {metrics['num_nodes']}\")\n",
"print(f\" Edges : {metrics['num_edges']}\")\n",
"if \"density\" in metrics:\n",
" print(f\" Density : {metrics['density']:.3f}\")\n",
"if \"is_connected\" in metrics:\n",
" print(f\" Connected : {metrics['is_connected']}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# ── Load KG relationships as EDB facts ────────────────────────────────────\n",
"# GraphBuilder output dicts use the same source/target/type shape that\n",
"# DatalogReasoner.add_fact() natively understands\n",
"dr = DatalogReasoner()\n",
"\n",
"for rel in kg[\"relationships\"]:\n",
" dr.add_fact(rel) # dict path: {\"source\": ..., \"target\": ..., \"type\": ...}\n",
"\n",
"print(f\"EDB loaded: {len(dr._all_facts)} dependency facts\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# ── Transitive dependency closure ─────────────────────────────────────────\n",
"# 'depends_on' is the predicate name that add_fact inferred from 'type'\n",
"dr.add_rule(\"transitive_dep(X, Y) :- depends_on(X, Y).\")\n",
"dr.add_rule(\"transitive_dep(X, Y) :- depends_on(X, Z), transitive_dep(Z, Y).\")\n",
"\n",
"dr.derive_all()\n",
"\n",
"# Everything that transitively depends on the database\n",
"db_deps = sorted(r[\"X\"] for r in dr.query(\"transitive_dep(?X, database)\"))\n",
"print(\"Components that transitively depend on Database:\")\n",
"for c in db_deps:\n",
" print(\" \", c)\n",
"\n",
"# What does pythonsdk transitively depend on?\n",
"sdk_chain = sorted(r[\"Y\"] for r in dr.query(\"transitive_dep(pythonsdk, ?Y)\"))\n",
"print(f\"\\nPython SDK full dependency chain: {sdk_chain}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"\n",
"## Part 3 — ContextGraph + `load_from_graph()`\n",
"\n",
"`DatalogReasoner.load_from_graph(graph)` accepts any `ContextGraph` directly: it calls `graph.find_edges()` and `graph.find_nodes()` and converts each result into EDB facts automatically."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# ── Build an in-memory ContextGraph ───────────────────────────────────────\n",
"# ContextGraph.add_node / add_edge are the canonical way to build in-memory KGs\n",
"cg = ContextGraph()\n",
"\n",
"# Nodes\n",
"for person in [\"alice\", \"bob\", \"carol\", \"dave\", \"eve\"]:\n",
" cg.add_node(person, node_type=\"person\", name=person.capitalize())\n",
"\n",
"# Directed \"follows\" edges\n",
"for src, dst in [(\"alice\", \"bob\"), (\"bob\", \"carol\"), (\"carol\", \"dave\"), (\"alice\", \"eve\"), (\"eve\", \"carol\")]:\n",
" cg.add_edge(src, dst, edge_type=\"follows\")\n",
"\n",
"# Verify the graph built correctly\n",
"nodes = cg.find_nodes(node_type=\"person\")\n",
"edges = cg.find_edges(edge_type=\"follows\")\n",
"print(f\"ContextGraph — nodes: {len(nodes)}, edges: {len(edges)}\")\n",
"print(\"Edges:\", [(e.get(\"source\", e.get(\"source_id\")), e.get(\"target\", e.get(\"target_id\"))) for e in edges])"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# ── load_from_graph() ingests the ContextGraph directly ───────────────────\n",
"dr = DatalogReasoner()\n",
"n_loaded = dr.load_from_graph(cg) # calls cg.find_edges() + cg.find_nodes() internally\n",
"print(f\"Facts loaded from ContextGraph: {n_loaded}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# ── Influence reach via transitive 'follows' ──────────────────────────────\n",
"dr.add_rule(\"influence(X, Y) :- follows(X, Y).\")\n",
"dr.add_rule(\"influence(X, Y) :- follows(X, Z), influence(Z, Y).\")\n",
"\n",
"dr.derive_all()\n",
"\n",
"# Who can alice reach?\n",
"alice_reach = sorted(r[\"Y\"] for r in dr.query(\"influence(alice, ?Y)\"))\n",
"print(f\"Alice's influence reach : {alice_reach}\")\n",
"\n",
"# Who can reach dave?\n",
"reach_dave = sorted(r[\"X\"] for r in dr.query(\"influence(?X, dave)\"))\n",
"print(f\"Who can influence dave : {reach_dave}\")\n",
"\n",
"# Full influence matrix\n",
"all_influence = dr.query(\"influence(?X, ?Y)\")\n",
"print(f\"\\nTotal influence pairs: {len(all_influence)}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"\n",
"## Part 4 — RBAC Access-Control Policy\n",
"\n",
"We model a role-based access-control (RBAC) system:\n",
"\n",
"1. Use `GraphBuilder` to build a structured KG of users, roles, and permissions.\n",
"2. Load it into `DatalogReasoner` for policy inference.\n",
"3. Use `ExplanationGenerator` to produce audit-ready NL justifications."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# ── Build RBAC graph with GraphBuilder ────────────────────────────────────\n",
"rbac_entities = [\n",
" # Users\n",
" {\"id\": \"alice\", \"type\": \"User\", \"name\": \"Alice\"},\n",
" {\"id\": \"bob\", \"type\": \"User\", \"name\": \"Bob\"},\n",
" {\"id\": \"carol\", \"type\": \"User\", \"name\": \"Carol\"},\n",
" {\"id\": \"dave\", \"type\": \"User\", \"name\": \"Dave\"},\n",
" # Roles\n",
" {\"id\": \"admin\", \"type\": \"Role\", \"name\": \"Administrator\"},\n",
" {\"id\": \"editor\", \"type\": \"Role\", \"name\": \"Editor\"},\n",
" {\"id\": \"viewer\", \"type\": \"Role\", \"name\": \"Viewer\"},\n",
" # Permissions\n",
" {\"id\": \"read\", \"type\": \"Permission\"},\n",
" {\"id\": \"write\", \"type\": \"Permission\"},\n",
" {\"id\": \"delete\", \"type\": \"Permission\"},\n",
" {\"id\": \"manage_users\", \"type\": \"Permission\"},\n",
"]\n",
"rbac_relationships = [\n",
" # User → Role assignments\n",
" {\"source\": \"alice\", \"target\": \"admin\", \"type\": \"has_role\"},\n",
" {\"source\": \"bob\", \"target\": \"editor\", \"type\": \"has_role\"},\n",
" {\"source\": \"carol\", \"target\": \"viewer\", \"type\": \"has_role\"},\n",
" {\"source\": \"dave\", \"target\": \"editor\", \"type\": \"has_role\"},\n",
" # Role hierarchy (admin inherits from editor, editor from viewer)\n",
" {\"source\": \"admin\", \"target\": \"editor\", \"type\": \"role_inherits\"},\n",
" {\"source\": \"editor\", \"target\": \"viewer\", \"type\": \"role_inherits\"},\n",
" # Role → Permission grants\n",
" {\"source\": \"viewer\", \"target\": \"read\", \"type\": \"role_has_perm\"},\n",
" {\"source\": \"editor\", \"target\": \"write\", \"type\": \"role_has_perm\"},\n",
" {\"source\": \"admin\", \"target\": \"delete\", \"type\": \"role_has_perm\"},\n",
" {\"source\": \"admin\", \"target\": \"manage_users\", \"type\": \"role_has_perm\"},\n",
"]\n",
"\n",
"builder = GraphBuilder(merge_entities=True, resolve_conflicts=False)\n",
"rbac_kg = builder.build([{\"entities\": rbac_entities, \"relationships\": rbac_relationships}])\n",
"\n",
"print(f\"RBAC KG — entities: {len(rbac_kg['entities'])}, relationships: {len(rbac_kg['relationships'])}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# ── Analyse RBAC graph structure ──────────────────────────────────────────\n",
"analyzer = GraphAnalyzer()\n",
"metrics = analyzer.compute_metrics(graph=rbac_kg)\n",
"centrality = analyzer.calculate_centrality(rbac_kg, centrality_type=\"degree\")\n",
"\n",
"print(f\"RBAC graph — {metrics['num_nodes']} nodes, {metrics['num_edges']} edges\")\n",
"if isinstance(centrality, dict) and \"degree\" in centrality:\n",
" top = sorted(centrality[\"degree\"].items(), key=lambda x: x[1], reverse=True)[:3]\n",
" print(\"Top-3 nodes by degree centrality:\", top)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# ── Load RBAC KG into DatalogReasoner ────────────────────────────────────\n",
"dr = DatalogReasoner()\n",
"\n",
"for rel in rbac_kg[\"relationships\"]:\n",
" dr.add_fact(rel) # {source, target, type} → predicate(source, target)\n",
"\n",
"# ── IDB rules: transitive role hierarchy ─────────────────────────────────\n",
"dr.add_rule(\"effective_role(R, R2) :- role_inherits(R, R2).\")\n",
"dr.add_rule(\"effective_role(R, R2) :- role_inherits(R, Z), effective_role(Z, R2).\")\n",
"\n",
"# ── IDB rules: inherited permissions ─────────────────────────────────────\n",
"dr.add_rule(\"role_can(R, P) :- role_has_perm(R, P).\")\n",
"dr.add_rule(\"role_can(R, P) :- effective_role(R, R2), role_has_perm(R2, P).\")\n",
"\n",
"# ── IDB rules: user effective permissions ────────────────────────────────\n",
"dr.add_rule(\"can(U, P) :- has_role(U, R), role_can(R, P).\")\n",
"\n",
"dr.derive_all()\n",
"\n",
"print(\"User permissions derived via role-hierarchy inference:\")\n",
"for user in [\"alice\", \"bob\", \"carol\", \"dave\"]:\n",
" perms = sorted(r[\"P\"] for r in dr.query(f\"can({user}, ?P)\"))\n",
" print(f\" {user:6s}: {perms}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# ── ExplanationGenerator — audit-ready NL justification ──────────────────\n",
"# ExplanationGenerator works with InferenceResult objects.\n",
"# We construct one manually to represent a derived Datalog conclusion.\n",
"\n",
"explainer = ExplanationGenerator(detail_level=\"detailed\")\n",
"\n",
"# Build the Rule object that represents the permission derivation chain\n",
"perm_rule = Rule(\n",
" rule_id=\"rbac_perm_chain\",\n",
" name=\"RBAC permission via role hierarchy\",\n",
" conditions=[\"has_role(alice, admin)\", \"effective_role(admin, viewer)\", \"role_has_perm(viewer, read)\"],\n",
" conclusion=\"can(alice, read)\",\n",
" rule_type=RuleType.IMPLICATION,\n",
" confidence=1.0,\n",
")\n",
"\n",
"# Build InferenceResult representing the Datalog conclusion\n",
"result = InferenceResult(\n",
" conclusion=\"can(alice, read)\",\n",
" rule_used=perm_rule,\n",
" premises=[\n",
" \"has_role(alice, admin)\",\n",
" \"role_inherits(admin, editor)\",\n",
" \"role_inherits(editor, viewer)\",\n",
" \"role_has_perm(viewer, read)\",\n",
" ],\n",
" confidence=1.0,\n",
")\n",
"\n",
"# Generate NL explanation\n",
"explanation = explainer.generate_explanation(result)\n",
"print(\"Explanation type :\", explanation.explanation_type)\n",
"print(\"Conclusion :\", explanation.conclusion)\n",
"print(\"Natural language :\", explanation.natural_language)\n",
"print(\"Reasoning steps :\", len(explanation.reasoning_path.steps))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# ── Inverse queries ───────────────────────────────────────────────────────\n",
"deleters = sorted(r[\"U\"] for r in dr.query(\"can(?U, delete)\"))\n",
"print(\"Who can delete:\", deleters)\n",
"\n",
"writers = sorted(r[\"U\"] for r in dr.query(\"can(?U, write)\"))\n",
"print(\"Who can write: \", writers)\n",
"\n",
"# All (user, permission) pairs — full policy matrix\n",
"all_caps = dr.query(\"can(?U, ?P)\")\n",
"print(f\"\\nTotal (user, permission) pairs: {len(all_caps)}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"\n",
"## Part 5 — Organisation Hierarchy with ContextGraph\n",
"\n",
"We model a company org-chart using `ContextGraph` and derive:\n",
"- `manages(M, E)` — direct and transitive management\n",
"- `skip_level(M, E)` — two hops up the chain\n",
"- `same_team(X, Y)` — shared team membership"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# ── ContextGraph: org chart ────────────────────────────────────────────────\n",
"org = ContextGraph()\n",
"\n",
"# Add employees as nodes with metadata\n",
"staff = [\n",
" (\"eng1\", \"engineer\", \"backend\"),\n",
" (\"eng2\", \"engineer\", \"backend\"),\n",
" (\"eng3\", \"engineer\", \"frontend\"),\n",
" (\"techlead\", \"lead\", \"engineering\"),\n",
" (\"design1\", \"designer\", \"ux\"),\n",
" (\"design2\", \"designer\", \"ux\"),\n",
" (\"designlead\",\"lead\", \"design\"),\n",
" (\"vpeng\", \"vp\", \"engineering\"),\n",
" (\"cto\", \"executive\", \"leadership\"),\n",
"]\n",
"for emp_id, role, team in staff:\n",
" org.add_node(emp_id, node_type=\"employee\", role=role, team=team)\n",
"\n",
"# Reporting lines\n",
"reports_to = [\n",
" (\"eng1\", \"techlead\"), (\"eng2\", \"techlead\"), (\"eng3\", \"techlead\"),\n",
" (\"techlead\", \"vpeng\"),\n",
" (\"design1\", \"designlead\"), (\"design2\", \"designlead\"),\n",
" (\"designlead\", \"vpeng\"),\n",
" (\"vpeng\", \"cto\"),\n",
"]\n",
"for employee, manager in reports_to:\n",
" org.add_edge(employee, manager, edge_type=\"reports_to\")\n",
"\n",
"# Team membership edges\n",
"teams = [\n",
" (\"eng1\", \"backend\"), (\"eng2\", \"backend\"), (\"eng3\", \"frontend\"),\n",
" (\"design1\", \"ux\"), (\"design2\", \"ux\"),\n",
"]\n",
"for emp, team in teams:\n",
" org.add_edge(emp, team, edge_type=\"in_team\")\n",
" if not org.find_nodes(node_type=\"team\"):\n",
" org.add_node(team, node_type=\"team\")\n",
"\n",
"print(f\"ContextGraph — nodes: {len(org.find_nodes())}, edges: {len(org.find_edges())}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# ── Load org chart into DatalogReasoner ───────────────────────────────────\n",
"dr = DatalogReasoner()\n",
"n = dr.load_from_graph(org) # uses org.find_edges() + org.find_nodes()\n",
"print(f\"Facts loaded via load_from_graph(): {n}\")\n",
"\n",
"# ── IDB rules ─────────────────────────────────────────────────────────────\n",
"# Transitive management chain\n",
"dr.add_rule(\"manages(M, E) :- reports_to(E, M).\")\n",
"dr.add_rule(\"manages(M, E) :- reports_to(E, Z), manages(M, Z).\")\n",
"\n",
"# Skip-level: exactly two reporting hops\n",
"dr.add_rule(\"skip_level(M, E) :- reports_to(E, Z), reports_to(Z, M).\")\n",
"\n",
"# Same team\n",
"dr.add_rule(\"same_team(X, Y) :- in_team(X, T), in_team(Y, T).\")\n",
"\n",
"dr.derive_all()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# ── Query org hierarchy ────────────────────────────────────────────────────\n",
"# Everyone under CTO\n",
"under_cto = sorted(r[\"E\"] for r in dr.query(\"manages(cto, ?E)\"))\n",
"print(f\"CTO manages ({len(under_cto)} people): {under_cto}\")\n",
"\n",
"# VP Eng's direct + indirect reports\n",
"under_vp = sorted(r[\"E\"] for r in dr.query(\"manages(vpeng, ?E)\"))\n",
"print(f\"VP Eng manages : {under_vp}\")\n",
"\n",
"# Skip-level reports to CTO (people two hops below CTO)\n",
"skip = sorted(r[\"E\"] for r in dr.query(\"skip_level(cto, ?E)\"))\n",
"print(f\"CTO skip-level reports : {skip}\")\n",
"\n",
"# eng1's teammates\n",
"mates = [r[\"Y\"] for r in dr.query(\"same_team(eng1, ?Y)\") if r[\"Y\"] != \"eng1\"]\n",
"print(f\"eng1's teammates : {sorted(mates)}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# ── Build an InferenceResult and explain an org query ────────────────────\n",
"explainer = ExplanationGenerator(detail_level=\"verbose\")\n",
"\n",
"mgmt_rule = Rule(\n",
" rule_id=\"transitive_manages\",\n",
" name=\"Transitive management chain\",\n",
" conditions=[\"reports_to(eng1, techlead)\", \"manages(vpeng, techlead)\"],\n",
" conclusion=\"manages(vpeng, eng1)\",\n",
" rule_type=RuleType.IMPLICATION,\n",
" confidence=1.0,\n",
")\n",
"result = InferenceResult(\n",
" conclusion=\"manages(vpeng, eng1)\",\n",
" rule_used=mgmt_rule,\n",
" premises=[\"reports_to(eng1, techlead)\", \"reports_to(techlead, vpeng)\"],\n",
" confidence=1.0,\n",
")\n",
"\n",
"exp = explainer.generate_explanation(result)\n",
"print(exp.natural_language)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"\n",
"## Part 6 — Engine Introspection: DatalogFact & DatalogRule\n",
"\n",
"After reasoning, the engine's internal state is fully accessible via `DatalogFact` and `DatalogRule` data-classes. Use this for auditing, debugging, or downstream export."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# ── Inspect DatalogRule objects ────────────────────────────────────────────\n",
"# dr._rules → List[DatalogRule]\n",
"# DatalogRule.head_predicate, .head_args, .body (body = List[BodyAtom])\n",
"print(\"Rules in engine:\")\n",
"for rule in dr._rules:\n",
" body_str = \", \".join(\n",
" f\"{atom.predicate}({', '.join(atom.args)})\"\n",
" for atom in rule.body\n",
" )\n",
" head_str = f\"{rule.head_predicate}({', '.join(rule.head_args)})\"\n",
" print(f\" {head_str} :- {body_str}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# ── Inspect DatalogFact objects ────────────────────────────────────────────\n",
"# dr._all_facts → Set[DatalogFact] (EDB + IDB combined after derive_all)\n",
"# dr._fact_index → Dict[predicate, Set[DatalogFact]]\n",
"\n",
"from collections import Counter\n",
"\n",
"# Count facts per predicate\n",
"predicate_counts = Counter(f.predicate for f in dr._all_facts)\n",
"print(\"Facts per predicate (EDB + derived IDB):\")\n",
"for pred, count in sorted(predicate_counts.items()):\n",
" print(f\" {pred:20s}: {count}\")\n",
"print(f\"\\n TOTAL: {len(dr._all_facts)}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# ── Separate EDB from IDB ─────────────────────────────────────────────────\n",
"# EDB predicates are the ones we added via add_fact (not derived by rules)\n",
"idb_predicates = {rule.head_predicate for rule in dr._rules}\n",
"edb_predicates = {f.predicate for f in dr._all_facts} - idb_predicates\n",
"\n",
"print(f\"EDB predicates (base facts) : {sorted(edb_predicates)}\")\n",
"print(f\"IDB predicates (derived) : {sorted(idb_predicates)}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# ── Sample DatalogFact structure ──────────────────────────────────────────\n",
"# DatalogFact is a frozen dataclass: predicate: str, args: Tuple[str, ...]\n",
"manages_facts = sorted(dr._fact_index.get(\"manages\", []), key=lambda f: f.args)\n",
"print(f\"First 5 'manages' DatalogFact objects ({len(manages_facts)} total):\")\n",
"for fact in manages_facts[:5]:\n",
" # Access predicate and args directly from the dataclass\n",
" print(f\" DatalogFact(predicate={fact.predicate!r}, args={fact.args})\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# ── clear() resets the engine completely ─────────────────────────────────\n",
"print(f\"Facts before clear(): {len(dr._all_facts)}\")\n",
"dr.clear()\n",
"print(f\"Facts after clear(): {len(dr._all_facts)}\")\n",
"print(f\"Rules after clear(): {len(dr._rules)}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"\n",
"## API Summary\n",
"\n",
"### DatalogReasoner\n",
"\n",
"| Method | Input | Output | Notes |\n",
"|--------|-------|--------|-------|\n",
"| `add_fact(f)` | `str` or `dict` | `None` | string: `\"pred(a, b)\"` · dict: `{source, target, type}` |\n",
"| `add_rule(s)` | `str` | `None` | Horn clause: `\"head(X) :- body(X, Y).\"` |\n",
"| `derive_all()` | — | `list[str]` | semi-naive fixpoint; idempotent |\n",
"| `query(pat)` | `str` | `list[dict]` | `\"pred(a, ?Y)\"` → `[{\"Y\": ...}]` |\n",
"| `load_from_graph(g)` | `ContextGraph` | `int` | facts loaded count |\n",
"| `clear()` | — | `None` | resets engine |\n",
"\n",
"### Syntax rules\n",
"\n",
"| Item | Rule | Example |\n",
"|------|------|---------|\n",
"| Variable | Starts **uppercase** | `X`, `Role`, `Parent` |\n",
"| Constant | All **lowercase** | `tom`, `admin`, `database` |\n",
"| Query var | Prefix `?` | `?X`, `?Y`, `?Role` |\n",
"| Rule body | `:-` separator, comma between atoms | `head(X) :- a(X, Z), b(Z, Y).` |\n",
"\n",
"### Class map\n",
"\n",
"```\n",
"GraphBuilder.build() → kg dict {entities, relationships}\n",
" ↓ kg[\"relationships\"] → dr.add_fact(rel)\n",
" \n",
"ContextGraph.add_node/add_edge → in-memory graph\n",
" ↓ dr.load_from_graph(cg)\n",
" \n",
"DatalogReasoner.add_rule() → Horn clause rules\n",
"DatalogReasoner.derive_all() → semi-naive fixpoint\n",
"DatalogReasoner.query() → result rows\n",
" ↓ build InferenceResult\n",
" \n",
"ExplanationGenerator → natural language justification\n",
"GraphAnalyzer → graph structure metrics pre/post reasoning\n",
"DatalogFact / DatalogRule → introspect engine state\n",
"```"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.10.0"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
+113 -141
View File
@@ -7,17 +7,26 @@ This module provides comprehensive examples of using the Snowflake ingestor.
import os
from datetime import datetime, timedelta
from rich import box
from rich.console import Console
from rich.rule import Rule
from rich.table import Table
from semantica.ingest import SnowflakeIngestor
from semantica.utils.logging import get_logger
logger = get_logger("snowflake_examples")
console = Console()
def _section(title: str) -> None:
console.print(Rule(f"[bold cyan]{title}[/bold cyan]", style="cyan"))
def example_basic_ingestion():
"""Example: Basic table ingestion."""
print("\n=== Example 1: Basic Table Ingestion ===\n")
_section("Example 1: Basic Table Ingestion")
# Initialize ingestor with password authentication
ingestor = SnowflakeIngestor(
account=os.getenv("SNOWFLAKE_ACCOUNT"),
user=os.getenv("SNOWFLAKE_USER"),
@@ -27,26 +36,23 @@ def example_basic_ingestion():
schema="PUBLIC",
)
# Ingest a table
data = ingestor.ingest_table("CUSTOMERS", limit=10)
print(f"Retrieved {data.row_count} rows")
print(f"Columns: {data.columns}")
print(f"\nFirst row:")
print(data.data[0])
console.print(f"[green]✓[/green] Retrieved [cyan]{data.row_count}[/cyan] rows")
console.print(f" Columns: [dim]{data.columns}[/dim]")
console.print(f" First row: [dim]{data.data[0]}[/dim]")
ingestor.close()
def example_query_execution():
"""Example: Execute custom SQL queries."""
print("\n=== Example 2: Query Execution ===\n")
_section("Example 2: Query Execution")
ingestor = SnowflakeIngestor()
# Execute aggregation query
query = """
SELECT
SELECT
COUNTRY,
COUNT(*) AS CUSTOMER_COUNT,
SUM(TOTAL_PURCHASES) AS TOTAL_REVENUE
@@ -58,34 +64,34 @@ def example_query_execution():
data = ingestor.ingest_query(query)
print(f"Top 10 countries by revenue:")
table = Table(title="[bold]Top 10 Countries by Revenue[/bold]",
box=box.SIMPLE_HEAD, show_edge=False, padding=(0, 1))
table.add_column("Country", style="cyan", no_wrap=True)
table.add_column("Customers", style="green", justify="right")
table.add_column("Revenue", style="green", justify="right")
for row in data.data:
print(
f" {row['COUNTRY']}: {row['CUSTOMER_COUNT']} customers, "
f"${row['TOTAL_REVENUE']:,.2f} revenue"
table.add_row(
row["COUNTRY"],
str(row["CUSTOMER_COUNT"]),
f"${row['TOTAL_REVENUE']:,.2f}",
)
console.print(table)
ingestor.close()
def example_parameterized_query():
"""Example: Parameterized queries."""
print("\n=== Example 3: Parameterized Queries ===\n")
_section("Example 3: Parameterized Queries")
ingestor = SnowflakeIngestor()
# Calculate date range
end_date = datetime.now()
start_date = end_date - timedelta(days=30)
# Execute parameterized query
query = """
SELECT
ORDER_ID,
CUSTOMER_ID,
PRODUCT_NAME,
AMOUNT,
ORDER_DATE
SELECT
ORDER_ID, CUSTOMER_ID, PRODUCT_NAME, AMOUNT, ORDER_DATE
FROM ORDERS
WHERE ORDER_DATE BETWEEN %(start_date)s AND %(end_date)s
AND AMOUNT > %(min_amount)s
@@ -101,122 +107,125 @@ def example_parameterized_query():
},
)
print(f"Found {data.row_count} orders in the last 30 days over $100")
console.print(
f"[green]✓[/green] Found [cyan]{data.row_count}[/cyan] orders "
"in the last 30 days over $100"
)
ingestor.close()
def example_schema_introspection():
"""Example: Table schema introspection."""
print("\n=== Example 4: Schema Introspection ===\n")
_section("Example 4: Schema Introspection")
ingestor = SnowflakeIngestor()
# Get table schema
schema = ingestor.get_table_schema("CUSTOMERS")
print("Table schema for CUSTOMERS:")
print(f"Primary keys: {schema['primary_keys']}\n")
console.print(f" Primary keys: [cyan]{schema['primary_keys']}[/cyan]")
print("Columns:")
table = Table(title="[bold]CUSTOMERS Schema[/bold]",
box=box.SIMPLE_HEAD, show_edge=False, padding=(0, 1))
table.add_column("Column", style="cyan", no_wrap=True)
table.add_column("Type")
table.add_column("Nullable")
table.add_column("Default", style="dim")
for col in schema["columns"]:
nullable = "NULL" if col["nullable"] else "NOT NULL"
default = f" DEFAULT {col['default']}" if col["default"] else ""
print(f" {col['name']}: {col['type']} {nullable}{default}")
table.add_row(
col["name"],
col["type"],
"NULL" if col["nullable"] else "NOT NULL",
str(col["default"]) if col["default"] else "",
)
console.print(table)
ingestor.close()
def example_list_tables():
"""Example: List all tables in a schema."""
print("\n=== Example 5: List Tables ===\n")
_section("Example 5: List Tables")
ingestor = SnowflakeIngestor()
# List tables in current schema
tables = ingestor.list_tables()
print(f"Found {len(tables)} tables:")
for table in tables:
print(f" - {table}")
table = Table(title=f"[bold]Tables ({len(tables)} found)[/bold]",
box=box.SIMPLE_HEAD, show_edge=False, padding=(0, 1))
table.add_column("Table", style="cyan")
for t in tables:
table.add_row(t)
console.print(table)
ingestor.close()
def example_pagination():
"""Example: Paginate large result sets."""
print("\n=== Example 6: Pagination ===\n")
_section("Example 6: Pagination")
ingestor = SnowflakeIngestor()
PAGE_SIZE = 100
total_rows = 0
# Paginate through large table
page = 0
while True:
data = ingestor.ingest_table(
"LARGE_TABLE", limit=PAGE_SIZE, offset=page * PAGE_SIZE
)
if data.row_count == 0:
break
total_rows += data.row_count
print(f"Page {page + 1}: {data.row_count} rows")
# Process page
console.print(
f" [dim]Page {page + 1}:[/dim] [cyan]{data.row_count}[/cyan] rows"
)
process_page(data)
page += 1
print(f"\nTotal rows processed: {total_rows}")
console.print(
f"[green]✓[/green] Total rows processed: [cyan]{total_rows}[/cyan]"
)
ingestor.close()
def example_batch_processing():
"""Example: Batch processing with fetchmany."""
print("\n=== Example 7: Batch Processing ===\n")
_section("Example 7: Batch Processing")
ingestor = SnowflakeIngestor()
# Execute query with batching
data = ingestor.ingest_query(
"SELECT * FROM LARGE_TABLE WHERE STATUS = 'ACTIVE'", batch_size=1000
)
print(f"Retrieved {data.row_count} rows in batches of 1000")
console.print(
f"[green]✓[/green] Retrieved [cyan]{data.row_count}[/cyan] rows "
"in batches of 1000"
)
ingestor.close()
def example_export_documents():
"""Example: Export to Semantica document format."""
print("\n=== Example 8: Export as Documents ===\n")
_section("Example 8: Export as Documents")
ingestor = SnowflakeIngestor()
# Ingest product data
data = ingestor.ingest_table("PRODUCTS", limit=10)
# Convert to documents
documents = ingestor.export_as_documents(
data, id_field="PRODUCT_ID", text_fields=["PRODUCT_NAME", "DESCRIPTION"]
)
print(f"Exported {len(documents)} documents")
print("\nFirst document:")
print(f" ID: {documents[0]['id']}")
print(f" Text: {documents[0]['text'][:100]}...")
print(f" Metadata: {documents[0]['metadata']}")
console.print(
f"[green]✓[/green] Exported [cyan]{len(documents)}[/cyan] documents"
)
if documents:
d = documents[0]
console.print(f" [dim]First doc — ID:[/dim] {d['id']}")
console.print(f" [dim]Text:[/dim] {d['text'][:100]}")
console.print(f" [dim]Metadata:[/dim] {d['metadata']}")
ingestor.close()
def example_key_pair_auth():
"""Example: Key-pair authentication."""
print("\n=== Example 9: Key-Pair Authentication ===\n")
_section("Example 9: Key-Pair Authentication")
ingestor = SnowflakeIngestor(
account=os.getenv("SNOWFLAKE_ACCOUNT"),
@@ -224,80 +233,66 @@ def example_key_pair_auth():
private_key_path=os.getenv("SNOWFLAKE_PRIVATE_KEY_PATH"),
warehouse="COMPUTE_WH",
)
data = ingestor.ingest_table("CUSTOMERS", limit=5)
print(f"Successfully authenticated and retrieved {data.row_count} rows")
console.print(
f"[green]✓[/green] Authenticated — retrieved [cyan]{data.row_count}[/cyan] rows"
)
ingestor.close()
def example_context_manager():
"""Example: Using context manager."""
print("\n=== Example 10: Context Manager ===\n")
_section("Example 10: Context Manager")
with SnowflakeIngestor() as ingestor:
data = ingestor.ingest_table("CUSTOMERS", limit=5)
print(f"Retrieved {data.row_count} rows")
# Connection automatically closed
print("Connection closed automatically")
console.print(
f"[green]✓[/green] Retrieved [cyan]{data.row_count}[/cyan] rows"
)
console.print("[dim] Connection closed automatically.[/dim]")
def example_multi_schema():
"""Example: Multi-schema ingestion."""
print("\n=== Example 11: Multi-Schema Ingestion ===\n")
_section("Example 11: Multi-Schema Ingestion")
ingestor = SnowflakeIngestor()
prod = ingestor.ingest_table("CUSTOMERS", database="PROD_DB", schema="PUBLIC", limit=10)
staging = ingestor.ingest_table("CUSTOMERS", database="STAGING_DB", schema="PUBLIC", limit=10)
# Ingest from different schemas
prod_customers = ingestor.ingest_table(
"CUSTOMERS", database="PROD_DB", schema="PUBLIC", limit=10
)
staging_customers = ingestor.ingest_table(
"CUSTOMERS", database="STAGING_DB", schema="PUBLIC", limit=10
)
print(f"Production customers: {prod_customers.row_count}")
print(f"Staging customers: {staging_customers.row_count}")
console.print(f" Production: [cyan]{prod.row_count}[/cyan] customers")
console.print(f" Staging: [cyan]{staging.row_count}[/cyan] customers")
ingestor.close()
def example_error_handling():
"""Example: Error handling."""
print("\n=== Example 12: Error Handling ===\n")
_section("Example 12: Error Handling")
from semantica.utils.exceptions import ProcessingError, ValidationError
try:
# Try to connect with invalid credentials
ingestor = SnowflakeIngestor(
account="invalid_account", user="invalid_user", password="invalid_password"
)
data = ingestor.ingest_table("CUSTOMERS")
ingestor.ingest_table("CUSTOMERS")
except ValidationError as e:
print(f"Validation error: {e}")
console.print(f"[bold yellow] ⚠[/bold yellow] Validation error: {e}")
except ProcessingError as e:
print(f"Processing error: {e}")
console.print(f"[bold red] ✗[/bold red] Processing error: {e}")
except Exception as e:
print(f"Unexpected error: {e}")
console.print(f"[bold red] ✗[/bold red] Unexpected error: {e}")
def example_incremental_load():
"""Example: Incremental data loading."""
print("\n=== Example 13: Incremental Loading ===\n")
_section("Example 13: Incremental Loading")
ingestor = SnowflakeIngestor()
last_load = get_last_load_timestamp()
# Get last load timestamp (from your metadata store)
last_load = get_last_load_timestamp() # Your function
# Query only new/updated records
query = """
SELECT *
FROM CUSTOMERS
@@ -306,10 +301,10 @@ def example_incremental_load():
"""
data = ingestor.ingest_query(query, params={"last_load": last_load})
print(f"Loaded {data.row_count} new/updated records since {last_load}")
# Update last load timestamp
console.print(
f"[green]✓[/green] Loaded [cyan]{data.row_count}[/cyan] new/updated "
f"records since [dim]{last_load}[/dim]"
)
if data.row_count > 0:
update_last_load_timestamp(datetime.now())
@@ -318,20 +313,14 @@ def example_incremental_load():
def example_etl_pipeline():
"""Example: Full ETL pipeline."""
print("\n=== Example 14: ETL Pipeline ===\n")
_section("Example 14: ETL Pipeline")
# Extract
ingestor = SnowflakeIngestor()
sales_query = """
SELECT
s.ORDER_ID,
s.CUSTOMER_ID,
c.CUSTOMER_NAME,
s.PRODUCT_ID,
p.PRODUCT_NAME,
s.AMOUNT,
s.ORDER_DATE
SELECT
s.ORDER_ID, s.CUSTOMER_ID, c.CUSTOMER_NAME,
s.PRODUCT_ID, p.PRODUCT_NAME, s.AMOUNT, s.ORDER_DATE
FROM SALES s
JOIN CUSTOMERS c ON s.CUSTOMER_ID = c.ID
JOIN PRODUCTS p ON s.PRODUCT_ID = p.ID
@@ -339,43 +328,33 @@ def example_etl_pipeline():
"""
data = ingestor.ingest_query(sales_query)
print(f"Extracted {data.row_count} sales records")
console.print(f" [dim]Extract:[/dim] [cyan]{data.row_count}[/cyan] sales records")
# Transform
documents = ingestor.export_as_documents(
data, id_field="ORDER_ID", text_fields=["CUSTOMER_NAME", "PRODUCT_NAME"]
)
print(f"Transformed to {len(documents)} documents")
console.print(f" [dim]Transform:[/dim] [cyan]{len(documents)}[/cyan] documents")
# Load (into Semantica)
from semantica.pipeline import Pipeline
pipeline = Pipeline()
for doc in documents:
pipeline.process_document(doc)
print("Loaded documents into Semantica pipeline")
console.print("[green]✓[/green] Loaded documents into Semantica pipeline")
ingestor.close()
# Utility functions for examples
# ─── Utility stubs ────────────────────────────────────────────────────────────
def process_page(data):
"""Process a page of data."""
# Your processing logic here
pass
def get_last_load_timestamp():
"""Get the last load timestamp from metadata store."""
# Your implementation here
return (datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d %H:%M:%S")
def update_last_load_timestamp(timestamp):
"""Update the last load timestamp in metadata store."""
# Your implementation here
pass
@@ -395,17 +374,10 @@ def main():
for example_func in examples:
try:
example_func()
console.print()
except Exception as e:
logger.error(f"Example {example_func.__name__} failed: {e}")
logger.error("Example %s failed: %s", example_func.__name__, e)
if __name__ == "__main__":
# Set up environment variables
# export SNOWFLAKE_ACCOUNT=your_account
# export SNOWFLAKE_USER=your_user
# export SNOWFLAKE_PASSWORD=your_password
# export SNOWFLAKE_WAREHOUSE=COMPUTE_WH
# export SNOWFLAKE_DATABASE=SAMPLE_DB
# export SNOWFLAKE_SCHEMA=PUBLIC
main()
@@ -3,81 +3,7 @@
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Amazon Neptune Graph Store\n",
"\n",
"## Overview\n",
"\n",
"This notebook covers the Amazon Neptune Database integration in Semantica. Amazon Neptune is a fully managed graph database service that supports both property graphs (via OpenCypher/Gremlin) and RDF graphs (via SPARQL).\n",
"\n",
"### Key Features\n",
"\n",
"- **IAM Authentication**: Secure access using AWS SigV4 signatures via AuthManager\n",
"- **OpenCypher Support**: Query using standard OpenCypher syntax\n",
"- **Bolt Protocol**: Uses Neo4j Bolt driver for efficient binary communication\n",
"- **Native ~id Support**: Leverages Neptune's native element ID handling\n",
"- **Full CRUD Operations**: Create, read, update, delete nodes and relationships\n",
"- **Automatic Retry**: Built-in retry logic with exponential backoff for transient errors\n",
"\n",
"### Prerequisites\n",
"\n",
"- An Amazon Neptune Database cluster\n",
"- AWS credentials configured (boto3, environment variables, or IAM role)\n",
"- Network access to your Neptune cluster (VPC, security groups)\n",
"\n",
"#### Quick Setup with CloudFormation\n",
"\n",
"If you don't have a Neptune cluster, use the provided CloudFormation template to create one with a public endpoint and IAM authentication:\n",
"\n",
"```bash\n",
"# Deploy the Neptune stack (takes ~15-20 minutes)\n",
"aws cloudformation create-stack \\\n",
" --stack-name semantica-neptune \\\n",
" --template-body file://neptune-setup.yaml \\\n",
" --capabilities CAPABILITY_NAMED_IAM\n",
"\n",
"# Wait for stack creation to complete\n",
"aws cloudformation wait stack-create-complete --stack-name semantica-neptune\n",
"\n",
"# Get the outputs (endpoint, port, credentials)\n",
"aws cloudformation describe-stacks --stack-name semantica-neptune \\\n",
" --query 'Stacks[0].Outputs' --output table\n",
"```\n",
"\n",
"The template creates:\n",
"- VPC with public subnets and Internet Gateway\n",
"- Neptune cluster (`db.t3.medium`) with IAM authentication enabled\n",
"- IAM user with least-privilege access for OpenCypher queries\n",
"- Security group allowing Bolt protocol (port 8182) access\n",
"\n",
"> ⚠️ **Security Note**: This template creates an IAM User with static access keys for simplicity in demo/test environments. For production use, we recommend IAM Roles (EC2 instance roles, ECS task roles, Lambda execution roles) which provide temporary credentials that are automatically rotated. The secret access key in the Cloudformation outputs is provided in plaintext to simplify initial setup - in production, use AWS Secrets Manager.\n",
"\n",
"**Outputs:**\n",
"- `NeptuneEndpoint` - Cluster hostname (use as `NEPTUNE_ENDPOINT`)\n",
"- `NeptunePort` - 8182 (use as `NEPTUNE_PORT`)\n",
"- `AwsAccessKeyId` - IAM user access key (use as `AWS_ACCESS_KEY_ID`)\n",
"- `AwsSecretAccessKey` - IAM user secret key in **plaintext** (use as `AWS_SECRET_ACCESS_KEY`)\n",
"- `AwsRegion` - Deployment region (use as `AWS_REGION`)\n",
"\n",
"**Cleanup:**\n",
"```bash\n",
"aws cloudformation delete-stack --stack-name semantica-neptune\n",
"```\n",
"\n",
"**Estimated Monthly Cost (approximately 100-105 USD/month at 100% utilization):**\n",
"\n",
"| Resource | Cost (USD) |\n",
"| --- | --- |\n",
"| Neptune db.t3.medium instance | ~96/month (0.132/hr) |\n",
"| Storage (10 GB) | ~1/month |\n",
"| I/O requests | ~1-5/month |\n",
"| Public IPv4 address | ~3.60/month (0.005/hr) |\n",
"| VPC, subnets, route tables, Internet Gateway, IAM | No Additional Charge |\n",
"\n",
"> **Free Tier**: New Neptune users get 30 days free (750 hours of db.t3.medium, 10M I/Os, 1 GB storage). Delete the stack when not in use to avoid charges.\n",
"\n",
"---"
]
"source": "# Amazon Neptune Graph Store\n\n## Overview\n\nThis notebook covers the Amazon Neptune Database integration in Semantica. Amazon Neptune is a fully managed graph database service that supports both property graphs (via OpenCypher/Gremlin) and RDF graphs (via SPARQL).\n\n### Key Features\n\n- **IAM Authentication**: Secure access using AWS SigV4 signatures via AuthManager\n- **OpenCypher Support**: Query using standard OpenCypher syntax\n- **Bolt Protocol**: Uses Neo4j Bolt driver for efficient binary communication\n- **Native ~id Support**: Leverages Neptune's native element ID handling\n- **Full CRUD Operations**: Create, read, update, delete nodes and relationships\n- **Automatic Retry**: Built-in retry logic with exponential backoff for transient errors\n\n### Prerequisites\n\n- An Amazon Neptune Database cluster\n- AWS credentials configured (boto3, environment variables, or IAM role)\n- Network access to your Neptune cluster (VPC, security groups)\n- Your public IP address or VPN/office CIDR (run `curl ifconfig.me` to find your public IP), used below to restrict database access\n\n#### Quick Setup with CloudFormation\n\nIf you don't have a Neptune cluster, use the provided CloudFormation template to create one with a public endpoint and IAM authentication:\n\n```bash\n# Deploy the Neptune stack (takes ~15-20 minutes)\n# Replace 203.0.113.25/32 with your own public IP (run `curl ifconfig.me` to find it)\n# or your office/VPN CIDR. This restricts who can reach the database on the\n# network level - never widen it to 0.0.0.0/0 outside of a short-lived local experiment.\naws cloudformation create-stack \\\n --stack-name semantica-neptune \\\n --template-body file://neptune-setup.yaml \\\n --parameters ParameterKey=ClientCidr,ParameterValue=203.0.113.25/32 \\\n --capabilities CAPABILITY_NAMED_IAM\n\n# Wait for stack creation to complete\naws cloudformation wait stack-create-complete --stack-name semantica-neptune\n\n# Get the outputs (endpoint, port, credentials)\naws cloudformation describe-stacks --stack-name semantica-neptune \\\n --query 'Stacks[0].Outputs' --output table\n```\n\nThe template creates:\n- VPC with public subnets, Internet Gateway, and VPC Flow Logs (to CloudWatch Logs)\n- Neptune cluster (`db.t3.medium`) with IAM authentication enabled\n- IAM user with least-privilege access for OpenCypher queries\n- Security group allowing Bolt protocol (port 8182) access only from the `ClientCidr` you specify\n\n> ⚠️ **Security Note**: This template creates an IAM User with static access keys for simplicity in demo/test environments. For production use, we recommend IAM Roles (EC2 instance roles, ECS task roles, Lambda execution roles) which provide temporary credentials that are automatically rotated. The secret access key in the Cloudformation outputs is provided in plaintext to simplify initial setup - in production, use AWS Secrets Manager. The `ClientCidr` parameter is required (no default) precisely so the database is never silently exposed to the whole internet.\n\n**Outputs:**\n- `NeptuneEndpoint` - Cluster hostname (use as `NEPTUNE_ENDPOINT`)\n- `NeptunePort` - 8182 (use as `NEPTUNE_PORT`)\n- `AwsAccessKeyId` - IAM user access key (use as `AWS_ACCESS_KEY_ID`)\n- `AwsSecretAccessKey` - IAM user secret key in **plaintext** (use as `AWS_SECRET_ACCESS_KEY`)\n- `AwsRegion` - Deployment region (use as `AWS_REGION`)\n\n**Cleanup:**\n```bash\naws cloudformation delete-stack --stack-name semantica-neptune\n```\n\n**Estimated Monthly Cost (approximately 100-105 USD/month at 100% utilization):**\n\n| Resource | Cost (USD) |\n| --- | --- |\n| Neptune db.t3.medium instance | ~96/month (0.132/hr) |\n| Storage (10 GB) | ~1/month |\n| I/O requests | ~1-5/month |\n| Public IPv4 address | ~3.60/month (0.005/hr) |\n| VPC Flow Logs (CloudWatch Logs) | ~1-2/month depending on traffic |\n| VPC, subnets, route tables, Internet Gateway, IAM | No Additional Charge |\n\n> **Free Tier**: New Neptune users get 30 days free (750 hours of db.t3.medium, 10M I/Os, 1 GB storage). Delete the stack when not in use to avoid charges.\n\n---"
},
{
"cell_type": "markdown",
@@ -722,4 +648,4 @@
},
"nbformat": 4,
"nbformat_minor": 4
}
}
+73 -3
View File
@@ -1,7 +1,14 @@
# ts:skip=AC_AWS_0148 IAM password policy is an AWS-account-wide singleton, not a
# per-stack resource. Managing it here would mean every learner who deploys or
# deletes this cookbook stack also mutates (or removes) their account's password
# policy as a side effect. Account password policy should be set once, out of
# band, by the account owner - not by a disposable tutorial stack.
AWSTemplateFormatVersion: '2010-09-09'
Description: >
Amazon Neptune cluster with public endpoint, IAM authentication, and least-privilege
IAM user for Semantica cookbook. Uses db.t3.medium (most cost-effective Neptune instance type).
Network access to the Bolt/OpenCypher port is restricted to an operator-supplied CIDR
(see ClientCidr) - do not widen this to 0.0.0.0/0 outside of a short-lived local experiment.
Parameters:
EnvironmentName:
@@ -9,6 +16,16 @@ Parameters:
Default: semantica-neptune
Description: Environment name prefix for resource naming
ClientCidr:
Type: String
Description: >-
CIDR block allowed to reach the Neptune Bolt/OpenCypher endpoint (port 8182) - e.g. your
workstation's public IP as "x.x.x.x/32", or your office/VPN CIDR. Required: there is no
default, so you must explicitly choose a range. Passing 0.0.0.0/0 is possible but exposes
the database to the entire internet and is strongly discouraged beyond a brief local test.
AllowedPattern: '^((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])/(3[0-2]|[12]?[0-9])$'
ConstraintDescription: Must be a valid IPv4 CIDR block with octets 0-255 and prefix 0-32, e.g. 203.0.113.25/32
Resources:
# =============================================================================
# VPC & NETWORKING
@@ -87,6 +104,57 @@ Resources:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnet2
# =============================================================================
# VPC FLOW LOGS
# =============================================================================
FlowLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub /aws/vpc/${EnvironmentName}-flow-logs
RetentionInDays: 30
FlowLogRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ${EnvironmentName}-flow-log-role
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: vpc-flow-logs.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: flow-log-publish
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:DescribeLogGroups
- logs:DescribeLogStreams
Resource: "*"
- Effect: Allow
Action:
- logs:CreateLogStream
- logs:PutLogEvents
Resource: !GetAtt FlowLogGroup.Arn
VPCFlowLog:
Type: AWS::EC2::FlowLog
Properties:
ResourceType: VPC
ResourceId: !Ref VPC
TrafficType: ALL
LogDestinationType: cloud-watch-logs
LogGroupName: !Ref FlowLogGroup
DeliverLogsPermissionArn: !GetAtt FlowLogRole.Arn
Tags:
- Key: Name
Value: !Sub ${EnvironmentName}-vpc-flow-log
# =============================================================================
# SECURITY GROUP
# =============================================================================
@@ -95,14 +163,14 @@ Resources:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub ${EnvironmentName}-neptune-sg
GroupDescription: Security group for Neptune cluster - allows Bolt protocol access
GroupDescription: Security group for Neptune cluster - allows Bolt protocol access from ClientCidr only
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 8182
ToPort: 8182
CidrIp: 0.0.0.0/0
Description: Allow Bolt protocol access from anywhere
CidrIp: !Ref ClientCidr
Description: Allow Bolt/OpenCypher protocol access from the operator-specified CIDR
SecurityGroupEgress:
- IpProtocol: -1
CidrIp: 0.0.0.0/0
@@ -138,6 +206,8 @@ Resources:
IamAuthEnabled: true
StorageEncrypted: true
DeletionProtection: false
EnableCloudwatchLogsExports:
- audit
Tags:
- Key: Name
Value: !Sub ${EnvironmentName}-cluster
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,13 +0,0 @@
Graph Retrieval-Augmented Generation (GraphRAG): A New Era for Intelligent Search
GraphRAG is an advanced technique that combines the retrieval capabilities of vector databases with the structural reasoning of knowledge graphs. Unlike traditional RAG, which relies solely on vector similarity, GraphRAG leverages the relationships between entities to provide more contextually accurate and comprehensive answers.
Key Components:
1. Knowledge Graph: A structured representation of data where nodes represent entities and edges represent relationships.
2. Vector Search: Finds semantically similar text chunks.
3. Graph Traversal: Navigates the knowledge graph to find related entities that might not be semantically similar but are structurally relevant.
Benefits:
- Improved Context: By following relationships, the system can understand the broader context of a query.
- Multi-hop Reasoning: Can answer complex questions that require connecting multiple pieces of information.
- Reduced Hallucinations: Grounding answers in a verified knowledge structure reduces the likelihood of generating false information.
@@ -1,5 +0,0 @@
RETINOL CLINICAL GUIDE
Mechanism: Binds to retinoic acid receptors to increase cellular turnover.
Precautions: Should not be used with high-concentration AHA/BHA exfoliants.
Synergy: Highly effective when paired with Niacinamide to offset potential erythema.
@@ -1,6 +0,0 @@
RETINOL CLINICAL GUIDE v2.1
Mechanism: Binds to retinoic acid receptors (RAR) to increase cellular turnover.
Precautions: Should not be used with high-concentration AHA/BHA exfoliants.
Synergy: Highly effective when paired with Niacinamide to offset potential erythema.
Target: Stratum corneum thickening and dermal collagen synthesis.
@@ -1,254 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns
http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
<key id="type" for="node" attr.name="type" attr.type="string"/>
<key id="confidence" for="node" attr.name="confidence" attr.type="double"/>
<graph id="G" edgedefault="directed">
<node id="makeup_and_beauty_blog">
<data key="label">Makeup and Beauty Blog</data>
<data key="type">ORG</data>
<data key="confidence">1.0</data>
</node>
<node id="monday_poll">
<data key="label">Monday Poll</data>
<data key="type">EVENT</data>
<data key="confidence">1.0</data>
</node>
<node id="2007">
<data key="label">2007</data>
<data key="type">DATE</data>
<data key="confidence">1.0</data>
</node>
<node id="rosacea">
<data key="label">Rosacea</data>
<data key="type">CONCEPT</data>
<data key="confidence">1.0</data>
</node>
<node id="dr._bailey">
<data key="label">Dr. Bailey</data>
<data key="type">PERSON</data>
<data key="confidence">1.0</data>
</node>
<node id="green_tea_antioxidant_skin_therapy">
<data key="label">Green Tea Antioxidant Skin Therapy</data>
<data key="type">PRODUCT</data>
<data key="confidence">1.0</data>
</node>
<node id="vol._892">
<data key="label">Vol. 892</data>
<data key="type">EVENT</data>
<data key="confidence">1.0</data>
</node>
<node id="laneige">
<data key="label">Laneige</data>
<data key="type">ORG</data>
<data key="confidence">1.0</data>
</node>
<node id="sausalito">
<data key="label">Sausalito</data>
<data key="type">GPE</data>
<data key="confidence">1.0</data>
</node>
<node id="ulta">
<data key="label">Ulta</data>
<data key="type">ORG</data>
<data key="confidence">1.0</data>
</node>
<node id="december_15,_2025">
<data key="label">December 15, 2025</data>
<data key="type">DATE</data>
<data key="confidence">1.0</data>
</node>
<node id="jo_malone">
<data key="label">Jo Malone</data>
<data key="type">ORG</data>
<data key="confidence">1</data>
</node>
<node id="trader_joe">
<data key="label">Trader Joe</data>
<data key="type">ORG</data>
<data key="confidence">1</data>
</node>
<node id="hawaii">
<data key="label">hawaii</data>
<data key="type">GPE</data>
<data key="confidence">1.0</data>
</node>
<node id="benzoyl_peroxide_cream">
<data key="label">Benzoyl Peroxide Cream</data>
<data key="type">PRODUCT</data>
<data key="confidence">1</data>
</node>
<node id="facial_dandruff">
<data key="label">Facial dandruff</data>
<data key="type">CONCEPT</data>
<data key="confidence">1</data>
</node>
<node id="calming_zinc_soap">
<data key="label">Calming Zinc Soap</data>
<data key="type">PRODUCT</data>
<data key="confidence">1</data>
</node>
<node id="hydrate">
<data key="label">Hydrate</data>
<data key="type">CONCEPT</data>
<data key="confidence">1.0</data>
</node>
<node id="daily_moisturizing_face_cream">
<data key="label">Daily Moisturizing Face Cream</data>
<data key="type">PRODUCT</data>
<data key="confidence">1.0</data>
</node>
<node id="omega_enriched_face_booster_oil">
<data key="label">Omega Enriched Face Booster Oil</data>
<data key="type">PRODUCT</data>
<data key="confidence">1.0</data>
</node>
<edge source="Makeup and Beauty Blog" target="Monday Poll">
<data key="label">hosts</data>
<data key="confidence">0.9</data>
</edge>
<edge source="Monday Poll" target="December 15, 2025">
<data key="label">occurs on</data>
<data key="confidence">0.9</data>
</edge>
<edge source="Makeup and Beauty Blog" target="Makeup and Beauty Blog Monday Poll, Vol. 893">
<data key="label">publishes</data>
<data key="confidence">0.9</data>
</edge>
<edge source="Makeup and Beauty Blog" target="Monday">
<data key="label">has</data>
<data key="confidence">0.9</data>
</edge>
<edge source="Makeup and Beauty Blog" target="2007">
<data key="label">has</data>
<data key="confidence">0.9</data>
</edge>
<edge source="Makeup and Beauty Blog" target="Monday Poll">
<data key="label">hosts</data>
<data key="confidence">0.9</data>
</edge>
<edge source="Makeup and Beauty Blog" target="Makeup and Beauty Blog Monday Poll">
<data key="label">posts</data>
<data key="confidence">0.9</data>
</edge>
<edge source="Makeup and Beauty Blog" target="Vol. 892">
<data key="label">posts</data>
<data key="confidence">0.9</data>
</edge>
<edge source="Makeup and Beauty Blog" target="2007">
<data key="label">has been active since</data>
<data key="confidence">0.9</data>
</edge>
<edge source="MBB" target="Makeup and Beauty Blog">
<data key="label">related_to</data>
<data key="confidence">0.9</data>
</edge>
<edge source="Makeup and Beauty Blog" target="Makeup and Beauty Blog">
<data key="label">related_to</data>
<data key="confidence">0.9</data>
</edge>
<edge source="Makeup and Beauty Blog" target="Monday Poll">
<data key="label">hosts</data>
<data key="confidence">0.9</data>
</edge>
<edge source="Makeup and Beauty Blog" target="Vol. 891">
<data key="label">posts</data>
<data key="confidence">0.9</data>
</edge>
<edge source="Makeup and Beauty Blog" target="Monday Poll">
<data key="label">posts</data>
<data key="confidence">0.9</data>
</edge>
<edge source="Cavallo Point" target="Sausalito">
<data key="label">located_in</data>
<data key="confidence">0.9</data>
</edge>
<edge source="Dr. Bailey" target="Green Tea Antioxidant Skin Therapy">
<data key="label">prescribes</data>
<data key="confidence">0.9</data>
</edge>
<edge source="Green Tea Antioxidant Skin Therapy" target="Rosacea Therapy Skin Care Kit">
<data key="label">part of</data>
<data key="confidence">0.9</data>
</edge>
<edge source="Dr. Bailey" target="Rosacea Therapy Skin Care Kit">
<data key="label">uses</data>
<data key="confidence">0.9</data>
</edge>
<edge source="Rosacea Therapy Skin Care Kit" target="rosacea treatment routine">
<data key="label">part of</data>
<data key="confidence">0.9</data>
</edge>
<edge source="Dr. Bailey" target="rosacea treatment routine">
<data key="label">uses</data>
<data key="confidence">0.9</data>
</edge>
<edge source="Facial dandruff" target="rosacea">
<data key="label">often occurs with</data>
<data key="confidence">0.9</data>
</edge>
<edge source="Facial dandruff" target="rosacea">
<data key="label">needs to be addressed</data>
<data key="confidence">0.9</data>
</edge>
<edge source="Calming Zinc Soap" target="Facial dandruff">
<data key="label">is often sufficient to control</data>
<data key="confidence">0.9</data>
</edge>
<edge source="Calming Zinc Soap" target="rosacea">
<data key="label">is often sufficient to control</data>
<data key="confidence">0.9</data>
</edge>
<edge source="Green Tea Antioxidant Skin Therapy" target="Facial dandruff">
<data key="label">is often sufficient to control</data>
<data key="confidence">0.9</data>
</edge>
<edge source="Green Tea Antioxidant Skin Therapy" target="rosacea">
<data key="label">is often sufficient to control</data>
<data key="confidence">0.9</data>
</edge>
<edge source="Dr. Bailey's Skincare" target="Calming Zinc Soap">
<data key="label">produces</data>
<data key="confidence">0.9</data>
</edge>
<edge source="Dr. Bailey's Skincare" target="Green Tea Antioxidant Skin Therapy">
<data key="label">produces</data>
<data key="confidence">0.9</data>
</edge>
<edge source="Dr. Bailey" target="Calming Zinc Soap">
<data key="label">prescribes</data>
<data key="confidence">0.9</data>
</edge>
<edge source="Dr. Bailey" target="Green Tea Antioxidant Skin Therapy">
<data key="label">prescribes</data>
<data key="confidence">0.9</data>
</edge>
<edge source="Hydrate" target="Daily Moisturizing Face Cream">
<data key="label">is_achieved_by</data>
<data key="confidence">0.9</data>
</edge>
<edge source="Daily Moisturizing Face Cream" target="Omega Enriched Face Booster Oil">
<data key="label">can_be_combined_with</data>
<data key="confidence">0.9</data>
</edge>
<edge source="Omega Enriched Face Booster Oil" target="castor seed oil">
<data key="label">contains</data>
<data key="confidence">0.9</data>
</edge>
<edge source="Omega Enriched Face Booster Oil" target="sea buckthorn">
<data key="label">contains</data>
<data key="confidence">0.9</data>
</edge>
<edge source="Daily Moisturizing Face Cream" target="Omega Enriched Face Booster Oil">
<data key="label">can_be_replaced_with</data>
<data key="confidence">0.9</data>
</edge>
</graph>
</graphml>
@@ -1,678 +0,0 @@
{
"nodes": [
{
"id": "makeup_and_beauty_blog",
"label": "Makeup and Beauty Blog",
"type": "ORG",
"attributes": {
"confidence": 1.0,
"provenance": {
"merged_from": [
{
"id": "makeup_and_beauty_blog",
"name": "Makeup and Beauty Blog",
"source": null
},
{
"id": "makeup_and_beauty_blog",
"name": "Makeup and Beauty Blog",
"source": null
},
{
"id": "makeup_and_beauty_blog_monday_poll,_vol._893",
"name": "Makeup and Beauty Blog Monday Poll, Vol. 893",
"source": null
},
{
"id": "makeup_and_beauty_blog_monday_poll",
"name": "Makeup and Beauty Blog Monday Poll",
"source": null
},
{
"id": "mbb",
"name": "MBB",
"source": null
}
],
"merge_count": 5
}
}
},
{
"id": "monday_poll",
"label": "Monday Poll",
"type": "EVENT",
"attributes": {
"confidence": 1.0,
"provenance": {
"merged_from": [
{
"id": "monday_poll",
"name": "Monday Poll",
"source": null
},
{
"id": "monday_poll",
"name": "Monday Poll",
"source": null
},
{
"id": "monday",
"name": "Monday",
"source": null
},
{
"id": "holiday",
"name": "holiday",
"source": null
},
{
"id": "holiday",
"name": "holiday",
"source": null
}
],
"merge_count": 5
}
}
},
{
"id": "2007",
"label": "2007",
"type": "DATE",
"attributes": {
"confidence": 1.0,
"provenance": {
"merged_from": [
{
"id": "2007",
"name": "2007",
"source": null
},
{
"id": "2007",
"name": "2007",
"source": null
},
{
"id": "2024",
"name": "2024",
"source": null
}
],
"merge_count": 3
}
}
},
{
"id": "rosacea",
"label": "Rosacea",
"type": "CONCEPT",
"attributes": {
"confidence": 1.0,
"provenance": {
"merged_from": [
{
"id": "rosacea",
"name": "Rosacea",
"source": null
},
{
"id": "rosacea",
"name": "rosacea",
"source": null
},
{
"id": "rosacea_treatment_routine",
"name": "rosacea treatment routine",
"source": null
},
{
"id": "rosie",
"name": "Rosie",
"source": null
},
{
"id": "rosacea_therapy_skin_care_kit",
"name": "Rosacea Therapy Skin Care Kit",
"source": null
},
{
"id": "marnie",
"name": "Marnie",
"source": null
},
{
"id": "cavallo_point",
"name": "Cavallo Point",
"source": null
},
{
"id": "castor_seed_oil",
"name": "castor seed oil",
"source": null
}
],
"merge_count": 8
}
}
},
{
"id": "dr._bailey",
"label": "Dr. Bailey",
"type": "PERSON",
"attributes": {
"confidence": 1.0,
"provenance": {
"merged_from": [
{
"id": "dr._bailey",
"name": "Dr. Bailey",
"source": null
},
{
"id": "dr._bailey",
"name": "Dr. Bailey",
"source": null
},
{
"id": "dr._bailey's_skincare",
"name": "Dr. Bailey's Skincare",
"source": null
},
{
"id": "dr._bailey's_skincare",
"name": "Dr. Bailey's Skincare",
"source": null
}
],
"merge_count": 4
}
}
},
{
"id": "green_tea_antioxidant_skin_therapy",
"label": "Green Tea Antioxidant Skin Therapy",
"type": "PRODUCT",
"attributes": {
"confidence": 1.0,
"provenance": {
"merged_from": [
{
"id": "green_tea_antioxidant_skin_therapy",
"name": "Green Tea Antioxidant Skin Therapy",
"source": null
},
{
"id": "green_tea_antioxidant_skin_therapy",
"name": "Green Tea Antioxidant Skin Therapy",
"source": null
}
],
"merge_count": 2
}
}
},
{
"id": "vol._892",
"label": "Vol. 892",
"type": "EVENT",
"attributes": {
"confidence": 1.0,
"provenance": {
"merged_from": [
{
"id": "vol._892",
"name": "Vol. 892",
"source": null
},
{
"id": "vol._891",
"name": "Vol. 891",
"source": null
}
],
"merge_count": 2
}
}
},
{
"id": "laneige",
"label": "Laneige",
"type": "ORG",
"attributes": {
"confidence": 1.0,
"provenance": {
"merged_from": [
{
"id": "laneige",
"name": "Laneige",
"source": null
},
{
"id": "lanikai",
"name": "Lanikai",
"source": null
}
],
"merge_count": 2
}
}
},
{
"id": "sausalito",
"label": "Sausalito",
"type": "GPE",
"attributes": {
"confidence": 1.0,
"provenance": {
"merged_from": [
{
"id": "sausalito",
"name": "Sausalito",
"source": null
},
{
"id": "sea_buckthorn",
"name": "sea buckthorn",
"source": null
}
],
"merge_count": 2
}
}
},
{
"id": "ulta",
"label": "Ulta",
"type": "ORG",
"attributes": {
"confidence": 1.0,
"provenance": {
"merged_from": [
{
"id": "ulta",
"name": "Ulta",
"source": null
},
{
"id": "clotrimazole",
"name": "clotrimazole",
"source": null
}
],
"merge_count": 2
}
}
},
{
"id": "december_15,_2025",
"label": "December 15, 2025",
"type": "DATE",
"attributes": {
"confidence": 1.0
}
},
{
"id": "jo_malone",
"label": "Jo Malone",
"type": "ORG",
"attributes": {
"confidence": 1
}
},
{
"id": "trader_joe",
"label": "Trader Joe",
"type": "ORG",
"attributes": {
"confidence": 1
}
},
{
"id": "hawaii",
"label": "hawaii",
"type": "GPE",
"attributes": {
"confidence": 1.0
}
},
{
"id": "benzoyl_peroxide_cream",
"label": "Benzoyl Peroxide Cream",
"type": "PRODUCT",
"attributes": {
"confidence": 1
}
},
{
"id": "facial_dandruff",
"label": "Facial dandruff",
"type": "CONCEPT",
"attributes": {
"confidence": 1
}
},
{
"id": "calming_zinc_soap",
"label": "Calming Zinc Soap",
"type": "PRODUCT",
"attributes": {
"confidence": 1
}
},
{
"id": "hydrate",
"label": "Hydrate",
"type": "CONCEPT",
"attributes": {
"confidence": 1.0
}
},
{
"id": "daily_moisturizing_face_cream",
"label": "Daily Moisturizing Face Cream",
"type": "PRODUCT",
"attributes": {
"confidence": 1.0
}
},
{
"id": "omega_enriched_face_booster_oil",
"label": "Omega Enriched Face Booster Oil",
"type": "PRODUCT",
"attributes": {
"confidence": 1.0
}
}
],
"edges": [
{
"source": "Makeup and Beauty Blog",
"target": "Monday Poll",
"type": "hosts",
"attributes": {
"confidence": 0.9
}
},
{
"source": "Monday Poll",
"target": "December 15, 2025",
"type": "occurs on",
"attributes": {
"confidence": 0.9
}
},
{
"source": "Makeup and Beauty Blog",
"target": "Makeup and Beauty Blog Monday Poll, Vol. 893",
"type": "publishes",
"attributes": {
"confidence": 0.9
}
},
{
"source": "Makeup and Beauty Blog",
"target": "Monday",
"type": "has",
"attributes": {
"confidence": 0.9
}
},
{
"source": "Makeup and Beauty Blog",
"target": "2007",
"type": "has",
"attributes": {
"confidence": 0.9
}
},
{
"source": "Makeup and Beauty Blog",
"target": "Monday Poll",
"type": "hosts",
"attributes": {
"confidence": 0.9
}
},
{
"source": "Makeup and Beauty Blog",
"target": "Makeup and Beauty Blog Monday Poll",
"type": "posts",
"attributes": {
"confidence": 0.9
}
},
{
"source": "Makeup and Beauty Blog",
"target": "Vol. 892",
"type": "posts",
"attributes": {
"confidence": 0.9
}
},
{
"source": "Makeup and Beauty Blog",
"target": "2007",
"type": "has been active since",
"attributes": {
"confidence": 0.9
}
},
{
"source": "MBB",
"target": "Makeup and Beauty Blog",
"type": "related_to",
"attributes": {
"confidence": 0.9
}
},
{
"source": "Makeup and Beauty Blog",
"target": "Makeup and Beauty Blog",
"type": "related_to",
"attributes": {
"confidence": 0.9
}
},
{
"source": "Makeup and Beauty Blog",
"target": "Monday Poll",
"type": "hosts",
"attributes": {
"confidence": 0.9
}
},
{
"source": "Makeup and Beauty Blog",
"target": "Vol. 891",
"type": "posts",
"attributes": {
"confidence": 0.9
}
},
{
"source": "Makeup and Beauty Blog",
"target": "Monday Poll",
"type": "posts",
"attributes": {
"confidence": 0.9
}
},
{
"source": "Cavallo Point",
"target": "Sausalito",
"type": "located_in",
"attributes": {
"confidence": 0.9
}
},
{
"source": "Dr. Bailey",
"target": "Green Tea Antioxidant Skin Therapy",
"type": "prescribes",
"attributes": {
"confidence": 0.9
}
},
{
"source": "Green Tea Antioxidant Skin Therapy",
"target": "Rosacea Therapy Skin Care Kit",
"type": "part of",
"attributes": {
"confidence": 0.9
}
},
{
"source": "Dr. Bailey",
"target": "Rosacea Therapy Skin Care Kit",
"type": "uses",
"attributes": {
"confidence": 0.9
}
},
{
"source": "Rosacea Therapy Skin Care Kit",
"target": "rosacea treatment routine",
"type": "part of",
"attributes": {
"confidence": 0.9
}
},
{
"source": "Dr. Bailey",
"target": "rosacea treatment routine",
"type": "uses",
"attributes": {
"confidence": 0.9
}
},
{
"source": "Facial dandruff",
"target": "rosacea",
"type": "often occurs with",
"attributes": {
"confidence": 0.9
}
},
{
"source": "Facial dandruff",
"target": "rosacea",
"type": "needs to be addressed",
"attributes": {
"confidence": 0.9
}
},
{
"source": "Calming Zinc Soap",
"target": "Facial dandruff",
"type": "is often sufficient to control",
"attributes": {
"confidence": 0.9
}
},
{
"source": "Calming Zinc Soap",
"target": "rosacea",
"type": "is often sufficient to control",
"attributes": {
"confidence": 0.9
}
},
{
"source": "Green Tea Antioxidant Skin Therapy",
"target": "Facial dandruff",
"type": "is often sufficient to control",
"attributes": {
"confidence": 0.9
}
},
{
"source": "Green Tea Antioxidant Skin Therapy",
"target": "rosacea",
"type": "is often sufficient to control",
"attributes": {
"confidence": 0.9
}
},
{
"source": "Dr. Bailey's Skincare",
"target": "Calming Zinc Soap",
"type": "produces",
"attributes": {
"confidence": 0.9
}
},
{
"source": "Dr. Bailey's Skincare",
"target": "Green Tea Antioxidant Skin Therapy",
"type": "produces",
"attributes": {
"confidence": 0.9
}
},
{
"source": "Dr. Bailey",
"target": "Calming Zinc Soap",
"type": "prescribes",
"attributes": {
"confidence": 0.9
}
},
{
"source": "Dr. Bailey",
"target": "Green Tea Antioxidant Skin Therapy",
"type": "prescribes",
"attributes": {
"confidence": 0.9
}
},
{
"source": "Hydrate",
"target": "Daily Moisturizing Face Cream",
"type": "is_achieved_by",
"attributes": {
"confidence": 0.9
}
},
{
"source": "Daily Moisturizing Face Cream",
"target": "Omega Enriched Face Booster Oil",
"type": "can_be_combined_with",
"attributes": {
"confidence": 0.9
}
},
{
"source": "Omega Enriched Face Booster Oil",
"target": "castor seed oil",
"type": "contains",
"attributes": {
"confidence": 0.9
}
},
{
"source": "Omega Enriched Face Booster Oil",
"target": "sea buckthorn",
"type": "contains",
"attributes": {
"confidence": 0.9
}
},
{
"source": "Daily Moisturizing Face Cream",
"target": "Omega Enriched Face Booster Oil",
"type": "can_be_replaced_with",
"attributes": {
"confidence": 0.9
}
}
],
"metadata": {
"num_entities": 20,
"num_relationships": 35,
"temporal_enabled": false,
"timestamp": "2025-12-24T12:46:41.535755",
"entity_resolution_applied": true
}
}
@@ -1 +0,0 @@
{"entities": [{"id": "python_org", "name": "Python Software Foundation", "type": "Organization"}, {"id": "guido_van_rossum", "name": "Guido van Rossum", "type": "Person"}], "relationships": [{"source": "guido_van_rossum", "target": "python_org", "type": "FOUNDED"}]}
@@ -1,38 +0,0 @@
{
"entities": [
{
"id": "hyaluronic_acid",
"name": "Hyaluronic Acid",
"type": "Ingredient",
"properties": {
"role": "Humectant"
}
},
{
"id": "retinol",
"name": "Retinol",
"type": "Ingredient",
"properties": {
"role": "Anti-aging actives"
}
},
{
"id": "niacinamide",
"name": "Niacinamide",
"type": "Ingredient",
"properties": {
"role": "Barrier repair"
}
}
],
"relationships": [
{
"source": "hyaluronic_acid",
"target": "niacinamide",
"type": "COMPLEMENTS",
"properties": {
"benefit": "Hydration + Barrier"
}
}
]
}
@@ -1,693 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/Hawksight-AI/semantica/blob/main/cookbook/use_cases/biomedical/01_Drug_Discovery_Pipeline.ipynb)\n",
"\n",
"# Drug Discovery Pipeline - Vector Similarity Search\n",
"\n",
"## Overview\n",
"\n",
"This notebook demonstrates a **complete drug discovery pipeline** using Semantica's modular architecture. We'll use individual modules directly to build a comprehensive system for drug-target interaction prediction using vector similarity search and knowledge graphs.\n",
"\n",
"### Key Features\n",
"\n",
"- **Modular Architecture**: Uses Semantica modules directly (`NERExtractor`, `GraphBuilder`, `EmbeddingGenerator`, `VectorStore`)\n",
"- **Multiple Data Sources**: Ingests from 15+ PubMed RSS feeds, preprint servers, and journal feeds\n",
"- **Vector Similarity Search**: Emphasizes embeddings and vector similarity for drug-target interaction prediction\n",
"- **Entity Extraction**: Extracts drug compounds, proteins, targets, enzymes, and receptors\n",
"- **Knowledge Graph**: Builds structured drug-target relationship graphs\n",
"- **GraphRAG**: Hybrid vector + graph retrieval for enhanced querying\n",
"\n",
"### What You'll Learn\n",
"\n",
"- How to use Semantica modules directly (avoiding the core orchestrator)\n",
"- How to ingest biomedical data from multiple sources\n",
"- How to extract entities using `NERExtractor`\n",
"- How to extract relationships using `RelationExtractor`\n",
"- How to generate embeddings with `EmbeddingGenerator`\n",
"- How to build knowledge graphs with `GraphBuilder`\n",
"- How to perform similarity search with `VectorStore`\n",
"- How to use GraphRAG with `AgentContext` for hybrid retrieval\n",
"\n",
"### Pipeline Flow\n",
"\n",
"```mermaid\n",
"graph LR\n",
" A[Data Ingestion] --> B[Text Processing]\n",
" B --> C[Entity Extraction]\n",
" C --> D[Relationship Extraction]\n",
" D --> E[Deduplication]\n",
" E --> F[Embedding Generation]\n",
" F --> G[Vector Store]\n",
" G --> H[Knowledge Graph]\n",
" H --> I[Similarity Search]\n",
" H --> J[GraphRAG Queries]\n",
" I --> K[Visualization]\n",
" J --> K\n",
"```\n",
"\n",
"### Data Sources\n",
"\n",
"**PubMed RSS Feeds:**\n",
"- Drug Discovery, Drug Target Interaction, Pharmacokinetics, Pharmacodynamics\n",
"- Clinical Trials, Protein Targets, Drug Repurposing, Molecular Docking\n",
"- ADME, Drug Metabolism, Drug Safety, Precision Medicine\n",
"- Biomarkers, Drug Resistance, Combinatorial Therapy\n",
"\n",
"**Preprint Servers:**\n",
"- BioRxiv (Pharmacology & Toxicology, Drug Discovery)\n",
"- MedRxiv (Clinical Trials)\n",
"- ChemRxiv\n",
"\n",
"**Journal RSS Feeds:**\n",
"- Nature (Drug Discovery, Pharmacology)\n",
"- Science Translational Medicine\n",
"- Cell Chemical Biology\n",
"- Journal of Medicinal Chemistry\n",
"- Drug Discovery Today\n",
"- Trends in Pharmacological Sciences\n",
"\n",
"\n",
"---\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Installation\n",
"\n",
"Install Semantica and required dependencies:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%pip install -qU semantica networkx matplotlib plotly pandas faiss-cpu beautifulsoup4 groq sentence-transformers scikit-learn\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Configuration & Setup\n",
"\n",
"Set up environment variables and configuration constants.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"\n",
"os.environ[\"GROQ_API_KEY\"] = os.getenv(\"GROQ_API_KEY\", \"\")\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"EMBEDDING_DIMENSION = 384\n",
"EMBEDDING_MODEL = \"all-MiniLM-L6-v2\"\n",
"CHUNK_SIZE = 1000\n",
"CHUNK_OVERLAP = 200\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Ingesting Biomedical Data from Multiple Sources\n",
"\n",
"Ingest data from comprehensive biomedical sources including PubMed RSS feeds, preprint servers, and journal feeds.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from semantica.ingest import FeedIngestor, FileIngestor\n",
"import os\n",
"from contextlib import redirect_stderr\n",
"from io import StringIO\n",
"\n",
"os.makedirs(\"data\", exist_ok=True)\n",
"\n",
"feed_sources = [\n",
" # Nature Feeds\n",
" (\"Nature - Drug Discovery\", \"https://www.nature.com/subjects/drug-discovery.rss\"),\n",
" (\"Nature - Pharmacology\", \"https://www.nature.com/subjects/pharmacology.rss\"),\n",
" (\"Nature Reviews Drug Discovery\", \"https://www.nature.com/nrd.rss\"),\n",
" \n",
" # FDA & Government Sources\n",
" (\"FDA MedWatch\", \"https://www.fda.gov/AboutFDA/ContactFDA/StayInformed/RSSFeeds/MedWatch/rss.xml\"),\n",
" (\"NCI News\", \"https://www.cancer.gov/syndication/rss\"),\n",
" \n",
" # Drug Information & News\n",
" (\"Drugs.com - MedNews\", \"https://www.drugs.com/rss/mednews.xml\"),\n",
" (\"Drugs.com - FDA Alerts\", \"https://www.drugs.com/rss/fda-alerts.xml\"),\n",
" (\"Drugs.com - Clinical Trials\", \"https://www.drugs.com/rss/clinical-trials.xml\"),\n",
" \n",
" # Medical News\n",
" (\"Labroots Health & Medicine\", \"http://www.labroots.com/rss/trending/health-and-medicine\"),\n",
" (\"Biology News Net\", \"https://www.biologynews.net/rss.php\"),\n",
" \n",
" # Open Access Journals\n",
" (\"PLOS ONE - Medicine\", \"https://journals.plos.org/plosone/feed/atom\"),\n",
" (\"PLOS Biology\", \"https://journals.plos.org/plosbiology/feed/atom\"),\n",
" (\"PLOS Medicine\", \"https://journals.plos.org/plosmedicine/feed/atom\"),\n",
" \n",
" # Preprint Servers\n",
" (\"arXiv - q-bio\", \"http://arxiv.org/rss/q-bio\"),\n",
" (\"arXiv - q-bio.BM\", \"http://arxiv.org/rss/q-bio.BM\"),\n",
"]\n",
"\n",
"feed_ingestor = FeedIngestor()\n",
"all_documents = []\n",
"\n",
"print(f\"Ingesting from {len(feed_sources)} feed sources...\")\n",
"for i, (feed_name, feed_url) in enumerate(feed_sources, 1):\n",
" try:\n",
" with redirect_stderr(StringIO()):\n",
" feed_data = feed_ingestor.ingest_feed(feed_url, validate=False)\n",
" \n",
" feed_count = 0\n",
" for item in feed_data.items:\n",
" if not item.content:\n",
" item.content = item.description or item.title or \"\"\n",
" if item.content:\n",
" if not hasattr(item, 'metadata'):\n",
" item.metadata = {}\n",
" item.metadata['source'] = feed_name\n",
" all_documents.append(item)\n",
" feed_count += 1\n",
" \n",
" if feed_count > 0:\n",
" print(f\" [{i}/{len(feed_sources)}] {feed_name}: {feed_count} documents\")\n",
" except Exception:\n",
" continue\n",
"\n",
"if not all_documents:\n",
" sample_drug_data = \"\"\"\n",
" Aspirin (acetylsalicylic acid) is a medication used to reduce pain, fever, or inflammation. \n",
" It targets cyclooxygenase enzymes COX-1 and COX-2. Aspirin is commonly used for cardiovascular protection.\n",
" Ibuprofen is a nonsteroidal anti-inflammatory drug (NSAID) that targets COX-1 and COX-2 enzymes.\n",
" Metformin is an antidiabetic medication that targets AMP-activated protein kinase (AMPK).\n",
" Insulin targets the insulin receptor (INSR) to regulate glucose metabolism.\n",
" Warfarin is an anticoagulant that targets vitamin K epoxide reductase complex subunit 1 (VKORC1).\n",
" Atorvastatin is a statin medication that targets HMG-CoA reductase.\n",
" \"\"\"\n",
" \n",
" with open(\"data/sample_drugs.txt\", \"w\") as f:\n",
" f.write(sample_drug_data)\n",
" \n",
" file_ingestor = FileIngestor()\n",
" all_documents = file_ingestor.ingest(\"data/sample_drugs.txt\")\n",
"\n",
"documents = all_documents\n",
"print(f\"Ingested {len(documents)} documents\")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Normalizing and Chunking Documents\n",
"\n",
"Clean and normalize text, then split into chunks using entity-aware chunking to preserve drug/protein entity boundaries.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from semantica.normalize import TextNormalizer\n",
"from semantica.split import TextSplitter\n",
"\n",
"normalizer = TextNormalizer()\n",
"splitter = TextSplitter(\n",
" method=\"entity_aware\",\n",
" ner_method=\"spacy\",\n",
" chunk_size=CHUNK_SIZE,\n",
" chunk_overlap=CHUNK_OVERLAP\n",
")\n",
"\n",
"print(f\"Normalizing {len(documents)} documents...\")\n",
"normalized_documents = []\n",
"for i, doc in enumerate(documents, 1):\n",
" normalized_text = normalizer.normalize(\n",
" doc.content if hasattr(doc, 'content') else str(doc),\n",
" clean_html=True,\n",
" normalize_entities=True,\n",
" remove_extra_whitespace=True,\n",
" lowercase=False\n",
" )\n",
" normalized_documents.append(normalized_text)\n",
" if i % 50 == 0 or i == len(documents):\n",
" print(f\" Normalized {i}/{len(documents)} documents...\")\n",
"\n",
"print(f\"Chunking {len(normalized_documents)} documents...\")\n",
"chunked_documents = []\n",
"for i, doc_text in enumerate(normalized_documents, 1):\n",
" try:\n",
" with redirect_stderr(StringIO()):\n",
" chunks = splitter.split(doc_text)\n",
" chunked_documents.extend(chunks)\n",
" except Exception:\n",
" simple_splitter = TextSplitter(method=\"recursive\", chunk_size=CHUNK_SIZE, chunk_overlap=CHUNK_OVERLAP)\n",
" chunks = simple_splitter.split(doc_text)\n",
" chunked_documents.extend(chunks)\n",
" if i % 50 == 0 or i == len(normalized_documents):\n",
" print(f\" Chunked {i}/{len(normalized_documents)} documents ({len(chunked_documents)} chunks so far)\")\n",
"\n",
"print(f\"Created {len(chunked_documents)} chunks from {len(normalized_documents)} documents\")\n",
"\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from semantica.semantic_extract import NERExtractor\n",
"\n",
"# Using spaCy ML method (similar to NER cell)\n",
"entity_extractor = NERExtractor(method=\"ml\", model=\"en_core_web_sm\")\n",
"\n",
"all_entities = []\n",
"print(f\"Extracting entities from {len(chunked_documents)} chunks...\")\n",
"\n",
"for i, chunk in enumerate(chunked_documents, 1):\n",
" chunk_text = chunk.text if hasattr(chunk, 'text') else str(chunk)\n",
" try:\n",
" entities = entity_extractor.extract_entities(chunk_text)\n",
" all_entities.extend(entities)\n",
" except Exception:\n",
" continue\n",
" \n",
" if i % 20 == 0 or i == len(chunked_documents):\n",
" remaining = len(chunked_documents) - i\n",
" print(f\" Processed {i}/{len(chunked_documents)} chunks ({len(all_entities)} entities found, {remaining} remaining)\")\n",
"\n",
"# Filter entities - spaCy returns standard types (PERSON, ORG, PRODUCT, etc.)\n",
"# Map to biomedical categories based on context\n",
"drugs = [e for e in all_entities if e.label == \"PRODUCT\" or (e.label == \"ORG\" and any(kw in e.text.lower() for kw in [\"drug\", \"pharma\", \"medication\"]))]\n",
"proteins = [e for e in all_entities if e.label == \"ORG\" or (e.label == \"PRODUCT\" and any(kw in e.text.lower() for kw in [\"protein\", \"enzyme\", \"receptor\", \"kinase\", \"target\"]))]\n",
"\n",
"print(f\"Extracted {len(drugs)} drugs and {len(proteins)} proteins\")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Extracting Drug-Target Relationships\n",
"\n",
"Extract relationships between drugs and proteins to understand drug-target interactions.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from semantica.semantic_extract import RelationExtractor\n",
"\n",
"# Using spaCy dependency parsing (similar to NER cell)\n",
"relation_extractor = RelationExtractor(method=\"dependency\", model=\"en_core_web_sm\")\n",
"\n",
"all_relationships = []\n",
"print(f\"Extracting relationships from {len(chunked_documents)} chunks...\")\n",
"\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",
" relation_types=[\"targets\", \"inhibits\", \"activates\", \"binds_to\", \"interacts_with\"]\n",
" )\n",
" all_relationships.extend(relationships)\n",
" except Exception:\n",
" continue\n",
" \n",
" if i % 20 == 0 or i == len(chunked_documents):\n",
" print(f\" Processed {i}/{len(chunked_documents)} chunks ({len(all_relationships)} relationships found)\")\n",
"\n",
"print(f\"Extracted {len(all_relationships)} relationships\")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Resolving Duplicate Entities\n",
"\n",
"Detect and merge duplicate entities to ensure data quality and consistency.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Conflict Detection and Resolution\n",
"\n",
"Detect and resolve conflicts in drug-target relationships from multiple research sources.\n",
"\n",
"- **Detection Method**: Relationship conflict detection identifies discrepancies in drug-target interactions across sources\n",
"- **Resolution Strategy**: Credibility-weighted resolution prioritizes higher-credibility sources (e.g., Nature journals over arXiv preprints)\n",
"- **Use Case**: Handles conflicting information when multiple sources report different drug-target relationships\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from semantica.conflicts import ConflictDetector, ConflictResolver\n",
"\n",
"detector = ConflictDetector()\n",
"resolver = ConflictResolver(default_strategy=\"credibility_weighted\")\n",
"\n",
"# Convert to dict format for conflict detection\n",
"entities = [\n",
" {\n",
" \"id\": ent.text if hasattr(ent, 'text') else str(ent),\n",
" \"name\": ent.text if hasattr(ent, 'text') else str(ent),\n",
" \"type\": ent.label if hasattr(ent, 'label') else \"ENTITY\",\n",
" \"confidence\": getattr(ent, 'confidence', 1.0),\n",
" \"source\": ent.metadata.get(\"source\", \"unknown\") if hasattr(ent, 'metadata') and ent.metadata else \"unknown\"\n",
" }\n",
" for ent in all_entities if hasattr(ent, 'text') or hasattr(ent, 'label')\n",
"]\n",
"\n",
"relationships = [\n",
" {\n",
" \"id\": f\"{rel.subject.text}_{rel.object.text}_{rel.predicate}\",\n",
" \"source_id\": rel.subject.text,\n",
" \"target_id\": rel.object.text,\n",
" \"type\": rel.predicate,\n",
" \"confidence\": getattr(rel, 'confidence', 1.0),\n",
" \"source\": rel.metadata.get(\"source\", \"unknown\") if hasattr(rel, 'metadata') and rel.metadata else \"unknown\"\n",
" }\n",
" for rel in all_relationships if hasattr(rel, 'subject')\n",
"]\n",
"\n",
"# Detect and resolve conflicts\n",
"print(f\"Detecting conflicts in {len(entities)} entities, {len(relationships)} relationships...\")\n",
"entity_conflicts = detector.detect_conflicts(entities)\n",
"relationship_conflicts = detector.detect_relationship_conflicts(relationships)\n",
"print(f\"Detected {len(entity_conflicts)} entity conflicts, {len(relationship_conflicts)} relationship conflicts\")\n",
"\n",
"# Resolve conflicts\n",
"if entity_conflicts:\n",
" resolver.resolve_conflicts(entity_conflicts, strategy=\"credibility_weighted\")\n",
" print(f\"Resolved {len(entity_conflicts)} entity conflicts\")\n",
"\n",
"if relationship_conflicts:\n",
" resolver.resolve_conflicts(relationship_conflicts, strategy=\"credibility_weighted\")\n",
" print(f\"Resolved {len(relationship_conflicts)} relationship conflicts\")\n",
"\n",
"# GraphBuilder will use resolve_conflicts=True to apply resolutions automatically\n",
"print(\"Conflicts resolved. GraphBuilder will use cleaned data.\")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Generating Vector Embeddings\n",
"\n",
"Generate embeddings for drugs and proteins to enable similarity search.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from semantica.embeddings import EmbeddingGenerator\n",
"from semantica.vector_store import VectorStore\n",
"\n",
"embedding_gen = EmbeddingGenerator(\n",
" provider=\"sentence_transformers\",\n",
" model=EMBEDDING_MODEL\n",
")\n",
"\n",
"vector_store = VectorStore(backend=\"faiss\", dimension=EMBEDDING_DIMENSION)\n",
"\n",
"print(f\"Generating embeddings for {len(drugs)} drugs and {len(proteins)} proteins...\")\n",
"drug_texts = [d.text for d in drugs]\n",
"drug_embeddings = embedding_gen.generate_embeddings(drug_texts)\n",
"\n",
"protein_texts = [p.text for p in proteins]\n",
"protein_embeddings = embedding_gen.generate_embeddings(protein_texts)\n",
"\n",
"print(f\"Generated {len(drug_embeddings)} drug embeddings and {len(protein_embeddings)} protein embeddings\")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Populating Vector Database\n",
"\n",
"Store drug and protein embeddings in the vector database with metadata for efficient similarity search.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(f\"Storing {len(drug_embeddings)} drug vectors and {len(protein_embeddings)} protein vectors...\")\n",
"drug_ids = vector_store.store_vectors(\n",
" vectors=drug_embeddings,\n",
" metadata=[{\"type\": \"drug\", \"name\": d.text, \"label\": d.label} for d in drugs]\n",
")\n",
"\n",
"protein_ids = vector_store.store_vectors(\n",
" vectors=protein_embeddings,\n",
" metadata=[{\"type\": \"protein\", \"name\": p.text, \"label\": p.label} for p in proteins]\n",
")\n",
"\n",
"print(f\"Stored {len(drug_ids)} drug vectors and {len(protein_ids)} protein vectors\")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Building Drug-Target Knowledge Graph\n",
"\n",
"Construct a knowledge graph from extracted entities and relationships to enable graph-based reasoning.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from semantica.kg import GraphBuilder\n",
"\n",
"graph_builder = GraphBuilder()\n",
"\n",
"print(f\"Building graph from {len(all_entities)} entities, {len(all_relationships)} relationships...\")\n",
"kg = graph_builder.build({\n",
" \"entities\": all_entities,\n",
" \"relationships\": all_relationships\n",
"})\n",
"\n",
"entities_count = len(kg.get('entities', []))\n",
"relationships_count = len(kg.get('relationships', []))\n",
"print(f\"Graph: {entities_count} entities, {relationships_count} relationships\")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Finding Similar Drugs via Vector Search\n",
"\n",
"Use vector similarity search to find drugs similar to a query drug based on their embeddings.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"query_drug = \"Aspirin\"\n",
"query_embedding = embedding_gen.generate_embeddings([query_drug])[0]\n",
"similar_drugs = vector_store.search_vectors(query_embedding, k=5)\n",
"\n",
"print(f\"Drugs similar to '{query_drug}':\")\n",
"for i, result in enumerate(similar_drugs, 1):\n",
" metadata = result.get('metadata', {})\n",
" name = metadata.get('name', 'Unknown') if metadata else 'Unknown'\n",
" score = result.get('score', 0.0)\n",
" print(f\"{i}. {name} (similarity: {score:.3f})\")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## GraphRAG: Hybrid Vector + Graph Retrieval\n",
"\n",
"Use GraphRAG to combine vector similarity search with knowledge graph traversal for enhanced retrieval and reasoning.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from semantica.context import AgentContext, ContextRetriever\n",
"\n",
"# Option 1: Use AgentContext (high-level, recommended)\n",
"context = AgentContext(\n",
" vector_store=vector_store, \n",
" knowledge_graph=kg,\n",
" hybrid_alpha=0.6,\n",
" max_expansion_hops=2\n",
")\n",
"\n",
"# Option 2: Use ContextRetriever directly (more control)\n",
"retriever = ContextRetriever(\n",
" vector_store=vector_store,\n",
" knowledge_graph=kg,\n",
" hybrid_alpha=0.6,\n",
" max_expansion_hops=2\n",
")\n",
"\n",
"# GraphRAG query using AgentContext\n",
"query = \"What drugs target COX enzymes?\"\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",
")\n",
"\n",
"\n",
"print(f\"Query: '{query}'\")\n",
"print(f\"Retrieved {len(results)} results:\\n\")\n",
"for i, result in enumerate(results[:5], 1):\n",
" print(f\"{i}. Score: {result.get('score', 0):.3f}\")\n",
" if result.get('content'):\n",
" print(f\" {result['content'][:250]}\")\n",
" if result.get('related_entities'):\n",
" entities = result['related_entities']\n",
" names = [e.get('name', e.get('id', '')) for e in entities[:3]]\n",
" print(f\" Entities: {', '.join(names)}\" + (f\" (+{len(entities)-3})\" if len(entities) > 3 else \"\"))\n",
" if result.get('related_relationships'):\n",
" print(f\" Relationships: {len(result['related_relationships'])}\")\n",
" print()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Visualizing the Knowledge Graph\n",
"\n",
"Generate an interactive visualization of the drug-target knowledge graph.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from semantica.visualization import KGVisualizer\n",
"\n",
"# Display interactive Plotly graph directly in notebook\n",
"visualizer = KGVisualizer(layout=\"force\", node_size=20)\n",
"fig = visualizer.visualize_network(kg, output=\"interactive\")\n",
"\n",
"# Display the figure (Plotly will show it automatically in notebook)\n",
"fig.show() if fig else None"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Exporting Results\n",
"\n",
"Export the knowledge graph to various formats for further analysis or integration with other tools.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from semantica.export import GraphExporter\n",
"\n",
"exporter = GraphExporter()\n",
"exporter.export(kg, output_path=\"drug_target_kg.json\", format=\"json\")\n",
"exporter.export(kg, output_path=\"drug_target_kg.graphml\", format=\"graphml\")\n",
"\n",
"print(\"Exported knowledge graph to JSON and GraphML formats\")\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"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,
"nbformat_minor": 2
}

Some files were not shown because too many files have changed in this diff Show More