fix(hindsight): pass retain_async=True in hindsight_retain tool handler - #37838
fix(hindsight): pass retain_async=True in hindsight_retain tool handler#37838kkangg1 wants to merge 1 commit into
Conversation
The hindsight_retain tool calls client.aretain() without retain_async, defaulting to False (synchronous mode). On banks with significant data, synchronous processing exceeds the 120s timeout, producing:
Tool hindsight_retain returned error (120.01s): {"error": "Failed to store memory: "}
The auto-retain path (sync_turn) works correctly because it passes retain_async=self._retain_async (True) to aretain_batch.
Fix add retain_async=True to _build_retain_kwargs in the tool handler (1 line).
## What does this PR do?
Add retain_async=True to the _build_retain_kwargs call in the hindsight_retain tool handler, making the tool use asynchronous processing consistent with the auto-retain path (sync_turn). Previously the tool defaulted to retain_async=False (synchronous mode), causing timeouts on banks with significant data when the Hindsight APIs LLM extraction took longer than 120 seconds.
## Related Issues
- Closes NousResearch#29079 (Embedded Hindsight retain reports failure while async retain later appears in recall)
- References NousResearch#7974 (Bug hindsight_retain tool fails with Connection refused while hindsight_recall works)
- Follow-up to PR NousResearch#13987 (feat richer session-scoped retain metadata introduced _build_retain_kwargs without retain_async)
- Similar to PR NousResearch#9869 (Hindsight plugin hardcoded 30s timeout causes hindsight_reflect to fail same timeout pattern)
## Type of Change
- [x] Bug fix (non-breaking change that fixes an issue)
## Changes Made
- plugins/memory/hindsight/__init__.py Add retain_async=True parameter to _build_retain_kwargs() call in handle_tool_call for hindsight_retain (line 1508)
## How to Test
1. Start Hermes with memory provider hindsight (local or cloud)
2. Call hindsight_retain with any content via CLI or gateway session
3. Observe the tool returns success within seconds instead of timing out at 120s
4. Verify retained content appears in hindsight_recall
## Checklist
### Code
- [x] Ive read the Contributing Guide
- [x] My commit messages follow Conventional Commits
- [x] I searched for existing PRs to make sure this isnt a duplicate
- [x] My PR contains only changes related to this fix/feature (no unrelated commits)
### Documentation
- [x] N/A Documentation update not needed (one-line fix
|
Independent reproduction + a note on the fix approach. ReproductionEnvironment: Hermes Agent, Hindsight Symptom: Daemon logs show worker tasks stuck at The auto-retain path ( Note on the fix approachThis PR adds The working fix is to pass client.aretain_batch(bank_id=self._bank_id, items=[item], retain_async=True)This matches the existing comment at line ~1715: "aretain_batch takes bank_id/retain_async as call args, not item keys." Would the maintainer prefer this PR updated, or should I submit a separate one with the corrected approach? Happy to help either way — just want to see this land. |
|
Thanks for this PR! I was investigating the same issue (#29079) and noticed that this approach may not be effective. The issueThe if retain_async is not None:
kwargs["retain_async"] = retain_asyncHowever, immediately after item = self._build_retain_kwargs(
content,
context=context,
tags=args.get("tags"),
retain_async=True, # ← this PR adds it here
)
# aretain_batch takes bank_id/retain_async as call args, not item keys.
item.pop("bank_id", None)
item.pop("retain_async", None) # ← immediately removed, making the change a no-opSo Suggested fixPass self._run_hindsight_operation(
lambda client: client.aretain_batch(
bank_id=self._bank_id, items=[item], retain_async=True,
)
)I have opened #60648 with this approach — happy to close it if you'd like to update this PR instead. Either way, wanted to flag the issue so the fix actually takes effect. |
|
Closing in favor of #60648, which is the correct minimal fix for current main. As documented in #60648's description: adding |
|
Marking this as superseded by #60648, which is the correct minimal fix for current main. As documented in #60648's description: adding A maintainer can close this PR. |
teknium1
left a comment
There was a problem hiding this comment.
Thanks for isolating the tool-path timeout issue. The underlying behavior is still present on current main, but this specific placement does not reach the Hindsight client.
Problems
plugins/memory/hindsight/__init__.py:1715-1721removesitem["retain_async"]before callingaretain_batch, so adding it to_build_retain_kwargs()is a no-op.plugins/memory/hindsight/__init__.py:1354keeps the configured value inself._retain_async; the auto-retain path forwards that value at:1681-1686. Hard-codingTruewould diverge from configuredretain_async: falsebehavior.tests/plugins/memory/test_hindsight_provider.py:641-653has no assertion for the tool call's call-levelretain_asynckeyword.
Suggested changes
- Pass
retain_async=self._retain_asyncdirectly toclient.aretain_batch(...)in the tool handler after item cleanup. - Add regression coverage for default-true and configured-false tool retains.
Automated hermes-sweeper review.
| content, | ||
| context=context, | ||
| tags=args.get("tags"), | ||
| retain_async=True, |
There was a problem hiding this comment.
handle_tool_call removes item["retain_async"] immediately before aretain_batch() (current main: :1715-1721), so this item-level value never reaches Hindsight. Pass retain_async=self._retain_async to aretain_batch(...) instead, matching the auto-retain path and preserving configured retain_async: false.
|
Closing in favor of #60648, which is the correct minimal fix. As @yingliang-zhang documented: adding Thanks @yingliang-zhang for the careful reproduction and @teknium1 for the review. |
The hindsight_retain tool calls client.aretain() without retain_async, defaulting to False (synchronous mode). On banks with significant data, synchronous processing exceeds the 120s timeout, producing:
Tool hindsight_retain returned error (120.01s): {"error": "Failed to store memory: "}
The auto-retain path (sync_turn) works correctly because it passes retain_async=self._retain_async (True) to aretain_batch.
Fix add retain_async=True to _build_retain_kwargs in the tool handler (1 line).
What does this PR do?
Add retain_async=True to the _build_retain_kwargs call in the hindsight_retain tool handler, making the tool use asynchronous processing consistent with the auto-retain path (sync_turn). Previously the tool defaulted to retain_async=False (synchronous mode), causing timeouts on banks with significant data when the Hindsight APIs LLM extraction took longer than 120 seconds.
Related Issues
Type of Change
Changes Made
How to Test
Checklist
Code
Documentation