Conversation
|
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
Models re-emit an identical tool call while a slow tool is still running or repeat it in a later step; the reporter of google#3940 saw production tools loop this way, and long-running tools re-fire while the model waits. Wrap the tool execution step of the shared tool-calling pipeline in an invocation-scoped single-flight cache keyed by agent, branch, tool name and canonical arguments: the first call runs the tool and publishes a snapshot that concurrent and later duplicates reuse as their own copy. It is opt-in via RunConfig.dedupe_tool_calls and always on for LongRunningFunctionTool; callbacks still run per call, a failed run is evicted so a later call retries, and a result that transfers, escalates or requests confirmation or auth is never shared. Implemented with Claude Code (Claude Fable 5.1); the author reviewed and tested the change. Closes google#3940
jayy-77
force-pushed
the
fix-tool-dedup-3940-v2
branch
from
September 18, 2026 17:53
e8e4ebe to
c92aac2
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Successor of #4189 (closed for merge conflicts), re-implemented on the current tool pipeline instead of rebased. Gemini's two findings on #4189 (duplicated dedupe predicate, duplicated cache-hit metadata block) are single helpers here.
Link to Issue or Description of Change
1. Link to an existing issue (if applicable):
Problem: models re-emit an identical tool call while a slow tool is still running, or repeat it in a later step, and every repeat runs the tool again.
Solution:
RunConfig.dedupe_tool_calls(defaultFalse). Within one invocation, identical calls (same agent, branch, tool name and canonical arguments) share one tool execution through an invocation-scoped single-flight cache around Step 3 of_execute_single_prepared_call, so async and live modes use the same hook.custom_metadata['adk_tool_call_cache_hit'] = True.stop_streaming, live streaming tools,_defers_responsetools, and calls carrying a confirmation answer.LongRunningFunctionToolcalls are always deduped. Deliberate default; happy to make it opt-in too.LongRunningFunctionTool: Step 3 is the unchangedawait tool_runner().Testing Plan
Unit Tests:
33 new tests in
tests/unittests/flows/llm_flows/tools/test_functions_dedupe.py,test_caller.py,test_batch_executor.pyandtests/unittests/agents/test_run_config.py: duplicates across steps and within one step, default off, long-running default, argument identity (type and key order), side effects applied once, eviction on failure including concurrent waiters, per-call callbacks, confirmation and rejection, transfers, per-agent and per-branch scope.Full
tests/unittestssuite, fresh venv per interpreter with thetestextra,pytest -n auto:pre-commit run --files <touched files>: all hooks pass.mypywith the repo's strict config on the touched modules: no issues.Manual End-to-End (E2E) Tests:
Real model (
gemini-3.6-flash), this branch,InMemoryRunner, each scenario with the flag off and on:output_schema+output_key+ "call exactly once", 12 s tool), five variants incl. the exact prompt,ParallelAgentandLongRunningFunctionToolWhen the model repeats a call, the duplicate is answered from the first execution and the run is one tool latency shorter. Without duplicates the flag changes nothing. The loop described on #3940 did not reproduce on current
mainwith this model; this change removes the redundant execution when a model does repeat a call.Checklist
Additional context
docs/guides/runners/runner/index.mdgains theRunConfigrow; the user-facing reference in adk-docs will follow in a separate PR.