extensions icon indicating copy to clipboard operation
extensions copied to clipboard

Fix message ordering when processing function approval responses

Open Copilot opened this issue 2 weeks ago • 0 comments

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: ExtractAndRemoveApprovalRequestsAndResponses now returns the index where approval requests were originally located
  • Insert instead of append: ProcessFunctionApprovalResponses uses InsertRange/Insert at the tracked position rather than AddRange/Add
  • Thread index through call chain: ProcessFunctionCallsAsync accepts an insertionIndex parameter 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 FunctionInvokingChatClient processes FunctionApprovalResponseContent, 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, the ProcessFunctionApprovalResponses method:

  1. Calls ExtractAndRemoveApprovalRequestsAndResponses which removes FunctionApprovalRequestContent and FunctionApprovalResponseContent from their original positions in the message list
  2. Then appends the reconstructed FunctionCallContent and FunctionResultContent messages at the end of the list using AddRange and Add (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 last

Actual message order:

user: 1st message
user: 2nd message  ← Incorrectly positioned before tool call
assistant: tool_call
tool_call: reject

Required Fix

The ExtractAndRemoveApprovalRequestsAndResponses method needs to track the original index/position of the approval request messages. Then ProcessFunctionApprovalResponses should:

  1. Insert the reconstructed FunctionCallContent message at the original position of the approval request (not at the end)
  2. Insert the rejection FunctionResultContent immediately after
  3. 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.cs

Testing

The fix should be validated with test cases covering:

  1. Rejection with a user message added after the approval response
  2. Approval with a user message added after the approval response
  3. Multiple approval/rejection scenarios with interleaved user messages

Existing tests in test/Libraries/Microsoft.Extensions.AI.Tests/ChatCompletion/FunctionInvokingChatClientApprovalsTests.cs should 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 FunctionInvokingChatClient processes FunctionApprovalResponseContent, 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, the ProcessFunctionApprovalResponses method:

  1. Calls ExtractAndRemoveApprovalRequestsAndResponses which removes FunctionApprovalRequestContent and FunctionApprovalResponseContent from their original positions in the message list
  2. Then appends the reconstructed FunctionCallContent and FunctionResultContent messages at the end of the list using AddRange and Add (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 last

Actual message order:

user: 1st message
user: 2nd message  ← Incorrectly positioned before tool call
assistant: tool_call
tool_call: reject

Required Fix

The ExtractAndRemoveApprovalRequestsAndResponses method needs to track the original index/position of the approval request messages. Then ProcessFunctionApprovalResponses should:

  1. Insert the reconstructed FunctionCallContent message at the original position of the approval request (not at the end)
  2. Insert the rejection FunctionResultContent immediately after
  3. 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.cs

Testing

The fix should be validated with test cases covering:

  1. Rejection with a user message added after the approval response
  2. Approval with a user message added after the approval response
  3. Multiple approval/rejection scenarios with interleaved user messages

Existing tests in test/Libraries/Microsoft.Extensions.AI.Tests/ChatCompletion/FunctionInvokingChatClientApprovalsTests.cs should 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.

Microsoft Reviewers: Open in CodeFlow

Copilot avatar Dec 16 '25 15:12 Copilot