Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
7 changes: 6 additions & 1 deletion src/google/adk/tools/agent_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,12 @@ async def run_async(

if not last_content:
return ''
merged_text = '\n'.join(p.text for p in last_content.parts if p.text)
parts = last_content.parts if last_content.parts else []
merged_text = '\n'.join(
str(text)
for part in parts
if (text := getattr(part, 'text', None)) is not None
)
if isinstance(self.agent, LlmAgent) and self.agent.output_schema:
tool_result = self.agent.output_schema.model_validate_json(
merged_text
Expand Down
7 changes: 6 additions & 1 deletion src/google/adk/tools/google_search_agent_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,12 @@ async def run_async(

if not last_content:
return ''
merged_text = '\n'.join(p.text for p in last_content.parts if p.text)
parts = last_content.parts if last_content.parts else []
merged_text = '\n'.join(
str(text)
for part in parts
if (text := getattr(part, 'text', None)) is not None
)
if isinstance(self.agent, LlmAgent) and self.agent.output_schema:
tool_result = self.agent.output_schema.model_validate_json(
merged_text
Expand Down
58 changes: 58 additions & 0 deletions tests/unittests/tools/test_google_search_agent_tool_repro.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from google.adk.agents.invocation_context import InvocationContext
from google.adk.agents.llm_agent import Agent
from google.adk.models.llm_response import LlmResponse
from google.adk.sessions.in_memory_session_service import InMemorySessionService
from google.adk.tools.google_search_agent_tool import GoogleSearchAgentTool
from google.adk.tools.tool_context import ToolContext
from google.genai import types
from google.genai.types import Part
from pytest import mark

from .. import testing_utils

function_call_no_schema = Part.from_function_call(
name='tool_agent', args={'request': 'test1'}
)


@mark.asyncio
async def test_run_async_handles_none_parts_in_response():
"""Verify run_async handles None parts in the response without crashing."""

# Mock model for the tool_agent that returns content with parts=None
# This simulates the condition causing the TypeError
tool_agent_model = testing_utils.MockModel.create(
responses=[
LlmResponse(
content=types.Content(parts=None),
)
]
)

tool_agent = Agent(
name='tool_agent',
model=tool_agent_model,
)

agent_tool = GoogleSearchAgentTool(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)

# This should not raise TypeError: 'NoneType' object is not iterable
# It should ideally return an empty string or handle it gracefully
tool_result = await agent_tool.run_async(
args=function_call_no_schema.function_call.args, tool_context=tool_context
)

assert tool_result == ''