@@ -18,6 +18,11 @@ class MockOpenAI {
18
18
throw error ;
19
19
}
20
20
21
+ // If stream is requested, return an async generator
22
+ if ( params . stream ) {
23
+ return this . _createChatCompletionStream ( params ) ;
24
+ }
25
+
21
26
return {
22
27
id : 'chatcmpl-mock123' ,
23
28
object : 'chat.completion' ,
@@ -48,14 +53,19 @@ class MockOpenAI {
48
53
create : async params => {
49
54
await new Promise ( resolve => setTimeout ( resolve , 10 ) ) ;
50
55
56
+ // If stream is requested, return an async generator
57
+ if ( params . stream ) {
58
+ return this . _createResponsesApiStream ( params ) ;
59
+ }
60
+
51
61
return {
52
62
id : 'resp_mock456' ,
53
63
object : 'response' ,
54
- created : 1677652290 ,
64
+ created_at : 1677652290 ,
55
65
model : params . model ,
56
66
input_text : params . input ,
57
67
output_text : `Response to: ${ params . input } ` ,
58
- finish_reason : 'stop ' ,
68
+ status : 'completed ' ,
59
69
usage : {
60
70
input_tokens : 5 ,
61
71
output_tokens : 8 ,
@@ -65,6 +75,163 @@ class MockOpenAI {
65
75
} ,
66
76
} ;
67
77
}
78
+
79
+ // Create a mock streaming response for chat completions
80
+ async * _createChatCompletionStream ( params ) {
81
+ // First chunk with basic info
82
+ yield {
83
+ id : 'chatcmpl-stream-123' ,
84
+ object : 'chat.completion.chunk' ,
85
+ created : 1677652300 ,
86
+ model : params . model ,
87
+ system_fingerprint : 'fp_stream_123' ,
88
+ choices : [
89
+ {
90
+ index : 0 ,
91
+ delta : {
92
+ role : 'assistant' ,
93
+ content : 'Hello' ,
94
+ } ,
95
+ finish_reason : null ,
96
+ } ,
97
+ ] ,
98
+ } ;
99
+
100
+ // Second chunk with more content
101
+ yield {
102
+ id : 'chatcmpl-stream-123' ,
103
+ object : 'chat.completion.chunk' ,
104
+ created : 1677652300 ,
105
+ model : params . model ,
106
+ system_fingerprint : 'fp_stream_123' ,
107
+ choices : [
108
+ {
109
+ index : 0 ,
110
+ delta : {
111
+ content : ' from OpenAI streaming!' ,
112
+ } ,
113
+ finish_reason : 'stop' ,
114
+ } ,
115
+ ] ,
116
+ usage : {
117
+ prompt_tokens : 12 ,
118
+ completion_tokens : 18 ,
119
+ total_tokens : 30 ,
120
+ completion_tokens_details : {
121
+ accepted_prediction_tokens : 0 ,
122
+ audio_tokens : 0 ,
123
+ reasoning_tokens : 0 ,
124
+ rejected_prediction_tokens : 0 ,
125
+ } ,
126
+ prompt_tokens_details : {
127
+ audio_tokens : 0 ,
128
+ cached_tokens : 0 ,
129
+ } ,
130
+ } ,
131
+ } ;
132
+ }
133
+
134
+ // Create a mock streaming response for responses API
135
+ async * _createResponsesApiStream ( params ) {
136
+ // Response created event
137
+ yield {
138
+ type : 'response.created' ,
139
+ response : {
140
+ id : 'resp_stream_456' ,
141
+ object : 'response' ,
142
+ created_at : 1677652310 ,
143
+ model : params . model ,
144
+ status : 'in_progress' ,
145
+ error : null ,
146
+ incomplete_details : null ,
147
+ instructions : params . instructions ,
148
+ max_output_tokens : 1000 ,
149
+ parallel_tool_calls : false ,
150
+ previous_response_id : null ,
151
+ reasoning : {
152
+ effort : null ,
153
+ summary : null ,
154
+ } ,
155
+ store : false ,
156
+ temperature : 0.7 ,
157
+ text : {
158
+ format : {
159
+ type : 'text' ,
160
+ } ,
161
+ } ,
162
+ tool_choice : 'auto' ,
163
+ tools : [ ] ,
164
+ top_p : 1.0 ,
165
+ truncation : 'disabled' ,
166
+ user : null ,
167
+ metadata : { } ,
168
+ output : [ ] ,
169
+ output_text : '' ,
170
+ usage : {
171
+ input_tokens : 0 ,
172
+ output_tokens : 0 ,
173
+ total_tokens : 0 ,
174
+ } ,
175
+ } ,
176
+ sequence_number : 1 ,
177
+ } ;
178
+
179
+ // Response in progress with output text delta
180
+ yield {
181
+ type : 'response.output_text.delta' ,
182
+ delta : 'Streaming response to: ' ,
183
+ sequence_number : 2 ,
184
+ } ;
185
+
186
+ yield {
187
+ type : 'response.output_text.delta' ,
188
+ delta : params . input ,
189
+ sequence_number : 3 ,
190
+ } ;
191
+
192
+ // Response completed event
193
+ yield {
194
+ type : 'response.completed' ,
195
+ response : {
196
+ id : 'resp_stream_456' ,
197
+ object : 'response' ,
198
+ created_at : 1677652310 ,
199
+ model : params . model ,
200
+ status : 'completed' ,
201
+ error : null ,
202
+ incomplete_details : null ,
203
+ instructions : params . instructions ,
204
+ max_output_tokens : 1000 ,
205
+ parallel_tool_calls : false ,
206
+ previous_response_id : null ,
207
+ reasoning : {
208
+ effort : null ,
209
+ summary : null ,
210
+ } ,
211
+ store : false ,
212
+ temperature : 0.7 ,
213
+ text : {
214
+ format : {
215
+ type : 'text' ,
216
+ } ,
217
+ } ,
218
+ tool_choice : 'auto' ,
219
+ tools : [ ] ,
220
+ top_p : 1.0 ,
221
+ truncation : 'disabled' ,
222
+ user : null ,
223
+ metadata : { } ,
224
+ output : [ ] ,
225
+ output_text : params . input ,
226
+ usage : {
227
+ input_tokens : 6 ,
228
+ output_tokens : 10 ,
229
+ total_tokens : 16 ,
230
+ } ,
231
+ } ,
232
+ sequence_number : 4 ,
233
+ } ;
234
+ }
68
235
}
69
236
70
237
async function run ( ) {
@@ -93,7 +260,7 @@ async function run() {
93
260
instructions : 'You are a translator' ,
94
261
} ) ;
95
262
96
- // Third test: error handling
263
+ // Third test: error handling in chat completions
97
264
try {
98
265
await client . chat . completions . create ( {
99
266
model : 'error-model' ,
@@ -102,6 +269,51 @@ async function run() {
102
269
} catch {
103
270
// Error is expected and handled
104
271
}
272
+
273
+ // Fourth test: chat completions streaming
274
+ const stream1 = await client . chat . completions . create ( {
275
+ model : 'gpt-4' ,
276
+ messages : [
277
+ { role : 'system' , content : 'You are a helpful assistant.' } ,
278
+ { role : 'user' , content : 'Tell me about streaming' } ,
279
+ ] ,
280
+ stream : true ,
281
+ temperature : 0.8 ,
282
+ } ) ;
283
+
284
+ // Consume the stream to trigger span instrumentation
285
+ for await ( const chunk of stream1 ) {
286
+ // Stream chunks are processed automatically by instrumentation
287
+ void chunk ; // Prevent unused variable warning
288
+ }
289
+
290
+ // Fifth test: responses API streaming
291
+ const stream2 = await client . responses . create ( {
292
+ model : 'gpt-4' ,
293
+ input : 'Test streaming responses API' ,
294
+ instructions : 'You are a streaming assistant' ,
295
+ stream : true ,
296
+ } ) ;
297
+
298
+ for await ( const chunk of stream2 ) {
299
+ void chunk ;
300
+ }
301
+
302
+ // Sixth test: error handling in streaming context
303
+ try {
304
+ const errorStream = await client . chat . completions . create ( {
305
+ model : 'error-model' ,
306
+ messages : [ { role : 'user' , content : 'This will fail' } ] ,
307
+ stream : true ,
308
+ } ) ;
309
+
310
+ // Try to consume the stream (this should not execute)
311
+ for await ( const chunk of errorStream ) {
312
+ void chunk ;
313
+ }
314
+ } catch {
315
+ // Error is expected and handled
316
+ }
105
317
} ) ;
106
318
}
107
319
0 commit comments