Skip to content

Conversation

@vinitkadam03
Copy link
Contributor

@vinitkadam03 vinitkadam03 commented Dec 23, 2025

Description

Add StepStartEvent and StepFinishEvent to All Streaming Providers

Description

This PR adds StepStartEvent and StepFinishEvent to the streaming handlers of all providers, aligning with the AI SDK step event implementation. This enables consumers to track the lifecycle of individual processing steps during multi-step conversations (e.g., when tool calls are involved).

Motivation

In multi-step conversations (especially those involving multiple tool calls), streaming responses without step boundaries can flatten execution into a single assistant turn in subsequent requests sent by frontend after calling convertToModelMessages. This leads to:

  • Models losing the correct sequence of execution
  • Incorrect message history being sent back to the model in subsequent requests

Step events introduce explicit boundaries between execution steps, ensuring deterministic ordering and correct state management.

Problem Illustration

Without step events (flattened execution)

[
  {
    "role": "assistant",
    "content": [
      { "type": "text", "text": "Some message" },
      { "type": "tool-call", "toolCallId": "1", "input": { "param": "1" } },
      { "type": "text", "text": "Next message" },
      { "type": "tool-call", "toolCallId": "2", "input": { "param": "2" } }
    ]
  },
  {
    "role": "tool",
    "content": [
      { "type": "tool-result", "toolCallId": "1", "output": "abc" },
      { "type": "tool-result", "toolCallId": "2", "output": "pqr" }
    ]
  }
]

In this case, tool calls and text from different logical steps are merged, making it difficult for the model to reason about execution order in subsequent requests sent after converting UI Messages to model messages using convertToModelMessages

With step events (explicit sequencing)

[
  {
    "role": "assistant",
    "content": [
      { "type": "text", "text": "Some message" },
      { "type": "tool-call", "toolCallId": "1", "input": { "param": "1" } }
    ]
  },
  {
    "role": "tool",
    "content": [
      { "type": "tool-result", "toolCallId": "1", "output": "abc" }
    ]
  },
  {
    "role": "assistant",
    "content": [
      { "type": "text", "text": "Next message" },
      { "type": "tool-call", "toolCallId": "2", "input": { "param": "2" } }
    ]
  },
  {
    "role": "tool",
    "content": [
      { "type": "tool-result", "toolCallId": "2", "output": "pqr" }
    ]
  }
]

Each logical step is clearly separated, preserving execution order and allowing consumers to correctly replay or resend message history.

Event Lifecycle

StreamStartEvent
  └── StepStartEvent          ← Step 1 begins
        └── TextStart/TextDelta/ToolCall/ToolResult events...
        └── StepFinishEvent   ← Step 1 ends (e.g., tool call completed)
  └── StepStartEvent          ← Step 2 begins
        └── TextStart/TextDelta/TextComplete events...
        └── StepFinishEvent   ← Step 2 ends
StreamEndEvent

Breaking Changes

None. This is a purely additive change. Existing code will continue to work - consumers can simply ignore the new events if not needed.

@vinitkadam03 vinitkadam03 marked this pull request as ready for review December 26, 2025 13:27
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.

1 participant