mirror of
https://github.com/encounter/adk-python.git
synced 2026-07-09 18:19:28 -07:00
fix: Update empty event check to include executable code and execution results
Close #3859 Close #3921 Co-authored-by: George Weale <gweale@google.com> PiperOrigin-RevId: 852375447
This commit is contained in:
committed by
Copybara-Service
parent
6f259f08b3
commit
688f48fffb
@@ -572,6 +572,38 @@ async def test_events_with_empty_content_are_skipped():
|
||||
role="user",
|
||||
),
|
||||
),
|
||||
# Event with content that has executable code part
|
||||
Event(
|
||||
invocation_id="inv10",
|
||||
author="test_agent",
|
||||
content=types.Content(
|
||||
parts=[
|
||||
types.Part(
|
||||
executable_code=types.ExecutableCode(
|
||||
code="print('hello')",
|
||||
language="PYTHON",
|
||||
)
|
||||
)
|
||||
],
|
||||
role="model",
|
||||
),
|
||||
),
|
||||
# Event with content that has code execution result part
|
||||
Event(
|
||||
invocation_id="inv11",
|
||||
author="test_agent",
|
||||
content=types.Content(
|
||||
parts=[
|
||||
types.Part(
|
||||
code_execution_result=types.CodeExecutionResult(
|
||||
outcome="OUTCOME_OK",
|
||||
output="hello",
|
||||
)
|
||||
)
|
||||
],
|
||||
role="model",
|
||||
),
|
||||
),
|
||||
]
|
||||
invocation_context.session.events = events
|
||||
|
||||
@@ -608,4 +640,153 @@ async def test_events_with_empty_content_are_skipped():
|
||||
parts=[types.Part(text=""), types.Part(text="Mixed content")],
|
||||
role="user",
|
||||
),
|
||||
types.Content(
|
||||
parts=[
|
||||
types.Part(
|
||||
executable_code=types.ExecutableCode(
|
||||
code="print('hello')",
|
||||
language="PYTHON",
|
||||
)
|
||||
)
|
||||
],
|
||||
role="model",
|
||||
),
|
||||
types.Content(
|
||||
parts=[
|
||||
types.Part(
|
||||
code_execution_result=types.CodeExecutionResult(
|
||||
outcome="OUTCOME_OK",
|
||||
output="hello",
|
||||
)
|
||||
)
|
||||
],
|
||||
role="model",
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_code_execution_result_events_are_not_skipped():
|
||||
"""Test that events with code execution result are not skipped.
|
||||
|
||||
This is a regression test for the endless loop bug where code executor
|
||||
outputs were not passed to the LLM because the events were incorrectly
|
||||
filtered as empty.
|
||||
"""
|
||||
agent = Agent(model="gemini-2.5-flash", name="test_agent")
|
||||
llm_request = LlmRequest(model="gemini-2.5-flash")
|
||||
invocation_context = await testing_utils.create_invocation_context(
|
||||
agent=agent
|
||||
)
|
||||
|
||||
events = [
|
||||
Event(
|
||||
invocation_id="inv1",
|
||||
author="user",
|
||||
content=types.UserContent("Write code to calculate factorial"),
|
||||
),
|
||||
# Model generates code
|
||||
Event(
|
||||
invocation_id="inv2",
|
||||
author="test_agent",
|
||||
content=types.Content(
|
||||
parts=[
|
||||
types.Part(text="Here's the code:"),
|
||||
types.Part(
|
||||
executable_code=types.ExecutableCode(
|
||||
code=(
|
||||
"def factorial(n):\n return 1 if n <= 1 else n *"
|
||||
" factorial(n-1)\nprint(factorial(5))"
|
||||
),
|
||||
language="PYTHON",
|
||||
)
|
||||
),
|
||||
],
|
||||
role="model",
|
||||
),
|
||||
),
|
||||
# Code execution result
|
||||
Event(
|
||||
invocation_id="inv3",
|
||||
author="test_agent",
|
||||
content=types.Content(
|
||||
parts=[
|
||||
types.Part(
|
||||
code_execution_result=types.CodeExecutionResult(
|
||||
outcome="OUTCOME_OK",
|
||||
output="120",
|
||||
)
|
||||
)
|
||||
],
|
||||
role="model",
|
||||
),
|
||||
),
|
||||
]
|
||||
invocation_context.session.events = events
|
||||
|
||||
# Process the request
|
||||
async for _ in contents.request_processor.run_async(
|
||||
invocation_context, llm_request
|
||||
):
|
||||
pass
|
||||
|
||||
# Verify all three events are included, especially the code execution result
|
||||
assert len(llm_request.contents) == 3
|
||||
assert llm_request.contents[0] == types.UserContent(
|
||||
"Write code to calculate factorial"
|
||||
)
|
||||
# Second event has executable code
|
||||
assert llm_request.contents[1].parts[1].executable_code is not None
|
||||
# Third event has code execution result - this was the bug!
|
||||
assert llm_request.contents[2].parts[0].code_execution_result is not None
|
||||
assert llm_request.contents[2].parts[0].code_execution_result.output == "120"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_code_execution_result_not_in_first_part_is_not_skipped():
|
||||
"""Test that code execution results aren't skipped.
|
||||
|
||||
This covers results that appear in a non-first part.
|
||||
"""
|
||||
agent = Agent(model="gemini-2.5-flash", name="test_agent")
|
||||
llm_request = LlmRequest(model="gemini-2.5-flash")
|
||||
invocation_context = await testing_utils.create_invocation_context(
|
||||
agent=agent
|
||||
)
|
||||
|
||||
events = [
|
||||
Event(
|
||||
invocation_id="inv1",
|
||||
author="user",
|
||||
content=types.UserContent("Run some code."),
|
||||
),
|
||||
Event(
|
||||
invocation_id="inv2",
|
||||
author="test_agent",
|
||||
content=types.Content(
|
||||
parts=[
|
||||
types.Part(text=""),
|
||||
types.Part(
|
||||
code_execution_result=types.CodeExecutionResult(
|
||||
outcome="OUTCOME_OK",
|
||||
output="42",
|
||||
)
|
||||
),
|
||||
],
|
||||
role="model",
|
||||
),
|
||||
),
|
||||
]
|
||||
invocation_context.session.events = events
|
||||
|
||||
async for _ in contents.request_processor.run_async(
|
||||
invocation_context, llm_request
|
||||
):
|
||||
pass
|
||||
|
||||
assert len(llm_request.contents) == 2
|
||||
assert any(
|
||||
part.code_execution_result is not None
|
||||
and part.code_execution_result.output == "42"
|
||||
for part in llm_request.contents[1].parts
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user