1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
//! Wire types for the OpenAI-compatible streaming response.
//!
//! Split out of `openai.rs` so the request/message half and the chunk/delta half each stay
//! within the repository's source-file limits. A real module rather than an `include!`
//! fragment because `OpenAIDelta::reasoning_delta` is executable logic that the mutation
//! gate must see; the re-export in `types.rs` keeps the public paths unchanged. The types
//! here have public fields throughout, so nothing depends on module-private access.
use Deserialize;
/// A single chunk from OpenAI's streaming response.
///
/// When the SDK requests streaming responses (`stream: true`), the API
/// returns the response incrementally as a series of chunks. Each chunk
/// represents a small piece of the complete response, allowing the SDK
/// to process and display content as it's generated.
///
/// # Streaming Architecture
///
/// Instead of waiting for the entire response, streaming sends many small
/// chunks in rapid succession. Each chunk contains:
/// - Metadata (id, model, timestamp)
/// - One or more choices (usually just one for single completions)
/// - Incremental deltas with new content
///
/// # Server-Sent Events Format
///
/// Chunks are transmitted as Server-Sent Events (SSE) over HTTP:
/// ```text
/// data: {"id":"chunk_1","object":"chat.completion.chunk",...}
/// data: {"id":"chunk_2","object":"chat.completion.chunk",...}
/// data: [DONE]
/// ```
///
/// # Example Chunk JSON
///
/// ```json
/// {
/// "id": "chatcmpl-123",
/// "object": "chat.completion.chunk",
/// "created": 1677652288,
/// "model": "gpt-4",
/// "choices": [{
/// "index": 0,
/// "delta": {"content": "Hello"},
/// "finish_reason": null
/// }]
/// }
/// ```
/// A single choice/completion option in a streaming chunk.
///
/// In streaming responses, each chunk can theoretically contain multiple
/// choices (parallel completions), but in practice there's usually just one.
/// Each choice contains a delta with incremental updates and optionally a
/// finish reason when the generation is complete.
///
/// # Delta vs Complete Content
///
/// Unlike non-streaming responses that send complete messages, streaming
/// sends deltas - just the new content added in this chunk. The SDK
/// accumulates these deltas to build the complete response.
///
/// # Finish Reason
///
/// - `None`: More content is coming
/// - `Some("stop")`: Normal completion
/// - `Some("length")`: Hit max token limit
/// - `Some("tool_calls")`: Model wants to call tools
/// - `Some("content_filter")`: Blocked by content policy
/// Incremental update in a streaming chunk.
///
/// Represents the new content/changes added in this specific chunk.
/// Unlike complete messages, deltas only contain what's new, not the
/// entire accumulated content. The SDK accumulates these deltas to
/// build the complete response.
///
/// # Incremental Nature
///
/// If the complete response is "Hello, world!", the deltas might be:
/// 1. `content: Some("Hello")`
/// 2. `content: Some(", ")`
/// 3. `content: Some("world")`
/// 4. `content: Some("!")`
///
/// The SDK concatenates these to build the full text.
///
/// # Tool Call Deltas
///
/// Tool calls are also streamed incrementally. The first delta might
/// include the tool ID and name, while subsequent deltas stream the
/// arguments JSON string piece by piece.
/// Incremental update for a tool call in streaming.
///
/// Tool calls are streamed piece-by-piece, with different chunks potentially
/// updating different parts. The SDK must accumulate these deltas to
/// reconstruct complete tool calls.
///
/// # Streaming Pattern
///
/// A complete tool call is typically streamed as:
/// 1. First chunk: `index: 0, id: Some("call_123"), type: Some("function")`
/// 2. Second chunk: `index: 0, function: Some(FunctionDelta { name: Some("search"), ... })`
/// 3. Multiple chunks: `index: 0, function: Some(FunctionDelta { arguments: Some("part") })`
///
/// The SDK uses the `index` to know which tool call to update, as multiple
/// tool calls can be streamed simultaneously.
///
/// # Index-Based Accumulation
///
/// The `index` field is crucial for tracking which tool call is being updated.
/// When the model calls multiple tools, each has a different index, and deltas
/// specify which one they're updating.
/// Incremental update for function details in streaming tool calls.
///
/// As the model streams a tool call, the function name and arguments are
/// sent incrementally. The name usually comes first in one chunk, then
/// arguments are streamed piece-by-piece as a JSON string.
///
/// # Arguments Streaming
///
/// The arguments field is particularly important to understand. It contains
/// **fragments of a JSON string** that must be accumulated and then parsed:
///
/// 1. Chunk 1: `arguments: Some("{")`
/// 2. Chunk 2: `arguments: Some("\"query\":")`
/// 3. Chunk 3: `arguments: Some("\"hello\"")`
/// 4. Chunk 4: `arguments: Some("}")`
///
/// The SDK concatenates these into `"{\"query\":\"hello\"}"` and then
/// parses it as JSON.