-
Notifications
You must be signed in to change notification settings - Fork 2.8k
fix: persist events after plugin on_event_callback modifications #4239
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Summary of ChangesHello @Ponts, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request addresses an issue where modifications made to events by plugins via Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
|
Response from ADK Triaging Agent Hello @Ponts, thank you for creating this PR! Could you please update the issue number in the PR description? It currently says In addition, could you please provide more details in the "Testing Plan" section, such as the commands you ran and the output you observed? This information will help reviewers to review your PR more efficiently. Thanks! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request correctly addresses the issue of persisting events after on_event_callback modifications by reordering the event processing logic. The on_event_callback is now executed, and any modifications are applied to the event, before the event is appended to the session service. The logging for modified events is a good addition for debugging. The simplification of the yield statement is also a positive change.
src/google/adk/runners.py
Outdated
| _apply_run_config_custom_metadata( | ||
| event, invocation_context.run_config | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The _apply_run_config_custom_metadata function is called here, after the on_event_callback has potentially modified the event. However, this function is also called earlier in the _exec_with_plugin method (around line 781, outside this diff). Applying custom metadata twice, even if it's a merge operation, is redundant and could lead to confusion or subtle bugs if the run_config.custom_metadata were to change dynamically or if plugins were expected to override certain metadata. Consider ensuring _apply_run_config_custom_metadata is called only once, at the final stage before persistence and yielding, to ensure consistency and avoid unnecessary processing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is done again on the modified events to ensure the custom metadata from the run config is applied.
I am unsure if the live-call code makes use of the metadata for something, and that is why it is applied at the beginning.
I wanted to make a minimal change to ensure modified events are persisted
|
Thanks for this fix! The approach is correct for non-live calls, and I confirmed locally that it resolves the persistence issue. However, I noticed that the fix moves on_event_callback inside the else block, which means it no longer runs for live/bidi calls (is_live_call=True). In the original code, the callback was outside both branches and ran for all events. Could you update the PR to ensure on_event_callback also runs (before append_event) for live calls? The live branch has multiple append_event calls that would need similar treatment. |
5428ca1 to
17d8718
Compare
|
I have updated the code to run the callbacks in the live/bidi calls block too and fixed the mistake with that it only ran on not-live calls. |
17d8718 to
40c8b0f
Compare
|
/gemini review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
The pull request effectively resolves the issue where plugin on_event_callback modifications were not being persisted by correctly reordering the event processing. The introduction of the __append_event helper method is a good step towards improving code organization and reducing duplication. However, a critical regression has been introduced by the removal of the after_run_callback execution, which is essential for global cleanup and finalization tasks. Additionally, there is a minor type hinting inaccuracy in the new __append_event method.
I am having trouble creating individual review comments. Click here to see my feedback.
src/google/adk/runners.py (846-851)
The plugin_manager.run_after_run_callback was responsible for executing global cleanup tasks or finalizing logs and metrics data after the main event processing loop in _exec_with_plugin. Its removal means these crucial post-run operations will no longer be performed, which could lead to resource leaks, incomplete logging, or other unexpected behavior. Please ensure this callback is reinstated to maintain proper lifecycle management.
src/google/adk/runners.py (828)
The return type hint for __append_event is None, but the method explicitly returns an Event object. Please update the type hint to reflect the actual return type.
async def __append_event(self, invocation_context: InvocationContext, session: Session, event: Event) -> Event:
|
/gemini review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
The pull request effectively addresses the issue where plugin modifications were not being persisted by ensuring that on_event_callback is executed and its results are applied before an event is appended to the session. This is achieved by encapsulating the event processing and appending logic into a new private helper method, __append_event. This change improves the logical consistency of event handling within the runner, ensuring that plugin-driven transformations are correctly integrated into the session's event history.
src/google/adk/runners.py
Outdated
| # This does NOT emit any event. | ||
| await plugin_manager.run_after_run_callback( | ||
| invocation_context=invocation_context | ||
| async def __append_event(self, invocation_context: InvocationContext, session: Session, event: Event) -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The return type annotation for __append_event is None, but the method explicitly returns an Event object on line 846. Please update the type hint to -> Event for accuracy and better type checking.
| async def __append_event(self, invocation_context: InvocationContext, session: Session, event: Event) -> None: | |
| async def __append_event(self, invocation_context: InvocationContext, session: Session, event: Event) -> Event: |
|
Hi @Ponts , can you also fix the formatting errors. |
40c8b0f to
82068d5
Compare
|
@ryanaiagent fixed :) |
|
/gemini review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request effectively addresses the issue where plugin modifications to events were not being persisted by ensuring that on_event_callback is executed before the event is appended to the session. The refactoring into a new private helper method, __append_event, centralizes this logic, which is a good improvement. However, there are a couple of areas for further refinement regarding Pythonic naming conventions for internal methods and ensuring consistent application of run configuration metadata.
| if modified_event: | ||
| event = modified_event | ||
| _apply_run_config_custom_metadata(event, invocation_context.run_config) | ||
| await self.session_service.append_event(session=session, event=event) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The _apply_run_config_custom_metadata function is currently applied inside the if modified_event: block. This leads to inconsistent behavior: if a plugin modifies the event, metadata is applied twice (once at line 780 in _exec_with_plugin and again here); if no plugin modifies the event, metadata is applied only once (at line 780). To ensure metadata is applied exactly once to the final event before persistence, it should be applied unconditionally after the modified_event check, but before self.session_service.append_event.
| if modified_event: | |
| event = modified_event | |
| _apply_run_config_custom_metadata(event, invocation_context.run_config) | |
| await self.session_service.append_event(session=session, event=event) | |
| if modified_event: | |
| event = modified_event | |
| _apply_run_config_custom_metadata(event, invocation_context.run_config) | |
| await self.session_service.append_event(session=session, event=event) |
src/google/adk/runners.py
Outdated
| # This does NOT emit any event. | ||
| await plugin_manager.run_after_run_callback( | ||
| invocation_context=invocation_context | ||
| async def __append_event( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method __append_event uses a double underscore prefix, which triggers Python's name mangling. While this indicates privacy, the conventional Pythonic way to denote a method intended for internal use within a class (but not strictly private to prevent subclass access) is a single underscore prefix, e.g., _append_event. This improves readability and maintainability while still signaling that it's not part of the public API. Please update this method name and all its call sites.
| async def __append_event( | |
| async def _append_event( |
src/google/adk/runners.py
Outdated
| if self._should_append_event(event, is_live_call): | ||
| await self.session_service.append_event( | ||
| session=session, event=event | ||
| event = await self.__append_event( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
src/google/adk/runners.py
Outdated
| logger.debug('Appending buffered event: %s', buffered_event) | ||
| await self.session_service.append_event( | ||
| session=session, event=buffered_event | ||
| buffered_event = await self.__append_event( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
src/google/adk/runners.py
Outdated
| logger.debug('Appending non-buffered event: %s', event) | ||
| await self.session_service.append_event( | ||
| session=session, event=event | ||
| event = await self.__append_event( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
src/google/adk/runners.py
Outdated
| if modified_event: | ||
| _apply_run_config_custom_metadata( | ||
| modified_event, invocation_context.run_config | ||
| event = await self.__append_event( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
Hi @Ponts , can you address the suggestions. |
82068d5 to
e98b846
Compare
|
@ryanaiagent suggestions addressed |
|
/gemini review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
The pull request successfully addresses the issue of plugin on_event_callback modifications not being persisted by introducing a new _append_event helper method. This refactoring correctly places the plugin callback execution and custom metadata application before the event is saved to the session. However, there is an issue with the placement of the run_after_run_callback, which is now incorrectly executed for every event instead of once at the end of the run.
src/google/adk/runners.py
Outdated
| await plugin_manager.run_after_run_callback( | ||
| invocation_context=invocation_context | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The run_after_run_callback is designed for global cleanup tasks or finalizing logs and metrics data, and should be executed once after the entire _exec_with_plugin generator has finished yielding all events. Its current placement inside the async for event in agen: loop means it will be called for every event, which is an incorrect change in behavior and could lead to unintended side effects or performance issues. Please move this call outside the async for event in agen: loop to ensure it's executed only once per _exec_with_plugin invocation.
e98b846 to
eab80da
Compare
|
/gemini review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request refactors the event persistence logic to correctly handle modifications from on_event_callback plugins. By introducing a new _append_event helper method, the code now ensures that plugin callbacks are executed before an event is persisted to the session, which resolves the issue of plugin modifications not being saved. The changes are well-structured and improve code clarity by centralizing the event appending logic. I have a couple of minor suggestions to further improve the code's readability.
| event = await self._append_event( | ||
| invocation_context, session, event | ||
| ) | ||
| # Run the on_event callbacks to optionally modify the event. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| if modified_event: | ||
| event = modified_event |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For clarity and to avoid potential issues with an Event object that might evaluate to False in a boolean context (though unlikely with Pydantic models), it's safer to explicitly check for is not None.
| if modified_event: | |
| event = modified_event | |
| if modified_event is not None: | |
| event = modified_event |
Link to Issue or Description of Change
1. Link to an existing issue (if applicable):
Problem:
on_event_callback executes after append_event, preventing plugin modifications from being persisted #3990
Solution:
Run callbacks before persisting events.
Testing Plan
Ran python tests/unittests and verified all tests passed.
There exists tests that verify that events are yielded from the runner.
Unit Tests:
Manual End-to-End (E2E) Tests:
Checklist