66using System . ComponentModel ;
77using System . Linq ;
88using System . Text . Json ;
9+ using System . Threading . Tasks ;
910using OpenAI . Assistants ;
1011using OpenAI . Chat ;
1112using OpenAI . Realtime ;
@@ -77,8 +78,10 @@ private static void ValidateSchemaParameters(BinaryData parameters)
7778 Assert . Equal ( "The name parameter" , nameProperty . GetProperty ( "description" ) . GetString ( ) ) ;
7879 }
7980
80- [ Fact ]
81- public void AsOpenAIChatMessages_ProducesExpectedOutput ( )
81+ [ Theory ]
82+ [ InlineData ( false ) ]
83+ [ InlineData ( true ) ]
84+ public void AsOpenAIChatMessages_ProducesExpectedOutput ( bool withOptions )
8285 {
8386 Assert . Throws < ArgumentNullException > ( "messages" , ( ) => ( ( IEnumerable < ChatMessage > ) null ! ) . AsOpenAIChatMessages ( ) ) ;
8487
@@ -99,17 +102,31 @@ public void AsOpenAIChatMessages_ProducesExpectedOutput()
99102 new ( ChatRole . Assistant , "The answer is 42." ) ,
100103 ] ;
101104
102- var convertedMessages = messages . AsOpenAIChatMessages ( ) . ToArray ( ) ;
105+ ChatOptions ? options = withOptions ? new ChatOptions { Instructions = "You talk like a parrot." } : null ;
106+
107+ var convertedMessages = messages . AsOpenAIChatMessages ( options ) . ToArray ( ) ;
103108
104- Assert . Equal ( 5 , convertedMessages . Length ) ;
109+ int index = 0 ;
110+ if ( withOptions )
111+ {
112+ Assert . Equal ( 6 , convertedMessages . Length ) ;
113+
114+ index = 1 ;
115+ SystemChatMessage instructionsMessage = Assert . IsType < SystemChatMessage > ( convertedMessages [ 0 ] ) ;
116+ Assert . Equal ( "You talk like a parrot." , Assert . Single ( instructionsMessage . Content ) . Text ) ;
117+ }
118+ else
119+ {
120+ Assert . Equal ( 5 , convertedMessages . Length ) ;
121+ }
105122
106- SystemChatMessage m0 = Assert . IsType < SystemChatMessage > ( convertedMessages [ 0 ] ) ;
123+ SystemChatMessage m0 = Assert . IsType < SystemChatMessage > ( convertedMessages [ index ] ) ;
107124 Assert . Equal ( "You are a helpful assistant." , Assert . Single ( m0 . Content ) . Text ) ;
108125
109- UserChatMessage m1 = Assert . IsType < UserChatMessage > ( convertedMessages [ 1 ] ) ;
126+ UserChatMessage m1 = Assert . IsType < UserChatMessage > ( convertedMessages [ index + 1 ] ) ;
110127 Assert . Equal ( "Hello" , Assert . Single ( m1 . Content ) . Text ) ;
111128
112- AssistantChatMessage m2 = Assert . IsType < AssistantChatMessage > ( convertedMessages [ 2 ] ) ;
129+ AssistantChatMessage m2 = Assert . IsType < AssistantChatMessage > ( convertedMessages [ index + 2 ] ) ;
113130 Assert . Single ( m2 . Content ) ;
114131 Assert . Equal ( "Hi there!" , m2 . Content [ 0 ] . Text ) ;
115132 var tc = Assert . Single ( m2 . ToolCalls ) ;
@@ -121,11 +138,11 @@ public void AsOpenAIChatMessages_ProducesExpectedOutput()
121138 [ "param2" ] = 42
122139 } ) , JsonSerializer . Deserialize < JsonElement > ( tc . FunctionArguments . ToMemory ( ) . Span ) ) ) ;
123140
124- ToolChatMessage m3 = Assert . IsType < ToolChatMessage > ( convertedMessages [ 3 ] ) ;
141+ ToolChatMessage m3 = Assert . IsType < ToolChatMessage > ( convertedMessages [ index + 3 ] ) ;
125142 Assert . Equal ( "callid123" , m3 . ToolCallId ) ;
126143 Assert . Equal ( "theresult" , Assert . Single ( m3 . Content ) . Text ) ;
127144
128- AssistantChatMessage m4 = Assert . IsType < AssistantChatMessage > ( convertedMessages [ 4 ] ) ;
145+ AssistantChatMessage m4 = Assert . IsType < AssistantChatMessage > ( convertedMessages [ index + 4 ] ) ;
129146 Assert . Equal ( "The answer is 42." , Assert . Single ( m4 . Content ) . Text ) ;
130147 }
131148
@@ -216,4 +233,69 @@ public void AsChatResponse_ConvertsOpenAIChatCompletion()
216233 Assert . Equal ( "http://example.com/image.png" , Assert . IsType < UriContent > ( message . Contents [ 1 ] ) . Uri . ToString ( ) ) ;
217234 Assert . Equal ( "functionName" , Assert . IsType < FunctionCallContent > ( message . Contents [ 2 ] ) . Name ) ;
218235 }
236+
237+ [ Fact ]
238+ public async Task AsChatResponse_ConvertsOpenAIStreamingChatCompletionUpdates ( )
239+ {
240+ Assert . Throws < ArgumentNullException > ( "chatCompletionUpdates" , ( ) => ( ( IAsyncEnumerable < StreamingChatCompletionUpdate > ) null ! ) . AsChatResponseUpdatesAsync ( ) ) ;
241+
242+ List < ChatResponseUpdate > updates = [ ] ;
243+ await foreach ( var update in CreateUpdates ( ) . AsChatResponseUpdatesAsync ( ) )
244+ {
245+ updates . Add ( update ) ;
246+ }
247+
248+ ChatResponse response = updates . ToChatResponse ( ) ;
249+
250+ Assert . Equal ( "id" , response . ResponseId ) ;
251+ Assert . Equal ( ChatFinishReason . ToolCalls , response . FinishReason ) ;
252+ Assert . Equal ( "model123" , response . ModelId ) ;
253+ Assert . Equal ( new DateTimeOffset ( 2025 , 1 , 1 , 0 , 0 , 0 , TimeSpan . Zero ) , response . CreatedAt ) ;
254+ Assert . NotNull ( response . Usage ) ;
255+ Assert . Equal ( 1 , response . Usage . InputTokenCount ) ;
256+ Assert . Equal ( 2 , response . Usage . OutputTokenCount ) ;
257+ Assert . Equal ( 3 , response . Usage . TotalTokenCount ) ;
258+
259+ ChatMessage message = Assert . Single ( response . Messages ) ;
260+ Assert . Equal ( ChatRole . Assistant , message . Role ) ;
261+
262+ Assert . Equal ( 3 , message . Contents . Count ) ;
263+ Assert . Equal ( "Hello, world!" , Assert . IsType < TextContent > ( message . Contents [ 0 ] ) . Text ) ;
264+ Assert . Equal ( "http://example.com/image.png" , Assert . IsType < UriContent > ( message . Contents [ 1 ] ) . Uri . ToString ( ) ) ;
265+ Assert . Equal ( "functionName" , Assert . IsType < FunctionCallContent > ( message . Contents [ 2 ] ) . Name ) ;
266+
267+ static async IAsyncEnumerable < StreamingChatCompletionUpdate > CreateUpdates ( )
268+ {
269+ await Task . Yield ( ) ;
270+ yield return OpenAIChatModelFactory . StreamingChatCompletionUpdate (
271+ "id" ,
272+ new ChatMessageContent (
273+ ChatMessageContentPart . CreateTextPart ( "Hello, world!" ) ,
274+ ChatMessageContentPart . CreateImagePart ( new Uri ( "http://example.com/image.png" ) ) ) ,
275+ null ,
276+ [ OpenAIChatModelFactory . StreamingChatToolCallUpdate ( 0 , "id" , ChatToolCallKind . Function , "functionName" , BinaryData . FromString ( "test" ) ) ] ,
277+ ChatMessageRole . Assistant ,
278+ null , null , null , OpenAI . Chat . ChatFinishReason . ToolCalls , new DateTimeOffset ( 2025 , 1 , 1 , 0 , 0 , 0 , TimeSpan . Zero ) ,
279+ "model123" , null , OpenAIChatModelFactory . ChatTokenUsage ( 2 , 1 , 3 ) ) ;
280+ }
281+ }
282+
283+ [ Fact ]
284+ public void AsChatResponse_ConvertsOpenAIResponse ( )
285+ {
286+ Assert . Throws < ArgumentNullException > ( "response" , ( ) => ( ( OpenAIResponse ) null ! ) . AsChatResponse ( ) ) ;
287+
288+ // The OpenAI library currently doesn't provide any way to create an OpenAIResponse instance,
289+ // as all constructors/factory methods currently are internal. Update this test when such functionality is available.
290+ }
291+
292+ [ Fact ]
293+ public void AsChatResponseUpdatesAsync_ConvertsOpenAIStreamingResponseUpdates ( )
294+ {
295+ Assert . Throws < ArgumentNullException > ( "responseUpdates" , ( ) => ( ( IAsyncEnumerable < StreamingResponseUpdate > ) null ! ) . AsChatResponseUpdatesAsync ( ) ) ;
296+
297+ // The OpenAI library currently doesn't provide any way to create a StreamingResponseUpdate instance,
298+ // as all constructors/factory methods currently are internal. Update this test when such functionality is available.
299+ }
300+
219301}
0 commit comments