Skip to content

Commit f977db8

Browse files
committed
Bump to latest AI abstractions, adapt code to it
Changes around MCP, auth and outputs.
1 parent f7e1116 commit f977db8

8 files changed

Lines changed: 48 additions & 35 deletions

File tree

src/xAI.Protocol/xAI.Protocol.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
<ItemGroup>
1616
<PackageReference Include="NuGetizer" Version="1.4.7" PrivateAssets="all" />
1717
<PackageReference Include="Google.Protobuf" Version="3.34.0" />
18-
<PackageReference Include="Grpc.Net.Client" Version="2.71.0" />
19-
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.71.0" />
18+
<PackageReference Include="Grpc.Net.Client" Version="2.76.0" />
19+
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.76.0" />
2020
<PackageReference Include="Grpc.Tools" Version="2.78.0" PrivateAssets="all" />
2121
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.4" />
2222
</ItemGroup>

src/xAI.Tests/ChatClientTests.cs

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -456,45 +456,45 @@ public async Task GrokInvokesHostedMcp()
456456
{
457457
var messages = new Chat()
458458
{
459-
{ "user", "When was GrokClient v1.0.0 released on the devlooped/GrokClient repo? Respond with just the date, in YYYY-MM-DD format." },
459+
{ "user", "When was GrokClient v1.1.0 released on the devlooped/GrokClient repo? Respond with just the date, in YYYY-MM-DD format." },
460460
};
461461

462-
var grok = new GrokClient(Configuration["XAI_API_KEY"]!).AsIChatClient("grok-4-fast");
462+
var grok = new GrokClient(Configuration["XAI_API_KEY"]!).AsIChatClient("grok-4-1-fast-non-reasoning");
463463

464464
var options = new ChatOptions
465465
{
466466
Tools = [new HostedMcpServerTool("GitHub", "https://api.githubcopilot.com/mcp/") {
467-
AuthorizationToken = Configuration["GITHUB_TOKEN"]!,
467+
Headers = new Dictionary<string, string> { ["Authorization"] = Configuration["GITHUB_TOKEN"]! },
468468
AllowedTools = ["list_releases"],
469469
}]
470470
};
471471

472472
var response = await grok.GetResponseAsync(messages, options);
473473
var text = response.Text;
474474

475-
Assert.Equal("2025-11-29", text);
475+
Assert.Equal("2026-01-05", text);
476476
var call = Assert.Single(response.Messages
477477
.SelectMany(x => x.Contents)
478478
.OfType<McpServerToolCallContent>());
479479

480-
Assert.Equal("GitHub.list_releases", call.ToolName);
480+
Assert.Equal("GitHub.list_releases", call.Name);
481481
}
482482

483483
[SecretsFact("XAI_API_KEY", "GITHUB_TOKEN")]
484484
public async Task GrokInvokesHostedMcpWithOutput()
485485
{
486486
var messages = new Chat()
487487
{
488-
{ "user", "When was GrokClient v1.0.0 released on the devlooped/GrokClient repo? Respond with just the date, in YYYY-MM-DD format." },
488+
{ "user", "When was GrokClient v1.1.0 released on the devlooped/GrokClient repo? Respond with just the date, in YYYY-MM-DD format." },
489489
};
490490

491-
var grok = new GrokClient(Configuration["XAI_API_KEY"]!).AsIChatClient("grok-4-fast");
491+
var grok = new GrokClient(Configuration["XAI_API_KEY"]!).AsIChatClient("grok-4-1-fast-non-reasoning");
492492

493493
var options = new GrokChatOptions
494494
{
495495
Include = { IncludeOption.McpCallOutput },
496496
Tools = [new HostedMcpServerTool("GitHub", "https://api.githubcopilot.com/mcp/") {
497-
AuthorizationToken = Configuration["GITHUB_TOKEN"]!,
497+
Headers = new Dictionary<string, string> { ["Authorization"] = Configuration["GITHUB_TOKEN"]! },
498498
AllowedTools = ["list_releases"],
499499
}]
500500
};
@@ -506,16 +506,16 @@ public async Task GrokInvokesHostedMcpWithOutput()
506506
.SelectMany(x => x.Contents)
507507
.OfType<McpServerToolResultContent>());
508508

509-
Assert.NotNull(output.Output);
510-
Assert.Single(output.Output);
511-
var json = Assert.Single(output.Output!.OfType<TextContent>()).Text;
509+
Assert.NotNull(output.Outputs);
510+
Assert.Single(output.Outputs);
511+
var json = Assert.Single(output.Outputs!.OfType<TextContent>()).Text;
512512
var tags = JsonSerializer.Deserialize<List<Release>>(json, new JsonSerializerOptions(JsonSerializerDefaults.Web)
513513
{
514514
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
515515
});
516516

517517
Assert.NotNull(tags);
518-
Assert.Contains(tags, x => x.TagName == "v1.0.0");
518+
Assert.Contains(tags, x => x.TagName == "v1.1.0");
519519
}
520520

521521
record Release(string TagName, DateTimeOffset CreatedAt);
@@ -527,20 +527,20 @@ public async Task GrokStreamsUpdatesFromAllTools()
527527
{
528528
{ "user",
529529
"""
530-
What's the oldest stable version released on the devlooped/GrokClient repo on GitHub?,
530+
What's the latest stable version released on the devlooped/GrokClient repo on GitHub?,
531531
what is the current price of Tesla stock,
532532
and what is the current date? Respond with the following JSON:
533533
{
534-
"today": "[get_date result]",
535-
"release": "[first stable release of devlooped/GrokClient, using GitHub MCP tool]",
534+
"today": "[get_date result in yyyy-MM-dd format]",
535+
"release": "[latest stable release of devlooped/GrokClient, using GitHub MCP tool]",
536536
"price": [$TSLA price using web search tool]
537537
}
538538
"""
539539
},
540540
};
541541

542542
var grok = new GrokClient(Configuration["XAI_API_KEY"]!)
543-
.AsIChatClient("grok-4-fast")
543+
.AsIChatClient("grok-4-1-fast-non-reasoning")
544544
.AsBuilder()
545545
.UseFunctionInvocation()
546546
.UseLogging(output.AsLoggerFactory())
@@ -554,12 +554,12 @@ public async Task GrokStreamsUpdatesFromAllTools()
554554
[
555555
new HostedWebSearchTool(),
556556
new HostedMcpServerTool("GitHub", "https://api.githubcopilot.com/mcp/") {
557-
AuthorizationToken = Configuration["GITHUB_TOKEN"]!,
557+
Headers = new Dictionary<string, string> { ["Authorization"] = Configuration["GITHUB_TOKEN"]! },
558558
AllowedTools = ["list_releases", "get_release_by_tag"],
559559
},
560560
AIFunctionFactory.Create(() => {
561561
getDateCalls++;
562-
return DateTimeOffset.Now.ToString("O");
562+
return DateTimeOffset.UtcNow.ToString("O");
563563
}, "get_date", "Gets the current date")
564564
]
565565
};
@@ -581,8 +581,8 @@ public async Task GrokStreamsUpdatesFromAllTools()
581581

582582
Assert.Equal(1, getDateCalls);
583583

584-
Assert.Equal(DateOnly.FromDateTime(DateTime.Today), typed.Today);
585-
Assert.EndsWith("1.0.0", typed.Release);
584+
Assert.Equal(DateOnly.FromDateTime(DateTime.UtcNow), typed.Today);
585+
Assert.EndsWith("1.1.0", typed.Release);
586586
Assert.True(typed.Price > 100);
587587
}
588588

src/xAI.Tests/GrokConversionTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ public void AsTool_WithHostedMcpTool()
265265
})
266266
{
267267
AllowedTools = ["list"],
268-
AuthorizationToken = accessToken,
268+
Headers = new Dictionary<string, string> { ["Authorization"] = accessToken },
269269
};
270270

271271
var tool = mcpTool.AsProtocolTool();

src/xAI.Tests/SanityChecks.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ public async Task AgenticMcpServer(bool streaming)
299299
[
300300
new HostedMcpServerTool("GitHub", "https://api.githubcopilot.com/mcp/")
301301
{
302-
AuthorizationToken = Env.GetString("GITHUB_TOKEN")!,
302+
Headers = new Dictionary < string, string > {["Authorization"] = Env.GetString("GITHUB_TOKEN") ! },
303303
AllowedTools = ["list_releases", "get_release_by_tag"],
304304
}
305305
]

src/xAI.Tests/xAI.Tests.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
<ItemGroup>
1212
<PackageReference Include="coverlet.collector" Version="8.0.0" />
13-
<PackageReference Include="Devlooped.Extensions.AI" Version="1.0.0" />
13+
<PackageReference Include="Devlooped.Extensions.AI" Version="2.0.0" />
1414
<PackageReference Include="Moq" Version="4.20.72" />
1515
<PackageReference Include="ThisAssembly.Git" Version="2.1.2" PrivateAssets="all" />
1616
<PackageReference Include="xunit" Version="2.9.3" />
@@ -22,7 +22,7 @@
2222
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="10.0.4" />
2323
<PackageReference Include="Microsoft.Extensions.Options" Version="10.0.4" />
2424

25-
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.3" />
25+
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.4" />
2626
<PackageReference Include="Microsoft.Extensions.Http" Version="10.0.4" />
2727
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="10.4.0" />
2828
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.4" />

src/xAI/GrokChatClient.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,14 +194,17 @@ GetCompletionsRequest MapToRequest(IEnumerable<ChatMessage> messages, ChatOption
194194
Role = MessageRole.RoleTool,
195195
Content = { new Content { Text = JsonSerializer.Serialize(resultContent.Result) ?? "null" } }
196196
};
197+
197198
if (resultContent.CallId is { Length: > 0 } callId)
198199
msg.ToolCallId = callId;
200+
199201
request.Messages.Add(msg);
200202
}
201203
else if (content is McpServerToolResultContent mcpResult &&
202204
mcpResult.RawRepresentation is ToolCall mcpToolCall &&
203-
mcpResult.Output is { Count: 1 } &&
204-
mcpResult.Output[0] is TextContent mcpText)
205+
// TODO: what if there are multiple outputs?
206+
mcpResult.Outputs is { Count: 1 } &&
207+
mcpResult.Outputs[0] is TextContent mcpText)
205208
{
206209
request.Messages.Add(new Message
207210
{
@@ -212,6 +215,7 @@ mcpResult.RawRepresentation is ToolCall mcpToolCall &&
212215
}
213216
else if (content is CodeInterpreterToolResultContent codeResult &&
214217
codeResult.RawRepresentation is ToolCall codeToolCall &&
218+
// TODO: what if there are multiple outputs?
215219
codeResult.Outputs is { Count: 1 } &&
216220
codeResult.Outputs[0] is TextContent codeText)
217221
{

src/xAI/GrokProtocolExtensions.cs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -126,13 +126,24 @@ grokSearch.City is not null ||
126126
case HostedMcpServerTool mcpTool:
127127
var mcp = new MCP
128128
{
129-
Authorization = mcpTool.AuthorizationToken,
130129
ServerLabel = mcpTool.ServerName,
131130
ServerUrl = mcpTool.ServerAddress,
132131
AllowedToolNames = { mcpTool.AllowedTools ?? Array.Empty<string>() },
133132
};
134133

134+
if (mcpTool.Headers?.TryGetValue("Authorization", out var authHeader) == true && !string.IsNullOrEmpty(authHeader))
135+
{
136+
mcp.Authorization = authHeader;
137+
foreach (var item in mcpTool.Headers.Where(x => x.Key != "Authorization"))
138+
mcp.ExtraHeaders.Add(item.Key, item.Value);
139+
}
140+
else if (mcpTool.Headers != null)
141+
{
142+
mcp.ExtraHeaders.Add(mcpTool.Headers);
143+
}
144+
135145
// We can set an entire dictionary with a specific key
146+
// We keep this for backs compat, but makes little sense now with McpTool.Headers property available.
136147
if (mcpTool.GetProperty<IDictionary<string, string>>(nameof(MCP.ExtraHeaders)) is { } headers)
137148
mcp.ExtraHeaders.Add(headers);
138149

@@ -239,23 +250,21 @@ internal static IEnumerable<AIContent> AsContents(this IEnumerable<ToolCall> too
239250
{
240251
Annotations = annotations,
241252
RawRepresentation = toolCall,
242-
Output = [new TextContent(content)]
253+
Outputs = [new TextContent(content)]
243254
};
244255
break;
245256

246257
case ToolCallType.CodeExecutionTool:
247-
yield return new CodeInterpreterToolCallContent()
258+
yield return new CodeInterpreterToolCallContent(toolCall.Id)
248259
{
249260
Annotations = annotations,
250261
RawRepresentation = toolCall,
251-
CallId = toolCall.Id,
252262
};
253263
if (content is not null)
254-
yield return new CodeInterpreterToolResultContent()
264+
yield return new CodeInterpreterToolResultContent(toolCall.Id)
255265
{
256266
Annotations = annotations,
257267
RawRepresentation = toolCall,
258-
CallId = toolCall.Id,
259268
Outputs = [new TextContent(content)]
260269
};
261270
break;

src/xAI/xAI.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
<PackageReference Include="NuGetizer" Version="1.4.7" PrivateAssets="all" />
1919
<PackageReference Include="Google.Protobuf" Version="3.34.0" />
2020
<PackageReference Include="Grpc.Net.Client" Version="2.76.0" />
21-
<PackageReference Include="Microsoft.Extensions.AI.Abstractions" Version="10.3.0" />
21+
<PackageReference Include="Microsoft.Extensions.AI.Abstractions" Version="10.4.0" />
2222
<PackageReference Include="System.ClientModel" Version="1.9.0" />
2323
</ItemGroup>
2424

0 commit comments

Comments
 (0)