Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/google/adk/tools/agent_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,16 @@ async def run_async(
merged_text = '\n'.join(
p.text for p in last_content.parts if p.text and not p.thought
)
# Fallback: if model returned only code_execution_result parts, use their output.
if not merged_text.strip():
code_outputs = [
p.code_execution_result.output
for p in last_content.parts
if p.code_execution_result and p.code_execution_result.output
]
if code_outputs:
merged_text = '\n\n'.join(code_outputs)

output_schema = _get_output_schema(self.agent)
if output_schema:
tool_result = validate_schema(output_schema, merged_text)
Expand Down
87 changes: 87 additions & 0 deletions tests/unittests/tools/test_agent_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from google.adk.agents.run_config import RunConfig
from google.adk.agents.sequential_agent import SequentialAgent
from google.adk.artifacts.in_memory_artifact_service import InMemoryArtifactService
from google.adk.events.event import Event
from google.adk.features import FeatureName
from google.adk.features._feature_registry import temporary_feature_override
from google.adk.memory.in_memory_memory_service import InMemoryMemoryService
Expand Down Expand Up @@ -943,6 +944,92 @@ class CustomOutput(BaseModel):
}


@mark.asyncio
async def test_run_async_uses_code_execution_result_output_when_no_text(
monkeypatch,
):
"""Verify run_async falls back to code execution output when text is empty."""

from google.adk.events.event import Event

async def _event_stream():
yield Event(
author='tool_agent',
content=types.Content(
parts=[
types.Part(
code_execution_result=types.CodeExecutionResult(
output='IRR=0.1472',
outcome=types.Outcome.OUTCOME_OK,
)
)
]
),
)

class StubRunner:

def __init__(
self,
*,
app_name: str,
agent: Agent,
artifact_service,
session_service,
memory_service,
credential_service,
plugins,
):
del app_name, agent, artifact_service, memory_service
del credential_service, plugins
self.session_service = session_service

def run_async(
self,
*,
user_id: str,
session_id: str,
invocation_id: Optional[str] = None,
new_message: Optional[types.Content] = None,
state_delta: Optional[dict[str, Any]] = None,
run_config: Optional[RunConfig] = None,
):
del user_id, session_id, invocation_id, new_message, state_delta
del run_config
return _event_stream()

async def close(self):
pass

monkeypatch.setattr('google.adk.runners.Runner', StubRunner)

tool_agent = Agent(
name='tool_agent',
model=testing_utils.MockModel.create(responses=['unused']),
)
agent_tool = AgentTool(agent=tool_agent)

session_service = InMemorySessionService()
session = await session_service.create_session(
app_name='test_app', user_id='test_user'
)

invocation_context = InvocationContext(
invocation_id='invocation_id',
agent=tool_agent,
session=session,
session_service=session_service,
)
tool_context = ToolContext(invocation_context=invocation_context)

tool_result = await agent_tool.run_async(
args={'request': 'test request'},
tool_context=tool_context,
)

assert tool_result == 'IRR=0.1472'


@mark.asyncio
async def test_run_async_handles_none_parts_in_response():
"""Verify run_async handles None parts in response without raising TypeError."""
Expand Down