@@ -168,6 +168,186 @@ public async Task BasicRequestResponse_NonStreaming()
168168 Assert . Equal ( 36 , response . Usage . TotalTokenCount ) ;
169169 }
170170
171+ [ Fact ]
172+ public async Task BasicReasoningResponse_Streaming ( )
173+ {
174+ const string Input = """
175+ {
176+ "input":[{
177+ "type":"message",
178+ "role":"user",
179+ "content":[{
180+ "type":"input_text",
181+ "text":"Calculate the sum of the first 5 positive integers."
182+ }]
183+ }],
184+ "reasoning": {
185+ "summary": "detailed",
186+ "effort": "low"
187+ },
188+ "model": "o4-mini",
189+ "stream": true
190+ }
191+ """ ;
192+
193+ // Compressed down for testing purposes; real-world output would be larger.
194+ const string Output = """
195+ event: response.created
196+ data: {"type":"response.created","sequence_number":0,"response":{"id":"resp_68b5ebab461881969ed94149372c2a530698ecbf1b9f2704","object":"response","created_at":1756752811,"status":"in_progress","background":false,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"o4-mini-2025-04-16","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"reasoning":{"effort":"low","summary":"detailed"},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
197+
198+ event: response.in_progress
199+ data: {"type":"response.in_progress","sequence_number":1,"response":{"id":"resp_68b5ebab461881969ed94149372c2a530698ecbf1b9f2704","object":"response","created_at":1756752811,"status":"in_progress","background":false,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"o4-mini-2025-04-16","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"reasoning":{"effort":"low","summary":"detailed"},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}}
200+
201+ event: response.output_item.added
202+ data: {"type":"response.output_item.added","sequence_number":2,"output_index":0,"item":{"id":"rs_68b5ebabc0088196afb9fa86b487732d0698ecbf1b9f2704","type":"reasoning","summary":[]}}
203+
204+ event: response.reasoning_summary_part.added
205+ data: {"type":"response.reasoning_summary_part.added","sequence_number":3,"item_id":"rs_68b5ebabc0088196afb9fa86b487732d0698ecbf1b9f2704","output_index":0,"summary_index":0,"part":{"type":"summary_text","text":""}}
206+
207+ event: response.reasoning_summary_text.delta
208+ data: {"type":"response.reasoning_summary_text.delta","sequence_number":4,"item_id":"rs_68b5ebabc0088196afb9fa86b487732d0698ecbf1b9f2704","output_index":0,"summary_index":0,"delta":"**Calcul","obfuscation":"sLkbFySM"}
209+
210+ event: response.reasoning_summary_text.delta
211+ data: {"type":"response.reasoning_summary_text.delta","sequence_number":5,"item_id":"rs_68b5ebabc0088196afb9fa86b487732d0698ecbf1b9f2704","output_index":0,"summary_index":0,"delta":"ating","obfuscation":"dkm1f6DKqUj"}
212+
213+ event: response.reasoning_summary_text.delta
214+ data: {"type":"response.reasoning_summary_text.delta","sequence_number":6,"item_id":"rs_68b5ebabc0088196afb9fa86b487732d0698ecbf1b9f2704","output_index":0,"summary_index":0,"delta":" a","obfuscation":"X8ahc2lfCf9eA1"}
215+
216+ event: response.reasoning_summary_text.delta
217+ data: {"type":"response.reasoning_summary_text.delta","sequence_number":7,"item_id":"rs_68b5ebabc0088196afb9fa86b487732d0698ecbf1b9f2704","output_index":0,"summary_index":0,"delta":" simple","obfuscation":"1rLVyIaNl"}
218+
219+ event: response.reasoning_summary_text.delta
220+ data: {"type":"response.reasoning_summary_text.delta","sequence_number":8,"item_id":"rs_68b5ebabc0088196afb9fa86b487732d0698ecbf1b9f2704","output_index":0,"summary_index":0,"delta":" sum","obfuscation":"jCK7mgNR80Re"}
221+
222+ event: response.reasoning_summary_text.done
223+ data: {"type":"response.reasoning_summary_text.done","sequence_number":9,"item_id":"rs_68b5ebabc0088196afb9fa86b487732d0698ecbf1b9f2704","output_index":0,"summary_index":0,"text":"**Calculating a simple sum**"}
224+
225+ event: response.reasoning_summary_part.done
226+ data: {"type":"response.reasoning_summary_part.done","sequence_number":10,"item_id":"rs_68b5ebabc0088196afb9fa86b487732d0698ecbf1b9f2704","output_index":0,"summary_index":0,"part":{"type":"summary_text","text":"**Calculating a simple sum**"}}
227+
228+ event: response.output_item.done
229+ data: {"type":"response.output_item.done","sequence_number":11,"output_index":0,"item":{"id":"rs_68b5ebabc0088196afb9fa86b487732d0698ecbf1b9f2704","type":"reasoning","summary":[{"type":"summary_text","text":"**Calculating a simple sum**"}]}}
230+
231+ event: response.output_item.added
232+ data: {"type":"response.output_item.added","sequence_number":12,"output_index":1,"item":{"id":"msg_68b5ebae5a708196b74b94f22ca8995e0698ecbf1b9f2704","type":"message","status":"in_progress","content":[],"role":"assistant"}}
233+
234+ event: response.content_part.added
235+ data: {"type":"response.content_part.added","sequence_number":13,"item_id":"msg_68b5ebae5a708196b74b94f22ca8995e0698ecbf1b9f2704","output_index":1,"content_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":""}}
236+
237+ event: response.output_text.delta
238+ data: {"type":"response.output_text.delta","sequence_number":14,"item_id":"msg_68b5ebae5a708196b74b94f22ca8995e0698ecbf1b9f2704","output_index":1,"content_index":0,"delta":"The","logprobs":[],"obfuscation":"japg2KaCkjNsp"}
239+
240+ event: response.output_text.delta
241+ data: {"type":"response.output_text.delta","sequence_number":15,"item_id":"msg_68b5ebae5a708196b74b94f22ca8995e0698ecbf1b9f2704","output_index":1,"content_index":0,"delta":" sum","logprobs":[],"obfuscation":"1BEqjKQ0KU41"}
242+
243+ event: response.output_text.delta
244+ data: {"type":"response.output_text.delta","sequence_number":16,"item_id":"msg_68b5ebae5a708196b74b94f22ca8995e0698ecbf1b9f2704","output_index":1,"content_index":0,"delta":" of","logprobs":[],"obfuscation":"GUqom1rsdZsnT"}
245+
246+ event: response.output_text.delta
247+ data: {"type":"response.output_text.delta","sequence_number":17,"item_id":"msg_68b5ebae5a708196b74b94f22ca8995e0698ecbf1b9f2704","output_index":1,"content_index":0,"delta":" the","logprobs":[],"obfuscation":"UmCms91yrTlg"}
248+
249+ event: response.output_text.delta
250+ data: {"type":"response.output_text.delta","sequence_number":18,"item_id":"msg_68b5ebae5a708196b74b94f22ca8995e0698ecbf1b9f2704","output_index":1,"content_index":0,"delta":" first","logprobs":[],"obfuscation":"AyNbZpfTXo"}
251+
252+ event: response.output_text.delta
253+ data: {"type":"response.output_text.delta","sequence_number":19,"item_id":"msg_68b5ebae5a708196b74b94f22ca8995e0698ecbf1b9f2704","output_index":1,"content_index":0,"delta":" ","logprobs":[],"obfuscation":"tuyz4HkKODFQRtk"}
254+
255+ event: response.output_text.delta
256+ data: {"type":"response.output_text.delta","sequence_number":20,"item_id":"msg_68b5ebae5a708196b74b94f22ca8995e0698ecbf1b9f2704","output_index":1,"content_index":0,"delta":"5","logprobs":[],"obfuscation":"QAwyISolmjXfTlc"}
257+
258+ event: response.output_text.delta
259+ data: {"type":"response.output_text.delta","sequence_number":21,"item_id":"msg_68b5ebae5a708196b74b94f22ca8995e0698ecbf1b9f2704","output_index":1,"content_index":0,"delta":" positive","logprobs":[],"obfuscation":"2Euge1H"}
260+
261+ event: response.output_text.delta
262+ data: {"type":"response.output_text.delta","sequence_number":22,"item_id":"msg_68b5ebae5a708196b74b94f22ca8995e0698ecbf1b9f2704","output_index":1,"content_index":0,"delta":" integers","logprobs":[],"obfuscation":"ih0Znt8"}
263+
264+ event: response.output_text.delta
265+ data: {"type":"response.output_text.delta","sequence_number":23,"item_id":"msg_68b5ebae5a708196b74b94f22ca8995e0698ecbf1b9f2704","output_index":1,"content_index":0,"delta":" is","logprobs":[],"obfuscation":"oQihR5Pw8jRz5"}
266+
267+ event: response.output_text.delta
268+ data: {"type":"response.output_text.delta","sequence_number":24,"item_id":"msg_68b5ebae5a708196b74b94f22ca8995e0698ecbf1b9f2704","output_index":1,"content_index":0,"delta":" 15","logprobs":[],"obfuscation":"7TdJ1FWlZF8lTd"}
269+
270+ event: response.output_text.delta
271+ data: {"type":"response.output_text.delta","sequence_number":25,"item_id":"msg_68b5ebae5a708196b74b94f22ca8995e0698ecbf1b9f2704","output_index":1,"content_index":0,"delta":".","logprobs":[],"obfuscation":"x2VAJKlWI8qjgYq"}
272+
273+ event: response.output_text.done
274+ data: {"type":"response.output_text.done","sequence_number":26,"item_id":"msg_68b5ebae5a708196b74b94f22ca8995e0698ecbf1b9f2704","output_index":1,"content_index":0,"text":"The sum of the first 5 positive integers is 15.","logprobs":[]}
275+
276+ event: response.content_part.done
277+ data: {"type":"response.content_part.done","sequence_number":27,"item_id":"msg_68b5ebae5a708196b74b94f22ca8995e0698ecbf1b9f2704","output_index":1,"content_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":"The sum of the first 5 positive integers is 15."}}
278+
279+ event: response.output_item.done
280+ data: {"type":"response.output_item.done","sequence_number":28,"output_index":1,"item":{"id":"msg_68b5ebae5a708196b74b94f22ca8995e0698ecbf1b9f2704","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"The sum of the first 5 positive integers is 15."}],"role":"assistant"}}
281+
282+ event: response.completed
283+ data: {"type":"response.completed","sequence_number":29,"response":{"id":"resp_68b5ebab461881969ed94149372c2a530698ecbf1b9f2704","object":"response","created_at":1756752811,"status":"completed","background":false,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"o4-mini-2025-04-16","output":[{"id":"rs_68b5ebabc0088196afb9fa86b487732d0698ecbf1b9f2704","type":"reasoning","summary":[{"type":"summary_text","text":"**Calculating a simple sum**"}]},{"id":"msg_68b5ebae5a708196b74b94f22ca8995e0698ecbf1b9f2704","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"The sum of the first 5 positive integers is 15."}],"role":"assistant"}],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"reasoning":{"effort":"low","summary":"detailed"},"safety_identifier":null,"service_tier":"default","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":17,"input_tokens_details":{"cached_tokens":0},"output_tokens":122,"output_tokens_details":{"reasoning_tokens":64},"total_tokens":139},"user":null,"metadata":{}}}
284+
285+
286+ """ ;
287+
288+ using VerbatimHttpHandler handler = new ( Input , Output ) ;
289+ using HttpClient httpClient = new ( handler ) ;
290+ using IChatClient client = CreateResponseClient ( httpClient , "o4-mini" ) ;
291+
292+ List < ChatResponseUpdate > updates = [ ] ;
293+ await foreach ( var update in client . GetStreamingResponseAsync ( "Calculate the sum of the first 5 positive integers." , new ( )
294+ {
295+ RawRepresentationFactory = options => new ResponseCreationOptions
296+ {
297+ ReasoningOptions = new ( )
298+ {
299+ ReasoningEffortLevel = ResponseReasoningEffortLevel . Low ,
300+ ReasoningSummaryVerbosity = ResponseReasoningSummaryVerbosity . Detailed
301+ }
302+ }
303+ } ) )
304+ {
305+ updates . Add ( update ) ;
306+ }
307+
308+ Assert . Equal ( "The sum of the first 5 positive integers is 15." , string . Concat ( updates . Select ( u => u . Text ) ) ) ;
309+
310+ var createdAt = DateTimeOffset . FromUnixTimeSeconds ( 1_756_752_811 ) ;
311+ Assert . Equal ( 30 , updates . Count ) ;
312+
313+ for ( int i = 0 ; i < updates . Count ; i ++ )
314+ {
315+ Assert . Equal ( "resp_68b5ebab461881969ed94149372c2a530698ecbf1b9f2704" , updates [ i ] . ResponseId ) ;
316+ Assert . Equal ( "resp_68b5ebab461881969ed94149372c2a530698ecbf1b9f2704" , updates [ i ] . ConversationId ) ;
317+ Assert . Equal ( createdAt , updates [ i ] . CreatedAt ) ;
318+ Assert . Equal ( "o4-mini-2025-04-16" , updates [ i ] . ModelId ) ;
319+ Assert . Null ( updates [ i ] . AdditionalProperties ) ;
320+
321+ if ( i is ( >= 4 and <= 8 ) )
322+ {
323+ // Reasoning updates
324+ Assert . Single ( updates [ i ] . Contents ) ;
325+ Assert . Null ( updates [ i ] . Role ) ;
326+
327+ var reasoning = Assert . IsType < TextReasoningContent > ( updates [ i ] . Contents . Single ( ) ) ;
328+ Assert . NotNull ( reasoning ) ;
329+ Assert . NotNull ( reasoning . Text ) ;
330+ }
331+ else if ( i is ( >= 14 and <= 25 ) or 29 )
332+ {
333+ // Response Complete and Assistant message updates
334+ Assert . Single ( updates [ i ] . Contents ) ;
335+ }
336+ else
337+ {
338+ // Other updates
339+ Assert . Empty ( updates [ i ] . Contents ) ;
340+ }
341+
342+ Assert . Equal ( i < updates . Count - 1 ? null : ChatFinishReason . Stop , updates [ i ] . FinishReason ) ;
343+ }
344+
345+ UsageContent usage = updates . SelectMany ( u => u . Contents ) . OfType < UsageContent > ( ) . Single ( ) ;
346+ Assert . Equal ( 17 , usage . Details . InputTokenCount ) ;
347+ Assert . Equal ( 122 , usage . Details . OutputTokenCount ) ;
348+ Assert . Equal ( 139 , usage . Details . TotalTokenCount ) ;
349+ }
350+
171351 [ Fact ]
172352 public async Task BasicRequestResponse_Streaming ( )
173353 {
0 commit comments