11using System . Text . Json ;
2+ using Google . Protobuf ;
23using Grpc . Core ;
34using Grpc . Net . Client ;
45using Microsoft . Extensions . AI ;
@@ -39,79 +40,86 @@ 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 ;
57+
58+ //foreach (var output in response.Outputs.OrderBy(x => x.Index))
59+ //{
60+ // if (output.Message.Content is { Length: > 0 } text)
61+ // {
62+ // // Special-case output from tools
63+ // if (output.Message.Role == MessageRole.RoleTool &&
64+ // output.Message.ToolCalls.Count == 1 &&
65+ // output.Message.ToolCalls[0] is { } toolCall)
66+ // {
67+ // if (toolCall.Type == ToolCallType.McpTool)
68+ // {
69+ // message.Contents.Add(new McpServerToolCallContent(toolCall.Id, toolCall.Function.Name, null)
70+ // {
71+ // RawRepresentation = toolCall
72+ // });
73+ // message.Contents.Add(new McpServerToolResultContent(toolCall.Id)
74+ // {
75+ // RawRepresentation = toolCall,
76+ // Output = [new TextContent(text)]
77+ // });
78+ // continue;
79+ // }
80+ // else if (toolCall.Type == ToolCallType.CodeExecutionTool)
81+ // {
82+ // message.Contents.Add(new CodeInterpreterToolCallContent()
83+ // {
84+ // CallId = toolCall.Id,
85+ // RawRepresentation = toolCall
86+ // });
87+ // message.Contents.Add(new CodeInterpreterToolResultContent()
88+ // {
89+ // CallId = toolCall.Id,
90+ // RawRepresentation = toolCall,
91+ // Outputs = [new TextContent(text)]
92+ // });
93+ // continue;
94+ // }
95+ // else if (toolCall.Type == ToolCallType.CollectionsSearchTool)
96+ // {
97+ // var settings = JsonParser.Settings.Default.WithIgnoreUnknownFields(true);
98+ // var parser = new JsonParser(settings);
99+ // var matches = parser.Parse<SearchResponse>(text);
100+ // }
101+ // }
102+
103+ // var content = new TextContent(text) { Annotations = citations };
104+
105+ // foreach (var citation in output.Message.Citations)
106+ // (content.Annotations ??= []).Add(MapInlineCitation(citation));
107+
108+ // message.Contents.Add(content);
109+ // }
110+
111+ // foreach (var toolCall in output.Message.ToolCalls)
112+ // message.Contents.Add(MapToolCall(toolCall));
113+ //}
114+
115+ //return new ChatResponse(message)
116+ //{
117+ // ResponseId = response.Id,
118+ // ModelId = response.Model,
119+ // CreatedAt = response.Created?.ToDateTimeOffset(),
120+ // FinishReason = lastOutput != null ? MapFinishReason(lastOutput.FinishReason) : null,
121+ // Usage = MapToUsage(response.Usage),
122+ //};
115123 }
116124
117125 AIContent MapToolCall ( ToolCall toolCall ) => toolCall . Type switch
@@ -134,7 +142,7 @@ public async Task<ChatResponse> GetResponseAsync(IEnumerable<ChatMessage> messag
134142 CallId = toolCall . Id ,
135143 RawRepresentation = toolCall
136144 } ,
137- _ => new HostedToolCallContent ( )
145+ _ => new CollectionSearchToolCallContent ( )
138146 {
139147 CallId = toolCall . Id ,
140148 RawRepresentation = toolCall
@@ -188,15 +196,16 @@ async IAsyncEnumerable<ChatResponseUpdate> CompleteChatStreamingCore(IEnumerable
188196
189197 static CitationAnnotation MapInlineCitation ( InlineCitation citation ) => citation . CitationCase switch
190198 {
191- InlineCitation . CitationOneofCase . WebCitation => new CitationAnnotation { Url = new ( citation . WebCitation . Url ) } ,
192- InlineCitation . CitationOneofCase . XCitation => new CitationAnnotation { Url = new ( citation . XCitation . Url ) } ,
199+ InlineCitation . CitationOneofCase . WebCitation => new CitationAnnotation { Url = new ( citation . WebCitation . Url ) , RawRepresentation = citation } ,
200+ InlineCitation . CitationOneofCase . XCitation => new CitationAnnotation { Url = new ( citation . XCitation . Url ) , RawRepresentation = citation } ,
193201 InlineCitation . CitationOneofCase . CollectionsCitation => new CitationAnnotation
194202 {
195203 FileId = citation . CollectionsCitation . FileId ,
196204 Snippet = citation . CollectionsCitation . ChunkContent ,
197205 ToolName = "file_search" ,
206+ RawRepresentation = citation
198207 } ,
199- _ => new CitationAnnotation ( )
208+ _ => new CitationAnnotation { RawRepresentation = citation }
200209 } ;
201210
202211 static CitationAnnotation MapCitation ( string citation )
@@ -210,12 +219,13 @@ static CitationAnnotation MapCitation(string citation)
210219 var file = url . AbsolutePath [ 7 ..] ;
211220 return new CitationAnnotation
212221 {
213- ToolName = "collections_search" ,
214- FileId = file ,
215222 AdditionalProperties = new AdditionalPropertiesDictionary
216- {
217- { "collection_id" , collection }
218- }
223+ {
224+ { "collection_id" , collection }
225+ } ,
226+ FileId = file ,
227+ ToolName = "collections_search" ,
228+ Url = new Uri ( $ "collections://{ collection } /files/{ file } ") ,
219229 } ;
220230 }
221231
0 commit comments