llmsdk_provider/language_model/stream_part.rs
1//! Streamed delta parts produced by `do_stream`.
2//!
3//! Mirrors `language-model-v4-stream-part.ts`. Wire format and tag names
4//! match ai-sdk exactly.
5// Rust guideline compliant 2026-02-21
6
7use serde::{Deserialize, Serialize};
8
9use crate::json::JsonValue;
10use crate::shared::{ProviderMetadata, Warning};
11
12use crate::shared::FileData;
13
14use super::content::{Source, ToolApprovalRequest, ToolResult};
15use super::finish_reason::FinishReason;
16use super::prompt::{FilePart, ToolCallPart};
17use super::result::ResponseMetadata;
18use super::usage::Usage;
19
20/// One unit emitted on the stream returned by
21/// [`super::LanguageModel::do_stream`].
22#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
23#[serde(tag = "type", rename_all = "kebab-case")]
24pub enum StreamPart {
25 /// Start of a text block.
26 TextStart {
27 /// Block id (used to correlate `text-delta` / `text-end`).
28 id: String,
29 /// Provider-specific metadata.
30 #[serde(
31 default,
32 rename = "providerMetadata",
33 skip_serializing_if = "Option::is_none"
34 )]
35 provider_metadata: Option<ProviderMetadata>,
36 },
37 /// Incremental text fragment.
38 TextDelta {
39 /// Block id.
40 id: String,
41 /// Text fragment.
42 delta: String,
43 /// Provider-specific metadata.
44 #[serde(
45 default,
46 rename = "providerMetadata",
47 skip_serializing_if = "Option::is_none"
48 )]
49 provider_metadata: Option<ProviderMetadata>,
50 },
51 /// End of a text block.
52 TextEnd {
53 /// Block id.
54 id: String,
55 /// Provider-specific metadata.
56 #[serde(
57 default,
58 rename = "providerMetadata",
59 skip_serializing_if = "Option::is_none"
60 )]
61 provider_metadata: Option<ProviderMetadata>,
62 },
63 /// Start of a reasoning block.
64 ReasoningStart {
65 /// Block id.
66 id: String,
67 /// Provider-specific metadata.
68 #[serde(
69 default,
70 rename = "providerMetadata",
71 skip_serializing_if = "Option::is_none"
72 )]
73 provider_metadata: Option<ProviderMetadata>,
74 },
75 /// Incremental reasoning fragment.
76 ReasoningDelta {
77 /// Block id.
78 id: String,
79 /// Reasoning fragment.
80 delta: String,
81 /// Provider-specific metadata.
82 #[serde(
83 default,
84 rename = "providerMetadata",
85 skip_serializing_if = "Option::is_none"
86 )]
87 provider_metadata: Option<ProviderMetadata>,
88 },
89 /// End of a reasoning block.
90 ReasoningEnd {
91 /// Block id.
92 id: String,
93 /// Provider-specific metadata.
94 #[serde(
95 default,
96 rename = "providerMetadata",
97 skip_serializing_if = "Option::is_none"
98 )]
99 provider_metadata: Option<ProviderMetadata>,
100 },
101 /// Start of a tool input being streamed.
102 ToolInputStart {
103 /// Tool call id.
104 id: String,
105 /// Tool name.
106 #[serde(rename = "toolName")]
107 tool_name: String,
108 /// `true` if executed by the provider.
109 #[serde(
110 default,
111 rename = "providerExecuted",
112 skip_serializing_if = "Option::is_none"
113 )]
114 provider_executed: Option<bool>,
115 /// `true` if defined at runtime.
116 #[serde(default, skip_serializing_if = "Option::is_none")]
117 dynamic: Option<bool>,
118 /// Optional display title.
119 #[serde(default, skip_serializing_if = "Option::is_none")]
120 title: Option<String>,
121 /// Provider-specific metadata.
122 #[serde(
123 default,
124 rename = "providerMetadata",
125 skip_serializing_if = "Option::is_none"
126 )]
127 provider_metadata: Option<ProviderMetadata>,
128 },
129 /// Streamed chunk of a tool's input JSON.
130 ToolInputDelta {
131 /// Tool call id.
132 id: String,
133 /// JSON fragment.
134 delta: String,
135 /// Provider-specific metadata.
136 #[serde(
137 default,
138 rename = "providerMetadata",
139 skip_serializing_if = "Option::is_none"
140 )]
141 provider_metadata: Option<ProviderMetadata>,
142 },
143 /// End of a tool's input stream.
144 ToolInputEnd {
145 /// Tool call id.
146 id: String,
147 /// Provider-specific metadata.
148 #[serde(
149 default,
150 rename = "providerMetadata",
151 skip_serializing_if = "Option::is_none"
152 )]
153 provider_metadata: Option<ProviderMetadata>,
154 },
155 /// Approval requested for a provider-executed tool call.
156 ToolApprovalRequest(ToolApprovalRequest),
157 /// Final tool call with assembled input.
158 ToolCall(ToolCallPart),
159 /// Tool result emitted by a provider-executed tool.
160 ToolResult(ToolResult),
161 /// Provider-specific custom content.
162 Custom {
163 /// Custom kind tag, e.g. `"openai.web_search_result"`.
164 kind: String,
165 /// Provider-specific metadata.
166 #[serde(
167 default,
168 rename = "providerMetadata",
169 skip_serializing_if = "Option::is_none"
170 )]
171 provider_metadata: Option<ProviderMetadata>,
172 },
173 /// Citation / grounding source.
174 Source(Source),
175 /// File generated by the model (mid-stream emission).
176 ///
177 /// Mirrors ai-sdk's `LanguageModelV4File` stream part. The wire tag is
178 /// `"file"` and shape matches a [`FilePart`] (filename / data / media
179 /// type / provider options).
180 File(FilePart),
181 /// File generated as part of a reasoning trace (mid-stream emission).
182 ///
183 /// Mirrors ai-sdk's `LanguageModelV4ReasoningFile` stream part. Wire
184 /// tag is `"reasoning-file"`.
185 ReasoningFile {
186 /// File payload.
187 data: FileData,
188 /// IANA media type.
189 #[serde(rename = "mediaType")]
190 media_type: String,
191 /// Provider-specific metadata.
192 #[serde(
193 default,
194 rename = "providerMetadata",
195 skip_serializing_if = "Option::is_none"
196 )]
197 provider_metadata: Option<ProviderMetadata>,
198 },
199 /// Stream-start metadata.
200 StreamStart {
201 /// Warnings for the call.
202 warnings: Vec<Warning>,
203 },
204 /// Response-level metadata available mid-stream.
205 ResponseMetadata(ResponseMetadata),
206 /// Terminal frame with totals.
207 Finish {
208 /// Final token usage.
209 usage: Usage,
210 /// Why the model stopped.
211 #[serde(rename = "finishReason")]
212 finish_reason: FinishReason,
213 /// Provider-specific metadata.
214 #[serde(
215 default,
216 rename = "providerMetadata",
217 skip_serializing_if = "Option::is_none"
218 )]
219 provider_metadata: Option<ProviderMetadata>,
220 },
221 /// Raw provider chunk (only when `include_raw_chunks` is set).
222 Raw {
223 /// Provider-native value.
224 #[serde(rename = "rawValue")]
225 raw_value: JsonValue,
226 },
227 /// In-stream error from the provider.
228 ///
229 /// The stream is still alive; the outer `Result` is `Ok`.
230 Error {
231 /// Error payload as provided by the upstream.
232 error: JsonValue,
233 },
234}