@@ -23,17 +23,62 @@ internal GrokChatClient(GrpcChannel channel, GrokClientOptions clientOptions, st
2323 public async Task < ChatResponse > GetResponseAsync ( IEnumerable < ChatMessage > messages , ChatOptions ? options = null , CancellationToken cancellationToken = default )
2424 {
2525 var requestDto = MapToRequest ( messages , options ) ;
26-
2726 var protoResponse = await client . GetCompletionAsync ( requestDto , cancellationToken : cancellationToken ) ;
27+ var lastOutput = protoResponse . Outputs . OrderByDescending ( x => x . Index ) . FirstOrDefault ( ) ;
2828
29- var chatMessages = protoResponse . Outputs
30- . Select ( x => MapToChatMessage ( x . Message , protoResponse . Citations ) )
31- . Where ( x => x . Contents . Count > 0 )
32- . ToList ( ) ;
29+ if ( lastOutput == null )
30+ {
31+ return new ChatResponse ( )
32+ {
33+ ResponseId = protoResponse . Id ,
34+ ModelId = protoResponse . Model ,
35+ CreatedAt = protoResponse . Created . ToDateTimeOffset ( ) ,
36+ Usage = MapToUsage ( protoResponse . Usage ) ,
37+ } ;
38+ }
3339
34- var lastOutput = protoResponse . Outputs . LastOrDefault ( ) ;
40+ var message = new ChatMessage ( MapRole ( lastOutput . Message . Role ) , default ( string ) ) ;
41+ var citations = protoResponse . Citations ? . Distinct ( ) . Select ( MapCitation ) . ToList < AIAnnotation > ( ) ;
3542
36- return new ChatResponse ( chatMessages )
43+ foreach ( var output in protoResponse . Outputs . OrderBy ( x => x . Index ) )
44+ {
45+ if ( output . Message . Content is { Length : > 0 } text )
46+ {
47+ var content = new TextContent ( text )
48+ {
49+ Annotations = citations
50+ } ;
51+
52+ foreach ( var citation in output . Message . Citations )
53+ {
54+ ( content . Annotations ??= [ ] ) . Add ( MapInlineCitation ( citation ) ) ;
55+ }
56+ message . Contents . Add ( content ) ;
57+ }
58+
59+ foreach ( var toolCall in output . Message . ToolCalls )
60+ {
61+ if ( toolCall . Type == ToolCallType . ClientSideTool )
62+ {
63+ var arguments = ! string . IsNullOrEmpty ( toolCall . Function . Arguments )
64+ ? JsonSerializer . Deserialize < IDictionary < string , object ? > > ( toolCall . Function . Arguments )
65+ : null ;
66+
67+ var content = new FunctionCallContent (
68+ toolCall . Id ,
69+ toolCall . Function . Name ,
70+ arguments ) ;
71+
72+ message . Contents . Add ( content ) ;
73+ }
74+ else
75+ {
76+ message . Contents . Add ( new HostedToolCallContent ( toolCall ) ) ;
77+ }
78+ }
79+ }
80+
81+ return new ChatResponse ( message )
3782 {
3883 ResponseId = protoResponse . Id ,
3984 ModelId = protoResponse . Model ,
@@ -80,7 +125,7 @@ async IAsyncEnumerable<ChatResponseUpdate> CompleteChatStreamingCore(IEnumerable
80125
81126 foreach ( var citation in citations . Distinct ( ) )
82127 {
83- ( textContent . Annotations ??= [ ] ) . Add ( new CitationAnnotation { Url = new ( citation ) } ) ;
128+ ( textContent . Annotations ??= [ ] ) . Add ( MapCitation ( citation ) ) ;
84129 }
85130 }
86131
@@ -89,46 +134,37 @@ async IAsyncEnumerable<ChatResponseUpdate> CompleteChatStreamingCore(IEnumerable
89134 }
90135 }
91136
92- static ChatMessage MapToChatMessage ( CompletionMessage message , IList < string > ? citations = null )
137+ static CitationAnnotation MapInlineCitation ( InlineCitation citation ) => citation . CitationCase switch
93138 {
94- var chatMessage = new ChatMessage ( ) { Role = MapRole ( message . Role ) } ;
139+ InlineCitation . CitationOneofCase . WebCitation => new CitationAnnotation { Url = new ( citation . WebCitation . Url ) } ,
140+ InlineCitation . CitationOneofCase . XCitation => new CitationAnnotation { Url = new ( citation . XCitation . Url ) } ,
141+ InlineCitation . CitationOneofCase . CollectionsCitation => new CitationAnnotation
142+ {
143+ FileId = citation . CollectionsCitation . FileId ,
144+ Snippet = citation . CollectionsCitation . ChunkContent ,
145+ ToolName = "file_search" ,
146+ } ,
147+ _ => new CitationAnnotation ( )
148+ } ;
95149
96- if ( message . Content is { Length : > 0 } text )
150+ static CitationAnnotation MapCitation ( string citation )
151+ {
152+ var url = new Uri ( citation ) ;
153+ if ( url . Scheme != "collections" )
154+ return new CitationAnnotation { Url = url } ;
155+
156+ // Special-case collection citations so we get better metadata
157+ var collection = url . Host ;
158+ var file = url . AbsolutePath [ 7 ..] ;
159+ return new CitationAnnotation
97160 {
98- var textContent = new TextContent ( text ) ;
99- if ( citations is { Count : > 0 } )
100- {
101- foreach ( var citation in citations . Distinct ( ) )
161+ ToolName = "collections_search" ,
162+ FileId = file ,
163+ AdditionalProperties = new AdditionalPropertiesDictionary
102164 {
103- ( textContent . Annotations ??= [ ] ) . Add ( new CitationAnnotation { Url = new ( citation ) } ) ;
165+ { "collection_id" , collection }
104166 }
105- }
106- chatMessage . Contents . Add ( textContent ) ;
107- }
108-
109- foreach ( var toolCall in message . ToolCalls )
110- {
111- // Only include client-side tools in the response messages
112- if ( toolCall . Type == ToolCallType . ClientSideTool )
113- {
114- var arguments = ! string . IsNullOrEmpty ( toolCall . Function . Arguments )
115- ? JsonSerializer . Deserialize < IDictionary < string , object ? > > ( toolCall . Function . Arguments )
116- : null ;
117-
118- var content = new FunctionCallContent (
119- toolCall . Id ,
120- toolCall . Function . Name ,
121- arguments ) ;
122-
123- chatMessage . Contents . Add ( content ) ;
124- }
125- else
126- {
127- chatMessage . Contents . Add ( new HostedToolCallContent ( toolCall ) ) ;
128- }
129- }
130-
131- return chatMessage ;
167+ } ;
132168 }
133169
134170 GetCompletionsRequest MapToRequest ( IEnumerable < ChatMessage > messages , ChatOptions ? options )
@@ -258,12 +294,25 @@ GetCompletionsRequest MapToRequest(IEnumerable<ChatMessage> messages, ChatOption
258294
259295 if ( fileSearch . Inputs ? . OfType < HostedVectorStoreContent > ( ) is { } vectorStores )
260296 toolProto . CollectionIds . AddRange ( vectorStores . Select ( x => x . VectorStoreId ) . Distinct ( ) ) ;
261-
297+
262298 if ( fileSearch . MaximumResultCount is { } maxResults )
263299 toolProto . Limit = maxResults ;
264-
300+
265301 request . Tools . Add ( new Tool { CollectionsSearch = toolProto } ) ;
266302 }
303+ else if ( tool is HostedMcpServerTool mcpTool )
304+ {
305+ request . Tools . Add ( new Tool
306+ {
307+ Mcp = new MCP
308+ {
309+ Authorization = mcpTool . AuthorizationToken ,
310+ ServerLabel = mcpTool . ServerName ,
311+ ServerUrl = mcpTool . ServerAddress ,
312+ AllowedToolNames = { mcpTool . AllowedTools ?? Array . Empty < string > ( ) }
313+ }
314+ } ) ;
315+ }
267316 }
268317 }
269318
0 commit comments