test: Make testing_utils.InMemoryRunner support ADK App and add utils for extracting event contents for testing resumability

PiperOrigin-RevId: 811933527
This commit is contained in:
Xinran (Sherry) Tang
2025-09-26 13:22:11 -07:00
committed by Copybara-Service
parent e172811bc7
commit 28d44a365a
+52 -4
View File
@@ -16,6 +16,7 @@ import asyncio
import contextlib import contextlib
from typing import AsyncGenerator from typing import AsyncGenerator
from typing import Generator from typing import Generator
from typing import Optional
from typing import Union from typing import Union
from google.adk.agents.invocation_context import InvocationContext from google.adk.agents.invocation_context import InvocationContext
@@ -23,6 +24,7 @@ from google.adk.agents.live_request_queue import LiveRequestQueue
from google.adk.agents.llm_agent import Agent from google.adk.agents.llm_agent import Agent
from google.adk.agents.llm_agent import LlmAgent from google.adk.agents.llm_agent import LlmAgent
from google.adk.agents.run_config import RunConfig from google.adk.agents.run_config import RunConfig
from google.adk.apps.app import App
from google.adk.artifacts.in_memory_artifact_service import InMemoryArtifactService from google.adk.artifacts.in_memory_artifact_service import InMemoryArtifactService
from google.adk.events.event import Event from google.adk.events.event import Event
from google.adk.memory.in_memory_memory_service import InMemoryMemoryService from google.adk.memory.in_memory_memory_service import InMemoryMemoryService
@@ -119,7 +121,32 @@ def append_user_content(
# Extracts the contents from the events and transform them into a list of # Extracts the contents from the events and transform them into a list of
# (author, simplified_content) tuples. # (author, simplified_content) tuples.
def simplify_events(events: list[Event]) -> list[(str, types.Part)]: def simplify_events(events: list[Event]) -> list[(str, types.Part)]:
return [(event.author, simplify_content(event.content)) for event in events] return [
(event.author, simplify_content(event.content))
for event in events
if event.content
]
END_OF_AGENT = 'end_of_agent'
# Extracts the contents from the events and transform them into a list of
# (author, simplified_content OR AgentState OR "end_of_agent") tuples.
#
# Could be used to compare events for testing resumability.
def simplify_resumable_app_events(
events: list[Event],
) -> list[(str, Union[types.Part, str])]:
results = []
for event in events:
if event.content:
results.append((event.author, simplify_content(event.content)))
elif event.actions.end_of_agent:
results.append((event.author, END_OF_AGENT))
elif event.actions.agent_state is not None:
results.append((event.author, event.actions.agent_state))
return results
# Simplifies the contents into a list of (author, simplified_content) tuples. # Simplifies the contents into a list of (author, simplified_content) tuples.
@@ -189,10 +216,22 @@ class InMemoryRunner:
def __init__( def __init__(
self, self,
root_agent: Union[Agent, LlmAgent], root_agent: Optional[Union[Agent, LlmAgent]] = None,
response_modalities: list[str] = None, response_modalities: list[str] = None,
plugins: list[BasePlugin] = [], plugins: list[BasePlugin] = [],
app: Optional[App] = None,
): ):
"""Initializes the InMemoryRunner.
Args:
root_agent: The root agent to run, won't be used if app is provided.
response_modalities: The response modalities of the runner.
plugins: The plugins to use in the runner, won't be used if app is
provided.
app: The app to use in the runner.
"""
if not app:
self.app_name = 'test_app'
self.root_agent = root_agent self.root_agent = root_agent
self.runner = Runner( self.runner = Runner(
app_name='test_app', app_name='test_app',
@@ -202,18 +241,27 @@ class InMemoryRunner:
memory_service=InMemoryMemoryService(), memory_service=InMemoryMemoryService(),
plugins=plugins, plugins=plugins,
) )
else:
self.app_name = app.name
self.root_agent = app.root_agent
self.runner = Runner(
app=app,
artifact_service=InMemoryArtifactService(),
session_service=InMemorySessionService(),
memory_service=InMemoryMemoryService(),
)
self.session_id = None self.session_id = None
@property @property
def session(self) -> Session: def session(self) -> Session:
if not self.session_id: if not self.session_id:
session = self.runner.session_service.create_session_sync( session = self.runner.session_service.create_session_sync(
app_name='test_app', user_id='test_user' app_name=self.app_name, user_id='test_user'
) )
self.session_id = session.id self.session_id = session.id
return session return session
return self.runner.session_service.get_session_sync( return self.runner.session_service.get_session_sync(
app_name='test_app', user_id='test_user', session_id=self.session_id app_name=self.app_name, user_id='test_user', session_id=self.session_id
) )
def run(self, new_message: types.ContentUnion) -> list[Event]: def run(self, new_message: types.ContentUnion) -> list[Event]: