Skip to content

Commit ce8febc

Browse files
authored
Merge branch 'main' into spelling-issue-template
2 parents fbd5dfc + e02f177 commit ce8febc

File tree

13 files changed

+231
-61
lines changed

13 files changed

+231
-61
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@
7979
* Fix issue with MCP tools throwing an error ([1a4261a](https://github.com/google/adk-python/commit/1a4261ad4b66cdeb39d39110a086bd6112b17516))
8080
* Remove redundant `format` field from LiteLLM content objects ([489c39d](https://github.com/google/adk-python/commit/489c39db01465e38ecbc2c7f32781c349b8cddc9))
8181
* Update the contribution analysis tool to use original write mode ([54db3d4](https://github.com/google/adk-python/commit/54db3d4434e0706b83a589fa2499d11d439a6e4e))
82+
* Fix agent evaluations detailed output rows wrapping issue([4284c61](https://github.com/google/adk-python/commit/4284c619010b8246c1ecaa011f14b6cc9de512dd))
83+
* Update dependency version constraints to be based on PyPI versions([0b1784e](https://github.com/google/adk-python/commit/0b1784e0e493a0e2df1edfe37e5ed5f4247e7d9d))
8284

8385
### Improvements
8486

@@ -97,6 +99,7 @@
9799
* Add sample agent for VertexAiCodeExecutor ([edfe553](https://github.com/google/adk-python/commit/edfe5539421d196ca4da14d3a37fac7b598f8c8d))
98100
* Adds a new sample agent that demonstrates how to integrate PostgreSQL databases using the Model Context Protocol (MCP) ([45a2168](https://github.com/google/adk-python/commit/45a2168e0e6773e595ecfb825d7e4ab0a38c3a38))
99101
* Add example for using ADK with Fast MCP sampling ([d3796f9](https://github.com/google/adk-python/commit/d3796f9b33251d28d05e6701f11e80f02a2a49e1))
102+
* Refactor gepa sample code and clean-up user demo colab([63353b2](https://github.com/google/adk-python/commit/63353b2b74e23e97385892415c5a3f2a59c3504f))
100103

101104
## [1.17.0](https://github.com/google/adk-python/compare/v1.16.0...v1.17.0) (2025-10-22)
102105

contributing/samples/gepa/adk_agent.py

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -263,30 +263,35 @@ def run_environment_loop(
263263
),
264264
],
265265
)
266-
runner = runners.InMemoryRunner(
267-
agent=agent,
268-
app_name='eval_app',
269-
plugins=plugins,
270-
)
271-
session = asyncio.run(
272-
runner.session_service.create_session(
273-
app_name='eval_app', user_id='eval_user'
274-
)
275-
)
276-
env_reset_res = env.reset(task_index=task_index)
277-
initial_message = types.Content(
278-
role='user', parts=[types.Part(text=env_reset_res.observation)]
279-
)
280-
# The initial message is generated by the environment `reset` within the
281-
# implementation of this function - as the first step of the trace.
282-
# We yield this first step to ensure we provide a full trace to the user.
283-
yield event_lib.Event(
284-
author='user',
285-
content=initial_message,
286-
)
287-
for event in runner.run(
288-
user_id=session.user_id,
289-
session_id=session.id,
290-
new_message=initial_message,
291-
):
292-
yield event
266+
267+
async def _async_run():
268+
runner = runners.InMemoryRunner(
269+
agent=agent,
270+
app_name='eval_app',
271+
plugins=plugins,
272+
)
273+
session = await runner.session_service.create_session(
274+
app_name='eval_app', user_id='eval_user'
275+
)
276+
env_reset_res = env.reset(task_index=task_index)
277+
initial_message = types.Content(
278+
role='user', parts=[types.Part(text=env_reset_res.observation)]
279+
)
280+
# The initial message is generated by the environment `reset` within the
281+
# implementation of this function - as the first step of the trace.
282+
# We yield this first step to ensure we provide a full trace to the user.
283+
events = [
284+
event_lib.Event(
285+
author='user',
286+
content=initial_message,
287+
)
288+
]
289+
async for event in runner.run_async(
290+
user_id=session.user_id,
291+
session_id=session.id,
292+
new_message=initial_message,
293+
):
294+
events.append(event)
295+
return events
296+
297+
return asyncio.run(_async_run())

contributing/samples/gepa/adk_agent_test.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -324,24 +324,25 @@ async def _run_async_impl(self, ctx):
324324
async def _mock_create_session(*args, **kwargs):
325325
del args, kwargs
326326
await asyncio.sleep(0.1)
327-
return
327+
mock_session = mock.Mock()
328+
mock.user_id = "fake-user=id"
329+
mock.id = "fake-session-id"
330+
return mock_session
328331

329332
with mock.patch.object(runners, "InMemoryRunner") as mock_runner_cls:
330333
mock_runner = mock_runner_cls.return_value
331334
mock_runner.session_service.create_session.side_effect = (
332335
_mock_create_session
333336
)
334337
mock_runner.run.return_value = []
335-
next(
336-
adk_agent.run_environment_loop(
337-
instruction="some-instruction",
338-
env=_TestEnv([]),
339-
temperature=0.123,
340-
tools=[],
341-
task_index=0,
342-
agent_model="some-test-model",
343-
plugins=[_TestPlugin([])],
344-
)
338+
adk_agent.run_environment_loop(
339+
instruction="some-instruction",
340+
env=_TestEnv([]),
341+
temperature=0.123,
342+
tools=[],
343+
task_index=0,
344+
agent_model="some-test-model",
345+
plugins=[_TestPlugin([])],
345346
)
346347
mock_runner_cls.assert_called_once()
347348
_, runner_kwargs = mock_runner_cls.call_args

contributing/samples/gepa/gepa_tau_bench.ipynb

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,21 +31,53 @@
3131
"execution_count": null,
3232
"metadata": {
3333
"id": "GqUHYdvRJ7pt",
34-
"language": "python"
34+
"language": "python",
35+
"cellView": "form"
3536
},
3637
"outputs": [],
3738
"source": [
3839
"#@title Install Tau-bench and GEPA\n",
40+
"!git clone https://github.com/google/adk-python.git\n",
3941
"!git clone https://github.com/sierra-research/tau-bench.git\n",
4042
"%cd tau-bench/\n",
4143
"!pip install -e . --quiet\n",
4244
"\n",
4345
"%cd ..\n",
4446
"!pip install gepa --quiet\n",
4547
"\n",
46-
"!pip install retry --quiet"
48+
"!pip install retry --quiet\n"
4749
]
4850
},
51+
{
52+
"cell_type": "code",
53+
"source": [
54+
"#@title Configure python dependencies\n",
55+
"import sys\n",
56+
"\n",
57+
"sys.path.append('/content/tau-bench')\n",
58+
"sys.path.append('/content/adk-python/contributing/samples/gepa')"
59+
],
60+
"metadata": {
61+
"cellView": "form",
62+
"id": "k0nrsIca0yXr"
63+
},
64+
"execution_count": null,
65+
"outputs": []
66+
},
67+
{
68+
"cell_type": "code",
69+
"source": [
70+
"#@title Authentication\n",
71+
"from google.colab import auth\n",
72+
"auth.authenticate_user()"
73+
],
74+
"metadata": {
75+
"cellView": "form",
76+
"id": "NsXa217t03vL"
77+
},
78+
"execution_count": null,
79+
"outputs": []
80+
},
4981
{
5082
"cell_type": "code",
5183
"execution_count": null,
@@ -55,7 +87,7 @@
5587
},
5688
"outputs": [],
5789
"source": [
58-
"#@title Setup and Authentication\n",
90+
"#@title Setup\n",
5991
"from datetime import datetime\n",
6092
"import json\n",
6193
"import logging\n",

contributing/samples/gepa/rater_lib.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ def format_user_agent_conversation(conv: list[dict[str, Any]]) -> str:
120120
res = (
121121
res
122122
+ 'The execution result from the agent of function'
123-
f' {fc["name"]} is: \n{fc["args"]}\n'
123+
f' {fc["name"]} is: \n{fc["response"]}\n'
124124
)
125125
return res
126126

contributing/samples/live_bidi_streaming_single_agent/agent.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,9 @@ async def check_prime(nums: list[int]) -> str:
6565

6666

6767
root_agent = Agent(
68-
# model='gemini-2.0-flash-live-preview-04-09', # for Vertex project
69-
model='gemini-2.0-flash-live-001', # for AI studio key
68+
model='gemini-live-2.5-flash-preview-native-audio-09-2025', # vertex
69+
# model='gemini-2.5-flash-native-audio-preview-09-2025', # for AI studio
70+
# key
7071
name='roll_dice_agent',
7172
description=(
7273
'hello world agent that can roll a dice of 6 sides and check prime'

src/google/adk/agents/base_agent.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -164,19 +164,16 @@ def _load_agent_state(
164164
ctx: InvocationContext,
165165
state_type: Type[AgentState],
166166
) -> Optional[AgentState]:
167-
"""Loads the agent state from the invocation context, handling resumption.
167+
"""Loads the agent state from the invocation context.
168168
169169
Args:
170170
ctx: The invocation context.
171171
state_type: The type of the agent state.
172172
173173
Returns:
174-
The current state if resuming, otherwise None.
174+
The current state if exists, otherwise None.
175175
"""
176-
if not ctx.is_resumable:
177-
return None
178-
179-
if self.name not in ctx.agent_states:
176+
if ctx.agent_states is None or self.name not in ctx.agent_states:
180177
return None
181178
else:
182179
return state_type.model_validate(ctx.agent_states.get(self.name))

src/google/adk/agents/llm_agent.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,16 @@
8585
list[_SingleAfterModelCallback],
8686
]
8787

88+
_SingleOnModelErrorCallback: TypeAlias = Callable[
89+
[CallbackContext, LlmRequest, Exception],
90+
Union[Awaitable[Optional[LlmResponse]], Optional[LlmResponse]],
91+
]
92+
93+
OnModelErrorCallback: TypeAlias = Union[
94+
_SingleOnModelErrorCallback,
95+
list[_SingleOnModelErrorCallback],
96+
]
97+
8898
_SingleBeforeToolCallback: TypeAlias = Callable[
8999
[BaseTool, dict[str, Any], ToolContext],
90100
Union[Awaitable[Optional[dict]], Optional[dict]],
@@ -364,6 +374,21 @@ class LlmAgent(BaseAgent):
364374
The content to return to the user. When present, the actual model response
365375
will be ignored and the provided content will be returned to user.
366376
"""
377+
on_model_error_callback: Optional[OnModelErrorCallback] = None
378+
"""Callback or list of callbacks to be called when a model call encounters an error.
379+
380+
When a list of callbacks is provided, the callbacks will be called in the
381+
order they are listed until a callback does not return None.
382+
383+
Args:
384+
callback_context: CallbackContext,
385+
llm_request: LlmRequest, The raw model request.
386+
error: The error from the model call.
387+
388+
Returns:
389+
The content to return to the user. When present, the error will be
390+
ignored and the provided content will be returned to user.
391+
"""
367392
before_tool_callback: Optional[BeforeToolCallback] = None
368393
"""Callback or list of callbacks to be called before calling the tool.
369394
@@ -587,6 +612,20 @@ def canonical_after_model_callbacks(self) -> list[_SingleAfterModelCallback]:
587612
return self.after_model_callback
588613
return [self.after_model_callback]
589614

615+
@property
616+
def canonical_on_model_error_callbacks(
617+
self,
618+
) -> list[_SingleOnModelErrorCallback]:
619+
"""The resolved self.on_model_error_callback field as a list of _SingleOnModelErrorCallback.
620+
621+
This method is only for use by Agent Development Kit.
622+
"""
623+
if not self.on_model_error_callback:
624+
return []
625+
if isinstance(self.on_model_error_callback, list):
626+
return self.on_model_error_callback
627+
return [self.on_model_error_callback]
628+
590629
@property
591630
def canonical_before_tool_callbacks(
592631
self,

src/google/adk/agents/remote_a2a_agent.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ def _construct_message_parts_from_session(
372372
if _is_other_agent_reply(self.name, event):
373373
event = _present_other_agent_message(event)
374374

375-
if not event.content or not event.content.parts:
375+
if not event or not event.content or not event.content.parts:
376376
continue
377377

378378
for part in event.content.parts:

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

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -977,6 +977,44 @@ async def _run_and_handle_error(
977977
Yields:
978978
A generator of LlmResponse.
979979
"""
980+
981+
from ...agents.llm_agent import LlmAgent
982+
983+
agent = invocation_context.agent
984+
if not isinstance(agent, LlmAgent):
985+
raise TypeError(
986+
f'Expected agent to be an LlmAgent, but got {type(agent)}'
987+
)
988+
989+
async def _run_on_model_error_callbacks(
990+
*,
991+
callback_context: CallbackContext,
992+
llm_request: LlmRequest,
993+
error: Exception,
994+
) -> Optional[LlmResponse]:
995+
error_response = (
996+
await invocation_context.plugin_manager.run_on_model_error_callback(
997+
callback_context=callback_context,
998+
llm_request=llm_request,
999+
error=error,
1000+
)
1001+
)
1002+
if error_response is not None:
1003+
return error_response
1004+
1005+
for callback in agent.canonical_on_model_error_callbacks:
1006+
error_response = callback(
1007+
callback_context=callback_context,
1008+
llm_request=llm_request,
1009+
error=error,
1010+
)
1011+
if inspect.isawaitable(error_response):
1012+
error_response = await error_response
1013+
if error_response is not None:
1014+
return error_response
1015+
1016+
return None
1017+
9801018
try:
9811019
async with Aclosing(response_generator) as agen:
9821020
async for response in agen:
@@ -985,12 +1023,10 @@ async def _run_and_handle_error(
9851023
callback_context = CallbackContext(
9861024
invocation_context, event_actions=model_response_event.actions
9871025
)
988-
error_response = (
989-
await invocation_context.plugin_manager.run_on_model_error_callback(
990-
callback_context=callback_context,
991-
llm_request=llm_request,
992-
error=model_error,
993-
)
1026+
error_response = await _run_on_model_error_callbacks(
1027+
callback_context=callback_context,
1028+
llm_request=llm_request,
1029+
error=model_error,
9941030
)
9951031
if error_response is not None:
9961032
yield error_response

0 commit comments

Comments
 (0)