Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions provider/copilotprovider/copilot.go
Original file line number Diff line number Diff line change
Expand Up @@ -668,10 +668,23 @@ func (p *provider) assistantMessageUpdate(event copilot.SessionEvent, data *copi
ContentHeader: message.ContentHeader{RawRepresentation: event},
}}
} else {
update.Contents = []message.Content{&message.TextContent{
textContent := &message.TextContent{
ContentHeader: message.ContentHeader{RawRepresentation: event},
Text: data.Content,
}}
}
// Surface native model citations (enabled via AgentConfig.EnableCitations)
// as CitationAnnotations, mirroring the OpenAI chat/Responses providers.
if data.Citations != nil {
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,
})
}
Comment on lines +678 to +685
}
update.Contents = []message.Content{textContent}
if data.ReasoningText != nil {
update.Contents = append(update.Contents, &message.TextReasoningContent{
ContentHeader: message.ContentHeader{RawRepresentation: event},
Expand Down Expand Up @@ -837,6 +850,13 @@ func int64Value(value *int64) int64 {
return *value
}

func derefString(value *string) string {
if value == nil {
return ""
}
return *value
}
Comment on lines +853 to +858

func firstNonNilString(values ...*string) string {
for _, value := range values {
if value != nil {
Expand Down
40 changes: 40 additions & 0 deletions provider/copilotprovider/copilot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1264,3 +1264,43 @@ func assertStringSlice(t *testing.T, got any, want []string, name string) {
}
}
}

func TestConvertToAgentResponseUpdate_AssistantMessageSurfacesCitations(t *testing.T) {
runtime := newFakeRuntime(t,
sessionEvent("assistant.message", map[string]any{
"messageId": "msg-cite",
"content": "The sky is blue.",
"citations": map[string]any{
"sources": []any{
map[string]any{
"id": "s1",
"provider": "openai",
"title": "Sky facts",
"url": "https://example.com/sky",
},
},
"spans": []any{},
},
}),
idleEvent(),
)
agent := copilotprovider.NewAgent(runtime.client(), copilotprovider.AgentConfig{})

response, err := runText(t, agent, "why is the sky blue?", agentpkg.Stream(false))
if err != nil {
t.Fatalf("RunText: %v", err)
}
text := firstContent[*message.TextContent](t, response)
var citation *message.CitationAnnotation
for _, ann := range text.Annotations {
if c, ok := ann.(*message.CitationAnnotation); ok {
citation = c
}
}
if citation == nil {
t.Fatalf("expected a CitationAnnotation on the assistant text, got %#v", text.Annotations)
}
if citation.Title != "Sky facts" || citation.URL != "https://example.com/sky" {
t.Errorf("citation = %#v, want Title=%q URL=%q", citation, "Sky facts", "https://example.com/sky")
}
}
Loading