Commit Graph
576 Commits
Author SHA1 Message Date
Sameer Kadam 6dad69cdb4 Merge branch 'main' into fix/mcp-export-graph 2026-08-24 14:45:37 +05:30
Sameer6305 9b30c8af94 fix(mcp): repair standalone export_graph 2026-08-24 14:00:07 +05:30
Mohd Kaif 7109040984 Merge branch 'main' into metadata-passthrough 2026-08-24 13:44:03 +05:30
KaifAhmad1 220fb10e5c fix(export): escape IRI-valued metadata to close a Turtle/N-Triples injection gap
_turtle_object() wrote an IRI-valued metadata value (currently only
sem:sourceUri, from the "uri" metadata key) straight into `<{value}>`
with no escaping. Turtle/N-Triples IRIREFs exclude control
characters, space, and <>"{}|^`\ unescaped, so a value shaped like
`<goodIRI> . <injected> <p> <o>` closed the reference early and let
the rest of the string be parsed as an attacker-chosen extra triple:

    metadata={"uri": "https://x> . <https://injected> <https://p> <https://o"}

produced a well-formed Turtle/N-Triples document containing a triple
the caller never asked for.

RDF/XML was already safe (_rdfxml_metadata_lines runs the value
through _escape_xml before putting it in an rdf:resource attribute),
and JSON-LD is safe by construction (json.dumps makes structural
injection impossible) — only the Turtle/N-Triples "iri" literal path
in _turtle_object was unguarded.

Adds _safe_iri_ref(), a narrow percent-encoder for exactly the
characters an IRIREF may not contain unescaped. It's deliberately not
_as_turtle_iri: that also resolves registered prefixes, which a
metadata value never needs, so a dedicated guard stays simpler than
threading namespaces into a module-level helper that has no `self`.

Two regression tests, parametrised over turtle/ntriples: the `>`
delimiter-breaking payload from the report, and a control-character
(newline/tab) variant covering the other half of the excluded set.
2026-08-24 13:38:07 +05:30
KaifAhmad1 fb02c868f8 Merge branch 'main' into metadata-passthrough
Resolves the conflict in semantica/export/rdf_exporter.py between this
branch's metadata clauses (entity/graph metadata statements) and
main's IRI-normalization and XML-escaping hardening
(_as_turtle_iri / xml_escape, landed after this branch's last sync).

Kept both: entity/relationship/graph subjects and objects now go
through _as_turtle_iri (Turtle) or _as_turtle_iri + xml_escape
(RDF/XML), same as every other identifier in these serializers,
while the metadata-clause list building and graph_uri handling from
this branch are preserved unchanged. graph_uri is now normalized the
same way for consistency with the rest of the file.

Verified: tests/export + tests/ontology (411 tests) and the existing
Turtle-IRI regression suite (test_rdf_exporter_turtle_iris.py, 9
tests) all pass against the merged code.
2026-08-24 13:27:45 +05:30
Sameer Kadam 346f98bdbf Merge branch 'main' into fix/mcp-export-graph 2026-08-24 13:02:27 +05:30
Mohd Kaif b9cb524514 Merge branch 'main' into fix/1185-non-tty-progress 2026-08-24 12:25:14 +05:30
Sameer Kadam cf4750ebf0 Merge branch 'main' into fix/mcp-export-graph 2026-08-24 12:06:57 +05:30
Freakz2z 49db007691 Merge remote-tracking branch 'upstream/main' into fix/rdf4j-repository-id 2026-08-24 09:02:42 +08:00
Freakz2z 4c997b5017 fix(triplet_store): encode RDF4J repository paths 2026-08-24 09:02:42 +08:00
Aldrin Joseph de31b43663 fix(utils): write console progress only to an interactive stdout
ProgressTracker attached ConsoleProgressDisplay unconditionally, so any
script or CI job that piped or redirected stdout had one progress bar per
stage written into its output, escape sequences included. A plain
`python demo.py > out.txt` captured 173 bytes of progress-bar noise around
10 bytes of the program's own output.

Console progress is now attached only when stdout is an interactive
terminal, when running under Jupyter, or when SEMANTICA_FORCE_PROGRESS is
set. FileProgressDisplay is untouched, so progress logging still works in
pipelines, and SEMANTICA_DISABLE_PROGRESS keeps its existing meaning and
still takes precedence.

Both progress environment variables are now documented in the README and
the utils reference; SEMANTICA_DISABLE_PROGRESS previously existed only in
the reference page.

Deviations from the issue: the issue suggested disabling the tracker on
non-TTY stdout. This gates the display instead, because disabling the
tracker would short-circuit before FileProgressDisplay and take file
progress logging down with it, and the ~20 modules that set
`progress_tracker.enabled = True` in __init__ would need the property
setter taught about TTY state to avoid undoing it. Gating the display
leaves both alone.

Design note: the claim comment on the issue proposed an
`enabled: Optional[bool] = None` constructor opt-in; during implementation
the opt-in became SEMANTICA_FORCE_PROGRESS, which needs no signature change
and follows the NO_COLOR/FORCE_COLOR convention. Known limitation: TTY
detection runs once at tracker construction (the tracker is a process-wide
singleton), so a process that redirects stdout after first use needs the
env vars to change behaviour.

Fixes #1185
2026-08-23 22:17:31 +05:30
KaifAhmad1 cf6c9b7b9c fix(export): stop double-encoding valid % escapes and fix built-in prefix shadowing
_as_turtle_iri() re-encoded absolute IRIs wholesale, turning already-valid
percent-escapes like %20 into %2520. Only spans outside existing valid
%XX escapes are quoted now, so malformed escapes (%zz) still get repaired
while valid ones pass through unchanged.

serialize_to_ntriples()/serialize_to_rdfxml() also passed only the
@context-derived namespaces into _as_turtle_iri(), which shadowed the
built-in semantica:/rdf:/rdfs:/owl: prefixes entirely whenever any
@context was present. _as_turtle_iri() now always merges the built-ins
with whatever namespaces the caller passes.
2026-08-23 21:52:51 +05:30
mikemikimike 52ba7b6890 fix(export): normalize IRIs across RDF serializers 2026-08-23 23:55:11 +08:00
mikemikimike 1f868b9779 fix(export): close Turtle IRI normalization gaps 2026-08-23 23:55:11 +08:00
mikemikimike 8d5479d22f fix(export): handle contextual turtle iris 2026-08-23 23:55:11 +08:00
mikemikimike 14107e51c3 fix(export): normalize turtle resource iris 2026-08-23 23:55:11 +08:00
Freakz2z e41993a6bd fix(triplet_store): honor RDF4J repository id 2026-08-23 23:02:15 +08:00
Mohd Kaif e63bad310e Merge branch 'main' into test-conflicts-865 2026-08-23 18:29:44 +05:30
Mohd Kaif 2b077c6d0e Merge branch 'main' into codex/context-graph-markdown-round-trip 2026-08-23 17:26:05 +05:30
Mohd Kaif 4820185924 Merge branch 'main' into codex/harden-markdown-import-symlinks 2026-08-23 16:55:43 +05:30
KaifAhmad1 f124df4229 fix: prevent duplicate dimension kwarg crash in create_index
vector_store_config.get_all() always includes a "dimension" key, so
forwarding it via **config into VectorIndexer(dimension=dimension, **config)
raised "got multiple values for keyword argument 'dimension'" any time the
default index-creation path ran with the default config — including
`semantica embed index`, which is exactly the second half of the #994
quick-start pipeline this PR fixes.
2026-08-23 15:51:25 +05:30
Sameer6305 a47954c19a Merge main into fix/issue-994-embed-fallback-recursion-corrupt-output 2026-08-23 14:35:22 +05:30
Mohd Kaif 93e4b97517 Merge branch 'main' into fix/1184-causal-edge-vocabulary 2026-08-22 22:57:04 +05:30
Aldrin Joseph 248ae57694 fix(context): extend causal vocabulary normalization to sibling methods (#1184)
Review feedback: analyze_decision_influence(), trace_decision_causality(),
and find_precedents() had the same vocabulary split as get_causal_chain().
The first two read edge_type_index, which is keyed by the RAW edge_type
string, so they now filter index keys by normalized type; find_precedents()
accepts the analyzer's 'precedes' spelling alongside PRECEDENT_FOR.
Adds regression tests for all three call sites.
2026-08-22 22:06:31 +05:30
Mohd Kaif e3b24ef872 Merge branch 'main' into feat/public-shacl-validation 2026-08-22 21:54:34 +05:30
mikemikimike b891902d6d test(shacl): compare stable report fields 2026-08-23 00:15:41 +08:00
Nitish Reddy M 9123dcc0bd fix(export): mint JSON-LD document @id from content, not the clock (#1181)
Closes #1147
2026-08-22 21:14:17 +05:00
mikemikimike 6cbe0ae438 test(shacl): cover conforming validation result 2026-08-23 00:13:12 +08:00
mikemikimike 50f2f82b95 feat(ontology): expose public SHACL validation API 2026-08-22 23:58:00 +08:00
Saurabh Meena 8dcbee386d Merge remote-tracking branch 'origin/main' into codex/context-graph-markdown-round-trip 2026-08-22 19:38:26 +05:30
Saurabh Meena e2f850e9e4 Merge remote-tracking branch 'origin/main' into codex/harden-markdown-import-symlinks 2026-08-22 19:38:20 +05:30
Aldrin Joseph 2d976963ab fix(context): keep ValueError for non-string causal relationship types (#1184)
Review feedback: normalization must not turn invalid inputs into
AttributeError. Non-string relationship types now raise ValueError before
normalization, matching the pre-change behavior; strings are stripped
before alias lookup.
2026-08-22 16:40:21 +05:30
Aldrin Joseph 283b7ada0c fix(context): accept analyzer vocabulary in causal edges (#1184)
get_causal_chain() matched only the canonical uppercase spellings
(CAUSED, INFLUENCED, PRECEDENT_FOR), while CausalChainAnalyzer's
vocabulary includes the present-tense forms (causes, influences,
leads_to, supports) — and the two differ in word form, not just case,
so case-insensitive matching alone would still miss them. An edge
recorded as "causes" produced an empty audit chain.

Storage normalizes both vocabularies onto the canonical types via
_CAUSAL_EDGE_ALIASES; traversal accepts the union (_CAUSAL_TRAVERSAL_TYPES).
add_causal_relationship() now accepts either spelling and stores the
canonical form.
2026-08-22 16:40:21 +05:30
Mohd Kaif 483f53aaa6 fix(tests): use exact-equality check to clear CodeQL substring-URL false positive (#1183)
CodeQL (py/incomplete-url-substring-sanitization) flagged the "https://schema.org/"
in flattened check because it pattern-matches on URL-ish strings tested with `in`.
flattened is always a list here, so the check was already exact membership, not a
substring test on untrusted input, but the ambiguous idiom tripped the scanner.
Rewrite as an explicit equality comparison so the intent is unambiguous.
2026-08-22 15:12:52 +05:30
cxzg007and江俊杰 14091d21fb fix(kg): compute real relationship duration for temporal stability metric (#1143)
analyze_evolution() previously appended a constant placeholder (durations.append(1)) for every bounded relationship, so the stability metric was always 1.0 when any bounded relationship existed and 0 otherwise, never reflecting actual valid-time durations.

Stability now computes the mean valid-time duration in seconds ((valid_until - valid_from).total_seconds()) across relationships with both bounds set; unbounded/half-open intervals are skipped and non-positive intervals clamped to 0. Adds unit tests and a CHANGELOG entry.

Co-authored-by: 江俊杰 <jiangjunjie.37@jd.com>
2026-08-22 14:24:47 +05:00
Kevin 58125a0a93 fix(dedup): never merge entities with different explicit types (closes #1137) (#1149)
* fix(dedup): never merge entities with different explicit types (fixes #1137)

The duplicate candidate confidence scoring only rewarded same-type pairs
but never penalized different-type pairs, so a Person 'Alice' and an
Organization 'Acme' (different id, type, and name) passed the confidence
threshold and were merged, silently dropping one entity. Add a type guard:
when both entities carry a non-empty type and they differ, the pair is
never a duplicate candidate (confidence 0, reason 'type_mismatch').

Untyped entities and genuinely duplicate same-type pairs keep their
previous behavior. Regression tests cover all three cases.

* fix(dedup): honor Entity.type and exclude mismatch structurally (review fixes)

Two gaps from code review (#1149):

1. _get_entity_value mapped object 'type' exclusively to .label, which
   Entity objects never have — their type lives on .type. The mismatch
   guard therefore never saw the type of Entity objects, and differently
   typed objects could still merge. Read .type first, fall back to .label.

2. The mismatch branch returned a normal candidate with confidence 0.0,
   but detection filters with >= confidence_threshold, and 0.0 is a
   documented valid threshold, so mismatches slipped through. Exclude
   type_mismatch candidates structurally at both filter sites regardless
   of threshold.

Adds tests for Entity objects with different types and for
confidence_threshold=0.0. 94 dedup tests pass.

---------
2026-08-22 14:13:45 +05:00
Aldrin JosephandClaude 394ce5fe61 fix(reasoning): refuse SPARQL query execution instead of returning empty results (#1087)
* fix(reasoning): refuse SPARQL query execution instead of returning empty results (#1083)

SPARQLReasoner.execute_query() never executed the query: both branches
returned an empty SPARQLQueryResult, with or without a triplet store, so
callers that trust an empty result as "no matches" silently drew wrong
conclusions. Until a real triplet-store execution path lands, the method
raises NotImplementedError with an explanation, per the issue's
suggestion. The dead cache/inference scaffolding after the execution
point is removed along with it.

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

* docs(reasoning): align execute_query() docs with the NotImplementedError contract (#1087)

Review feedback: the docstring still carried a "Returns" section and the
reasoning guide showed execute_query() returning bindings, both of which
now mislead. The docstring documents Raises only, the guide demonstrates
expand_query() and points to rdflib for execution until the triplet-store
path lands, and query_cache/clear_cache() are marked as reserved for that
future execution path.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-08-22 14:00:28 +05:00
Aldrin JosephandClaude 8e9f7c5526 fix(utils): bound caller-controlled keys in validation error messages (#1088)
* fix(utils): bound caller-controlled keys in validation error messages (#1001)

_require_recognized_keys() and _require_nothing_dropped() interpolated
supplied keys directly into ValidationError messages, so a megabyte-long
key produced a megabyte-long exception and, through the export wrappers
that log the full exception, an equally large log entry. Keys are now
rendered through _truncate_key(), which bounds the display at 64
characters with an ellipsis; the supplied payload is never modified.

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

* fix(utils): bound the count of keys shown in validation error messages (#1001)

Review feedback: per-key truncation did not bound the number of keys
shown, so a payload carrying many short unknown keys could still size the
message (and the log entry that records it). _truncate_key_list() caps
the display at 8 keys and appends "and N more", keeping the message
actionable without letting the payload size it.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-08-22 13:52:56 +05:00
hari d4fdc1f0d3 fix(normalize): accept unit aliases during conversion (#939)
Convert_units() was validating categories on raw input like "kg" or "ft"
instead of the normalized unit name, so aliases got checked against a
category list that only has canonical names in it. Any alias-based
conversion that should've worked just raised ValidationError instead.

Fixed by normalizing both units before the category check runs.

Also added foot/yard/mile/gallon to the alias map - they already had
conversion factors but weren't mapped to their canonical names, so they'd
still have failed even after the above fix.

Turned out there was a second bug hiding behind the first one: the category
check defaults both sides to None, and None == None is True, so two aliases
from different categories that neither resolved to a real category would
silently pass instead of raising. kg -> ft would just return a number
instead of erroring. Normalizing first fixes this too, since aliases now
resolve to their actual categories and the mismatch gets caught.

Added a regression test locking that second one down - kg->ft and gal->lb
now raise ValidationError instead of silently converting.

Fixes #931.
2026-08-22 13:30:35 +05:00
Mohd Kaif 6390652303 Merge branch 'main' into fix/kg-validator-entity-id 2026-08-21 19:33:26 +05:30
Mohd Kaif 719efa4794 Merge branch 'main' into jsonld-default-graph 2026-08-21 19:15:39 +05:30
Fabio Rovai 1a220da477 fix(export): address the review findings on the metadata pass-through 2026-08-21 14:43:02 +01:00
Fabio Rovai d06434ae31 Merge upstream/main into metadata-passthrough
#1123 through #1127 landed while this was open, and #1125 rewrote the same
four entity loops this branch extends. Confidence is now normalised through
normalize_confidence, which returns None for a value that has no xsd:decimal
form, so the clause can be absent.

Resolved by folding that into the clause list this branch already builds:
the Turtle path assembles its predicate-object clauses and then terminates
the last one, which is what makes a variable-length list work at all, and
an omitted confidence is simply one clause fewer. RDF/XML and JSON-LD take
the upstream conditional as written, with the metadata call after it.
2026-08-21 14:40:54 +01:00
Saurabh Meena b64e4b6600 Merge remote-tracking branch 'origin/main' into codex/context-graph-markdown-round-trip 2026-08-21 18:58:50 +05:30
Saurabh Meena db0a9e8bfd Merge remote-tracking branch 'origin/main' into codex/harden-markdown-import-symlinks 2026-08-21 18:58:46 +05:30
Saurabh Meena 54bae5dffe Merge remote-tracking branch 'origin/main' into codex/context-graph-markdown-round-trip 2026-08-21 18:51:30 +05:30
Saurabh Meena 5b01949dd8 Merge remote-tracking branch 'origin/main' into codex/harden-markdown-import-symlinks 2026-08-21 18:51:26 +05:30
Saurabh Meena 3b710c79d2 Merge upstream main into codex/context-graph-markdown-round-trip 2026-08-21 18:40:57 +05:30
Mohd Kaif 4b312ca2fa Merge branch 'main' into custom-methods-can-refuse 2026-08-21 18:39:51 +05:30
Saurabh Meena 363a9ad641 Merge upstream main into codex/harden-markdown-import-symlinks 2026-08-21 18:38:52 +05:30