Skip to content

feat(memory): optimize relationship taxonomy#121

Merged
jack-arturo merged 2 commits intomainfrom
feat/relationship-taxonomy
Mar 10, 2026
Merged

feat(memory): optimize relationship taxonomy#121
jack-arturo merged 2 commits intomainfrom
feat/relationship-taxonomy

Conversation

@jack-arturo
Copy link
Copy Markdown
Member

Summary

  • introduce a single relationship registry with tiered metadata for authorable, public, filterable, and default-expand relation sets
  • restrict public association writes to the 11 semantic relationship types while keeping system-generated and legacy relationships readable/filterable via DISCOVERED compatibility
  • align MCP/docs/graph behavior with the shared registry and sanitize benchmark comparison filenames so slash-containing branch names no longer break bench-compare-branch

Validation

  • make test
  • npm test (in mcp-sse-server)
  • make bench-compare-branch BRANCH=main CONFIG=baseline BENCH=locomo-mini
    • no significant change: 89.36% (210/235) on this branch vs 89.36% (210/235) on main
    • category deltas: 0.0% across single-hop, temporal, multi-hop, open-domain, and complex reasoning

Notes

  • this change is intended to improve agent clarity and API/docs consistency rather than directly increase benchmark scores
  • full feature-branch locomo eval also completed locally at 85.21% (1314/1542) with judge off

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 10, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5b3b881f-48c8-4f73-a742-14460a492a88

📥 Commits

Reviewing files that changed from the base of the PR and between 4462418 and 64e7042.

📒 Files selected for processing (3)
  • automem/api/recall.py
  • automem/config.py
  • consolidation.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • consolidation.py

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added system-generated relationship types (SIMILAR_TO, PRECEDED_BY, DISCOVERED) which appear in searches and can be optionally included in related-memories queries; consolidation can surface "DISCOVERED" edges with a kind and origin.
  • Documentation

    • Clarified that there are 11 authorable relationship types (creatable via API) and a separate set of system-generated/internal types; updated API/docs and prompts accordingly.
  • Improvements

    • Normalized and enriched relationship metadata, improving consistency of filtering, defaults, and recall behavior.

Walkthrough

Restructures relationship taxonomy into "authorable" vs. system-generated types, adds normalization/expansion utilities, exposes relation taxonomy for runtime wiring, and propagates normalized relation types/kinds through recall, consolidation, graph, and API blueprints. Documentation, tests, and test doubles updated accordingly.

Changes

Cohort / File(s) Summary
Docs & README
INSTALLATION.md, README.md, docs/API.md, docs/MCP_SSE.md
Reclassify relationships as "Authorable Relationship Types" (11) vs. system-generated types (SIMILAR_TO, PRECEDED_BY, DISCOVERED); clarify authoring vs. readable/filterable behavior.
Config & Registry
automem/config.py
Introduce structured RELATIONSHIP_TYPES registry, _relation_config helper, LEGACY_DISCOVERED_RELATIONS, canonicalize/normalize/expand helpers, and derived constants (AUTHORABLE_RELATIONS, PUBLIC_RELATIONS, SYSTEM_RELATIONS, DEFAULT_EXPAND_RELATIONS, FILTERABLE_RELATIONS, RELATION_COLORS, ALLOWED_RELATIONS).
App Exports / Wiring
app.py, automem/runtime_wiring.py, automem/api/runtime_bootstrap.py
Expose RELATION_TAXONOMY mapping; change blueprint registration/wiring to accept filterable_relations, default_expand_relations, authorable_relations and propagate them to recall/memory blueprints.
Recall / Search / Graph
automem/api/recall.py, automem/search/runtime_relations.py, automem/api/graph.py
Canonicalize and expand requested relation types for queries, propagate normalized_type and relation_kind from graph rows, initialize defaults from FILTERABLE_RELATIONS/DEFAULT_EXPAND_RELATIONS, and use PUBLIC_RELATIONS for relations endpoint.
Memory & Association APIs
automem/api/memory.py, automem/consolidation/runtime_routes.py, automem/consolidation/runtime_routes.py
Rename allowed_relations → authorable_relations in blueprints and association validation; update error messages to reference authorable_relations.
Consolidation & Enrichment Logic
consolidation.py
Detect creative discovered associations, tag them with kind (explains, shares_theme, parallel_context), normalize relation types via normalize_relation_type, validate against FILTERABLE_RELATIONS, and persist origin/kind/confidence for DISCOVERED relations.
MCP Server & Scripts
mcp-sse-server/server.js, scripts/bench/compare_branch.sh
Adjust MCP-visible RELATION_TYPES to match authorable set; sanitize branch names in bench script filenames to avoid invalid characters.
Tests & Test Utilities
tests/support/fake_graph.py, tests/test_api_endpoints.py, tests/test_consolidation_engine.py, tests/*
Extend fake graph rows with kind/origin/confidence/similarity and add related-memories query path; update and add tests asserting authorable vs. system-generated boundaries, legacy DISCOVERED mapping, default relation sets, and discovered-edge consolidation behavior.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant API as "API Blueprints"
  participant Config as "Relation Registry"
  participant Graph as "Graph DB"
  participant Consolidator

  Client->>API: POST /associate or GET /recall (relation types)
  API->>Config: validate / canonicalize requested types
  Config-->>API: AUTHORABLE/FILTERABLE/DEFAULT_EXPAND sets + normalized types
  API->>Graph: query/merge using expanded normalized types (include kind metadata)
  Graph-->>API: rows with (type, strength, kind, related)
  API->>Consolidator: normalization results for discovered associations
  Consolidator->>Graph: MERGE edges with rel_type, kind, origin, confidence
  API-->>Client: normalized relation responses (types, kinds, strengths)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.26% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: introducing a single, tiered relationship taxonomy registry to optimize relationship handling across the codebase.
Description check ✅ Passed The description is directly related to the changeset, clearly explaining the relationship registry refactoring, restrictions on association writes, alignment of multiple components, and filename sanitization for benchmarks.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/relationship-taxonomy

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot added the enhancement New feature or request label Mar 10, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
automem/api/recall.py (1)

1925-1940: ⚠️ Potential issue | 🟡 Minor

Fix extraneous f-string prefix (static analysis finding).

Line 1927 contains f']-' within the string interpolation, which has no placeholders. The f prefix is unnecessary.

🔧 Proposed fix
         rel_pattern = ":" + "|".join(query_relation_types) if query_relation_types else ""
         query = f"""
-            MATCH (m:Memory {{id: $id}}){'-[r' + rel_pattern + f']-' if rel_pattern else '-[r]-'}(related:Memory)
+            MATCH (m:Memory {{id: $id}}){'-[r' + rel_pattern + ']-' if rel_pattern else '-[r]-'}(related:Memory)
             WHERE m.id <> related.id
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@automem/api/recall.py` around lines 1925 - 1940, The f-string prefix on the
static literal segment is unnecessary: change the concatenation that builds the
Cypher fragment from "-[r' + rel_pattern + f']-'" to use a normal string for the
trailing part (e.g., "-[r" + rel_pattern + "]-" or combine with rel_pattern via
f"...{rel_pattern}..."), in the construction of rel_pattern and query (refer to
variables rel_pattern, query and query_relation_types) so the interpolation has
no stray f'' prefix and the resulting Cypher remains the same.
🧹 Nitpick comments (3)
automem/config.py (1)

364-364: Consider using frozenset for ALLOWED_RELATIONS for consistency.

ALLOWED_RELATIONS is defined as a mutable set while other derived collections use frozenset. This inconsistency could lead to accidental mutations.

♻️ Suggested fix
-ALLOWED_RELATIONS = set(FILTERABLE_RELATIONS)
+ALLOWED_RELATIONS = frozenset(FILTERABLE_RELATIONS)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@automem/config.py` at line 364, ALLOWED_RELATIONS is currently a mutable set
which is inconsistent with other derived collections; change its definition to
use an immutable frozenset by constructing ALLOWED_RELATIONS from
FILTERABLE_RELATIONS (e.g., replace set(FILTERABLE_RELATIONS) with
frozenset(FILTERABLE_RELATIONS)) so it cannot be mutated at runtime and matches
the immutability of related constants.
consolidation.py (1)

735-745: Consider moving allowed_labels to a shared constant.

The allowed_labels set duplicates knowledge about valid relation types. Consider importing or deriving this from FILTERABLE_RELATIONS in automem/config.py for consistency.

♻️ Suggested approach
+from automem.config import FILTERABLE_RELATIONS, normalize_relation_type
-from automem.config import normalize_relation_type
...
-                        allowed_labels = {
-                            "DISCOVERED",
-                            "CONTRADICTS",
-                            "RELATES_TO",
-                            "SIMILAR_TO",
-                            "PRECEDED_BY",
-                            "DERIVED_FROM",
-                            "PART_OF",
-                        }
-                        if rel_type not in allowed_labels:
+                        if rel_type not in FILTERABLE_RELATIONS:
                             rel_type = "RELATES_TO"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@consolidation.py` around lines 735 - 745, Replace the local allowed_labels
set with the canonical FILTERABLE_RELATIONS constant from automem.config: import
FILTERABLE_RELATIONS and use it (or a set(FILTERABLE_RELATIONS) if needed) in
place of allowed_labels when checking rel_type; keep the existing fallback
assignment rel_type = "RELATES_TO" if rel_type not in the imported set so
behavior remains the same. Ensure you reference the symbol FILTERABLE_RELATIONS
and update the check that currently uses allowed_labels to use that imported
constant.
automem/api/recall.py (1)

1628-1629: Consider narrowing the type hints.

The union type List[str] | set[str] | tuple[str, ...] | Any effectively becomes Any due to the Any inclusion. If flexibility is needed, consider Iterable[str] | None instead for clearer intent.

♻️ Suggested type hint refinement
-    filterable_relations: List[str] | set[str] | tuple[str, ...] | Any = (),
-    default_expand_relations: List[str] | set[str] | tuple[str, ...] | Any = (),
+    filterable_relations: Iterable[str] | None = None,
+    default_expand_relations: Iterable[str] | None = None,

This would require updating the default handling in lines 1654-1661 accordingly.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@automem/api/recall.py` around lines 1628 - 1629, The parameters
filterable_relations and default_expand_relations are overly broad because
including Any defeats the type check; change their annotations from "List[str] |
set[str] | tuple[str, ...] | Any" to "Iterable[str] | None" and set their
defaults to None. Then update the function logic that normalizes these params
(the block that currently treats empty tuples as defaults) to treat None as
meaning "no relations" (e.g., convert None to an empty list/tuple where needed)
so downstream code using filterable_relations and default_expand_relations
behaves the same.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@automem/api/recall.py`:
- Around line 1925-1940: The f-string prefix on the static literal segment is
unnecessary: change the concatenation that builds the Cypher fragment from "-[r'
+ rel_pattern + f']-'" to use a normal string for the trailing part (e.g., "-[r"
+ rel_pattern + "]-" or combine with rel_pattern via f"...{rel_pattern}..."), in
the construction of rel_pattern and query (refer to variables rel_pattern, query
and query_relation_types) so the interpolation has no stray f'' prefix and the
resulting Cypher remains the same.

---

Nitpick comments:
In `@automem/api/recall.py`:
- Around line 1628-1629: The parameters filterable_relations and
default_expand_relations are overly broad because including Any defeats the type
check; change their annotations from "List[str] | set[str] | tuple[str, ...] |
Any" to "Iterable[str] | None" and set their defaults to None. Then update the
function logic that normalizes these params (the block that currently treats
empty tuples as defaults) to treat None as meaning "no relations" (e.g., convert
None to an empty list/tuple where needed) so downstream code using
filterable_relations and default_expand_relations behaves the same.

In `@automem/config.py`:
- Line 364: ALLOWED_RELATIONS is currently a mutable set which is inconsistent
with other derived collections; change its definition to use an immutable
frozenset by constructing ALLOWED_RELATIONS from FILTERABLE_RELATIONS (e.g.,
replace set(FILTERABLE_RELATIONS) with frozenset(FILTERABLE_RELATIONS)) so it
cannot be mutated at runtime and matches the immutability of related constants.

In `@consolidation.py`:
- Around line 735-745: Replace the local allowed_labels set with the canonical
FILTERABLE_RELATIONS constant from automem.config: import FILTERABLE_RELATIONS
and use it (or a set(FILTERABLE_RELATIONS) if needed) in place of allowed_labels
when checking rel_type; keep the existing fallback assignment rel_type =
"RELATES_TO" if rel_type not in the imported set so behavior remains the same.
Ensure you reference the symbol FILTERABLE_RELATIONS and update the check that
currently uses allowed_labels to use that imported constant.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 94c8cc98-9ed5-4536-b3e4-6f57d6ad1ac9

📥 Commits

Reviewing files that changed from the base of the PR and between 451178d and 4462418.

📒 Files selected for processing (20)
  • INSTALLATION.md
  • README.md
  • app.py
  • automem/api/graph.py
  • automem/api/memory.py
  • automem/api/recall.py
  • automem/api/runtime_bootstrap.py
  • automem/api/runtime_recall_routes.py
  • automem/config.py
  • automem/consolidation/runtime_routes.py
  • automem/runtime_wiring.py
  • automem/search/runtime_relations.py
  • consolidation.py
  • docs/API.md
  • docs/MCP_SSE.md
  • mcp-sse-server/server.js
  • scripts/bench/compare_branch.sh
  • tests/support/fake_graph.py
  • tests/test_api_endpoints.py
  • tests/test_consolidation_engine.py

- Remove unnecessary f-string prefixes on static Cypher fragments
- Narrow type annotations from `Any` union to `Iterable[str] | None`
- Make ALLOWED_RELATIONS a frozenset for immutability consistency
- Replace hardcoded allowed_labels with canonical FILTERABLE_RELATIONS
@jack-arturo jack-arturo merged commit 605633e into main Mar 10, 2026
7 checks passed
@jack-arturo jack-arturo deleted the feat/relationship-taxonomy branch March 10, 2026 17:51
jack-arturo added a commit that referenced this pull request Mar 25, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.15.0](v0.14.0...v0.15.0)
(2026-03-12)


### Features

* **bench:** add opt-in LoCoMo cat5 judge
([#119](#119))
([8b7915c](8b7915c))
* **memory:** optimize relationship taxonomy
([#121](#121))
([605633e](605633e))


### Bug Fixes

* **benchmarks:** correct evaluator bugs, add agent guidelines,
establish honest baseline
([#117](#117))
([d9cbecb](d9cbecb))
* **recall:** priority_ids parameter only boosts relevance
([#79](#79))
([#125](#125))
([5d3708c](5d3708c))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant