Skip to main content

aimux_core/
stream_part.rs

1//! `StreamPart` — the individual chunk types emitted by `do_stream`.
2//!
3//! Aligned with V4 `LanguageModelV4StreamPart`. v0.1 implements the P0/P1 variants;
4//! P2 variants (reasoning, file, source) are defined but not yet emitted by providers.
5
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8use ts_rs::TS;
9
10use crate::error::AiMuxError;
11use crate::shared::FileData;
12use crate::types::{FinishReason, ProviderMetadata, Usage, Warning};
13
14/// A single chunk in the stream returned by `do_stream`.
15#[derive(Debug, Clone, Serialize, Deserialize, TS)]
16#[ts(export)]
17pub enum StreamPart {
18    // ── P0: text ──
19    /// Start of a text segment.
20    TextStart {
21        id: String,
22        #[serde(default, skip_serializing_if = "Option::is_none")]
23        provider_metadata: Option<ProviderMetadata>,
24    },
25    /// A delta of generated text.
26    TextDelta {
27        id: String,
28        delta: String,
29        #[serde(default, skip_serializing_if = "Option::is_none")]
30        provider_metadata: Option<ProviderMetadata>,
31    },
32    /// End of a text segment.
33    TextEnd {
34        id: String,
35        #[serde(default, skip_serializing_if = "Option::is_none")]
36        provider_metadata: Option<ProviderMetadata>,
37    },
38
39    // ── P0: stream lifecycle ──
40    /// First chunk — carries warnings from the provider.
41    StreamStart { warnings: Vec<Warning> },
42    /// Final chunk — carries usage, finish reason, and metadata.
43    Finish {
44        finish_reason: FinishReason,
45        usage: Usage,
46        provider_metadata: Option<ProviderMetadata>,
47    },
48    /// An error occurred mid-stream.
49    Error { error: AiMuxError },
50
51    // ── P1: tool calls ──
52    /// Start of a tool call's input streaming.
53    ToolInputStart {
54        id: String,
55        tool_name: String,
56        /// Whether the tool call will be executed by the provider.
57        #[serde(default, skip_serializing_if = "Option::is_none")]
58        provider_executed: Option<bool>,
59        /// Whether the tool is dynamic (defined at runtime, e.g. MCP tools).
60        #[serde(default, skip_serializing_if = "Option::is_none")]
61        dynamic: Option<bool>,
62        /// Optional title for the tool call.
63        #[serde(default, skip_serializing_if = "Option::is_none")]
64        title: Option<String>,
65        #[serde(default, skip_serializing_if = "Option::is_none")]
66        provider_metadata: Option<ProviderMetadata>,
67    },
68    /// A delta of tool call input (partial JSON).
69    ToolInputDelta {
70        id: String,
71        delta: String,
72        #[serde(default, skip_serializing_if = "Option::is_none")]
73        provider_metadata: Option<ProviderMetadata>,
74    },
75    /// End of a tool call's input streaming.
76    ToolInputEnd {
77        id: String,
78        #[serde(default, skip_serializing_if = "Option::is_none")]
79        provider_metadata: Option<ProviderMetadata>,
80    },
81    /// A complete tool call (alternative to the start/delta/end flow).
82    ToolCall {
83        tool_call_id: String,
84        tool_name: String,
85        input: Value,
86        /// Whether the tool call will be executed by the provider.
87        #[serde(default, skip_serializing_if = "Option::is_none")]
88        provider_executed: Option<bool>,
89        /// Whether the tool is dynamic (defined at runtime, e.g. MCP tools).
90        #[serde(default, skip_serializing_if = "Option::is_none")]
91        dynamic: Option<bool>,
92        /// Provider-assigned thought signature (e.g. Google Gemini
93        /// `thoughtSignature`). Must be echoed back verbatim on the follow-up
94        /// turn when the tool result is sent.
95        #[serde(default, skip_serializing_if = "Option::is_none")]
96        thought_signature: Option<String>,
97        #[serde(default, skip_serializing_if = "Option::is_none")]
98        provider_metadata: Option<ProviderMetadata>,
99    },
100    /// A tool result (provider-executed tools).
101    ToolResult {
102        tool_call_id: String,
103        tool_name: String,
104        result: Value,
105        /// Whether the result is an error or error message.
106        #[serde(default, skip_serializing_if = "Option::is_none")]
107        is_error: Option<bool>,
108        /// Whether the result is preliminary (replaces prior, e.g. image previews).
109        #[serde(default, skip_serializing_if = "Option::is_none")]
110        preliminary: Option<bool>,
111        /// Whether the tool is dynamic (defined at runtime, e.g. MCP tools).
112        #[serde(default, skip_serializing_if = "Option::is_none")]
113        dynamic: Option<bool>,
114        /// Additional provider-specific metadata for the tool result.
115        #[serde(default, skip_serializing_if = "Option::is_none")]
116        provider_metadata: Option<ProviderMetadata>,
117    },
118
119    // ── P2: file ──
120    /// A file generated by the model (e.g. an image or document).
121    ///
122    /// Mirrors the Vercel AI SDK `file` stream part. `data` is a tagged union
123    /// covering base64 data, URL, or provider reference.
124    File {
125        data: FileData,
126        media_type: String,
127        #[serde(default, skip_serializing_if = "Option::is_none")]
128        provider_metadata: Option<ProviderMetadata>,
129    },
130
131    // ── P2: reasoning ──
132    ReasoningStart {
133        id: String,
134        /// Provider-specific metadata (e.g. xAI `itemId`).
135        #[allow(unused)]
136        provider_metadata: Option<ProviderMetadata>,
137    },
138    ReasoningDelta {
139        id: String,
140        delta: String,
141        /// Provider-specific metadata (e.g. xAI `itemId`).
142        #[allow(unused)]
143        provider_metadata: Option<ProviderMetadata>,
144    },
145    ReasoningEnd {
146        id: String,
147        /// Provider-specific metadata (e.g. xAI `itemId`, `reasoningEncryptedContent`).
148        #[allow(unused)]
149        provider_metadata: Option<ProviderMetadata>,
150    },
151
152    // ── P2: metadata ──
153    /// Response metadata (id, timestamp, model_id).
154    ResponseMetadata {
155        id: Option<String>,
156        timestamp: Option<String>,
157        model_id: Option<String>,
158    },
159
160    /// A source / citation (e.g. URL citation from search-preview models).
161    Source {
162        id: String,
163        source_type: String,
164        url: Option<String>,
165        title: Option<String>,
166        #[serde(default, skip_serializing_if = "Option::is_none")]
167        provider_metadata: Option<ProviderMetadata>,
168    },
169
170    /// A raw chunk from the provider (for debugging, when `include_raw_chunks` is set).
171    Raw { raw_value: Value },
172}