Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Feb 4, 2026

Fix approval request/response correlation in FunctionInvokingChatClient

Summary

Fixed an inconsistency in how approval request messages are tracked vs looked up in FunctionInvokingChatClient.

Issue

  • Approval request messages were stored in a dictionary keyed by FunctionApprovalRequestContent.Id
  • Approval responses attempted to retrieve the original request message using approvalResponse.FunctionCall.CallId
  • This mismatch meant the request message often couldn't be found, causing reconstructed function call messages to lose metadata (e.g., original message id grouping) and fall back to synthetic ids

Changes Made

  • Changed dictionary lookup from approvalResponse.FunctionCall.CallId to approvalResponse.Id in ExtractAndRemoveApprovalRequestsAndResponses method
  • Dictionary key continues to use FunctionApprovalRequestContent.Id for storing approval request messages
  • Added regression test ApprovalResponsePreservesOriginalRequestMessageMetadata that validates the fix
  • Reverted accidental deletion in test method
  • All 440 tests in Microsoft.Extensions.AI.Tests pass
  • All 123 FunctionInvokingChatClient tests pass across net8.0, net9.0, and net10.0

Test Results

✅ All tests passing:

  • New regression test passes
  • All 26 FunctionInvokingChatClientApprovalsTests pass
  • All 123 FunctionInvokingChatClient tests pass
  • All 440 Microsoft.Extensions.AI.Tests pass
Original prompt

Summary

Fix approval request/response correlation in FunctionInvokingChatClient.

There is an inconsistency in how approval request messages are tracked vs looked up:

  • Approval request messages are currently stored in a dictionary keyed by FunctionApprovalRequestContent.Id.
  • Later, approval responses attempt to retrieve the original request message using approvalResponse.FunctionCall.CallId.

This mismatch means the request message often can’t be found, causing reconstructed function call messages to lose metadata (e.g., original message id grouping) and fall back to synthetic ids.

Code change

In src/Libraries/Microsoft.Extensions.AI/ChatCompletion/FunctionInvokingChatClient.cs, inside ExtractAndRemoveApprovalRequestsAndResponses, change the dictionary insertion for allApprovalRequestsMessages to use the function call id:

  • from: (allApprovalRequestsMessages ??= []).Add(farc.Id, message);
  • to: (allApprovalRequestsMessages ??= []).Add(farc.FunctionCall.CallId, message);

Regression test

Add a regression test that fails before this change and passes after.

Test scenario (high-level):

  1. Create chat history containing a ChatMessage that includes a FunctionApprovalRequestContent wrapping a FunctionCallContent with known CallId and known MessageId on the containing message.
  2. Add a ChatMessage containing a matching FunctionApprovalResponseContent for the same function call (same CallId), marked approved.
  3. Run the approval-processing path via FunctionInvokingChatClient.GetResponseAsync or GetStreamingResponseAsync (whichever is easiest to drive deterministically) such that ProcessFunctionApprovalResponses executes.
  4. Assert that the reconstructed function call message returned as part of the response stream/history preserves the original request message metadata (at minimum MessageId), rather than using the fallback synthetic id.

Notes:

  • Use an inner IChatClient test double that returns a deterministic response and does not itself require tool execution.
  • Keep the test minimal and focused on proving the request-message correlation.

Acceptance criteria

  • Dictionary key uses FunctionCall.CallId for request message tracking.
  • Regression test added and passes.
  • All tests pass.

This pull request was created from Copilot chat.


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Microsoft Reviewers: Open in CodeFlow

Copilot AI and others added 2 commits February 4, 2026 03:43
Co-authored-by: halter73 <54385+halter73@users.noreply.github.com>
Co-authored-by: halter73 <54385+halter73@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix approval request/response correlation in FunctionInvokingChatClient Fix approval request/response correlation in FunctionInvokingChatClient Feb 4, 2026
Copilot AI requested a review from halter73 February 4, 2026 03:50
Copy link
Member

@halter73 halter73 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot Update the allApprovalRequestsMessages dictionary to use FunctionApprovalRequestContent.Id and FunctionApprovalResponseContent.Id rather than FunctionCallContent.CallId as the key.

@dotnet-policy-service dotnet-policy-service bot added the waiting-author-feedback 📭 The author of this issue needs to respond in order for us to continue investigating this issue. label Feb 4, 2026
…tent.Id for correlation

Co-authored-by: halter73 <54385+halter73@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 4, 2026

@copilot Update the allApprovalRequestsMessages dictionary to use FunctionApprovalRequestContent.Id and FunctionApprovalResponseContent.Id rather than FunctionCallContent.CallId as the key.

Updated in commit 9034f0e. The dictionary now uses FunctionApprovalRequestContent.Id for storing (line 1470) and FunctionApprovalResponseContent.Id for lookup (line 1545), ensuring proper correlation of approval request/response pairs.

@dotnet-policy-service dotnet-policy-service bot removed the waiting-author-feedback 📭 The author of this issue needs to respond in order for us to continue investigating this issue. label Feb 4, 2026
Copilot AI requested a review from halter73 February 4, 2026 04:14
@halter73 halter73 marked this pull request as ready for review February 4, 2026 04:25
@halter73 halter73 requested a review from a team as a code owner February 4, 2026 04:25
Copilot AI review requested due to automatic review settings February 4, 2026 04:25
Copy link
Member

@halter73 halter73 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could consider also updating approvalRequestCallIds to be just approvalRequestIds, but I assume that the original function call IDs are more useful for diagnostic purposes than the approval IDs if they ever do happen to be different.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes approval request/response correlation in FunctionInvokingChatClient so that reconstructed function call messages can reliably reuse the original request message metadata (e.g., MessageId) instead of falling back to synthetic identifiers.

Changes:

  • Updated FunctionInvokingChatClient approval-response processing to look up the original approval request message by FunctionApprovalResponseContent.Id, matching the dictionary key FunctionApprovalRequestContent.Id.
  • Added a focused regression test ensuring that, for both non-streaming and streaming flows, the reconstructed function call message preserves the original request message’s MessageId.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
test/Libraries/Microsoft.Extensions.AI.Tests/ChatCompletion/FunctionInvokingChatClientApprovalsTests.cs Adds ApprovalResponsePreservesOriginalRequestMessageMetadata regression test verifying that approval processing preserves the original request message MessageId for both buffered and streaming paths.
src/Libraries/Microsoft.Extensions.AI/ChatCompletion/FunctionInvokingChatClient.cs Adjusts approval response handling to correlate responses with their requests via the shared approval Id, ensuring the original request ChatMessage is recovered and its metadata reused.

@stephentoub stephentoub enabled auto-merge (squash) February 4, 2026 13:34
This was referenced Feb 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants