@@ -186,6 +186,59 @@ public async Task ApprovedApprovalResponsesAreExecutedAsync()
186186 await InvokeAndAssertStreamingAsync ( options , input , downstreamClientOutput , output , expectedDownstreamClientInput ) ;
187187 }
188188
189+ [ Fact ]
190+ public async Task ApprovedApprovalResponsesAreGroupedWhenMessageIdIsNullAsync ( )
191+ {
192+ var options = new ChatOptions
193+ {
194+ Tools =
195+ [
196+ new ApprovalRequiredAIFunction ( AIFunctionFactory . Create ( ( ) => "Result 1" , "Func1" ) ) ,
197+ new ApprovalRequiredAIFunction ( AIFunctionFactory . Create ( ( int i ) => $ "Result 2: { i } ", "Func2" ) ) ,
198+ ]
199+ } ;
200+
201+ // Key difference from other tests: MessageId is NOT set on the assistant message
202+ List < ChatMessage > input =
203+ [
204+ new ChatMessage ( ChatRole . User , "hello" ) ,
205+ new ChatMessage ( ChatRole . Assistant ,
206+ [
207+ new FunctionApprovalRequestContent ( "callId1" , new FunctionCallContent ( "callId1" , "Func1" ) ) ,
208+ new FunctionApprovalRequestContent ( "callId2" , new FunctionCallContent ( "callId2" , "Func2" , arguments : new Dictionary < string , object ? > { { "i" , 42 } } ) )
209+ ] ) , // Note: No MessageId set - this is the bug trigger
210+ new ChatMessage ( ChatRole . User ,
211+ [
212+ new FunctionApprovalResponseContent ( "callId1" , true , new FunctionCallContent ( "callId1" , "Func1" ) ) ,
213+ new FunctionApprovalResponseContent ( "callId2" , true , new FunctionCallContent ( "callId2" , "Func2" , arguments : new Dictionary < string , object ? > { { "i" , 42 } } ) )
214+ ] ) ,
215+ ] ;
216+
217+ // Both FCCs should be in a SINGLE assistant message, not split across multiple messages
218+ List < ChatMessage > expectedDownstreamClientInput =
219+ [
220+ new ChatMessage ( ChatRole . User , "hello" ) ,
221+ new ChatMessage ( ChatRole . Assistant , [ new FunctionCallContent ( "callId1" , "Func1" ) , new FunctionCallContent ( "callId2" , "Func2" , arguments : new Dictionary < string , object ? > { { "i" , 42 } } ) ] ) ,
222+ new ChatMessage ( ChatRole . Tool , [ new FunctionResultContent ( "callId1" , result : "Result 1" ) , new FunctionResultContent ( "callId2" , result : "Result 2: 42" ) ] ) ,
223+ ] ;
224+
225+ List < ChatMessage > downstreamClientOutput =
226+ [
227+ new ChatMessage ( ChatRole . Assistant , "world" ) ,
228+ ] ;
229+
230+ List < ChatMessage > output =
231+ [
232+ new ChatMessage ( ChatRole . Assistant , [ new FunctionCallContent ( "callId1" , "Func1" ) , new FunctionCallContent ( "callId2" , "Func2" , arguments : new Dictionary < string , object ? > { { "i" , 42 } } ) ] ) ,
233+ new ChatMessage ( ChatRole . Tool , [ new FunctionResultContent ( "callId1" , result : "Result 1" ) , new FunctionResultContent ( "callId2" , result : "Result 2: 42" ) ] ) ,
234+ new ChatMessage ( ChatRole . Assistant , "world" ) ,
235+ ] ;
236+
237+ await InvokeAndAssertAsync ( options , input , downstreamClientOutput , output , expectedDownstreamClientInput ) ;
238+
239+ await InvokeAndAssertStreamingAsync ( options , input , downstreamClientOutput , output , expectedDownstreamClientInput ) ;
240+ }
241+
189242 [ Fact ]
190243 public async Task ApprovedApprovalResponsesFromSeparateFCCMessagesAreExecutedAsync ( )
191244 {
0 commit comments