.NET: fix(hosting): emit url_citation annotation events from streamed AI Search responses#6649
Open
anneheartrecord wants to merge 1 commit into
Open
Conversation
…arch responses OutputConverter.ConvertUpdatesToEventsAsync accumulated text content deltas but silently dropped CitationAnnotation metadata from TextContent.Annotations. As a result, hosted agents that use CreateAzureAISearchTool emitted citation markers in text (e.g. 【5:0†source】) but produced empty annotations arrays and no response.output_text.annotation.added SSE events. The fix accumulates UrlCitationBody SDK annotations across all TextContent updates for a message and emits them via TextContentBuilder.EmitAnnotationAdded after EmitTextDone (as required by the SDK lifecycle) and before EmitDone. Non-citation and region-less annotations are silently skipped, matching the existing OpenAI ChatCompletions path in AgentResponseExtensions. Adds 7 unit tests (N-01–N-07) covering: basic emission, ordering constraints, multiple annotations, multi-update accumulation, and skip conditions. Fixes microsoft#6641
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes the .NET Foundry Hosting SSE conversion path so that url_citation metadata (from Azure AI Search grounding) is not dropped during streaming: annotations from TextContent.Annotations are accumulated across deltas and emitted as response.output_text.annotation.added events via the Responses SDK builder lifecycle.
Changes:
- Accumulates MEAI
CitationAnnotationinstances from streamedTextContentupdates and converts them into Responses SDKUrlCitationBodyannotations. - Emits annotation-added events during message finalization (after
EmitTextDoneand beforeEmitDone) so clients receiveresponse.output_text.annotation.added. - Adds a new N-series unit test suite covering basic emission, ordering, multi-annotation, multi-update accumulation, and skip rules.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| dotnet/src/Microsoft.Agents.AI.Foundry.Hosting/OutputConverter.cs | Accumulates and emits citation annotations from streamed text updates as Responses SDK annotation events. |
| dotnet/tests/Microsoft.Agents.AI.Foundry.Hosting.UnitTests/OutputConverterTests.cs | Adds N-series unit tests validating url_citation annotation event emission, ordering, accumulation, and skip conditions. |
Comment on lines
+1398
to
+1405
| var annotationEvent = Assert.Single(events.OfType<ResponseOutputTextAnnotationAddedEvent>()); | ||
| var urlCitation = Assert.IsType<UrlCitationBody>(annotationEvent.Annotation); | ||
| Assert.Equal(new Uri("https://example.com/doc"), urlCitation.Url); | ||
| Assert.Equal("Example Document", urlCitation.Title); | ||
| Assert.Equal(0L, urlCitation.StartIndex); | ||
| Assert.Equal(5L, urlCitation.EndIndex); | ||
| Assert.IsType<ResponseCompletedEvent>(events[^1]); | ||
| } |
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.
Problem
When using
FoundryAITool.CreateAzureAISearchTool()with a hosted agent viaMapFoundryResponses, the SSE stream produced citation markers in text (e.g.【5:0†source】) but:response.output_text.annotation.addedevents were never emittedannotationsarrays inresponse.content_part.doneandresponse.output_item.donewere always emptyRoot Cause
OutputConverter.ConvertUpdatesToEventsAsyncprocessesTextContentfromAgentResponseUpdate.Contentsbut only extracts.Text— it ignoresTextContent.Annotations, which is where the agent framework surfacesCitationAnnotation(url_citation) metadata from Azure AI Search grounding.Fix
CitationAnnotationinstances from eachTextContentupdate across the full message (parallel to how text is accumulated inStringBuilder)UrlCitationBodySDK objects via a newConvertToSdkAnnotationshelper, matching the approach already used by the OpenAI ChatCompletions path inAgentResponseExtensions.ToChoiceMessageAnnotationsTextContentBuilder.EmitAnnotationAddedinCloseCurrentMessage, afterEmitTextDoneand beforeEmitDone(required by the SDK builder lifecycle)CitationAnnotationtypes and citations without explicitTextSpanAnnotatedRegionstart/end indices are silently skippedTests
7 new unit tests (N-01–N-07) in
OutputConverterTests:CitationAnnotationwith null URL is skippedCitationAnnotationwith noTextSpanAnnotatedRegionis skippedCitationAnnotationAIAnnotationis skippedFixes #6641