Skip to content

fix(events): exclude state-only events from final responses - #7095

Open
Epsilon006 wants to merge 1 commit into
google:mainfrom
Epsilon006:fix/7078-state-only-event-final-response
Open

fix(events): exclude state-only events from final responses#7095
Epsilon006 wants to merge 1 commit into
google:mainfrom
Epsilon006:fix/7078-state-only-event-final-response

Conversation

@Epsilon006

Copy link
Copy Markdown

Link to Issue or Description of Change

1. Link to an existing issue (if applicable):

Problem:
A before_agent_callback or after_agent_callback that only mutates state
causes BaseAgent to emit a content-less event carrying the state delta. The
default branch of Event.is_final_response() currently classifies that event
as final, so consumers can finish the turn before the actual agent response.

Solution:
Treat events with no content or structured output and a non-empty
state_delta as non-final. This reuses the event's existing shape instead of
adding framework-event metadata. Empty Content(parts=[]) events remain final,
preserving legitimate content-less model completion semantics.

Testing Plan

Unit Tests:

  • I have added or updated unit tests for my change.
  • All unit tests pass locally.

Added regression coverage for state-only events, empty Content compatibility,
and both before/after agent callback state-delta paths.

Relevant tests, including A2A converters/executor, pass on every supported
Python version:

Python 3.10: 227 passed, 2 skipped
Python 3.11: 227 passed, 2 skipped
Python 3.12: 227 passed, 2 skipped
Python 3.13: 227 passed, 2 skipped
Python 3.14: 227 passed, 2 skipped

pre-commit run --files passes all applicable hooks. Full tox was also run;
the Windows host has unrelated existing platform/environment failures (GBK
encoding, Unix shell/path assumptions, network-dependent tests, and telemetry
snapshots). None of those failures are in the changed files or relevant test
set.

Manual End-to-End (E2E) Tests:

Ran a deterministic agent through InMemoryRunner. Its
before_agent_callback sets callback_state, then the agent emits its response.
The state-only event is observable and persisted but is not final; the following
agent response is final.

event[0]: content=None, state_delta={'callback_state': 'updated'}, is_final_response=False
event[1]: content='actual agent response', state_delta={}, is_final_response=True
persisted_state={'callback_state': 'updated'}
RESULT: PASS

Checklist

  • I have read the CONTRIBUTING.md document.
  • I have performed a self-review of my own code.
  • I have commented my code, particularly in hard-to-understand areas.
  • I have added tests that prove my fix is effective or that my feature works.
  • New and existing unit tests pass locally with my changes.
  • I have manually tested my changes end-to-end.
  • Any dependent changes have been merged and published in downstream modules (N/A; no dependent changes).

Additional context

The patch intentionally does not add a new public event field. Existing
priority semantics for terminal errors and skip_summarization remain
unchanged, and plain empty events retain their historical final-response
behavior.

@google-cla

google-cla Bot commented Sep 11, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

A callback state mutation is persisted as a content-less framework event.
Exclude that state-only event from final-response detection while preserving
legitimate content-less agent responses.

Fixes google#7078
@Epsilon006
Epsilon006 force-pushed the fix/7078-state-only-event-final-response branch from 6ca8072 to 6bb1f0b Compare September 11, 2026 17:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A before_agent_callback which changes the state results in an empty Event being returned

1 participant