.NET: Fix GitHubCopilotAgent producing duplicated text content (#3979)#4145
Open
kallebelins wants to merge 1 commit intomicrosoft:mainfrom
Open
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes a bug where GitHubCopilotAgent produced duplicated text content during streaming responses. The bug occurred because both AssistantMessageDeltaEvent (incremental chunks) and AssistantMessageEvent (complete assembled message) were emitting TextContent, causing consumers to see the full response text twice.
Changes:
- Removed the explicit
AssistantMessageEventcase handler in the streaming switch statement, allowing it to fall through to the default handler that stores only raw representation - Modified
ConvertToAgentResponseUpdate(AssistantMessageEvent)to return genericAIContentinstead ofTextContentto prevent duplication - Changed visibility of conversion methods from
privatetointernalto enable unit testing - Added 8 comprehensive unit tests to verify the fix and prevent regression
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| GitHubCopilotAgent.cs | Fixed streaming event handling to prevent text duplication; removed AssistantMessageEvent case and updated conversion method to not produce TextContent |
| GitHubCopilotAgentDuplicateTextTests.cs | Added comprehensive test suite with 8 tests covering delta events, complete message events, usage events, session events, and end-to-end streaming simulation |
...t/tests/Microsoft.Agents.AI.GitHub.Copilot.UnitTests/GitHubCopilotAgentDuplicateTextTests.cs
Outdated
Show resolved
Hide resolved
) When streaming responses, GitHubCopilotAgent emitted TextContent from both AssistantMessageDeltaEvent (incremental chunks) and AssistantMessageEvent (complete assembled message). Consumers concatenating all streaming updates would see the full text duplicated. Root cause: RunCoreStreamingAsync had an explicit case for AssistantMessageEvent that called ConvertToAgentResponseUpdate, which created a TextContent with the full assembled text — the same text already streamed via delta events. Fix: - Remove the dedicated AssistantMessageEvent case from the streaming switch so it falls through to the default handler, which stores the event as RawRepresentation only (no TextContent). - Change ConvertToAgentResponseUpdate(AssistantMessageEvent) to return a generic AIContent with RawRepresentation instead of TextContent, matching the Python SDK approach. - Change ConvertToAgentResponseUpdate methods from private to internal to enable direct unit testing. Tests added (GitHubCopilotAgentDuplicateTextTests.cs — 8 tests): - ConvertDeltaEvent_ProducesTextContent - ConvertDeltaEvent_PreservesMessageId - ConvertAssistantMessageEvent_DoesNotProduceTextContent - ConvertAssistantMessageEvent_PreservesIdsAndTimestamp - StreamingSimulation_DeltasPlusComplete_NoDuplicatedText - ConvertDeltaEvent_EmptyDeltaContent_ProducesEmptyTextContent - ConvertUsageEvent_ProducesUsageContent_NotTextContent - ConvertSessionEvent_ProducesRawContent_NotTextContent All 22 existing + 8 new unit tests pass on net8.0, net9.0 and net10.0. Fixes microsoft#3979
832a912 to
5a19ab3
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation and Context
This change fixes a bug where
GitHubCopilotAgentproduces duplicated text content during streaming responses (issue #3979).When streaming, the agent emitted
TextContentfrom bothAssistantMessageDeltaEvent(incremental chunks) andAssistantMessageEvent(complete assembled message). Any consumer concatenating allTextContentfrom the streaming updates would see the full response text duplicated — e.g.,"Hello world!Hello world!"instead of"Hello world!".This is a correctness bug that affects all downstream integrations relying on the streaming pipeline, including AG-UI and CopilotKit clients.
Fixes #3979
Description
Root cause:
RunCoreStreamingAsynchad an explicitcase AssistantMessageEventin its switch statement that calledConvertToAgentResponseUpdate, which created aTextContentcontaining the full assembled text — the same text already streamed incrementally viaAssistantMessageDeltaEventchunks.Fix applied:
AssistantMessageEventcase from the streaming switch so it falls through to thedefaulthandler, which stores the event asRawRepresentationonly (noTextContent). A code comment explains the intentional omission.ConvertToAgentResponseUpdate(AssistantMessageEvent)to return a genericAIContentwithRawRepresentationinstead ofTextContent, so even if called directly it cannot produce duplicate text. This follows the same approach used by the Python SDK.ConvertToAgentResponseUpdatemethods fromprivatetointernalto enable direct unit testing of each event type conversion.Tests added (GitHubCopilotAgentDuplicateTextTests.cs — 8 new tests):
ConvertDeltaEvent_ProducesTextContentTextContentfor streamingConvertDeltaEvent_PreservesMessageIdConvertAssistantMessageEvent_DoesNotProduceTextContentTextContent(the fix)ConvertAssistantMessageEvent_PreservesIdsAndTimestampTextContentStreamingSimulation_DeltasPlusComplete_NoDuplicatedTextConvertDeltaEvent_EmptyDeltaContent_ProducesEmptyTextContentConvertUsageEvent_ProducesUsageContent_NotTextContentUsageContent, notTextContentConvertSessionEvent_ProducesRawContent_NotTextContentTextContentAll 22 existing + 8 new unit tests pass on net8.0, net9.0, and net10.0.
Contribution Checklist
AssistantMessageEventdata remains accessible viaRawRepresentationfor any consumer that needs it; only the spuriousTextContentduplication is removed.