fix: Invoke on_tool_error_callback for missing tools in live mode

In live mode, when the model calls an unregistered tool, ADK now runs on_tool_error_callback before failing. If the callback returns a response, ADK emits
that function response and continues; otherwise it keeps the old ValueError

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 872996178
This commit is contained in:
George Weale
2026-02-20 11:20:51 -08:00
committed by Copybara-Service
parent 7478bdaa98
commit e6b601a2ab
2 changed files with 131 additions and 3 deletions
+54 -3
View File
@@ -660,14 +660,65 @@ async def _execute_single_function_call_live(
streaming_lock: asyncio.Lock,
) -> Optional[Event]:
"""Execute a single function call for live mode with thread safety."""
tool, tool_context = _get_tool_and_context(
invocation_context, function_call, tools_dict
)
async def _run_on_tool_error_callbacks(
*,
tool: BaseTool,
tool_args: dict[str, Any],
tool_context: ToolContext,
error: Exception,
) -> Optional[dict[str, Any]]:
"""Runs the on_tool_error_callbacks for the given tool."""
error_response = (
await invocation_context.plugin_manager.run_on_tool_error_callback(
tool=tool,
tool_args=tool_args,
tool_context=tool_context,
error=error,
)
)
if error_response is not None:
return error_response
for callback in agent.canonical_on_tool_error_callbacks:
error_response = callback(
tool=tool,
args=tool_args,
tool_context=tool_context,
error=error,
)
if inspect.isawaitable(error_response):
error_response = await error_response
if error_response is not None:
return error_response
return None
# Do not use "args" as the variable name, because it is a reserved keyword
# in python debugger.
# Make a deep copy to avoid being modified.
function_args = (
copy.deepcopy(function_call.args) if function_call.args else {}
)
tool_context = _create_tool_context(invocation_context, function_call)
try:
tool = _get_tool(function_call, tools_dict)
except ValueError as tool_error:
tool = BaseTool(name=function_call.name, description='Tool not found')
error_response = await _run_on_tool_error_callbacks(
tool=tool,
tool_args=function_args,
tool_context=tool_context,
error=tool_error,
)
if error_response is not None:
return __build_response_event(
tool, error_response, tool_context, invocation_context
)
raise tool_error
async def _run_with_trace():
nonlocal function_args