Skip to content

Commit 01bd222

Browse files
committed
Add unit test that asserts tools are properly converted
1 parent e80afda commit 01bd222

1 file changed

Lines changed: 95 additions & 0 deletions

File tree

src/xAI.Tests/ChatClientTests.cs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -893,6 +893,101 @@ public async Task GrokPreservesEndUserIdFromClientOptions()
893893
Assert.Equal("kzu", capturedRequest.User);
894894
}
895895

896+
[Fact]
897+
public async Task GrokPassesToolsFromChatOptionsToGrpcRequest()
898+
{
899+
GetCompletionsRequest? capturedRequest = null;
900+
var client = new Mock<xAI.Protocol.Chat.ChatClient>(MockBehavior.Strict);
901+
client.Setup(x => x.GetCompletionAsync(It.IsAny<GetCompletionsRequest>(), null, null, CancellationToken.None))
902+
.Callback<GetCompletionsRequest, Metadata?, DateTime?, CancellationToken>((req, _, _, _) => capturedRequest = req)
903+
.Returns(CallHelpers.CreateAsyncUnaryCall(new GetChatCompletionResponse
904+
{
905+
Outputs =
906+
{
907+
new CompletionOutput
908+
{
909+
Message = new CompletionMessage { Content = "Done" }
910+
}
911+
}
912+
}));
913+
914+
var grok = new GrokChatClient(client.Object, "grok-4-1-fast-non-reasoning");
915+
var options = new ChatOptions
916+
{
917+
Tools =
918+
[
919+
AIFunctionFactory.Create(
920+
(string city, string unit) => $"72°{unit} in {city}",
921+
"get_weather",
922+
"Gets the current weather for a city"),
923+
]
924+
};
925+
926+
await grok.GetResponseAsync([new ChatMessage(ChatRole.User, "What's the weather in Seattle?")], options);
927+
928+
Assert.NotNull(capturedRequest);
929+
var tool = Assert.Single(capturedRequest.Tools);
930+
Assert.NotNull(tool.Function);
931+
932+
Assert.Equal("get_weather", tool.Function.Name);
933+
Assert.Equal("Gets the current weather for a city", tool.Function.Description);
934+
935+
// Parameters must be a non-empty JSON schema reflecting the function signature
936+
Assert.NotEmpty(tool.Function.Parameters);
937+
var schema = JsonDocument.Parse(tool.Function.Parameters);
938+
var properties = schema.RootElement.GetProperty("properties");
939+
Assert.True(properties.TryGetProperty("city", out _), "Schema should contain 'city' parameter");
940+
Assert.True(properties.TryGetProperty("unit", out _), "Schema should contain 'unit' parameter");
941+
}
942+
943+
[Fact]
944+
public async Task GrokPassesMultipleToolsFromChatOptionsToGrpcRequest()
945+
{
946+
GetCompletionsRequest? capturedRequest = null;
947+
var client = new Mock<xAI.Protocol.Chat.ChatClient>(MockBehavior.Strict);
948+
client.Setup(x => x.GetCompletionAsync(It.IsAny<GetCompletionsRequest>(), null, null, CancellationToken.None))
949+
.Callback<GetCompletionsRequest, Metadata?, DateTime?, CancellationToken>((req, _, _, _) => capturedRequest = req)
950+
.Returns(CallHelpers.CreateAsyncUnaryCall(new GetChatCompletionResponse
951+
{
952+
Outputs =
953+
{
954+
new CompletionOutput
955+
{
956+
Message = new CompletionMessage { Content = "Done" }
957+
}
958+
}
959+
}));
960+
961+
var grok = new GrokChatClient(client.Object, "grok-4-1-fast-non-reasoning");
962+
var options = new ChatOptions
963+
{
964+
Tools =
965+
[
966+
AIFunctionFactory.Create(() => DateTimeOffset.UtcNow.ToString("O"), "get_date", "Gets today's date"),
967+
AIFunctionFactory.Create((string city) => $"72°F in {city}", "get_weather", "Gets the weather"),
968+
new HostedWebSearchTool(),
969+
]
970+
};
971+
972+
await grok.GetResponseAsync([new ChatMessage(ChatRole.User, "Hello")], options);
973+
974+
Assert.NotNull(capturedRequest);
975+
Assert.Equal(3, capturedRequest.Tools.Count);
976+
977+
var getDate = capturedRequest.Tools.FirstOrDefault(t => t.Function?.Name == "get_date");
978+
Assert.NotNull(getDate?.Function);
979+
Assert.Equal("Gets today's date", getDate.Function.Description);
980+
981+
var getWeather = capturedRequest.Tools.FirstOrDefault(t => t.Function?.Name == "get_weather");
982+
Assert.NotNull(getWeather?.Function);
983+
Assert.Equal("Gets the weather", getWeather.Function.Description);
984+
var schema = JsonDocument.Parse(getWeather.Function.Parameters);
985+
Assert.True(schema.RootElement.GetProperty("properties").TryGetProperty("city", out _));
986+
987+
var webSearch = capturedRequest.Tools.FirstOrDefault(t => t.WebSearch != null);
988+
Assert.NotNull(webSearch?.WebSearch);
989+
}
990+
896991
class TestGrpcChannel(CallInvoker invoker) : ChannelBase("test")
897992
{
898993
public override CallInvoker CreateCallInvoker() => invoker;

0 commit comments

Comments
 (0)