.NET: Fix function approvals persistance with PerServiceCallChatHistoryPersistingChatClient#5805
Conversation
@microsoft-github-policy-service agree company="switchvisual" |
There was a problem hiding this comment.
Pull request overview
Fixes a regression in the .NET chat history persistence pipeline where per-service-call streaming persistence could miss FunctionInvokingChatClient’s rewrite from FunctionCallContent to ToolApprovalRequestContent for ApprovalRequiredAIFunction tools.
Changes:
- Update
PerServiceCallChatHistoryPersistingChatClient.GetStreamingResponseAsyncto retain the originalChatResponseUpdatereference (instead of a shallowClone()), so laterContentsrewrites are reflected in persisted history. - Add a unit test that reproduces the 1.4.0 regression and asserts that persisted history includes the approval-rewritten content.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| dotnet/src/Microsoft.Agents.AI/ChatClient/PerServiceCallChatHistoryPersistingChatClient.cs | Stops cloning streaming updates so persisted aggregation observes Contents rewrites performed by upstream middleware. |
| dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/PerServiceCallChatHistoryPersistingChatClientTests.cs | Adds a regression test ensuring approval-required function calls persist rewritten ToolApprovalRequestContent. |
PerServiceCallChatHistoryPersistingChatClient.GetStreamingResponseAsync snapshotted each update via update.Clone() (PR microsoft#5310, 1.4.0). Clone() is shallow: it shares the Contents list reference. FunctionInvokingChatClient rewrites FunctionCallContent → ToolApprovalRequestContent for ApprovalRequiredAIFunction tools by *reassigning* update.Contents to a new list, so the snapshot retained the pre-rewrite list and the ChatHistoryProvider received raw FunctionCallContent instead of ToolApprovalRequestContent. Revert to capturing the same reference so the aggregation sees FIC's rewrite. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
a3af4cd to
188a27b
Compare
|
@moonbox3 stuck at 1.3.0 due to this error. |
|
Adding @westey-m, @SergeyMenshykh, and @rogerbarreto to help. |
|
@scrodde, the clone introduction was on purpose to avoid storing the FunctionInvokingChatClient modified contents, since the modifications was resulting in failures, where follow up requests after a tool call failed due to mismatched FunctionCallContent and FunctionResultContent. Can you explain in more detail what problem you are trying to solve? |
Motivation and Context
PerServiceCallChatHistoryPersistingChatClient.GetStreamingResponseAsync snapshotted each update via update.Clone() (PR #5310, 1.4.0). Clone() is shallow: it shares the Contents list reference. FunctionInvokingChatClient rewrites FunctionCallContent → ToolApprovalRequestContent for ApprovalRequiredAIFunction tools by reassigning update.Contents to a new list, so the snapshot retained the pre-rewrite list and the ChatHistoryProvider received raw FunctionCallContent instead of ToolApprovalRequestContent.
Description
Revert to capturing the same reference so the so the persisted history sees FIC's rewrite
Contribution Checklist