Skip to content

fix: resolve 'TODO: last event is not final' error in streaming mode …#638

Open
mahendrarathore1742 wants to merge 2 commits intogoogle:mainfrom
mahendrarathore1742:fix/partial-last-event-error-600
Open

fix: resolve 'TODO: last event is not final' error in streaming mode …#638
mahendrarathore1742 wants to merge 2 commits intogoogle:mainfrom
mahendrarathore1742:fix/partial-last-event-error-600

Conversation

@mahendrarathore1742
Copy link

@mahendrarathore1742 mahendrarathore1742 commented Mar 10, 2026

…(#600)

When using streaming mode (e.g., with a2a remote agents), Flow.Run() would return an error 'TODO: last event is not final' if the last event from runOneStep had Partial=true. This is a legitimate state that occurs when:

  • The model reaches max token limit during streaming
  • A sub-agent (e.g., a2a remote agent) emits partial streaming events

The turn is complete in these cases, so we simply return instead of erroring.

Added regression test TestFlowRunPartialLastEvent with 3 sub-cases covering single partial, multiple partial, and mixed partial/final response scenarios.

Fixes #600

Please ensure you have read the contribution guide before creating a pull request.

Link to Issue or Description of Change

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

  • Closes: #issue_number
  • Related: #issue_number

2. Or, if no issue exists, describe the change:

If applicable, please follow the issue templates to provide as much detail as
possible.

Problem:
A clear and concise description of what the problem is.

Solution:
A clear and concise description of what you want to happen and why you choose
this solution.

Testing Plan

Please describe the tests that you ran to verify your changes. This is required
for all PRs that are not small documentation or typo fixes.

Unit Tests:

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

Please include a summary of passed go test results.

Manual End-to-End (E2E) Tests:

Please provide instructions on how to manually test your changes, including any
necessary setup or configuration. Please provide logs or screenshots to help
reviewers better understand the fix.

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.

Additional context

Add any other context or screenshots about the feature request here.

…oogle#600)

When using streaming mode (e.g., with a2a remote agents), Flow.Run()
would return an error 'TODO: last event is not final' if the last
event from runOneStep had Partial=true. This is a legitimate state
that occurs when:
- The model reaches max token limit during streaming
- A sub-agent (e.g., a2a remote agent) emits partial streaming events

The turn is complete in these cases, so we simply return instead of
erroring.

Added regression test TestFlowRunPartialLastEvent with 3 sub-cases
covering single partial, multiple partial, and mixed partial/final
response scenarios.

Fixes google#600
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, 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 enhances the robustness of the streaming functionality by addressing an issue where Flow.Run() would incorrectly terminate with an error if the last event received was marked as partial. The change ensures that such partial final events, which are legitimate in cases like reaching token limits or sub-agent streaming, are handled gracefully, allowing the process to complete as intended. Comprehensive regression tests have been added to validate this corrected behavior across various partial streaming scenarios.

Highlights

  • Error Resolution: Resolved the 'TODO: last event is not final' error that occurred in streaming mode when the final event from runOneStep had Partial=true.
  • Streaming Behavior: Modified Flow.Run() to correctly handle scenarios where the last event is partial, such as when a model reaches its max token limit or a sub-agent emits partial streaming events, allowing the turn to complete without error.
  • Regression Testing: Introduced a new regression test, TestFlowRunPartialLastEvent, which includes three sub-cases to cover single partial, multiple partial, and mixed partial/final response scenarios, ensuring the fix is robust.

🧠 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.

Changelog
  • internal/llminternal/base_flow.go
    • Removed the explicit error return when lastEvent.LLMResponse.Partial is true, allowing the flow to complete.
  • internal/llminternal/base_flow_test.go
    • Added context, errors, iter imports.
    • Added agent and runconfig imports.
    • Introduced a partialOnlyModel mock LLM to simulate partial responses.
    • Added TestFlowRunPartialLastEvent with three test cases to verify correct handling of partial last events.
Activity
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a 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 an error that occurred in streaming mode when the last event was a partial response. The fix involves simply returning instead of erroring out, which is the correct behavior for a completed turn. The addition of a comprehensive regression test with multiple sub-cases ensures this scenario is well-covered. My review includes a minor suggestion to improve the clarity and maintainability of the new test code.

Comment on lines +583 to +599
// partialOnlyModel is a mock LLM that always returns partial responses in streaming mode.
// This simulates the scenario where the model's last streaming chunk has Partial=true,
// for example when reaching a max token limit.
type partialOnlyModel struct {
responses []*model.LLMResponse
}

func (m *partialOnlyModel) Name() string { return "partial-mock" }
func (m *partialOnlyModel) GenerateContent(_ context.Context, _ *model.LLMRequest, _ bool) iter.Seq2[*model.LLMResponse, error] {
return func(yield func(*model.LLMResponse, error) bool) {
for _, r := range m.responses {
if !yield(r, nil) {
return
}
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The name partialOnlyModel and its associated comment are slightly misleading. The comment states it "always returns partial responses", but it's used in a test case that includes a non-partial response. To improve clarity and future maintainability, I've suggested renaming it to mockLLM and updating the comment. You will also need to update its usage on line 666.

Suggested change
// partialOnlyModel is a mock LLM that always returns partial responses in streaming mode.
// This simulates the scenario where the model's last streaming chunk has Partial=true,
// for example when reaching a max token limit.
type partialOnlyModel struct {
responses []*model.LLMResponse
}
func (m *partialOnlyModel) Name() string { return "partial-mock" }
func (m *partialOnlyModel) GenerateContent(_ context.Context, _ *model.LLMRequest, _ bool) iter.Seq2[*model.LLMResponse, error] {
return func(yield func(*model.LLMResponse, error) bool) {
for _, r := range m.responses {
if !yield(r, nil) {
return
}
}
}
}
// mockLLM is a mock LLM that returns a canned sequence of responses.
// This simulates the scenario where the model's last streaming chunk has Partial=true,
// for example when reaching a max token limit.
type mockLLM struct {
responses []*model.LLMResponse
}
func (m *mockLLM) Name() string { return "mock-llm" }
func (m *mockLLM) GenerateContent(_ context.Context, _ *model.LLMRequest, _ bool) iter.Seq2[*model.LLMResponse, error] {
return func(yield func(*model.LLMResponse, error) bool) {
for _, r := range m.responses {
if !yield(r, nil) {
return
}
}
}
}

Address code review feedback: the mock type name was misleading
since it's also used in a test case with non-partial responses.
Renamed to mockStreamingLLM to avoid both the naming confusion
and a conflict with the existing mockLLM in the same package.
@mahendrarathore1742
Copy link
Author

Thanks for the review! Good catch the name was indeed misleading. I renamed it to mockStreamingLLM instead of mockLLM because mockLLM already exists in outputschema_processor_test.go in the same package and would cause a compilation error. Updated and pushed.

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.

TODO: last event is not final error in a2a remote agent call

1 participant