11using System . Text . Json ;
2+ using Google . Protobuf ;
23using Grpc . Core ;
34using Grpc . Net . Client ;
45using Microsoft . Extensions . AI ;
@@ -39,82 +40,23 @@ public async Task<ChatResponse> GetResponseAsync(IEnumerable<ChatMessage> messag
3940 var response = await client . GetCompletionAsync ( request , cancellationToken : cancellationToken ) ;
4041 var lastOutput = response . Outputs . OrderByDescending ( x => x . Index ) . FirstOrDefault ( ) ;
4142
42- if ( lastOutput == null )
43- {
44- return new ChatResponse ( )
45- {
46- ResponseId = response . Id ,
47- ModelId = response . Model ,
48- CreatedAt = response . Created . ToDateTimeOffset ( ) ,
49- Usage = MapToUsage ( response . Usage ) ,
50- } ;
51- }
52-
53- var message = new ChatMessage ( MapRole ( lastOutput . Message . Role ) , default ( string ) ) ;
54- var citations = response . Citations ? . Distinct ( ) . Select ( MapCitation ) . ToList < AIAnnotation > ( ) ;
55-
56- foreach ( var output in response . Outputs . OrderBy ( x => x . Index ) )
57- {
58- if ( output . Message . Content is { Length : > 0 } text )
59- {
60- // Special-case output from tools
61- if ( output . Message . Role == MessageRole . RoleTool &&
62- output . Message . ToolCalls . Count == 1 &&
63- output . Message . ToolCalls [ 0 ] is { } toolCall )
64- {
65- if ( toolCall . Type == ToolCallType . McpTool )
66- {
67- message . Contents . Add ( new McpServerToolCallContent ( toolCall . Id , toolCall . Function . Name , null )
68- {
69- RawRepresentation = toolCall
70- } ) ;
71- message . Contents . Add ( new McpServerToolResultContent ( toolCall . Id )
72- {
73- RawRepresentation = toolCall ,
74- Output = [ new TextContent ( text ) ]
75- } ) ;
76- continue ;
77- }
78- else if ( toolCall . Type == ToolCallType . CodeExecutionTool )
79- {
80- message . Contents . Add ( new CodeInterpreterToolCallContent ( )
81- {
82- CallId = toolCall . Id ,
83- RawRepresentation = toolCall
84- } ) ;
85- message . Contents . Add ( new CodeInterpreterToolResultContent ( )
86- {
87- CallId = toolCall . Id ,
88- RawRepresentation = toolCall ,
89- Outputs = [ new TextContent ( text ) ]
90- } ) ;
91- continue ;
92- }
93- }
94-
95- var content = new TextContent ( text ) { Annotations = citations } ;
96-
97- foreach ( var citation in output . Message . Citations )
98- ( content . Annotations ??= [ ] ) . Add ( MapInlineCitation ( citation ) ) ;
99-
100- message . Contents . Add ( content ) ;
101- }
102-
103- foreach ( var toolCall in output . Message . ToolCalls )
104- message . Contents . Add ( MapToolCall ( toolCall ) ) ;
105- }
106-
107- return new ChatResponse ( message )
43+ var result = new ChatResponse ( )
10844 {
10945 ResponseId = response . Id ,
11046 ModelId = response . Model ,
11147 CreatedAt = response . Created ? . ToDateTimeOffset ( ) ,
11248 FinishReason = lastOutput != null ? MapFinishReason ( lastOutput . FinishReason ) : null ,
11349 Usage = MapToUsage ( response . Usage ) ,
11450 } ;
51+
52+ var citations = response . Citations ? . Distinct ( ) . Select ( MapCitation ) . ToList < AIAnnotation > ( ) ;
53+
54+ ( ( List < ChatMessage > ) result . Messages ) . AddRange ( response . Outputs . AsChatMessages ( citations ) ) ;
55+
56+ return result ;
11557 }
11658
117- AIContent MapToolCall ( ToolCall toolCall ) => toolCall . Type switch
59+ AIContent ? MapToolCall ( ToolCall toolCall ) => toolCall . Type switch
11860 {
11961 ToolCallType . ClientSideTool => new FunctionCallContent (
12062 toolCall . Id ,
@@ -134,11 +76,12 @@ public async Task<ChatResponse> GetResponseAsync(IEnumerable<ChatMessage> messag
13476 CallId = toolCall . Id ,
13577 RawRepresentation = toolCall
13678 } ,
137- _ => new HostedToolCallContent ( )
79+ ToolCallType . CollectionsSearchTool => new CollectionSearchToolCallContent ( )
13880 {
13981 CallId = toolCall . Id ,
14082 RawRepresentation = toolCall
141- }
83+ } ,
84+ _ => null
14285 } ;
14386
14487 public IAsyncEnumerable < ChatResponseUpdate > GetStreamingResponseAsync ( IEnumerable < ChatMessage > messages , ChatOptions ? options = null , CancellationToken cancellationToken = default )
@@ -161,44 +104,30 @@ async IAsyncEnumerable<ChatResponseUpdate> CompleteChatStreamingCore(IEnumerable
161104 ResponseId = chunk . Id ,
162105 ModelId = chunk . Model ,
163106 CreatedAt = chunk . Created ? . ToDateTimeOffset ( ) ,
107+ RawRepresentation = chunk ,
164108 FinishReason = output . FinishReason != FinishReason . ReasonInvalid ? MapFinishReason ( output . FinishReason ) : null ,
165109 } ;
166110
167- if ( chunk . Citations is { Count : > 0 } citations )
111+ var citations = chunk . Citations ? . Distinct ( ) . Select ( MapCitation ) . ToList < AIAnnotation > ( ) ;
112+ if ( citations ? . Count > 0 )
168113 {
169114 var textContent = update . Contents . OfType < TextContent > ( ) . FirstOrDefault ( ) ;
170115 if ( textContent == null )
171116 {
172117 textContent = new TextContent ( string . Empty ) ;
173118 update . Contents . Add ( textContent ) ;
174119 }
175-
176- foreach ( var citation in citations . Distinct ( ) )
177- ( textContent . Annotations ??= [ ] ) . Add ( MapCitation ( citation ) ) ;
120+ ( ( List < AIAnnotation > ) ( textContent . Annotations ??= [ ] ) ) . AddRange ( citations ) ;
178121 }
179122
180- foreach ( var toolCall in output . Delta . ToolCalls )
181- update . Contents . Add ( MapToolCall ( toolCall ) ) ;
123+ ( ( List < AIContent > ) update . Contents ) . AddRange ( output . Delta . ToolCalls . AsContents ( text , citations ) ) ;
182124
183125 if ( update . Contents . Any ( ) )
184126 yield return update ;
185127 }
186128 }
187129 }
188130
189- static CitationAnnotation MapInlineCitation ( InlineCitation citation ) => citation . CitationCase switch
190- {
191- InlineCitation . CitationOneofCase . WebCitation => new CitationAnnotation { Url = new ( citation . WebCitation . Url ) } ,
192- InlineCitation . CitationOneofCase . XCitation => new CitationAnnotation { Url = new ( citation . XCitation . Url ) } ,
193- InlineCitation . CitationOneofCase . CollectionsCitation => new CitationAnnotation
194- {
195- FileId = citation . CollectionsCitation . FileId ,
196- Snippet = citation . CollectionsCitation . ChunkContent ,
197- ToolName = "file_search" ,
198- } ,
199- _ => new CitationAnnotation ( )
200- } ;
201-
202131 static CitationAnnotation MapCitation ( string citation )
203132 {
204133 var url = new Uri ( citation ) ;
@@ -210,12 +139,13 @@ static CitationAnnotation MapCitation(string citation)
210139 var file = url . AbsolutePath [ 7 ..] ;
211140 return new CitationAnnotation
212141 {
213- ToolName = "collections_search" ,
214- FileId = file ,
215142 AdditionalProperties = new AdditionalPropertiesDictionary
216- {
217- { "collection_id" , collection }
218- }
143+ {
144+ { "collection_id" , collection }
145+ } ,
146+ FileId = file ,
147+ ToolName = "collections_search" ,
148+ Url = new Uri ( $ "collections://{ collection } /files/{ file } ") ,
219149 } ;
220150 }
221151
0 commit comments