Surface Copilot model citations as CitationAnnotations - #1009
Surface Copilot model citations as CitationAnnotations#1009PratikDhanave (PratikDhanave) wants to merge 1 commit into
Conversation
The Copilot provider plumbs AgentConfig.EnableCitations into the session config, so the model returns citations on AssistantMessageData.Citations - but assistantMessageUpdate never read that field, silently dropping every citation. Map each citation source onto the assistant TextContent's Annotations as a message.CitationAnnotation (title/url/file path), mirroring how the OpenAI chat and Responses providers surface url_citation annotations.
There was a problem hiding this comment.
🟢 Approval recommended
The change is narrowly scoped, aligns with existing provider behavior, and includes a focused regression test; remaining feedback is minor/defensive maintainability.
Pull request overview
Maps GitHub Copilot model citation sources returned on AssistantMessageData.Citations into message.CitationAnnotations on the assistant’s TextContent, ensuring citations aren’t silently dropped for non-streaming runs.
Changes:
- In
assistantMessageUpdate, appendCitationAnnotationentries for each citation source on non-streamingassistant.messageevents. - Add
derefStringhelper for optional*stringcitation fields. - Add a unit test asserting citations are surfaced as
TextContent.Annotations.
File summaries
| File | Description |
|---|---|
| provider/copilotprovider/copilot.go | Surfaces data.Citations.Sources as CitationAnnotations on non-streaming assistant text content. |
| provider/copilotprovider/copilot_test.go | Adds a regression test verifying citation annotations are produced from an assistant.message event. |
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| for _, source := range data.Citations.Sources { | ||
| textContent.Annotations = append(textContent.Annotations, &message.CitationAnnotation{ | ||
| FileID: derefString(source.Path), | ||
| Title: derefString(source.Title), | ||
| URL: derefString(source.URL), | ||
| RawRepresentation: source, | ||
| }) | ||
| } |
| func derefString(value *string) string { | ||
| if value == nil { | ||
| return "" | ||
| } | ||
| return *value | ||
| } |
|
Scope: user-visible behavior (bug fix), internal-only implementation Changed Go contract: No new exported API. Upstream evidence reviewed:
Result: aligned — this is a defensible bug fix that brings the Copilot provider's citation handling in line with the same-language precedent set by the OpenAI and Anthropic Go providers (map source citations onto Warning Firewall blocked 1 domainThe following domain was blocked by the firewall during workflow execution:
To allow these domains, add them to the network:
allowed:
- defaults
- "github.com"See Network Configuration for more information.
|
Problem
The Copilot provider already plumbs
AgentConfig.EnableCitationsinto the session config (copilot.go), so a citation-capable model returns citations onAssistantMessageData.Citations. ButassistantMessageUpdateonly readsContentandReasoningText— it never looks atCitations, so every returned citation is silently dropped. This is a one-sided gap: the request opts in, the response data is discarded.Fix
When
data.Citationsis present on the non-streaming assistant message, map eachCitationSourceonto the assistantTextContent'sAnnotationsas amessage.CitationAnnotation(title / URL / file path). This mirrors how the OpenAI chat (populateChatAnnotations) and Responses providers surfaceurl_citationannotations onto text content.Test
TestConvertToAgentResponseUpdate_AssistantMessageSurfacesCitationsemits anassistant.messageevent carrying a citation source and asserts the resultingTextContenthas aCitationAnnotationwith the source's title and URL. Fails before the fix (no annotations), passes after.Note: this maps the deduplicated citation sources; span-to-text-region mapping (
Citations.Spans→AnnotatedRegions) can follow as an enhancement.