* fix(tools)!: make tool-result caching opt-in instead of on by default
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>
* fix(agent): don't let copy() turn the cache default into an explicit 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>
* fix(agent): preserve cache_handler-only opt-in across Agent.copy()
copy() excludes cache_handler from the rebuilt agent, so an agent that
opted into tool-result caching solely via an explicit cache_handler
lost caching after copy() (Bugbot review finding). Carry the consent as
cache=True on the copy when the source has a handler wired and hasn't
explicitly disabled caching — the copy wires its own fresh handler,
matching pre-change copy semantics (copies never shared the source's
handler instance).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(crew): offer the crew cache handler to the hierarchical manager
The hierarchical manager agent is created in _create_manager_agent,
outside the validation-time agents loop that offers the crew's cache
handler — and managers no longer self-wire a handler — so
Crew(cache=True) hierarchical runs never cached the manager's
delegation tool calls (Bugbot review finding). Offer the shared crew
handler when the crew opted in; a user-provided manager with
cache=False stays excluded via the existing set_cache_handler gate.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(agent): only construction-time cache opt-ins survive Agent.copy()
The previous copy() fix treated any wired cache_handler as consent, but
agents that merely received the crew's shared handler at kickoff
(set_cache_handler from Crew(cache=True)) never opted in themselves —
their copies must not become standalone cachers (Bugbot review
finding). Record the opt-in signal in _setup_agent_executor, which runs
at construction before any crew wiring can happen, and have copy()
consult that flag instead of inspecting cache_handler after the fact.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>