From d890eae7fb570e0213f9ef74293ae4e5b959e4c7 Mon Sep 17 00:00:00 2001 From: Jah-yee Date: Mon, 13 Apr 2026 21:33:16 +0800 Subject: [PATCH 1/2] fix: clarify agent directory formats in adk web/api_server docs Updates documentation for 'adk web' and 'adk api_server' commands to clarify the multiple supported agent directory formats: - {agent_name}/agent.py (module with root_agent) - {agent_name}.py (module with root_agent) - {agent_name}/__init__.py (package with root_agent or app) - {agent_name}/root_agent.yaml (YAML config folder) The implementation already supports all these formats via AgentLoader, but the docstring only mentioned the YAML case, causing user confusion per issue #4425. Signed-off-by: RoomWithOutRoof --- src/google/adk/cli/cli_tools_click.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/google/adk/cli/cli_tools_click.py b/src/google/adk/cli/cli_tools_click.py index 07ccc15892..a41de4ef72 100644 --- a/src/google/adk/cli/cli_tools_click.py +++ b/src/google/adk/cli/cli_tools_click.py @@ -1632,8 +1632,12 @@ def cli_web( ): """Starts a FastAPI server with Web UI for agents. - AGENTS_DIR: The directory of agents, where each subdirectory is a single - agent, containing at least `__init__.py` and `agent.py` files. + AGENTS_DIR: The directory of agents. Each subdirectory (or file) can contain an agent + loaded via multiple formats: + - `{agent_name}/agent.py` (module with `root_agent`) + - `{agent_name}.py` (module with `root_agent`) + - `{agent_name}/__init__.py` (package with `root_agent` or `app`) + - `{agent_name}/root_agent.yaml` (YAML config folder) Example: @@ -1745,8 +1749,12 @@ def cli_api_server( ): """Starts a FastAPI server for agents. - AGENTS_DIR: The directory of agents, where each subdirectory is a single - agent, containing at least `__init__.py` and `agent.py` files. + AGENTS_DIR: The directory of agents. Each subdirectory (or file) can contain an agent + loaded via multiple formats: + - `{agent_name}/agent.py` (module with `root_agent`) + - `{agent_name}.py` (module with `root_agent`) + - `{agent_name}/__init__.py` (package with `root_agent` or `app`) + - `{agent_name}/root_agent.yaml` (YAML config folder) Example: From 14cc801d3d7dc1648c17bab179b7db6cc82a8a6b Mon Sep 17 00:00:00 2001 From: Jah-yee Date: Tue, 14 Apr 2026 01:13:30 +0800 Subject: [PATCH 2/2] fix: run on_event_callback before append_event to persist plugin modifications Previously, on_event_callback was executed AFTER append_event, causing plugin modifications to events (e.g., custom_metadata) to only be visible in the real-time event stream but not persisted to the database. This fix reorders the execution so that: 1. on_event_callback runs first to modify the event 2. append_event stores the (possibly modified) event to DB 3. The event is then yielded to the client This ensures plugin modifications are persisted and visible when fetching conversation history. Fixes: google/adk-python#3990 --- src/google/adk/runners.py | 56 +++++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/src/google/adk/runners.py b/src/google/adk/runners.py index 8e352794a4..75833f83c5 100644 --- a/src/google/adk/runners.py +++ b/src/google/adk/runners.py @@ -825,12 +825,26 @@ async def _exec_with_plugin( _apply_run_config_custom_metadata( early_exit_event, invocation_context.run_config ) - if self._should_append_event(early_exit_event, is_live_call): + # Step 3: Run the on_event callbacks to optionally modify the event. + # This MUST run before append_event so modifications are persisted. + modified_early_exit_event = await plugin_manager.run_on_event_callback( + invocation_context=invocation_context, event=early_exit_event + ) + if modified_early_exit_event: + _apply_run_config_custom_metadata( + modified_early_exit_event, invocation_context.run_config + ) + event_to_append = modified_early_exit_event + else: + event_to_append = early_exit_event + # Step 4: Append the (possibly modified) event to the database. + if self._should_append_event(event_to_append, is_live_call): await self.session_service.append_event( session=session, - event=early_exit_event, + event=event_to_append, ) - yield early_exit_event + # Step 5: Yield the modified event to the client. + yield modified_early_exit_event if modified_early_exit_event else early_exit_event else: # Step 2: Otherwise continue with normal execution # Note for live/bidi: @@ -868,6 +882,17 @@ async def _exec_with_plugin( # non-partial event; event.partial=True is considered as partial # event. if event.partial is not True: + # Step 3: Run the on_event callbacks to optionally modify the event. + # This MUST run before append_event so modifications are persisted. + modified_event = await plugin_manager.run_on_event_callback( + invocation_context=invocation_context, event=event + ) + if modified_event: + _apply_run_config_custom_metadata( + modified_event, invocation_context.run_config + ) + event = modified_event # Use modified event for appending + if _is_transcription(event) and ( _has_non_empty_transcription_text(event.input_transcription) or _has_non_empty_transcription_text( @@ -899,22 +924,25 @@ async def _exec_with_plugin( await self.session_service.append_event( session=session, event=event ) + # Step 5: Yield the event to the caller. + yield event else: + # Step 3: Run the on_event callbacks to optionally modify the event. + # This MUST run before append_event so modifications are persisted. + modified_event = await plugin_manager.run_on_event_callback( + invocation_context=invocation_context, event=event + ) + if modified_event: + _apply_run_config_custom_metadata( + modified_event, invocation_context.run_config + ) + event = modified_event # Use modified event for appending and yielding + # Step 4: Append the event to the database (after on_event_callback). if event.partial is not True: await self.session_service.append_event( session=session, event=event ) - - # Step 3: Run the on_event callbacks to optionally modify the event. - modified_event = await plugin_manager.run_on_event_callback( - invocation_context=invocation_context, event=event - ) - if modified_event: - _apply_run_config_custom_metadata( - modified_event, invocation_context.run_config - ) - yield modified_event - else: + # Step 5: Yield the event to the caller. yield event # Step 4: Run the after_run callbacks to perform global cleanup tasks or