fix: ABOUT entity linking for facts/preferences + logger fix#93
Open
AhmedHamadto wants to merge 1 commit into
Open
fix: ABOUT entity linking for facts/preferences + logger fix#93AhmedHamadto wants to merge 1 commit into
AhmedHamadto wants to merge 1 commit into
Conversation
When add_fact() or add_preference() is called, the new _link_to_entity_by_name() method looks up matching entities by name (case-insensitive) and creates ABOUT relationships in the graph. Changes: - Add _link_to_entity_by_name() to LongTermMemory with calls from add_fact() (subject + object) and add_preference() (category) - Add LINK_FACT_TO_ENTITY query constant and enhance GET_ENTITY_BY_NAME with case-insensitive matching - Add fallback name-based entity lookup and linked facts/preferences retrieval in MCP memory_get_entity tool - Fix missing 'import logging' in long_term.py that caused NameError in the except handler, silently breaking all entity linking via MCP - Add 5 integration tests verifying ABOUT relationship creation Fixes neo4j-labs#77 Fixes neo4j-labs#87
3 tasks
Contributor
There was a problem hiding this comment.
Pull request overview
This PR improves long-term memory graph connectivity by automatically linking newly stored Facts/Preferences to existing Entities via ABOUT relationships, and enhances entity retrieval (including MCP tool fallback) so linked knowledge is discoverable.
Changes:
- Add automatic ABOUT-linking in
LongTermMemory.add_fact()/add_preference()via_link_to_entity_by_name(). - Extend entity lookup behavior: case-insensitive name matching in
GET_ENTITY_BY_NAME, MCPmemory_get_entityname-based fallback, and inclusion of linked facts/preferences in tool output. - Add integration tests validating ABOUT relationship creation and case-insensitive linking.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
tests/integration/test_about_relationships.py |
New integration tests validating ABOUT relationship creation for facts/preferences. |
src/neo4j_agent_memory/memory/long_term.py |
Adds linking helper + calls from add_fact/add_preference; fixes missing module logger setup. |
src/neo4j_agent_memory/mcp/_tools.py |
Adds name-based fallback entity lookup and returns linked facts/preferences in memory_get_entity. |
src/neo4j_agent_memory/graph/queries.py |
Updates GET_ENTITY_BY_NAME to be more case-insensitive and adds LINK_FACT_TO_ENTITY. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+455
to
+460
| # Include linked facts and preferences | ||
| linked = await _get_entity_facts_and_preferences(client, str(entity.id)) | ||
| if linked.get("facts"): | ||
| result["facts"] = linked["facts"] | ||
| if linked.get("preferences"): | ||
| result["preferences"] = linked["preferences"] |
| WHERE e.name = $name OR e.canonical_name = $name OR $name IN COALESCE(e.aliases, []) | ||
| WHERE e.name = $name | ||
| OR toLower(e.name) = toLower($name) | ||
| OR e.canonical_name = toLower($name) |
Comment on lines
+431
to
+432
| MERGE (f)-[r:ABOUT]->(e) | ||
| ON CREATE SET r.role = $role |
Comment on lines
+816
to
+822
| OPTIONAL MATCH (f:Fact)-[:ABOUT]->(e) | ||
| OPTIONAL MATCH (p:Preference)-[:ABOUT]->(e) | ||
| WITH e, | ||
| collect(DISTINCT {type: 'fact', subject: f.subject, | ||
| predicate: f.predicate, object: f.object}) AS facts, | ||
| collect(DISTINCT {type: 'preference', category: p.category, | ||
| preference: p.preference}) AS preferences |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
_link_to_entity_by_name()toLongTermMemorythat automatically createsABOUTrelationships between Facts/Preferences and matching Entities whenadd_fact()oradd_preference()is calledLINK_FACT_TO_ENTITYquery constant and enhancesGET_ENTITY_BY_NAMEwith case-insensitive matching (toLower(),canonical_name, aliases)memory_get_entitytool when vector search returns nothing_get_entity_facts_and_preferences()helper to include linked facts/preferences in entity resultsimport logginginlong_term.pythat caused aNameErrorin the except handler, silently breaking all entity linking when called through the MCP serverHow It Works
When a fact is added with
add_fact(subject="Rust", predicate="USED_FOR", obj="edge binary"):_link_to_entity_by_name("Rust", fact_id, "fact", role="subject")is called(fact)-[:ABOUT {role: "subject"}]->(entity)Preferences link by category name:
add_preference(category="Python", ...)links to an entity named "Python".Bug Context
Without this fix,
add_fact()andadd_preference()create isolated nodes with zero relationships to entities — making the knowledge graph effectively a flat key-value store. Thememory_get_contexttool cannot traverse from entities to related facts, andmemory_get_entityreturns no linked knowledge.The missing
import logging(#87) caused a cascading failure: any exception during linking triggeredNameErrorinstead of the actual error, making the root cause invisible through the MCP server.Test Plan
5 new integration tests in
tests/integration/test_about_relationships.py:test_fact_linked_to_entity_via_about_subject— entity + fact with matching subject → ABOUT with role=subjecttest_fact_linked_to_entity_via_about_object— entity + fact with matching object → ABOUT with role=objecttest_preference_linked_to_entity_via_about— entity + preference with matching category → ABOUT linktest_fact_no_matching_entity_no_error— fact with no matching entity → no crash, zero ABOUT linkstest_fact_case_insensitive_linking— entity "Rust" + fact subject "rust" → still linkedAll tests use
clean_memory_client(real Neo4j via testcontainer), no external API calls.Additionally verified end-to-end through the MCP server against a live Neo4j instance — both fact and preference ABOUT linking confirmed working.
Fixes #77
Fixes #87
Supersedes #78 (based on pre-v0.1.0 code)