Skip to content

Commit 246b0fa

Browse files
donggyun112claude
andcommitted
refactor: address review feedback - simplify response handling and extract test fixture
- Remove redundant single-item special case in function_response_events handling - Extract common auto-healing test setup into pytest fixture Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 5436a13 commit 246b0fa

2 files changed

Lines changed: 34 additions & 46 deletions

File tree

src/google/adk/flows/llm_flows/contents.py

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -159,16 +159,11 @@ def _rearrange_events_for_async_function_responses_in_history(
159159
if not function_response_events_indices and not orphaned_calls:
160160
continue
161161
if function_response_events_indices:
162-
if len(function_response_events_indices) == 1:
163-
result_events.append(
164-
events[next(iter(function_response_events_indices))]
165-
)
166-
else: # Merge all async function_response as one response event
167-
result_events.append(
168-
_merge_function_response_events(
169-
[events[i] for i in sorted(function_response_events_indices)]
170-
)
171-
)
162+
result_events.append(
163+
_merge_function_response_events(
164+
[events[i] for i in sorted(function_response_events_indices)]
165+
)
166+
)
172167
# Inject synthetic responses for orphaned calls (issue #3971)
173168
if orphaned_calls:
174169
result_events.append(

tests/unittests/flows/llm_flows/test_contents_function.py

Lines changed: 29 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,17 @@
2626
from ... import testing_utils
2727

2828

29+
@pytest.fixture
30+
async def healing_test_setup():
31+
"""Provides a common setup for auto-healing tests."""
32+
agent = Agent(model="gemini-2.5-flash", name="test_agent")
33+
llm_request = LlmRequest(model="gemini-2.5-flash")
34+
invocation_context = await testing_utils.create_invocation_context(
35+
agent=agent
36+
)
37+
return agent, llm_request, invocation_context
38+
39+
2940
@pytest.mark.asyncio
3041
async def test_basic_function_call_response_processing():
3142
"""Test basic function call/response processing without rearrangement."""
@@ -595,7 +606,7 @@ async def test_error_when_function_response_without_matching_call():
595606

596607

597608
@pytest.mark.asyncio
598-
async def test_auto_healing_single_orphaned_function_call():
609+
async def test_auto_healing_single_orphaned_function_call(healing_test_setup):
599610
"""Test auto-healing injects synthetic response for orphaned function call.
600611
601612
When a session is interrupted after a function call but before the response
@@ -607,11 +618,7 @@ async def test_auto_healing_single_orphaned_function_call():
607618
- Synthetic error responses are injected with correct format
608619
- Session can continue without crashing
609620
"""
610-
agent = Agent(model="gemini-2.5-flash", name="test_agent")
611-
llm_request = LlmRequest(model="gemini-2.5-flash")
612-
invocation_context = await testing_utils.create_invocation_context(
613-
agent=agent
614-
)
621+
agent, llm_request, invocation_context = healing_test_setup
615622

616623
orphaned_call = types.FunctionCall(
617624
id="orphaned_123", name="get_weather", args={"location": "Seoul"}
@@ -652,13 +659,11 @@ async def test_auto_healing_single_orphaned_function_call():
652659

653660

654661
@pytest.mark.asyncio
655-
async def test_auto_healing_multiple_orphaned_function_calls():
662+
async def test_auto_healing_multiple_orphaned_function_calls(
663+
healing_test_setup,
664+
):
656665
"""Test auto-healing handles multiple orphaned function calls in one event."""
657-
agent = Agent(model="gemini-2.5-flash", name="test_agent")
658-
llm_request = LlmRequest(model="gemini-2.5-flash")
659-
invocation_context = await testing_utils.create_invocation_context(
660-
agent=agent
661-
)
666+
agent, llm_request, invocation_context = healing_test_setup
662667

663668
orphaned_call_1 = types.FunctionCall(
664669
id="orphaned_1", name="tool_a", args={"arg": "value1"}
@@ -703,17 +708,15 @@ async def test_auto_healing_multiple_orphaned_function_calls():
703708

704709

705710
@pytest.mark.asyncio
706-
async def test_auto_healing_partial_orphaned_function_calls():
711+
async def test_auto_healing_partial_orphaned_function_calls(
712+
healing_test_setup,
713+
):
707714
"""Test auto-healing only heals calls without responses.
708715
709716
When some function calls have responses and others don't, only the orphaned
710717
ones should receive synthetic responses.
711718
"""
712-
agent = Agent(model="gemini-2.5-flash", name="test_agent")
713-
llm_request = LlmRequest(model="gemini-2.5-flash")
714-
invocation_context = await testing_utils.create_invocation_context(
715-
agent=agent
716-
)
719+
agent, llm_request, invocation_context = healing_test_setup
717720

718721
completed_call = types.FunctionCall(
719722
id="completed_123", name="tool_complete", args={}
@@ -775,13 +778,11 @@ async def test_auto_healing_partial_orphaned_function_calls():
775778

776779

777780
@pytest.mark.asyncio
778-
async def test_auto_healing_no_healing_when_responses_exist():
781+
async def test_auto_healing_no_healing_when_responses_exist(
782+
healing_test_setup,
783+
):
779784
"""Test that no healing occurs when all function calls have responses."""
780-
agent = Agent(model="gemini-2.5-flash", name="test_agent")
781-
llm_request = LlmRequest(model="gemini-2.5-flash")
782-
invocation_context = await testing_utils.create_invocation_context(
783-
agent=agent
784-
)
785+
agent, llm_request, invocation_context = healing_test_setup
785786

786787
function_call = types.FunctionCall(
787788
id="complete_call", name="search_tool", args={"query": "test"}
@@ -828,13 +829,9 @@ async def test_auto_healing_no_healing_when_responses_exist():
828829

829830

830831
@pytest.mark.asyncio
831-
async def test_auto_healing_logs_warning(caplog):
832+
async def test_auto_healing_logs_warning(healing_test_setup, caplog):
832833
"""Test that auto-healing logs a warning for each orphaned call."""
833-
agent = Agent(model="gemini-2.5-flash", name="test_agent")
834-
llm_request = LlmRequest(model="gemini-2.5-flash")
835-
invocation_context = await testing_utils.create_invocation_context(
836-
agent=agent
837-
)
834+
agent, llm_request, invocation_context = healing_test_setup
838835

839836
orphaned_call = types.FunctionCall(
840837
id="log_test_123", name="test_tool", args={}
@@ -870,18 +867,14 @@ async def test_auto_healing_logs_warning(caplog):
870867

871868

872869
@pytest.mark.asyncio
873-
async def test_long_running_tool_not_detected_as_orphaned():
870+
async def test_long_running_tool_not_detected_as_orphaned(healing_test_setup):
874871
"""Test that long-running tool calls are NOT treated as orphaned.
875872
876873
Long-running tools (e.g., human-in-the-loop) intentionally don't produce
877874
immediate function_response events. They should be excluded from orphaned
878875
call detection.
879876
"""
880-
agent = Agent(model="gemini-2.5-flash", name="test_agent")
881-
llm_request = LlmRequest(model="gemini-2.5-flash")
882-
invocation_context = await testing_utils.create_invocation_context(
883-
agent=agent
884-
)
877+
agent, llm_request, invocation_context = healing_test_setup
885878

886879
long_running_call = types.FunctionCall(
887880
id="long_running_123", name="request_human_approval", args={}

0 commit comments

Comments
 (0)