Skip to content

unstructured2graph: decouple label promotion from ontology enforcement - #251

Merged
antejavor merged 3 commits into
mainfrom
unstructured2graph-decouple-label-promotion
Aug 6, 2026
Merged

antejavor merged 3 commits into
mainfrom
unstructured2graph-decouple-label-promotion

Conversation

@antejavor

Copy link
Copy Markdown
Contributor

Summary

Closes #249.

enforce_ontology (introduced in #248) previously conflated two different concerns under one flag: promoting entity_type to a real Memgraph label, and restricting/validating that promotion against a fixed vocabulary. This splits them into two independent parameters on from_texts()/from_unstructured():

  • promote_labels: bool = False — promote every entity_type to a real Memgraph label, sanitized into a safe Cypher identifier (e.g. "natural object" → :NaturalObject), with no fixed vocabulary and no ontology_conformant flagging (there's nothing to be non-conformant relative to).
  • enforce_ontology: bool = False — restrict promotion to ontology_path's vocabulary (or the bundled default), flagging anything outside it ontology_conformant: false. Takes precedence over promote_labels when both are set — it's the stricter, ontology-gated mode.

New promote_all_entity_types_to_labels() in memgraph.py implements the unrestricted path: since there's no fixed vocabulary to iterate over, it discovers distinct entity_type values actually present under the workspace label, then sanitizes each into a PascalCase label via _entity_type_to_label() — skipping (and logging) any value that can't be turned into a safe identifier.

sessions-graph wiring: reconcile_session() now accepts and forwards promote_labels/enforce_ontology/ontology_path. Its CLI (the actual production entry point) now passes enforce_ontology=True — previously it called from_texts() with neither, so reconciled session content got zero label promotion despite #248 landing.

Also opened #250: while verifying this, found that unstructured2graph's and sessions-graph's real-LLM e2e suites currently fail against live Memgraph for an unrelated, pre-existing reason — MemgraphDocStatusStorage is missing 4 abstract methods that lightrag-hku==1.5.5 (already pinned on main) requires. Confirmed this reproduces identically on a clean main checkout before touching any code here, so it's not something this PR introduced or something in scope to fix here.

Test plan

  • unstructured2graph unit tests — _entity_type_to_label() sanitization (multi-word, snake_case, digit-leading rejection), promote_all_entity_types_to_labels() (distinct-value discovery, per-value promotion, unsanitizable-value skipping, no ontology_conformant touching), promote_labels/enforce_ontology precedence in the loader layer.
  • sessions-graph unit tests — reconcile_session() forwards all three new kwargs to from_texts(); CLI passes enforce_ontology=True.
  • Full local suite (scripts/dev-memgraph.sh test) — 218 passed across all five packages; the only failures are the 6 real-LLM e2e tests blocked by lightrag-memgraph: MemgraphDocStatusStorage missing 4 abstract methods required by lightrag-hku 1.5.5 #250 (verified pre-existing, unrelated).

Closes #249. enforce_ontology previously conflated two concerns: promoting
entity_type to a real Memgraph label, and restricting/validating that
promotion against a fixed vocabulary. Splits them into two independent
parameters:

- promote_labels: bool = False -- promote every entity_type to a label
  (sanitized to a safe Cypher identifier, e.g. "natural object" ->
  :NaturalObject), no fixed vocabulary, no ontology_conformant flagging.
- enforce_ontology: bool = False -- restrict promotion to ontology_path's
  vocabulary and flag anything outside it ontology_conformant=false. Takes
  precedence over promote_labels when both are set.

New promote_all_entity_types_to_labels() in memgraph.py implements the
unrestricted path: discovers distinct entity_type values under the
workspace label (no static list to iterate, since there's no ontology),
sanitizes each into a PascalCase label via _entity_type_to_label(),
skipping values that can't be turned into a safe identifier.

Wires sessions-graph's reconcile_session() to accept and forward the same
three parameters, and updates its CLI (the real production entry point) to
pass enforce_ontology=True, so reconciled session content actually gets
typed labels going forward -- previously it called from_texts() with
neither, so it got no promotion at all despite #248 landing.

Note: unstructured2graph's real-LLM e2e suite (test_e2e_lightrag.py) and
sessions-graph's test_e2e_reconciliation.py currently fail against a live
Memgraph for an unrelated, pre-existing reason -- see #250
(MemgraphDocStatusStorage missing abstract methods required by
lightrag-hku 1.5.5). Verified this reproduces identically on a clean main
checkout before touching any code here. All non-e2e tests pass (60 in
unstructured2graph, 24 in sessions-graph; 218 across the full 5-package
suite).
@antejavor
antejavor force-pushed the unstructured2graph-decouple-label-promotion branch from ede3918 to 1f349e1 Compare August 4, 2026 08:19
nltk 3.10.1 (2026-08-01) added a CWD-import security check
(nltk/inisec.py) that raises ImportError during pytest collection --
uv pip install -e (what CI's test-unstructured2graph job runs) resolves
nltk live rather than from the lockfile, so it picked up the brand-new
release immediately. Legitimate upstream security hardening, not a
compromise -- verified nltk/inisec.py is real code in nltk/nltk's own
GitHub repo -- just not something to adapt to under time pressure here.

Verified against the exact CI install path (uv venv + uv pip install -e
.[test] in a throwaway venv): nltk now resolves to 3.9.4 and all 60 tests
pass, where they previously failed on collection.
@antejavor
antejavor merged commit 2ee2037 into main Aug 6, 2026
16 checks passed
antejavor added a commit that referenced this pull request Aug 18, 2026
…try_points registry) (#295)

PyPI has been stale for months relative to main -- these four packages
haven't been released since before most of maps #275/#288's work landed,
so the actual subagent-nesting model (Agent node, SPAWNED inference,
HAS_AGENT, USED_SKILL-on-Agent attachment) isn't published anywhere yet.

- actions-graph 0.1.2 -> 0.2.0: first-class Agent node model, SPAWNED
  inference rule, and the agent_spawning_tool_names default-name fix,
  all verified live against a real Claude Code session.
- skills-graph 0.1.3 -> 0.2.0: USED_SKILL attaching to the specific
  Agent (not just the flat Session) when skill usage happens inside a
  subagent.
- sessions-graph 0.4.0 -> 0.5.0: reconcile_session() now writes episodic
  Session.summary, plus config propagation fixes to the reconcile
  subprocess. (0.4.0 itself was already unreleased -- the ontology/label-
  promotion work from #248/#251.)
- agent-context-graph 0.1.9 -> 0.2.0: entry_points-based runtime plugin
  registry. (main's 0.1.9 already diverged from the last published 0.1.9
  build without a version bump -- #273 landed after the last release.)

No code changes -- version bumps only, verified compatible with existing
consumers (all internal >= constraints, no upper bounds).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

sessions-graph: wire up entity-type label promotion in reconcile_session

1 participant