@@ -18,6 +18,11 @@ class MockOpenAI {
1818 throw error ;
1919 }
2020
21+ // If stream is requested, return an async generator
22+ if ( params . stream ) {
23+ return this . _createChatCompletionStream ( params ) ;
24+ }
25+
2126 return {
2227 id : 'chatcmpl-mock123' ,
2328 object : 'chat.completion' ,
@@ -48,14 +53,19 @@ class MockOpenAI {
4853 create : async params => {
4954 await new Promise ( resolve => setTimeout ( resolve , 10 ) ) ;
5055
56+ // If stream is requested, return an async generator
57+ if ( params . stream ) {
58+ return this . _createResponsesApiStream ( params ) ;
59+ }
60+
5161 return {
5262 id : 'resp_mock456' ,
5363 object : 'response' ,
54- created : 1677652290 ,
64+ created_at : 1677652290 ,
5565 model : params . model ,
5666 input_text : params . input ,
5767 output_text : `Response to: ${ params . input } ` ,
58- finish_reason : 'stop ' ,
68+ status : 'completed ' ,
5969 usage : {
6070 input_tokens : 5 ,
6171 output_tokens : 8 ,
@@ -65,6 +75,163 @@ class MockOpenAI {
6575 } ,
6676 } ;
6777 }
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+ }
68235}
69236
70237async function run ( ) {
@@ -93,7 +260,7 @@ async function run() {
93260 instructions : 'You are a translator' ,
94261 } ) ;
95262
96- // Third test: error handling
263+ // Third test: error handling in chat completions
97264 try {
98265 await client . chat . completions . create ( {
99266 model : 'error-model' ,
@@ -102,6 +269,51 @@ async function run() {
102269 } catch {
103270 // Error is expected and handled
104271 }
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+ }
105317 } ) ;
106318}
107319
0 commit comments