Skip to content

feat: add turn_id to Event for grouping streaming chunks by LLM call#4817

Closed
ferponse wants to merge 1 commit intogoogle:mainfrom
ferponse:feat/add-turn-id-to-streaming-events
Closed

feat: add turn_id to Event for grouping streaming chunks by LLM call#4817
ferponse wants to merge 1 commit intogoogle:mainfrom
ferponse:feat/add-turn-id-to-streaming-events

Conversation

@ferponse
Copy link
Copy Markdown

Summary

  • Adds an optional turn_id: int field to Event that groups all streaming chunks belonging to the same LLM call
  • turn_id is a 1-based counter that increments with each LLM call inside run_async, making it easy to identify turn boundaries (turn 1, turn 2, …)
  • Consumers can use this field to trivially separate streaming chunks into message bubbles without brittle heuristics based on event type transitions

Problem

When using runner.run_async() with StreamingMode.SSE, there is no way to distinguish which partial streaming chunks belong to which LLM response turn. The invocation_id is shared across all events in the invocation, and id changes on every yield. The only workaround is observing transition patterns (partial → function_call → function_response), which is fragile.

Solution

A new turn_id: Optional[int] field on Event:

  • 1-based counter incremented with each LLM call in run_async
  • Remains the same across all partial chunks and the final aggregated event of that call
  • Increments when a new LLM call starts (next iteration of the while True loop)
  • Scoped to the SSE streaming flow only; BIDI (live) flow is not affected
  • None by default, so existing code is unaffected

Changes

File Change
src/google/adk/events/event.py Added turn_id: Optional[int] field with docstring
src/google/adk/flows/llm_flows/base_llm_flow.py Counter in run_async loop, passed to _run_one_step_async
tests/unittests/flows/llm_flows/test_turn_id.py 3 new tests

Test plan

  • test_partial_chunks_share_same_turn_id — partial chunks from one LLM call share turn_id=1
  • test_turn_id_present_on_final_response — a single final response carries turn_id=1
  • test_different_llm_calls_get_different_turn_ids — events from separate LLM calls (text → tool → text) get turn_id 1 and 2
  • All 108 existing flow + runner tests pass unchanged

@google-cla
Copy link
Copy Markdown

google-cla bot commented Mar 13, 2026

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.

@gemini-code-assist
Copy link
Copy Markdown
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 introduces a significant enhancement to how streaming events from LLM calls are managed and consumed. By adding a turn_id to events, it provides a robust mechanism to logically group related streaming chunks, addressing the previous difficulty in distinguishing which partial responses belonged to which specific LLM interaction within a multi-turn conversation. This change improves the clarity and usability of streaming data for downstream applications.

Highlights

  • New turn_id field: An optional turn_id: int field has been added to the Event class to group all streaming chunks originating from the same LLM call within a single invocation.
  • Turn ID Generation: The turn_id is a 1-based counter that increments with each distinct LLM call made within the run_async method, providing clear turn boundaries.
  • Improved Streaming Chunk Grouping: This new field allows consumers to easily separate streaming chunks into distinct message bubbles, eliminating the need for fragile heuristic-based grouping.

🧠 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
  • src/google/adk/events/event.py
    • Added turn_id: Optional[int] field to the Event class.
    • Included a detailed docstring for the new turn_id field explaining its purpose and behavior.
  • src/google/adk/flows/llm_flows/base_llm_flow.py
    • Initialized a turn_id counter to 0 before the while True loop in run_async.
    • Incremented turn_id at the beginning of each iteration of the while True loop in run_async.
    • Modified the _run_one_step_async method signature to accept turn_id as a keyword argument with a default value of 0.
    • Passed the current turn_id to the Event constructor within _run_one_step_async.
  • tests/unittests/flows/llm_flows/test_turn_id.py
    • Added a new test file test_turn_id.py.
    • Implemented BaseLlmFlowForTesting and StreamingMockModel for testing purposes.
    • Included test_partial_chunks_share_same_turn_id to verify that partial chunks from a single LLM call share the same turn_id.
    • Added test_turn_id_present_on_final_response to confirm that a single final response event also carries a turn_id.
    • Created test_different_llm_calls_get_different_turn_ids to ensure events from separate LLM calls receive distinct, incrementing turn_id values.
Activity
  • The author, ferponse, created this pull request to introduce the turn_id feature.
  • New tests were added to cover the functionality of the turn_id field, ensuring its correct behavior in various streaming scenarios.
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.

@adk-bot adk-bot added the live [Component] This issue is related to live, voice and video chat label Mar 13, 2026
@adk-bot
Copy link
Copy Markdown
Collaborator

adk-bot commented Mar 13, 2026

Response from ADK Triaging Agent

Hello @ferponse, thank you for your contribution!

It looks like the Contributor License Agreement (CLA) check has failed. Before we can merge this pull request, you'll need to sign the CLA.

You can find more information and sign the agreement here: https://cla.developers.google.com/

Once you've signed, the check should automatically update. Thanks!

Copy link
Copy Markdown
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 introduces a turn_id to the Event class, which is a great addition for consumers of streaming events to group chunks belonging to the same LLM call. The implementation is clean, and the new logic is well-contained within the SSE streaming flow (run_async). The inclusion of comprehensive unit tests is also appreciated. I have one minor suggestion to improve the type hint for turn_id for better consistency and robustness.

self,
invocation_context: InvocationContext,
*,
turn_id: int = 0,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

For consistency with the Event.turn_id field which is Optional[int], and to avoid potentially confusing turn_id=0 values (since the docstring states it's a 1-based counter), it would be safer to type this as Optional[int] = None.

Suggested change
turn_id: int = 0,
turn_id: Optional[int] = None,

When using StreamingMode.SSE, all partial chunks from the same LLM call
now share a stable turn_id (1-based integer counter). This allows
consumers to trivially group streaming chunks by turn without fragile
heuristics based on event type transitions.

The invocation_id groups all events in a single agent invocation, while
id changes on every yield. The new turn_id sits in between: it stays
constant across all events produced by one LLM call and increments when
a new call starts (e.g. after tool execution).
@ferponse ferponse force-pushed the feat/add-turn-id-to-streaming-events branch from 4444916 to efa7a2b Compare March 13, 2026 11:56
@ferponse
Copy link
Copy Markdown
Author

Closing in favour of a new PR with review feedback applied.

@ferponse ferponse closed this Mar 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

live [Component] This issue is related to live, voice and video chat

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants