Skip to content

Commit 6472409

Browse files
committed
fix(tools):Fallback to code_execution_result.output when merged text is empty
1 parent 7de5bc5 commit 6472409

2 files changed

Lines changed: 97 additions & 0 deletions

File tree

src/google/adk/tools/agent_tool.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,16 @@ async def run_async(
272272
merged_text = '\n'.join(
273273
p.text for p in last_content.parts if p.text and not p.thought
274274
)
275+
# Fallback: if model returned only code_execution_result parts, use their output.
276+
if not merged_text.strip():
277+
code_outputs = [
278+
p.code_execution_result.output
279+
for p in last_content.parts
280+
if p.code_execution_result and p.code_execution_result.output
281+
]
282+
if code_outputs:
283+
merged_text = '\n\n'.join(code_outputs)
284+
275285
output_schema = _get_output_schema(self.agent)
276286
if output_schema:
277287
tool_result = validate_schema(output_schema, merged_text)

tests/unittests/tools/test_agent_tool.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
from pydantic import BaseModel
3838
import pytest
3939
from pytest import mark
40+
from google.adk.events.event import Event
4041

4142
from .. import testing_utils
4243

@@ -943,6 +944,92 @@ class CustomOutput(BaseModel):
943944
}
944945

945946

947+
@mark.asyncio
948+
async def test_run_async_uses_code_execution_result_output_when_no_text(
949+
monkeypatch,
950+
):
951+
"""Verify run_async falls back to code execution output when text is empty."""
952+
953+
from google.adk.events.event import Event
954+
955+
async def _event_stream():
956+
yield Event(
957+
author='tool_agent',
958+
content=types.Content(
959+
parts=[
960+
types.Part(
961+
code_execution_result=types.CodeExecutionResult(
962+
output='IRR=0.1472',
963+
outcome=types.Outcome.OUTCOME_OK,
964+
)
965+
)
966+
]
967+
),
968+
)
969+
970+
class StubRunner:
971+
972+
def __init__(
973+
self,
974+
*,
975+
app_name: str,
976+
agent: Agent,
977+
artifact_service,
978+
session_service,
979+
memory_service,
980+
credential_service,
981+
plugins,
982+
):
983+
del app_name, agent, artifact_service, memory_service
984+
del credential_service, plugins
985+
self.session_service = session_service
986+
987+
def run_async(
988+
self,
989+
*,
990+
user_id: str,
991+
session_id: str,
992+
invocation_id: Optional[str] = None,
993+
new_message: Optional[types.Content] = None,
994+
state_delta: Optional[dict[str, Any]] = None,
995+
run_config: Optional[RunConfig] = None,
996+
):
997+
del user_id, session_id, invocation_id, new_message, state_delta
998+
del run_config
999+
return _event_stream()
1000+
1001+
async def close(self):
1002+
pass
1003+
1004+
monkeypatch.setattr('google.adk.runners.Runner', StubRunner)
1005+
1006+
tool_agent = Agent(
1007+
name='tool_agent',
1008+
model=testing_utils.MockModel.create(responses=['unused']),
1009+
)
1010+
agent_tool = AgentTool(agent=tool_agent)
1011+
1012+
session_service = InMemorySessionService()
1013+
session = await session_service.create_session(
1014+
app_name='test_app', user_id='test_user'
1015+
)
1016+
1017+
invocation_context = InvocationContext(
1018+
invocation_id='invocation_id',
1019+
agent=tool_agent,
1020+
session=session,
1021+
session_service=session_service,
1022+
)
1023+
tool_context = ToolContext(invocation_context=invocation_context)
1024+
1025+
tool_result = await agent_tool.run_async(
1026+
args={'request': 'test request'},
1027+
tool_context=tool_context,
1028+
)
1029+
1030+
assert tool_result == 'IRR=0.1472'
1031+
1032+
9461033
@mark.asyncio
9471034
async def test_run_async_handles_none_parts_in_response():
9481035
"""Verify run_async handles None parts in response without raising TypeError."""

0 commit comments

Comments
 (0)