Fix message ordering when processing function approval responses
When processing FunctionApprovalResponseContent, the client appends reconstructed FunctionCallContent and FunctionResultContent messages to the end of the message list, reordering any messages that came after the approval response.
Example of the issue:
// Input messages
User: "1st message"
Assistant: FunctionApprovalRequestContent
User: FunctionApprovalResponseContent (reject)
User: "2nd message"
// Before fix - "2nd message" incorrectly moved
User: "1st message"
User: "2nd message" // ❌ Wrong position
Assistant: FunctionCallContent
Tool: FunctionResultContent (rejection)
// After fix - "2nd message" stays at end
User: "1st message"
Assistant: FunctionCallContent
Tool: FunctionResultContent (rejection)
User: "2nd message" // ✓ Correct position
Changes
- Track insertion index:
ExtractAndRemoveApprovalRequestsAndResponsesnow returns the index where approval requests were originally located - Insert instead of append:
ProcessFunctionApprovalResponsesusesInsertRange/Insertat the tracked position rather thanAddRange/Add - Thread index through call chain:
ProcessFunctionCallsAsyncaccepts aninsertionIndexparameter to place approved function results at the correct position - Handle already-executed approvals: When function results already exist in the message list, new function calls are appended to preserve existing ordering
Tests
Added test cases for rejection and approval scenarios with user messages following the approval response.
Original prompt
Problem
When
FunctionInvokingChatClientprocessesFunctionApprovalResponseContent, it incorrectly reorders chat messages. User messages added after a function approval rejection are moved before the tool call/result instead of remaining at the end of the conversation.This is reported in issue #7156: https://github.com/dotnet/extensions/issues/7156
Root Cause
In
FunctionInvokingChatClient.cs, theProcessFunctionApprovalResponsesmethod:
- Calls
ExtractAndRemoveApprovalRequestsAndResponseswhich removesFunctionApprovalRequestContentandFunctionApprovalResponseContentfrom their original positions in the message list- Then appends the reconstructed
FunctionCallContentandFunctionResultContentmessages at the end of the list usingAddRangeandAdd(lines 1276-1287)This destroys the relative ordering with any messages that came after the approval response.
Expected message order:
user: 1st message assistant: tool_call tool_call: reject user: 2nd message ← Should come lastActual message order:
user: 1st message user: 2nd message ← Incorrectly positioned before tool call assistant: tool_call tool_call: rejectRequired Fix
The
ExtractAndRemoveApprovalRequestsAndResponsesmethod needs to track the original index/position of the approval request messages. ThenProcessFunctionApprovalResponsesshould:
- Insert the reconstructed
FunctionCallContentmessage at the original position of the approval request (not at the end)- Insert the rejection
FunctionResultContentimmediately after- Ensure any user messages that came after the approval response remain at the end
The key change is in
ProcessFunctionApprovalResponses- instead of:originalMessages.AddRange(preDownstreamCallHistory); // ... originalMessages.Add(rejectedPreDownstreamCallResultsMessage);It should insert at the correct position to preserve message ordering relative to any subsequent user messages.
Files to Modify
src/Libraries/Microsoft.Extensions.AI/ChatCompletion/FunctionInvokingChatClient.csTesting
The fix should be validated with test cases covering:
- Rejection with a user message added after the approval response
- Approval with a user message added after the approval response
- Multiple approval/rejection scenarios with interleaved user messages
Existing tests in
test/Libraries/Microsoft.Extensions.AI.Tests/ChatCompletion/FunctionInvokingChatClientApprovalsTests.csshould continue to pass, and new tests should be added for the scenarios described in issue #7156.
This pull request was created as a result of the following prompt from Copilot chat.
Problem
When
FunctionInvokingChatClientprocessesFunctionApprovalResponseContent, it incorrectly reorders chat messages. User messages added after a function approval rejection are moved before the tool call/result instead of remaining at the end of the conversation.This is reported in issue #7156: https://github.com/dotnet/extensions/issues/7156
Root Cause
In
FunctionInvokingChatClient.cs, theProcessFunctionApprovalResponsesmethod:
- Calls
ExtractAndRemoveApprovalRequestsAndResponseswhich removesFunctionApprovalRequestContentandFunctionApprovalResponseContentfrom their original positions in the message list- Then appends the reconstructed
FunctionCallContentandFunctionResultContentmessages at the end of the list usingAddRangeandAdd(lines 1276-1287)This destroys the relative ordering with any messages that came after the approval response.
Expected message order:
user: 1st message assistant: tool_call tool_call: reject user: 2nd message ← Should come lastActual message order:
user: 1st message user: 2nd message ← Incorrectly positioned before tool call assistant: tool_call tool_call: rejectRequired Fix
The
ExtractAndRemoveApprovalRequestsAndResponsesmethod needs to track the original index/position of the approval request messages. ThenProcessFunctionApprovalResponsesshould:
- Insert the reconstructed
FunctionCallContentmessage at the original position of the approval request (not at the end)- Insert the rejection
FunctionResultContentimmediately after- Ensure any user messages that came after the approval response remain at the end
The key change is in
ProcessFunctionApprovalResponses- instead of:originalMessages.AddRange(preDownstreamCallHistory); // ... originalMessages.Add(rejectedPreDownstreamCallResultsMessage);It should insert at the correct position to preserve message ordering relative to any subsequent user messages.
Files to Modify
src/Libraries/Microsoft.Extensions.AI/ChatCompletion/FunctionInvokingChatClient.csTesting
The fix should be validated with test cases covering:
- Rejection with a user message added after the approval response
- Approval with a user message added after the approval response
- Multiple approval/rejection scenarios with interleaved user messages
Existing tests in
test/Libraries/Microsoft.Extensions.AI.Tests/ChatCompletion/FunctionInvokingChatClientApprovalsTests.csshould continue to pass, and new tests should be added for the scenarios described in issue #7156.
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.