fix(memory): translate time_window_days for memory_tree query_global#2273
Conversation
The consolidated `memory_tree` tool's `parameters_schema()` advertises a shared `time_window_days` field for both `query_source` and `query_global`, but `QueryGlobalRequest` in `memory/tree/retrieval/rpc.rs` deserializes from `window_days`. Calls that followed the LLM-facing contract failed with `missing field 'window_days'` whenever `mode = "query_global"` was selected. `query_source` natively uses `time_window_days` (its `QuerySourceRequest` matches the schema), so the bug is isolated to the `query_global` dispatch arm. Closing it in the dispatcher keeps the LLM-facing schema stable and leaves the standalone `MemoryTreeQueryGlobalTool` (which advertises `window_days` natively) untouched. The translator only renames `time_window_days` -> `window_days` when an explicit `window_days` isn't already set, so callers using the underlying contract directly are unaffected. Closes tinyhumansai#2252.
📝 WalkthroughWalkthroughThe PR adds a schema translation layer to the ChangesQuery Global Payload Translation
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related issues
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 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 (2)
src/openhuman/tools/impl/memory/tree/mod.rs (2)
135-159: ⚡ Quick winMove new translation logic out of
mod.rsinto a sibling module.This helper is operational code, so it should live in a sibling file (for example,
dispatch_args.rs) withmod.rsstaying mostly export/assembly focused.As per coding guidelines: "
src/openhuman/**/mod.rs: Keep domainmod.rsfiles light and export-focused. Put operational code in sibling files (ops.rs,store.rs,schedule.rs,types.rs,bus.rs)."🤖 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 `@src/openhuman/tools/impl/memory/tree/mod.rs` around lines 135 - 159, Extract the helper function translate_query_global_args from mod.rs into a new sibling module (e.g., dispatch_args.rs) and make it public (pub fn translate_query_global_args) so callers within the crate can use it; then in mod.rs replace the function body with a re-export (pub use self::dispatch_args::translate_query_global_args) and add a mod dispatch_args; declaration so mod.rs remains export/assembly-focused while operational logic lives in the new file. Ensure imports/serde_json types resolve in the new file and update any references to translate_query_global_args to the public path if needed.
152-156: Translator behavior is safe from deserialization errors; minor test coverage gap remains.The
QueryGlobalRequeststruct does not use#[serde(deny_unknown_fields)]— it uses default serde behavior which silently ignores unknown fields. Therefore, dual-field payloads containing bothwindow_daysandtime_window_dayswill deserialize without error.However, a behavioral inconsistency exists: the translator removes
time_window_daysonly whenwindow_daysis absent (lines 152–156), but preserves it when both fields are present. The test at lines 253–265 verifies thatwindow_daystakes precedence but does not assert thattime_window_daysis cleaned from the object in the dual-field case. For consistency with the single-field test (line 249:assert!(translated.get("time_window_days").is_none())), consider removingtime_window_daysunconditionally after extracting its value, or add an assertion to the dual-field test to document the preservation behavior.🤖 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 `@src/openhuman/tools/impl/memory/tree/mod.rs` around lines 152 - 156, The translator currently only removes "time_window_days" when "window_days" is absent, leaving dual-field payloads with both keys; update the translation logic in the function handling QueryGlobalRequest to always remove the "time_window_days" key after reading it: call obj.remove("time_window_days") unconditionally and, if there was a value and "window_days" is not already present, insert that value as "window_days" (i.e., extract then conditionally insert), so the original legacy key is always cleaned up; alternatively, if you prefer to keep current behavior, add an assertion in the dual-field test to document preservation, but prefer the unconditional removal approach for consistency.
🤖 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 `@src/openhuman/tools/impl/memory/tree/mod.rs`:
- Around line 135-159: Extract the helper function translate_query_global_args
from mod.rs into a new sibling module (e.g., dispatch_args.rs) and make it
public (pub fn translate_query_global_args) so callers within the crate can use
it; then in mod.rs replace the function body with a re-export (pub use
self::dispatch_args::translate_query_global_args) and add a mod dispatch_args;
declaration so mod.rs remains export/assembly-focused while operational logic
lives in the new file. Ensure imports/serde_json types resolve in the new file
and update any references to translate_query_global_args to the public path if
needed.
- Around line 152-156: The translator currently only removes "time_window_days"
when "window_days" is absent, leaving dual-field payloads with both keys; update
the translation logic in the function handling QueryGlobalRequest to always
remove the "time_window_days" key after reading it: call
obj.remove("time_window_days") unconditionally and, if there was a value and
"window_days" is not already present, insert that value as "window_days" (i.e.,
extract then conditionally insert), so the original legacy key is always cleaned
up; alternatively, if you prefer to keep current behavior, add an assertion in
the dual-field test to document preservation, but prefer the unconditional
removal approach for consistency.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: cefaf2fb-ae3f-4c2c-8012-6e4aba68f3f2
📒 Files selected for processing (1)
src/openhuman/tools/impl/memory/tree/mod.rs
|
nice catch on the window_days vs time_window_days mismatch @justinhsu1477, that kind of schema/backend drift is a pain to track down 🙌 always a treat seeing you back in the repo, thanks for keeping memory_tree honest! |
Summary
Fix the schema/backend impedance mismatch reported in #2252. The consolidated
memory_treetool advertisestime_window_daysas the look-back field for bothquery_sourceandquery_global, but the underlyingQueryGlobalRequestdeserializes fromwindow_days. Any LLM call that followed the consolidated contract withmode = "query_global"failed withmissing field 'window_days'.Problem
query_sourcenatively usestime_window_days(itsQuerySourceRequestmatches the consolidated schema verbatim), so the bug is isolated to thequery_globalarm.Solution
Translate
time_window_days→window_daysin the consolidated dispatch arm forquery_global, mirroring the existing thin-adapter pattern used insrc/openhuman/mcp_server/tools.rs(wheretree.read_chunkmaps MCPchunk_id→ controllerid).MemoryTreeQueryGlobalTool(which advertiseswindow_daysnatively) is unchanged.window_daysalways wins, so callers using the underlying contract directly aren't surprised when both fields are present.Submission Checklist
window_daysis already set, explicitwindow_dayswinning over a staletime_window_days, and the no-field case being untouched.cargo test --lib memory_tree_dispatcher_tests::runs 9/9 locally; the new translator helper has a dedicated test per branch.N/A: behaviour-only fix on an existing consolidated tool path.## RelatedN/A: backend-only fix, not on a release-cut surface.Closes #NNN—Closes #2252(commit message + this PR description).Impact
memory_treeconsolidated tool only. The standalonememory_tree_query_globaltool path is unchanged.window_days(the only callers that worked before this fix) continue to work. Callers that were passingtime_window_days(which previously errored) now succeed.Map::contains_key+ at most oneremove+ oneinsertperquery_globalcall.Related
src/openhuman/tools/impl/memory/tree/mod.rssrc/openhuman/memory/tree/retrieval/rpc.rs(QueryGlobalRequest,QuerySourceRequest)src/openhuman/mcp_server/tools.rs— same thin-adapter translation idiom used fortree.read_chunk'schunk_id → idmapping.Summary by CodeRabbit
Bug Fixes
Tests