Repository navigation
feat: add tool result offload compactor - #12487
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Coverage reportClick to see where and how coverage changed
This report was generated by python-coverage-comment-action |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
a314631 to
d54b902
Compare
There was a problem hiding this comment.
Changes to this file are for moving common functionality out into utils.py
There was a problem hiding this comment.
This file contains shared utility functions that used to live in hooks.py The utility functions are used in tool_result_offloading.py
bogdankostic
left a comment
There was a problem hiding this comment.
Looks good in general, just found one case where the keys of distinct tool calls might collide.
| hook = CompactionHook(compactor=ToolResultOffloadCompactor(store=store), context_window=400_000) | ||
| agent = Agent( | ||
| chat_generator=OpenAIResponsesChatGenerator(model="gpt-5.4-nano"), | ||
| tools=[web_search, read_offloaded_result], |
There was a problem hiding this comment.
web_search is not defined here, so this code block can't be used out of the box. Should we add a comment about this?
There was a problem hiding this comment.
Added a comment noting web_search stands in for the user's own tool.
| message=message, | ||
| content_blocks=content_blocks, | ||
| store=self.store, | ||
| key_prefix=f"compacted_{result.origin.tool_name}_{result.origin.id or f'call{index}'}", |
There was a problem hiding this comment.
I don't think this happens often in practice, but with OllamaChatGenerator, for example, tool calls don't have an ID, so we would fall back to f'call{index}'. Two separate runs on the same Agent can then get the same keys for results at the same position, with the second run overwriting the first one's results. If the first conversation is continued in a later run, e.g. a chat app passing the history back, the model would read the other run's result.
There was a problem hiding this comment.
Thanks, good catch. Keys now get a random suffix so separate runs never share a key, and I added a regression test that runs compact() twice on the same store.
Related Issues
Proposed Changes:
Added the experimental
ToolResultOffloadCompactorfor use withCompactionHook. It keeps fresh tool output directly in the Agent's context, then writes eligible older results to aToolResultStoreonly when context compaction is triggered. The compacted conversation retains a reference and configurable preview, allowing the Agent to retrieve the full stored output later with a read tool scoped to the same store. Image and file parts of a result are offloaded too when the store setssupports_binary_content.The store-writing and pointer-building logic moved out of
ToolResultOffloadHookintohaystack/hooks/tool_result_offloading/utils.pyso the hook and the compactor share the same reference format andtool_result_offloadedmetadata. The oldest-first replace-until-target loop thatToolResultPruningCompactoralready had now lives inhaystack/hooks/compaction/utils.py, and both compactors use it.web_searchstands in for any tool whose results can grow large. For results with images or files, pass a provider token counter such asOpenAITokenCountertoCompactionHook; the defaultApproximateTokenCountercharges a flat rate per image and file.How did you test it?
New tests
Notes for the reviewer
The docs page for this feature is in a separate PR: #13119
Known limitations, tracked as follow-ups:
hook_contextlikeToolResultOffloadHookdoes, so a multi-user server currently needs an Agent per run: https://github.com/deepset-ai/haystack-private/issues/591store.writestops the Agent run instead of leaving the result in context. This applies toToolResultOffloadHooktoo: https://github.com/deepset-ai/haystack-private/issues/592Known limitation: when a tool call has no id, the store key falls back to the message's position in the conversation. If another compactor (e.g.
SlidingWindowCompactor) drops messages between two compaction rounds in the same run, a later result can land on the same position and overwrite a stored entry that an earlier reference still points to. Providers almost always set tool call ids, so this is left as is for now.Checklist
fix:,feat:,build:,chore:,ci:,docs:,style:,refactor:,perf:,test:and added!in case the PR includes breaking changes.