Skip to content

fix(tools)!: make tool-result caching opt-in instead of on by default#6509

Open
joaomdmoura wants to merge 2 commits into
mainfrom
joao/epd-180-tool-results-cached-by-default-silently-serve-stale-data-for
Open

fix(tools)!: make tool-result caching opt-in instead of on by default#6509
joaomdmoura wants to merge 2 commits into
mainfrom
joao/epd-180-tool-results-cached-by-default-silently-serve-stale-data-for

Conversation

@joaomdmoura

@joaomdmoura joaomdmoura commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes EPD-180 — flagged by the reporter as the highest-impact of the batch. Tool-result caching was on by default, so an LLM calling the same tool with identical arguments twice in one run silently received the first result without the tool executing. For live-data tools that's a confidently stale answer; for state-mutating tools the second action is silently dropped — the worst failure mode (silently wrong), and most enterprise data tools are freshness-sensitive.

⚠️ Behavior change (deliberate)

The ticket's expected behavior is "caching opt-in per tool — or at minimum a prominent docs warning". This PR implements opt-in, on the principle that the default should never produce silently wrong answers; users who want dedup can say so in one keyword. Calling it out for review since it flips a long-standing default:

  • Crew.cache now defaults to False. Crew(cache=True) restores today's behavior exactly — agents still default to participating when a crew offers its handler, Agent(cache=False) still opts an agent out, and per-tool cache_function write-gating is unchanged.
  • Standalone agents no longer self-wire a CacheHandler at construction. Agent(cache=True) or an explicit cache_handler= opts in. Notably, this self-wiring meant Crew(cache=False) never actually disabled caching before — agents cached through their own handler anyway; that inconsistency is fixed as a side effect.
  • All caching machinery (CacheHandler, ToolsHandler, cache_function, cache events with from_cache) is untouched — only the defaults changed. Field descriptions and the setup docstring document the opt-in contract and the live-data/stateful-tool hazard.

Docs note: the in-repo docs are versioned release snapshots, so the "Create Custom Tools" page warning the ticket suggests should land with the release notes / current docs when this ships.

Testing

  • The ticket's clean-room repro (fake OpenAI client scripting two identical tool calls) now prints actual executions: 2 / NOT REPRODUCED by default, and 1 (dedup) when re-run with Crew(cache=True) — opt-in preserves the old behavior byte-for-byte.
  • New tests/test_tool_cache_default.py (8 tests): offline end-to-end default-reexecutes + opt-in-dedupes (mirroring the repro), and agent-level wiring contracts (no cache by default; cache=True / cache_handler opt in; explicit cache=False refuses a crew-offered handler; default agents accept one).
  • The two existing tests that exercised caching machinery via the implicit default (test_cache_hitting_between_agents, test_tools_with_custom_caching) now opt in explicitly — machinery behavior unchanged.
  • Green: full test_crew.py (131), test_agent.py + tests/tools/ (230), test_lite_agent.py + test_task.py (96), ruff, and mypy.

🤖 Generated with Claude Code


Note

Medium Risk
Deliberate breaking default may surprise users who relied on implicit dedup; the change is scoped to wiring/defaults rather than cache implementation, with regression tests for the new contract.

Overview
Breaking behavior change: tool-result deduplication is off unless explicitly enabled, so identical tool calls re-run the tool instead of returning a cached first result.

Crew.cache now defaults to False; Crew(cache=True) still wires the shared CacheHandler to agents at crew setup (unchanged machinery). Standalone agents no longer auto-create a cache in _setup_agent_executor—only Agent(cache=True), a cache_handler, or the crew offering a handler when cache=True activates caching. Agent.cache=False still refuses a crew-offered handler via set_cache_handler.

BaseAgent.copy() drops cache from the dump when it was never explicitly set, so copies of default agents do not accidentally opt in. Field descriptions document the opt-in contract and live-data/stateful-tool hazards.

Tests: existing cache scenarios pass cache=True on the crew; new test_tool_cache_default.py covers EPD-180 (default re-executes, opt-in dedupes) and wiring/copy edge cases.

Reviewed by Cursor Bugbot for commit e7e3af7. Bugbot is set up for automated code reviews on this repo. Configure here.

Tool-result caching defaulted to on (Crew.cache=True, and standalone
agents self-wired a CacheHandler at construction), so an LLM calling
the same tool with identical arguments twice in one run silently got
the first result back without the tool executing. For live-data tools
that is a confidently stale answer; for state-mutating tools the second
action is silently dropped.

Caching is now opt-in with the machinery unchanged:
- Crew.cache defaults to False; Crew(cache=True) restores today's
  behavior exactly (agents still default to participating when a crew
  offers its handler, and Agent(cache=False) still opts an agent out).
- Standalone agents no longer self-wire a cache; Agent(cache=True) or
  an explicit cache_handler opts in. Previously even Crew(cache=False)
  agents cached via this self-wired handler.
- Per-tool cache_function write gating is unchanged once opted in.

Existing tests that exercised the caching machinery now opt in
explicitly; new regression tests cover the default (both identical
calls execute), crew-level opt-in dedup, and agent-level wiring.

Fixes EPD-180.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@linear

linear Bot commented Jul 10, 2026

Copy link
Copy Markdown

EPD-180

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

CrewAI tool-result caching is now opt-in by default. Agent executor setup respects explicit cache settings and handlers, while tests cover crew-level deduplication, standalone agents, copy semantics, and existing cache scenarios.

Changes

Tool-cache opt-in

Layer / File(s) Summary
Caching defaults and contracts
lib/crewai/src/crewai/crew.py, lib/crewai/src/crewai/agents/agent_builder/base_agent.py
Crew.cache defaults to False; agent metadata and copying preserve explicit opt-in, crew-level participation, and cache=False exclusion semantics.
Agent cache-handler initialization
lib/crewai/src/crewai/agent/core.py
Agent executor setup initializes caching only when an agent explicitly enables it or already has a cache handler.
Caching behavior regression coverage
lib/crewai/tests/test_tool_cache_default.py, lib/crewai/tests/test_crew.py
Offline tests verify repeated execution without caching, crew-level deduplication, standalone agent wiring, copy behavior, and explicit cache activation in existing tests.

Sequence Diagram(s)

sequenceDiagram
  participant Crew
  participant Agent
  participant CacheHandler
  participant Tool
  Crew->>Agent: kickoff with cache configuration
  Agent->>CacheHandler: use handler when caching is opted in
  Agent->>Tool: execute identical tool call
  CacheHandler-->>Agent: return cached result for repeated call
  Agent-->>Crew: complete task with tool results
Loading

Suggested reviewers: lorenzejay, akaKuruma

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title accurately summarizes the main change: tool-result caching is now opt-in instead of enabled by default.
Description check ✅ Passed The description is directly aligned with the changeset and explains the caching default change, wiring updates, and tests.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch joao/epd-180-tool-results-cached-by-default-silently-serve-stale-data-for

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread lib/crewai/src/crewai/agent/core.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
lib/crewai/tests/test_tool_cache_default.py (1)

142-183: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert standalone caching behavior, not only handler internals.

These tests inspect tools_handler.cache and cache_handler, so they can pass even if the executor ignores the handler. Reuse the offline tool harness to assert execution counts for standalone cache=True, explicit handlers, and cache=False.

As per coding guidelines, tests for new functionality should focus on behavior rather than implementation details.

🤖 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 `@lib/crewai/tests/test_tool_cache_default.py` around lines 142 - 183, The
TestAgentCacheWiring tests currently verify handler state rather than caching
behavior. Reuse the existing offline tool harness to execute the same standalone
agent tool multiple times and assert execution counts for cache=True, an
explicit cache handler, and cache=False; update the tests around _agent and the
related cache cases so they validate observable execution behavior instead of
tools_handler.cache or cache_handler internals.

Source: Coding guidelines

🤖 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.

Inline comments:
In `@lib/crewai/src/crewai/agents/agent_builder/base_agent.py`:
- Around line 269-277: Update BaseAgent.__doc__ to accurately document the cache
parameter: the default permits participation but does not activate caching,
which requires agent/handler or crew opt-in; explicitly state that cache=False
excludes the agent from caching even when enabled by the crew.

In `@lib/crewai/src/crewai/crew.py`:
- Around line 219-228: The class-level Crew.__doc__ Attributes.cache
documentation is outdated. Update it to match the cache field description,
covering opt-in behavior, reuse for identical tool arguments, and risks for
live-data or state-mutating tools unless cache_function prevents caching.

---

Nitpick comments:
In `@lib/crewai/tests/test_tool_cache_default.py`:
- Around line 142-183: The TestAgentCacheWiring tests currently verify handler
state rather than caching behavior. Reuse the existing offline tool harness to
execute the same standalone agent tool multiple times and assert execution
counts for cache=True, an explicit cache handler, and cache=False; update the
tests around _agent and the related cache cases so they validate observable
execution behavior instead of tools_handler.cache or cache_handler internals.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 29e0305d-c8ac-491e-bb36-f39ae7a87b73

📥 Commits

Reviewing files that changed from the base of the PR and between 7baf8f9 and e792d8a.

📒 Files selected for processing (5)
  • lib/crewai/src/crewai/agent/core.py
  • lib/crewai/src/crewai/agents/agent_builder/base_agent.py
  • lib/crewai/src/crewai/crew.py
  • lib/crewai/tests/test_crew.py
  • lib/crewai/tests/test_tool_cache_default.py

Comment thread lib/crewai/src/crewai/agents/agent_builder/base_agent.py
Comment thread lib/crewai/src/crewai/crew.py
…opt-in

Agent.copy() rebuilds from model_dump(), which includes the field
default cache=True, so the copy's model_fields_set contained "cache"
and _setup_agent_executor wired a CacheHandler the source agent never
opted into (Bugbot review finding). Drop "cache" from the dump when it
was not explicitly set on the source; explicit opt-ins still survive
copying.

Also sync the Crew and BaseAgent class docstrings with the new opt-in
cache semantics (CodeRabbit review findings).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions github-actions Bot added size/L and removed size/M labels Jul 10, 2026

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit e7e3af7. Configure here.

# field default via model_fields_set; don't let the dump turn the
# default into an explicit opt-in on the copy.
if "cache" not in self.model_fields_set:
copied_data.pop("cache", None)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copy drops cache_handler opt-in

Medium Severity

Agent.copy() omits cache_handler from the rebuilt agent and only treats an explicit cache field (via model_fields_set) as opt-in. An agent constructed with only cache_handler no longer wires tool-result caching after copy(), though the PR documents that handler as a valid opt-in path.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit e7e3af7. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant