Skip to content

feat: Add tool result offloading - #11849

Merged
sjrl merged 13 commits into
v3from
add-tool-result-offloading
Jul 7, 2026
Merged

feat: Add tool result offloading#11849
sjrl merged 13 commits into
v3from
add-tool-result-offloading

Conversation

@sjrl

@sjrl sjrl commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Related Issues

Proposed Changes:

Added tool result offloading for the Agent via the new haystack.hooks.tool_result_offloading module.
ToolResultOffloadHook is an after_tool hook that writes selected tool results to a ToolResultStore and
replaces them in the conversation with a compact pointer (reference plus a short preview), so the next LLM call
sees a reference instead of the full result. This helps manage the context window and is a step towards letting an
Agent operate on offloaded results with follow-up tools (e.g. a bash tool reading the referenced files).

Configure what gets offloaded, per tool, via offload_strategies:

  • Map a tool name, a tuple of tool names, or the wildcard "*" to an OffloadPolicy. More specific keys win over
    "*"; a tool with no matching key is not offloaded.
  • Built-in policies: AlwaysOffload, NeverOffload, and OffloadOverChars(threshold) (offload when the result
    exceeds a character threshold).
  • For custom conditions, implement the OffloadPolicy protocol (it provides default to_dict/from_dict, so a
    stateless policy only needs a should_offload method).

Choose where results are stored:

  • FileSystemToolResultStore writes results to the local file system.
  • Implement the ToolResultStore protocol to target other backends.
  • For request-scoped stores in server settings, pass a store per run via the Agent's hook_context.

What is offloaded:

  • Only successful, text results are offloaded. Error results (including before_tool human-in-the-loop rejections)
    are left in context.
  • Non-text results (image or file content) are left in context; supporting only text is a deliberate choice for now.
    A warning is logged when a non-text result has a matching offload policy.
  • Each result is offloaded at most once.
from typing import Annotated

from haystack.components.agents import Agent
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.dataclasses import ChatMessage
from haystack.hooks.tool_result_offloading import (
    FileSystemToolResultStore,
    OffloadOverChars,
    ToolResultOffloadHook,
)
from haystack.tools import tool


@tool
def search(query: Annotated[str, "The search query"]) -> str:
    """Search the web and return the (potentially large) results."""
    return f"... large result for {query} ..."


offload_hook = ToolResultOffloadHook(
    store=FileSystemToolResultStore(root="tool_results"),
    offload_strategies={"*": OffloadOverChars(4000)},
)
agent = Agent(
    chat_generator=OpenAIChatGenerator(model="gpt-5.4-nano"),
    tools=[search],
    hooks={"after_tool": [offload_hook]},
)
agent.run(messages=[ChatMessage.from_user("Summarize today's tech news")])

How did you test it?

Added new tests.

Also tested locally with this script
agent_tool_result_offloading_demo.py

Notes for the reviewer

Checklist

  • I have read the contributors guidelines and the code of conduct.
  • I have updated the related issue with new insights and changes.
  • I have added unit tests and updated the docstrings.
  • I've used one of the conventional commit types for my PR title: fix:, feat:, build:, chore:, ci:, docs:, style:, refactor:, perf:, test: and added ! in case the PR includes breaking changes.
  • I have documented my code.
  • I have added a release note file, following the contributors guidelines.
  • I have run pre-commit hooks and fixed any issue.

@vercel

vercel Bot commented Jul 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
haystack-docs Ignored Ignored Preview Jul 7, 2026 11:44am

Request Review

@github-actions github-actions Bot added topic:tests type:documentation Improvements on the docs labels Jul 2, 2026
@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Coverage report

Click to see where and how coverage changed

FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  haystack/hooks/tool_result_offloading
  __init__.py
  hooks.py 221
  policies.py
  stores.py
  haystack/hooks/tool_result_offloading/types
  __init__.py
  protocol.py 39, 44
Project Total  

This report was generated by python-coverage-comment-action

@sjrl
sjrl requested a review from julian-risch July 6, 2026 07:35
Base automatically changed from add-after-tool-hook to v3 July 6, 2026 07:53
@sjrl
sjrl removed the request for review from julian-risch July 6, 2026 07:54
@sjrl
sjrl marked this pull request as ready for review July 6, 2026 07:59
@sjrl
sjrl requested a review from a team as a code owner July 6, 2026 07:59
@sjrl
sjrl requested review from anakin87 and removed request for a team July 6, 2026 07:59

@anakin87 anakin87 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks generally good!

I left a few minor comments.

I also played with the demo script, making it more challenging and it seems to work well.

Comment thread haystack/hooks/tool_result_offloading/hooks.py Outdated
Comment thread haystack/hooks/tool_result_offloading/hooks.py Outdated
Comment thread haystack/hooks/tool_result_offloading/hooks.py Outdated
return None


def _serialize_offload_strategies(strategies: dict[str | tuple[str, ...], OffloadPolicy]) -> dict[str, Any]:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at this part made me think (partly unrelated): do we plan to move human_in_the_loop folder into hooks?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I was wondering the same actually. The only downside I see is that it would change the import structure, but that should probably be okay since we do have quite a few other breaking changes already. WDYT?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you think it makes more sense, let's do it (in another PR).

@claude

claude Bot commented Jul 7, 2026

Copy link
Copy Markdown

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

@anakin87 anakin87 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@sjrl
sjrl merged commit 48d9865 into v3 Jul 7, 2026
35 of 37 checks passed
@sjrl
sjrl deleted the add-tool-result-offloading branch July 7, 2026 13:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

topic:tests type:documentation Improvements on the docs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants