fix(tools)!: make tool-result caching opt-in instead of on by default#6509
fix(tools)!: make tool-result caching opt-in instead of on by default#6509joaomdmoura wants to merge 2 commits into
Conversation
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>
📝 WalkthroughWalkthroughCrewAI 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. ChangesTool-cache opt-in
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
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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.
Actionable comments posted: 2
🧹 Nitpick comments (1)
lib/crewai/tests/test_tool_cache_default.py (1)
142-183: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAssert standalone caching behavior, not only handler internals.
These tests inspect
tools_handler.cacheandcache_handler, so they can pass even if the executor ignores the handler. Reuse the offline tool harness to assert execution counts for standalonecache=True, explicit handlers, andcache=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
📒 Files selected for processing (5)
lib/crewai/src/crewai/agent/core.pylib/crewai/src/crewai/agents/agent_builder/base_agent.pylib/crewai/src/crewai/crew.pylib/crewai/tests/test_crew.pylib/crewai/tests/test_tool_cache_default.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>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ 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) |
There was a problem hiding this comment.
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.
Reviewed by Cursor Bugbot for commit e7e3af7. Configure here.


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.
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.cachenow defaults toFalse.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-toolcache_functionwrite-gating is unchanged.CacheHandlerat construction.Agent(cache=True)or an explicitcache_handler=opts in. Notably, this self-wiring meantCrew(cache=False)never actually disabled caching before — agents cached through their own handler anyway; that inconsistency is fixed as a side effect.CacheHandler,ToolsHandler,cache_function, cache events withfrom_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
actual executions: 2 / NOT REPRODUCEDby default, and1(dedup) when re-run withCrew(cache=True)— opt-in preserves the old behavior byte-for-byte.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_handleropt in; explicitcache=Falserefuses a crew-offered handler; default agents accept one).test_cache_hitting_between_agents,test_tools_with_custom_caching) now opt in explicitly — machinery behavior unchanged.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.cachenow defaults toFalse;Crew(cache=True)still wires the sharedCacheHandlerto agents at crew setup (unchanged machinery). Standalone agents no longer auto-create a cache in_setup_agent_executor—onlyAgent(cache=True), acache_handler, or the crew offering a handler whencache=Trueactivates caching.Agent.cache=Falsestill refuses a crew-offered handler viaset_cache_handler.BaseAgent.copy()dropscachefrom 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=Trueon the crew; newtest_tool_cache_default.pycovers 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.