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        /// Serialized argument text in a `Value::String` from `do_stream`;
86        /// parsed input after `stream_text`.
87        input: Value,
88        /// Whether the tool call will be executed by the provider.
89        #[serde(default, skip_serializing_if = "Option::is_none")]
90        provider_executed: Option<bool>,
91        /// Whether the tool is dynamic (defined at runtime, e.g. MCP tools).
92        #[serde(default, skip_serializing_if = "Option::is_none")]
93        dynamic: Option<bool>,
94        /// Provider-assigned thought signature (e.g. Google Gemini
95        /// `thoughtSignature`). Must be echoed back verbatim on the follow-up
96        /// turn when the tool result is sent.
97        #[serde(default, skip_serializing_if = "Option::is_none")]
98        thought_signature: Option<String>,
99        /// Set by Core when the call remains invalid after optional repair.
100        #[serde(default, skip_serializing_if = "Option::is_none")]
101        invalid: Option<bool>,
102        /// Typed lookup, parsing, schema, or repair failure.
103        #[serde(default, skip_serializing_if = "Option::is_none")]
104        error: Option<AiMuxError>,
105        #[serde(default, skip_serializing_if = "Option::is_none")]
106        provider_metadata: Option<ProviderMetadata>,
107    },
108    /// A tool result (provider-executed tools).
109    ToolResult {
110        tool_call_id: String,
111        tool_name: String,
112        result: Value,
113        /// Whether the result is an error or error message.
114        #[serde(default, skip_serializing_if = "Option::is_none")]
115        is_error: Option<bool>,
116        /// Whether the result is preliminary (replaces prior, e.g. image previews).
117        #[serde(default, skip_serializing_if = "Option::is_none")]
118        preliminary: Option<bool>,
119        /// Whether the tool is dynamic (defined at runtime, e.g. MCP tools).
120        #[serde(default, skip_serializing_if = "Option::is_none")]
121        dynamic: Option<bool>,
122        /// Additional provider-specific metadata for the tool result.
123        #[serde(default, skip_serializing_if = "Option::is_none")]
124        provider_metadata: Option<ProviderMetadata>,
125    },
126
127    // ── P2: file ──
128    /// A file generated by the model (e.g. an image or document).
129    ///
130    /// Mirrors the Vercel AI SDK `file` stream part. `data` is a tagged union
131    /// covering base64 data, URL, or provider reference.
132    File {
133        data: FileData,
134        media_type: String,
135        #[serde(default, skip_serializing_if = "Option::is_none")]
136        provider_metadata: Option<ProviderMetadata>,
137    },
138
139    // ── P2: reasoning ──
140    ReasoningStart {
141        id: String,
142        /// Provider-specific metadata (e.g. xAI `itemId`).
143        #[allow(unused)]
144        provider_metadata: Option<ProviderMetadata>,
145    },
146    ReasoningDelta {
147        id: String,
148        delta: String,
149        /// Provider-specific metadata (e.g. xAI `itemId`).
150        #[allow(unused)]
151        provider_metadata: Option<ProviderMetadata>,
152    },
153    ReasoningEnd {
154        id: String,
155        /// Provider-specific metadata (e.g. xAI `itemId`, `reasoningEncryptedContent`).
156        #[allow(unused)]
157        provider_metadata: Option<ProviderMetadata>,
158    },
159
160    // ── P2: metadata ──
161    /// Response metadata (id, timestamp, model_id).
162    ResponseMetadata {
163        id: Option<String>,
164        timestamp: Option<String>,
165        model_id: Option<String>,
166    },
167
168    /// A source / citation (e.g. URL citation from search-preview models).
169    Source {
170        id: String,
171        source_type: String,
172        url: Option<String>,
173        title: Option<String>,
174        #[serde(default, skip_serializing_if = "Option::is_none")]
175        provider_metadata: Option<ProviderMetadata>,
176    },
177
178    /// A raw chunk from the provider (for debugging, when `include_raw_chunks` is set).
179    Raw { raw_value: Value },
180}