fix: balance callback lifecycle for hallucinated tool calls#4808
fix: balance callback lifecycle for hallucinated tool calls#4808giulio-leone wants to merge 1 commit intogoogle:mainfrom
Conversation
When an LLM hallucinates a tool name, _get_tool() raises ValueError. Previously, on_tool_error_callback fired immediately — before before_tool_callback and outside the OTel tracer span. This caused plugins that push/pop spans (e.g. BigQueryAgentAnalyticsPlugin's TraceManager) to pop the parent agent span, corrupting the trace stack for all subsequent tool calls. Move the ValueError handling inside _run_with_trace() so that: 1. before_tool_callback always fires first (balanced push) 2. The error is surfaced within the OTel span context 3. on_tool_error_callback fires after before_tool_callback Applied to both handle_function_calls_async and handle_function_calls_live code paths. Fixes google#4775 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
|
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. |
|
Response from ADK Triaging Agent Hello @giulio-leone, thank you for your contribution! It looks like the Contributor License Agreement (CLA) check is failing. Before we can merge your PR, you'll need to sign the CLA. You can find more information and sign the agreement at https://cla.developers.google.com/. Thanks! |
|
Hi @giulio-leone , Thank you for your contribution! It appears you haven't yet signed the Contributor License Agreement (CLA). Please visit https://cla.developers.google.com/ to complete the signing process. Once the CLA is signed, we'll be able to proceed with the review of your PR. Thank you! |
Summary
Fixes #4775
When an LLM hallucinates a tool name that doesn't exist in
tools_dict,_get_tool()raisesValueError. Previously,on_tool_error_callbackfired immediately — beforebefore_tool_callbackand outside the OpenTelemetry tracer span. This caused plugins that push/pop spans (e.g.BigQueryAgentAnalyticsPlugin'sTraceManager) to pop the parent agent span, corrupting the trace stack for all subsequent tool calls in the session.Root Cause
The callback lifecycle contract is:
For hallucinated tools, the old code path was:
This violated the lifecycle invariant — plugins that push a span in
before_tool_callbacknever get to push, buton_tool_error_callbackstill pops, corrupting the stack.Fix
Move the
ValueErrorhandling inside_run_with_trace()so that:before_tool_callbackalways fires first (balanced push)on_tool_error_callbackfires afterbefore_tool_callbackApplied to both
handle_function_calls_asyncandhandle_function_calls_livecode paths.Testing
test_plugin_tool_callbacks.py:test_hallucinated_tool_fires_before_and_error_callbacks: Verifies callback order (before → error)test_hallucinated_tool_raises_when_no_error_callback: VerifiesValueErrorpropagates correctly