mirror of
https://github.com/encounter/adk-python.git
synced 2026-07-09 18:19:28 -07:00
chore: Add unit tests for contents.py
PiperOrigin-RevId: 775713101
This commit is contained in:
committed by
Copybara-Service
parent
6729edd08e
commit
f54b9b6ad1
@@ -0,0 +1,361 @@
|
||||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from google.adk.agents import Agent
|
||||
from google.adk.events.event import Event
|
||||
from google.adk.flows.llm_flows import contents
|
||||
from google.adk.flows.llm_flows.contents import _convert_foreign_event
|
||||
from google.adk.flows.llm_flows.contents import _get_contents
|
||||
from google.adk.flows.llm_flows.contents import _merge_function_response_events
|
||||
from google.adk.flows.llm_flows.contents import _rearrange_events_for_async_function_responses_in_history
|
||||
from google.adk.flows.llm_flows.contents import _rearrange_events_for_latest_function_response
|
||||
from google.adk.models import LlmRequest
|
||||
from google.genai import types
|
||||
import pytest
|
||||
|
||||
from ... import testing_utils
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_content_processor_no_contents():
|
||||
"""Test ContentLlmRequestProcessor when include_contents is 'none'."""
|
||||
agent = Agent(model="gemini-1.5-flash", name="agent", include_contents="none")
|
||||
llm_request = LlmRequest(model="gemini-1.5-flash")
|
||||
invocation_context = await testing_utils.create_invocation_context(
|
||||
agent=agent
|
||||
)
|
||||
|
||||
# Collect events from async generator
|
||||
events = []
|
||||
async for event in contents.request_processor.run_async(
|
||||
invocation_context, llm_request
|
||||
):
|
||||
events.append(event)
|
||||
|
||||
# Should not yield any events
|
||||
assert len(events) == 0
|
||||
# Contents should not be set when include_contents is 'none'
|
||||
assert llm_request.contents == []
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_content_processor_with_contents():
|
||||
"""Test ContentLlmRequestProcessor when include_contents is not 'none'."""
|
||||
agent = Agent(model="gemini-1.5-flash", name="agent")
|
||||
llm_request = LlmRequest(model="gemini-1.5-flash")
|
||||
invocation_context = await testing_utils.create_invocation_context(
|
||||
agent=agent
|
||||
)
|
||||
|
||||
# Add some test events to the session
|
||||
test_event = Event(
|
||||
invocation_id="test_inv",
|
||||
author="user",
|
||||
content=types.Content(
|
||||
role="user", parts=[types.Part.from_text(text="Hello")]
|
||||
),
|
||||
)
|
||||
invocation_context.session.events = [test_event]
|
||||
|
||||
# Collect events from async generator
|
||||
events = []
|
||||
async for event in contents.request_processor.run_async(
|
||||
invocation_context, llm_request
|
||||
):
|
||||
events.append(event)
|
||||
|
||||
# Should not yield any events (processor doesn't emit events, just modifies request)
|
||||
assert len(events) == 0
|
||||
# Contents should be set
|
||||
assert llm_request.contents is not None
|
||||
assert len(llm_request.contents) == 1
|
||||
assert llm_request.contents[0].role == "user"
|
||||
assert llm_request.contents[0].parts[0].text == "Hello"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_content_processor_non_llm_agent():
|
||||
"""Test ContentLlmRequestProcessor with non-LLM agent."""
|
||||
from google.adk.agents.base_agent import BaseAgent
|
||||
|
||||
# Create a base agent (not LLM agent)
|
||||
agent = BaseAgent(name="base_agent")
|
||||
llm_request = LlmRequest(model="gemini-1.5-flash")
|
||||
invocation_context = await testing_utils.create_invocation_context(
|
||||
agent=agent
|
||||
)
|
||||
|
||||
# Collect events from async generator
|
||||
events = []
|
||||
async for event in contents.request_processor.run_async(
|
||||
invocation_context, llm_request
|
||||
):
|
||||
events.append(event)
|
||||
|
||||
# Should not yield any events and not modify request
|
||||
assert len(events) == 0
|
||||
assert llm_request.contents == []
|
||||
|
||||
|
||||
def test_get_contents_empty_events():
|
||||
"""Test _get_contents with empty events list."""
|
||||
contents_result = _get_contents(None, [], "test_agent")
|
||||
assert contents_result == []
|
||||
|
||||
|
||||
def test_get_contents_with_events():
|
||||
"""Test _get_contents with valid events."""
|
||||
test_event = Event(
|
||||
invocation_id="test_inv",
|
||||
author="user",
|
||||
content=types.Content(
|
||||
role="user", parts=[types.Part.from_text(text="Hello")]
|
||||
),
|
||||
)
|
||||
|
||||
contents_result = _get_contents(None, [test_event], "test_agent")
|
||||
assert len(contents_result) == 1
|
||||
assert contents_result[0].role == "user"
|
||||
assert contents_result[0].parts[0].text == "Hello"
|
||||
|
||||
|
||||
def test_get_contents_filters_empty_events():
|
||||
"""Test _get_contents filters out events with empty content."""
|
||||
# Event with empty text
|
||||
empty_event = Event(
|
||||
invocation_id="test_inv",
|
||||
author="user",
|
||||
content=types.Content(role="user", parts=[types.Part.from_text(text="")]),
|
||||
)
|
||||
|
||||
# Event without content
|
||||
no_content_event = Event(
|
||||
invocation_id="test_inv",
|
||||
author="user",
|
||||
)
|
||||
|
||||
# Valid event
|
||||
valid_event = Event(
|
||||
invocation_id="test_inv",
|
||||
author="user",
|
||||
content=types.Content(
|
||||
role="user", parts=[types.Part.from_text(text="Hello")]
|
||||
),
|
||||
)
|
||||
|
||||
contents_result = _get_contents(
|
||||
None, [empty_event, no_content_event, valid_event], "test_agent"
|
||||
)
|
||||
assert len(contents_result) == 1
|
||||
assert contents_result[0].role == "user"
|
||||
assert contents_result[0].parts[0].text == "Hello"
|
||||
|
||||
|
||||
def test_convert_foreign_event():
|
||||
"""Test _convert_foreign_event function."""
|
||||
agent_event = Event(
|
||||
invocation_id="test_inv",
|
||||
author="agent1",
|
||||
content=types.Content(
|
||||
role="model", parts=[types.Part.from_text(text="Agent response")]
|
||||
),
|
||||
)
|
||||
|
||||
converted_event = _convert_foreign_event(agent_event)
|
||||
|
||||
assert converted_event.author == "user"
|
||||
assert converted_event.content.role == "user"
|
||||
assert len(converted_event.content.parts) == 2
|
||||
assert converted_event.content.parts[0].text == "For context:"
|
||||
assert (
|
||||
"[agent1] said: Agent response" in converted_event.content.parts[1].text
|
||||
)
|
||||
|
||||
|
||||
def test_convert_event_with_function_call():
|
||||
"""Test _convert_foreign_event with function call."""
|
||||
function_call = types.FunctionCall(
|
||||
id="func_123", name="test_function", args={"param": "value"}
|
||||
)
|
||||
|
||||
agent_event = Event(
|
||||
invocation_id="test_inv",
|
||||
author="agent1",
|
||||
content=types.Content(
|
||||
role="model", parts=[types.Part(function_call=function_call)]
|
||||
),
|
||||
)
|
||||
|
||||
converted_event = _convert_foreign_event(agent_event)
|
||||
|
||||
assert converted_event.author == "user"
|
||||
assert converted_event.content.role == "user"
|
||||
assert len(converted_event.content.parts) == 2
|
||||
assert converted_event.content.parts[0].text == "For context:"
|
||||
assert (
|
||||
"[agent1] called tool `test_function`"
|
||||
in converted_event.content.parts[1].text
|
||||
)
|
||||
assert "{'param': 'value'}" in converted_event.content.parts[1].text
|
||||
|
||||
|
||||
def test_convert_event_with_function_response():
|
||||
"""Test _convert_foreign_event with function response."""
|
||||
function_response = types.FunctionResponse(
|
||||
id="func_123", name="test_function", response={"result": "success"}
|
||||
)
|
||||
|
||||
agent_event = Event(
|
||||
invocation_id="test_inv",
|
||||
author="agent1",
|
||||
content=types.Content(
|
||||
role="user", parts=[types.Part(function_response=function_response)]
|
||||
),
|
||||
)
|
||||
|
||||
converted_event = _convert_foreign_event(agent_event)
|
||||
|
||||
assert converted_event.author == "user"
|
||||
assert converted_event.content.role == "user"
|
||||
assert len(converted_event.content.parts) == 2
|
||||
assert converted_event.content.parts[0].text == "For context:"
|
||||
assert (
|
||||
"[agent1] `test_function` tool returned result:"
|
||||
in converted_event.content.parts[1].text
|
||||
)
|
||||
assert "{'result': 'success'}" in converted_event.content.parts[1].text
|
||||
|
||||
|
||||
def test_merge_function_response_events():
|
||||
"""Test _merge_function_response_events function."""
|
||||
# Create initial function response event
|
||||
function_response1 = types.FunctionResponse(
|
||||
id="func_123", name="test_function", response={"status": "pending"}
|
||||
)
|
||||
|
||||
initial_event = Event(
|
||||
invocation_id="test_inv",
|
||||
author="user",
|
||||
content=types.Content(
|
||||
role="user", parts=[types.Part(function_response=function_response1)]
|
||||
),
|
||||
)
|
||||
|
||||
# Create final function response event
|
||||
function_response2 = types.FunctionResponse(
|
||||
id="func_123", name="test_function", response={"result": "success"}
|
||||
)
|
||||
|
||||
final_event = Event(
|
||||
invocation_id="test_inv2",
|
||||
author="user",
|
||||
content=types.Content(
|
||||
role="user", parts=[types.Part(function_response=function_response2)]
|
||||
),
|
||||
)
|
||||
|
||||
merged_event = _merge_function_response_events([initial_event, final_event])
|
||||
|
||||
assert (
|
||||
merged_event.invocation_id == "test_inv"
|
||||
) # Should keep initial event ID
|
||||
assert len(merged_event.content.parts) == 1
|
||||
# The first part should be replaced with the final response
|
||||
assert merged_event.content.parts[0].function_response.response == {
|
||||
"result": "success"
|
||||
}
|
||||
|
||||
|
||||
def test_rearrange_events_for_async_function_responses():
|
||||
"""Test _rearrange_events_for_async_function_responses_in_history function."""
|
||||
# Create function call event
|
||||
function_call = types.FunctionCall(
|
||||
id="func_123", name="test_function", args={"param": "value"}
|
||||
)
|
||||
|
||||
call_event = Event(
|
||||
invocation_id="test_inv1",
|
||||
author="agent",
|
||||
content=types.Content(
|
||||
role="model", parts=[types.Part(function_call=function_call)]
|
||||
),
|
||||
)
|
||||
|
||||
# Create function response event
|
||||
function_response = types.FunctionResponse(
|
||||
id="func_123", name="test_function", response={"result": "success"}
|
||||
)
|
||||
|
||||
response_event = Event(
|
||||
invocation_id="test_inv2",
|
||||
author="user",
|
||||
content=types.Content(
|
||||
role="user", parts=[types.Part(function_response=function_response)]
|
||||
),
|
||||
)
|
||||
|
||||
# Test rearrangement
|
||||
events = [call_event, response_event]
|
||||
rearranged = _rearrange_events_for_async_function_responses_in_history(events)
|
||||
|
||||
# Should have both events in correct order
|
||||
assert len(rearranged) == 2
|
||||
assert rearranged[0] == call_event
|
||||
assert rearranged[1] == response_event
|
||||
|
||||
|
||||
def test_rearrange_events_for_latest_function_response():
|
||||
"""Test _rearrange_events_for_latest_function_response function."""
|
||||
# Create function call event
|
||||
function_call = types.FunctionCall(
|
||||
id="func_123", name="test_function", args={"param": "value"}
|
||||
)
|
||||
|
||||
call_event = Event(
|
||||
invocation_id="test_inv1",
|
||||
author="agent",
|
||||
content=types.Content(
|
||||
role="model", parts=[types.Part(function_call=function_call)]
|
||||
),
|
||||
)
|
||||
|
||||
# Create intermediate event
|
||||
intermediate_event = Event(
|
||||
invocation_id="test_inv2",
|
||||
author="agent",
|
||||
content=types.Content(
|
||||
role="model", parts=[types.Part.from_text(text="Processing...")]
|
||||
),
|
||||
)
|
||||
|
||||
# Create function response event
|
||||
function_response = types.FunctionResponse(
|
||||
id="func_123", name="test_function", response={"result": "success"}
|
||||
)
|
||||
|
||||
response_event = Event(
|
||||
invocation_id="test_inv3",
|
||||
author="user",
|
||||
content=types.Content(
|
||||
role="user", parts=[types.Part(function_response=function_response)]
|
||||
),
|
||||
)
|
||||
|
||||
# Test with matching function call and response
|
||||
events = [call_event, intermediate_event, response_event]
|
||||
rearranged = _rearrange_events_for_latest_function_response(events)
|
||||
|
||||
# Should remove intermediate events and merge responses
|
||||
assert len(rearranged) == 2
|
||||
assert rearranged[0] == call_event
|
||||
Reference in New Issue
Block a user