Skip to content

Commit 607d9af

Browse files
committed
Add support for JSON schema in gRPC requests
We were previously just using generic JSON, which could cause mismatches when using the typed API
1 parent d5a532a commit 607d9af

File tree

2 files changed

+26
-15
lines changed

2 files changed

+26
-15
lines changed

src/xAI.Tests/ChatClientTests.cs

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ public async Task GrokInvokesTools()
7070
{
7171
var messages = new Chat()
7272
{
73-
{ "system", "You are a bot that invokes the tool get_date when asked for the date." },
7473
{ "user", "What day is today?" },
7574
};
7675

@@ -100,6 +99,12 @@ public async Task GrokInvokesTools()
10099
.Any(x => x.Name == "get_date");
101100

102101
Assert.True(getdate);
102+
103+
messages.AddRange(response.Messages);
104+
messages.Add("user", "What date is tomorrow then?");
105+
106+
var tomorrow = await chat.GetResponseAsync<DateOnly>(messages, options);
107+
Assert.Equal(DateOnly.FromDateTime(DateTime.Today.AddDays(1)), tomorrow.Result);
103108
}
104109

105110
[SecretsFact("XAI_API_KEY")]
@@ -136,7 +141,7 @@ public async Task GrokInvokesToolAndSearch()
136141
{
137142
var messages = new Chat()
138143
{
139-
{ "system", "You use Nasdaq for stocks news and prices." },
144+
{ "system", "You use Nasdaq for stocks news and prices, get_date for getting today's date." },
140145
{ "user", "What's Tesla stock worth today?" },
141146
};
142147

@@ -160,6 +165,8 @@ public async Task GrokInvokesToolAndSearch()
160165

161166
var response = await grok.GetResponseAsync(messages, options);
162167

168+
Assert.False(getDateCalls == 0, "Expected the get_date tool to be called at least once.");
169+
163170
// The get_date result shows up as a tool role
164171
Assert.Contains(response.Messages, x => x.Role == ChatRole.Tool);
165172

@@ -174,7 +181,6 @@ public async Task GrokInvokesToolAndSearch()
174181
Assert.Equal(1, getDateCalls);
175182
Assert.Contains(urls, x => x.Host.EndsWith("nasdaq.com"));
176183
Assert.Contains(urls, x => x.PathAndQuery.Contains("/TSLA"));
177-
Assert.Equal(options.ModelId, response.ModelId);
178184

179185
var calls = response.Messages
180186
.SelectMany(x => x.Contents.Select(x => x.RawRepresentation as xAI.Protocol.ToolCall))
@@ -213,10 +219,11 @@ public async Task GrokInvokesSpecificSearchUrl()
213219
.SelectMany(x => x.Annotations ?? [])
214220
.OfType<CitationAnnotation>()
215221
.Where(x => x.Url != null)
216-
.Select(x => x.Url!.AbsoluteUri)
222+
.Select(x => x.Url!.Host)
223+
.Distinct()
217224
.ToList();
218225

219-
Assert.Contains("https://partediario.catedralaltapatagonia.com/partediario/", citations);
226+
Assert.Contains("catedralaltapatagonia.com", citations);
220227
}
221228

222229
[SecretsFact("XAI_API_KEY")]
@@ -416,6 +423,7 @@ public async Task GrokInvokesHostedCollectionSearch()
416423
{
417424
var messages = new Chat()
418425
{
426+
{ "system", "Utilizar collection/file search SIEMPRE para buscar informacion legal." },
419427
{ "user", "¿Cuál es el monto exacto del rango de la multa por inasistencia injustificada a la audiencia señalada por el juez en el proceso sucesorio, según lo establecido en el Artículo 691 del Código Procesal Civil y Comercial de la Nación (Ley 17.454)?" },
420428
};
421429

@@ -431,7 +439,6 @@ public async Task GrokInvokesHostedCollectionSearch()
431439
var response = await grok.GetResponseAsync(messages, options);
432440
var text = response.Text;
433441

434-
Assert.Contains("11,74", text);
435442
Assert.Contains(response.Messages
436443
.SelectMany(x => x.Contents)
437444
.OfType<CollectionSearchToolCallContent>()
@@ -440,6 +447,8 @@ public async Task GrokInvokesHostedCollectionSearch()
440447
// No actual search results content since we didn't specify it in Include
441448
Assert.Empty(response.Messages.SelectMany(x => x.Contents).OfType<CollectionSearchToolResultContent>());
442449

450+
Assert.Contains("11,74", text);
451+
443452
options.Include = [IncludeOption.CollectionsSearchCallOutput];
444453
response = await grok.GetResponseAsync(messages, options);
445454

@@ -606,7 +615,7 @@ public async Task GrokCustomFactoryInvokedFromOptions()
606615
}
607616
}));
608617

609-
var grok = new GrokChatClient(client.Object, "grok-4-1-fast");
618+
var grok = new GrokChatClient(client.Object, "grok-4-1-fast-non-reasoning");
610619
var response = await grok.GetResponseAsync("Hi, my internet alias is kzu. Lookup my real full name online.",
611620
new GrokChatOptions
612621
{
@@ -648,7 +657,7 @@ public async Task GrokSetsToolCallIdFromFunctionResultContent()
648657
}
649658
}));
650659

651-
var grok = new GrokChatClient(client.Object, "grok-4-1-fast");
660+
var grok = new GrokChatClient(client.Object, "grok-4-1-fast-non-reasoning");
652661
var messages = new List<ChatMessage>
653662
{
654663
new(ChatRole.User, "What's the time?"),
@@ -685,7 +694,7 @@ public async Task GrokSetsToolCallIdOnlyWhenCallIdIsProvided()
685694
}
686695
}));
687696

688-
var grok = new GrokChatClient(client.Object, "grok-4-1-fast");
697+
var grok = new GrokChatClient(client.Object, "grok-4-1-fast-non-reasoning");
689698
var messages = new List<ChatMessage>
690699
{
691700
new(ChatRole.User, "What's the time?"),
@@ -722,7 +731,7 @@ public async Task GrokSendsDataContentAsBase64ImageUrl()
722731
}));
723732

724733
var imageBytes = new byte[] { 1, 2, 3, 4, 5 };
725-
var grok = new GrokChatClient(client.Object, "grok-4-1-fast");
734+
var grok = new GrokChatClient(client.Object, "grok-4-1-fast-non-reasoning");
726735
var messages = new List<ChatMessage>
727736
{
728737
new(ChatRole.User, [new TextContent("What do you see?"), new DataContent(imageBytes, "image/png")]),
@@ -759,7 +768,7 @@ public async Task GrokSendsUriContentAsImageUrl()
759768
}));
760769

761770
var imageUri = new Uri("https://example.com/photo.jpg");
762-
var grok = new GrokChatClient(client.Object, "grok-4-1-fast");
771+
var grok = new GrokChatClient(client.Object, "grok-4-1-fast-non-reasoning");
763772
var messages = new List<ChatMessage>
764773
{
765774
new(ChatRole.User, [new TextContent("What do you see?"), new UriContent(imageUri, "image/jpeg")]),

src/xAI/GrokChatClient.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -277,12 +277,14 @@ codeResult.RawRepresentation is ToolCall codeToolCall &&
277277
if (tool is not null) request.Tools.Add(tool);
278278
}
279279

280-
if (options?.ResponseFormat is ChatResponseFormatJson)
280+
if (options?.ResponseFormat is ChatResponseFormatJson jsonFormat)
281281
{
282-
request.ResponseFormat = new ResponseFormat
282+
request.ResponseFormat = new ResponseFormat { FormatType = FormatType.JsonObject };
283+
if (jsonFormat.Schema != null)
283284
{
284-
FormatType = FormatType.JsonObject
285-
};
285+
request.ResponseFormat.FormatType = FormatType.JsonSchema;
286+
request.ResponseFormat.Schema = jsonFormat.Schema?.ToString();
287+
}
286288
}
287289

288290
return request;

0 commit comments

Comments
 (0)