refactor(mcp): relocate user-facts logic out of EvolveClient#252
Conversation
Follow the save_trajectory / get_guidelines pattern so EvolveClient stays a pure CRUD layer and the LLM module stays pure LLM. After the refactor, all write-tools converge on client.update_entities and all read-tools on client.search_entities, which sets up the shared MCP-tool-wrapping helper from the original issue as a trivial follow-up. - Add categorize_facts() pure helper in altk_evolve/llm/fact_extraction - Remove store_user_facts / retrieve_user_facts from EvolveClient - Rewrite the matching MCP tools to orchestrate directly: store via extract_facts_from_messages + client.update_entities, retrieve via client.search_entities + the existing fallback chain + categorize_facts. JSON envelopes and tool signatures unchanged. - Move the fact tests from test_client.py into test_mcp_server.py, update mocks to target the CRUD methods, add parametrized coverage for None/""/whitespace messages and non-positive-limit early returns. Refs #241
📝 WalkthroughWalkthroughThis PR relocates fact extraction and retrieval logic from ChangesUser Facts Logic Migration
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
altk_evolve/frontend/mcp/mcp_server.py (1)
411-427: 💤 Low valueConsider adding
NamespaceNotFoundExceptionhandling for consistency.Unlike
get_entities_logic(lines 179-194) which handlesNamespaceNotFoundExceptionwith a retry pattern,retrieve_user_factsrelies solely on thenamespace_existspre-check. There's a potential TOCTOU race if the namespace is deleted between the check (line 416) and the search call (line 426).Given that namespace deletion is rare, this is low-risk, but adding exception handling would align with the established pattern in this file.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@altk_evolve/frontend/mcp/mcp_server.py` around lines 411 - 427, retrieve_user_facts currently does a namespace_exists pre-check but can still suffer a TOCTOU race if the namespace is deleted before _search_facts_with_fallback is called; add a try/except around the call to _search_facts_with_fallback to catch NamespaceNotFoundException and implement the same retry/handle logic used in get_entities_logic (i.e., on NamespaceNotFoundException re-check/create the namespace or retry the operation once, then return the empty JSON result if it still fails). Reference retrieve_user_facts, _search_facts_with_fallback, namespace_exists, and NamespaceNotFoundException when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@altk_evolve/frontend/mcp/mcp_server.py`:
- Around line 411-427: retrieve_user_facts currently does a namespace_exists
pre-check but can still suffer a TOCTOU race if the namespace is deleted before
_search_facts_with_fallback is called; add a try/except around the call to
_search_facts_with_fallback to catch NamespaceNotFoundException and implement
the same retry/handle logic used in get_entities_logic (i.e., on
NamespaceNotFoundException re-check/create the namespace or retry the operation
once, then return the empty JSON result if it still fails). Reference
retrieve_user_facts, _search_facts_with_fallback, namespace_exists, and
NamespaceNotFoundException when making the change.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: fd1d1b89-af79-4db3-9d26-16ebbce5a082
📒 Files selected for processing (6)
.gitignorealtk_evolve/frontend/client/evolve_client.pyaltk_evolve/frontend/mcp/mcp_server.pyaltk_evolve/llm/fact_extraction/fact_extraction.pytests/unit/test_client.pytests/unit/test_mcp_server.py
💤 Files with no reviewable changes (2)
- tests/unit/test_client.py
- altk_evolve/frontend/client/evolve_client.py
SummaryThis PR moves user-facts storage/retrieval logic from Findings
Testing
|
visahak
left a comment
There was a problem hiding this comment.
Not sure if this feature needs backwards compatibility.
|
@visahak Good call to raise it. My read: no back-compat needed here.
If an external caller surfaces later, re-adding a thin shim is a 10-line change — easier to design against a concrete caller than a hypothetical one. |
Summary
Relocates user-facts logic out of
EvolveClientso the client stays a pure CRUD layer and the LLM module stays pure LLM, mirroring the existingsave_trajectory/get_guidelinespattern already used inmcp_server.py.After this change, all three write-tools (
create_entity,store_user_facts,save_trajectory) converge onclient.update_entities, and all three read-tools (get_entities,get_guidelines,retrieve_user_facts) converge onclient.search_entities. This sets up the shared MCP-tool-wrapping helper from the original issue #241 ask as a trivial follow-up PR.Changes
altk_evolve/llm/fact_extraction/fact_extraction.py— addcategorize_facts()pure helper that groupslist[RecordedEntity]bymetadata.category.altk_evolve/frontend/client/evolve_client.py— removestore_user_factsandretrieve_user_factsmethods; drop now-unused imports (Any,ExtractedFact,extract_facts_from_messages).altk_evolve/frontend/mcp/mcp_server.py— rewrite both MCP tools to orchestrate directly:store_user_facts:_parse_metadata→ trim-and-empty-skip →_resolve_namespace→extract_facts_from_messages→ buildEntity(type="fact", ...)→client.update_entities(withNamespaceNotFoundExceptionretry) → serialize.retrieve_user_facts:limit <= 0/ namespace-missing early return →client.search_entitiesvia_search_facts_with_fallback(preserves the query-less retry and default-user fallback from the old client) →categorize_facts→ serialize.tests/unit/test_client.py— remove the fact tests (logic no longer lives inEvolveClient).tests/unit/test_mcp_server.py— update mocks to targetclient.update_entities/search_entities/namespace_exists; add parametrized coverage forNone/""/ whitespace messages, trimming, namespace-missing, and non-positive-limit early returns..gitignore— ignore.omc/workspace artifacts.Scope notes
NamespaceNotFoundExceptionretry +EntityUpdate→JSON serialization across the three write-tools), and the latent retry-gap fix instore_user_facts. Both become trivial follow-ups after this refactor.EvolveClient.store_user_facts/retrieve_user_factsare fully removed with no back-compat shims — only in-tree callers (MCP tools and tests) imported them.Test plan
uv run ruff check altk_evolve/ tests/unit/— cleanuv run ruff format --check— cleanuv run python -m mypyon changed files — success, no issuesuv run pytest tests/unit/— 271 passed, 0 failed (excluding optionalpsycopg/milvusbackends)test_store_and_retrieve_user_factse2e test is unchanged in intent (it exercises the public MCP tool surface, which is preserved)Refs #241
Summary by CodeRabbit
New Features
categorize_facts()utility function for organizing facts by category.Refactor
store_user_facts()andretrieve_user_facts()methods from EvolveClient.