openai/openai-dotnet

Public

mirrored from https://github.com/openai/openai-dotnetAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
OpenAI_2.0.0-beta.7

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

tests/Chat/ChatToolTests.cs

337lines · modecode

1using NUnit.Framework;
2using OpenAI.Chat;
3using OpenAI.Tests.Utility;
4using System;
5using System.ClientModel;
6using System.Collections.Generic;
7using System.Linq;
8using System.Text.Json;
9using System.Text.Json.Nodes;
10using System.Threading.Tasks;
11using static OpenAI.Tests.TestHelpers;
12
13namespace OpenAI.Tests.Chat;
14
15[TestFixture(true)]
16[TestFixture(false)]
17public partial class ChatToolTests : SyncAsyncTestBase
18{
19 public ChatToolTests(bool isAsync)
20 : base(isAsync)
21 {
22 }
23
24 private static ChatTool s_numberForWordTool = ChatTool.CreateFunctionTool(
25 "get_number_for_word",
26 "gets an arbitrary number assigned to a given word",
27 BinaryData.FromString("""
28 {
29 "type": "object",
30 "properties": {
31 "word": {
32 "type": "string"
33 }
34 }
35 }
36 """)
37 );
38
39 private const string GetFavoriteColorToolFunctionName = "get_favorite_color";
40
41 private static ChatTool s_getFavoriteColorTool = ChatTool.CreateFunctionTool(
42 GetFavoriteColorToolFunctionName,
43 "gets the favorite color of the caller"
44 );
45
46 private const string GetFavoriteColorForMonthToolFunctionName = "get_favorite_color_for_month";
47
48 private static ChatTool s_getFavoriteColorForMonthTool = ChatTool.CreateFunctionTool(
49 GetFavoriteColorForMonthToolFunctionName,
50 "gets the caller's favorite color for a given month",
51 BinaryData.FromString("""
52 {
53 "type": "object",
54 "properties": {
55 "month_name": {
56 "type": "string",
57 "description": "the name of a calendar month, e.g. February or October."
58 }
59 },
60 "required": [ "month_name" ]
61 }
62 """)
63 );
64
65 private const string GetFavoriteColorForMonthFunctionName = "get_favorite_color_for_month";
66
67#pragma warning disable CS0618
68 private static ChatFunction s_getFavoriteColorForMonthFunction = new ChatFunction(
69 GetFavoriteColorForMonthToolFunctionName,
70 "gets the caller's favorite color for a given month",
71 BinaryData.FromString("""
72 {
73 "type": "object",
74 "properties": {
75 "month_name": {
76 "type": "string",
77 "description": "the name of a calendar month, e.g. February or October."
78 }
79 },
80 "required": [ "month_name" ]
81 }
82 """)
83 );
84#pragma warning restore CS0618
85
86 private const string GetWeatherForCityToolName = "get_weather_for_city";
87
88 private static ChatTool s_getWeatherForCityTool = ChatTool.CreateFunctionTool(
89 GetWeatherForCityToolName,
90 "gets the current weather for a given city",
91 BinaryData.FromString("""
92 {
93 "type": "object",
94 "properties": {
95 "city_name": {
96 "type": "string",
97 "description": "the name of a city, e.g. Johannesburg or Ho Chi Minh City."
98 }
99 },
100 "required": [ "city_name" ]
101 }
102 """)
103 );
104
105 private const string GetMoodForWeatherToolName = "get_mood_for_weather";
106
107 private static ChatTool s_getMoodForWeatherTool = ChatTool.CreateFunctionTool(
108 GetMoodForWeatherToolName,
109 "gets the caller's mood for a given weather",
110 BinaryData.FromString("""
111 {
112 "type": "object",
113 "properties": {
114 "weather": {
115 "type": "string",
116 "description": "the current weather of where the caller is located, e.g. sunny or cloudy."
117 }
118 },
119 "required": [ "weather" ]
120 }
121 """)
122 );
123
124 [Test]
125 public async Task ConstraintsWork()
126 {
127 ChatClient client = GetTestClient<ChatClient>(TestScenario.Chat);
128 IEnumerable<ChatMessage> messages = [new UserChatMessage("What's the number for the word 'banana'?")];
129
130 foreach (var (choice, reason) in new (ChatToolChoice, ChatFinishReason)[]
131 {
132 (null, ChatFinishReason.ToolCalls),
133 (ChatToolChoice.None, ChatFinishReason.Stop),
134 (new ChatToolChoice(s_numberForWordTool), ChatFinishReason.Stop),
135 (ChatToolChoice.Auto, ChatFinishReason.ToolCalls),
136 // TODO: Add test for ChatToolChoice.Required
137 })
138 {
139 ChatCompletionOptions options = new()
140 {
141 Tools = { s_numberForWordTool },
142 ToolChoice = choice,
143 };
144 ClientResult<ChatCompletion> result = IsAsync
145 ? await client.CompleteChatAsync(messages, options)
146 : client.CompleteChat(messages, options);
147 Assert.That(result.Value.FinishReason, Is.EqualTo(reason));
148 }
149 }
150
151 [Test]
152 public async Task NoParameterToolWorks()
153 {
154 ChatClient client = GetTestClient<ChatClient>(TestScenario.Chat);
155 ICollection<ChatMessage> messages = [new UserChatMessage("What's my favorite color?")];
156 ChatCompletionOptions options = new()
157 {
158 Tools = { s_getFavoriteColorTool },
159 };
160 ClientResult<ChatCompletion> result = IsAsync
161 ? await client.CompleteChatAsync(messages, options)
162 : client.CompleteChat(messages, options);
163
164 Assert.That(result.Value.FinishReason, Is.EqualTo(ChatFinishReason.ToolCalls));
165 Assert.That(result.Value.ToolCalls.Count, Is.EqualTo(1));
166 var toolCall = result.Value.ToolCalls[0];
167 var toolCallArguments = BinaryData.FromString(toolCall.FunctionArguments).ToObjectFromJson<Dictionary<string, object>>();
168 Assert.That(toolCall.FunctionName, Is.EqualTo(GetFavoriteColorToolFunctionName));
169 Assert.That(toolCall.Id, Is.Not.Null.And.Not.Empty);
170 Assert.That(toolCallArguments.Count, Is.EqualTo(0));
171
172 messages.Add(new AssistantChatMessage(result.Value));
173 messages.Add(new ToolChatMessage(toolCall.Id, "green"));
174 result = IsAsync
175 ? await client.CompleteChatAsync(messages, options)
176 : client.CompleteChat(messages, options);
177
178 Assert.That(result.Value.FinishReason, Is.EqualTo(ChatFinishReason.Stop));
179 Assert.That(result.Value.Content[0].Text.ToLowerInvariant(), Contains.Substring("green"));
180 }
181
182 [Test]
183 public async Task ParametersWork()
184 {
185 ChatClient client = GetTestClient<ChatClient>(TestScenario.Chat);
186 ChatCompletionOptions options = new()
187 {
188 Tools = { s_getFavoriteColorForMonthTool },
189 };
190 List<ChatMessage> messages =
191 [
192 new UserChatMessage("What's my favorite color in February?"),
193 ];
194 ClientResult<ChatCompletion> result = IsAsync
195 ? await client.CompleteChatAsync(messages, options)
196 : client.CompleteChat(messages, options);
197 Assert.That(result.Value.FinishReason, Is.EqualTo(ChatFinishReason.ToolCalls));
198 Assert.That(result.Value.ToolCalls?.Count, Is.EqualTo(1));
199 var toolCall = result.Value.ToolCalls[0];
200 Assert.That(toolCall.FunctionName, Is.EqualTo(GetFavoriteColorForMonthToolFunctionName));
201 JsonObject argumentsJson = JsonSerializer.Deserialize<JsonObject>(toolCall.FunctionArguments);
202 Assert.That(argumentsJson.Count, Is.EqualTo(1));
203 Assert.That(argumentsJson.ContainsKey("month_name"));
204 Assert.That(argumentsJson["month_name"].ToString().ToLowerInvariant(), Is.EqualTo("february"));
205 messages.Add(new AssistantChatMessage(result.Value));
206 messages.Add(new ToolChatMessage(toolCall.Id, "chartreuse"));
207 result = IsAsync
208 ? await client.CompleteChatAsync(messages, options)
209 : client.CompleteChat(messages, options);
210 Assert.That(result.Value.Content[0].Text.ToLowerInvariant(), Contains.Substring("chartreuse"));
211 }
212
213 [Test]
214 public async Task FunctionsWork()
215 {
216 ChatClient client = GetTestClient<ChatClient>(TestScenario.Chat);
217 ChatCompletionOptions options = new()
218 {
219 Functions = { s_getFavoriteColorForMonthFunction },
220 };
221 List<ChatMessage> messages =
222 [
223 new UserChatMessage("What's my favorite color in February?"),
224 ];
225 ClientResult<ChatCompletion> result = IsAsync
226 ? await client.CompleteChatAsync(messages, options)
227 : client.CompleteChat(messages, options);
228 Assert.That(result.Value.FinishReason, Is.EqualTo(ChatFinishReason.FunctionCall));
229 var functionCall = result.Value.FunctionCall;
230 Assert.That(functionCall, Is.Not.Null);
231 Assert.That(functionCall.FunctionName, Is.EqualTo(GetFavoriteColorForMonthFunctionName));
232 JsonObject argumentsJson = JsonSerializer.Deserialize<JsonObject>(functionCall.FunctionArguments);
233 Assert.That(argumentsJson.Count, Is.EqualTo(1));
234 Assert.That(argumentsJson.ContainsKey("month_name"));
235 Assert.That(argumentsJson["month_name"].ToString().ToLowerInvariant(), Is.EqualTo("february"));
236 messages.Add(new AssistantChatMessage(result.Value));
237#pragma warning disable CS0618
238 messages.Add(new FunctionChatMessage(GetFavoriteColorForMonthFunctionName, "chartreuse"));
239#pragma warning restore CS0618
240 result = IsAsync
241 ? await client.CompleteChatAsync(messages, options)
242 : client.CompleteChat(messages, options);
243 Assert.That(result.Value.Content[0].Text.ToLowerInvariant(), Contains.Substring("chartreuse"));
244 }
245
246 [Test]
247 public async Task ParallelToolCalls()
248 {
249 ChatClient client = GetTestClient<ChatClient>(TestScenario.Chat);
250 ChatCompletionOptions options = new()
251 {
252 Tools = { s_getWeatherForCityTool },
253 };
254 List<ChatMessage> messages = [
255 new UserChatMessage("Tell me what's the current weather in the following cities: Santiago and Karachi."),
256 ];
257 ClientResult<ChatCompletion> result = IsAsync
258 ? await client.CompleteChatAsync(messages, options)
259 : client.CompleteChat(messages, options);
260
261 Assert.That(result.Value.FinishReason, Is.EqualTo(ChatFinishReason.ToolCalls));
262 Assert.That(result.Value.ToolCalls.Count, Is.EqualTo(2));
263
264 var santiagoToolCall = result.Value.ToolCalls.Single(call => call.FunctionArguments.ToLowerInvariant().Contains("santiago"));
265 var karachiToolCall = result.Value.ToolCalls.Single(call => call.FunctionArguments.ToLowerInvariant().Contains("karachi"));
266
267 JsonObject argumentsJson = JsonSerializer.Deserialize<JsonObject>(santiagoToolCall.FunctionArguments);
268 Assert.That(argumentsJson.Count, Is.EqualTo(1));
269 Assert.That(argumentsJson.ContainsKey("city_name"));
270 Assert.That(argumentsJson["city_name"].ToString().ToLowerInvariant(), Is.EqualTo("santiago"));
271
272 argumentsJson = JsonSerializer.Deserialize<JsonObject>(karachiToolCall.FunctionArguments);
273 Assert.That(argumentsJson.Count, Is.EqualTo(1));
274 Assert.That(argumentsJson.ContainsKey("city_name"));
275 Assert.That(argumentsJson["city_name"].ToString().ToLowerInvariant(), Is.EqualTo("karachi"));
276
277 messages.Add(new AssistantChatMessage(result.Value));
278 messages.Add(new ToolChatMessage(santiagoToolCall.Id, "rainy"));
279 messages.Add(new ToolChatMessage(karachiToolCall.Id, "sunny"));
280
281 result = IsAsync
282 ? await client.CompleteChatAsync(messages, options)
283 : client.CompleteChat(messages, options);
284
285 Assert.That(result.Value.FinishReason, Is.EqualTo(ChatFinishReason.Stop));
286 Assert.That(result.Value.Content[0].Text.ToLowerInvariant(), Contains.Substring("rainy"));
287 Assert.That(result.Value.Content[0].Text.ToLowerInvariant(), Contains.Substring("sunny"));
288 }
289
290 [Test]
291 public async Task ConsecutiveToolCalls()
292 {
293 ChatClient client = GetTestClient<ChatClient>(TestScenario.Chat);
294 ChatCompletionOptions options = new()
295 {
296 Tools = { s_getWeatherForCityTool, s_getMoodForWeatherTool },
297 };
298 List<ChatMessage> messages = [
299 new UserChatMessage("Can you guess my mood given that I'm currently located in Osaka?"),
300 ];
301 ClientResult<ChatCompletion> result = IsAsync
302 ? await client.CompleteChatAsync(messages, options)
303 : client.CompleteChat(messages, options);
304
305 Assert.That(result.Value.ToolCalls?.Count, Is.EqualTo(1));
306 var toolCall = result.Value.ToolCalls[0];
307 Assert.That(toolCall.FunctionName, Is.EqualTo(GetWeatherForCityToolName));
308
309 JsonObject argumentsJson = JsonSerializer.Deserialize<JsonObject>(toolCall.FunctionArguments);
310 Assert.That(argumentsJson.Count, Is.EqualTo(1));
311 Assert.That(argumentsJson.ContainsKey("city_name"));
312 Assert.That(argumentsJson["city_name"].ToString().ToLowerInvariant(), Is.EqualTo("osaka"));
313
314 messages.Add(new AssistantChatMessage(result.Value));
315 messages.Add(new ToolChatMessage(toolCall.Id, "rainy"));
316 result = IsAsync
317 ? await client.CompleteChatAsync(messages, options)
318 : client.CompleteChat(messages, options);
319
320 Assert.That(result.Value.ToolCalls?.Count, Is.EqualTo(1));
321 toolCall = result.Value.ToolCalls[0];
322 Assert.That(toolCall.FunctionName, Is.EqualTo(GetMoodForWeatherToolName));
323
324 argumentsJson = JsonSerializer.Deserialize<JsonObject>(toolCall.FunctionArguments);
325 Assert.That(argumentsJson.Count, Is.EqualTo(1));
326 Assert.That(argumentsJson.ContainsKey("weather"));
327 Assert.That(argumentsJson["weather"].ToString().ToLowerInvariant(), Is.EqualTo("rainy"));
328
329 messages.Add(new AssistantChatMessage(result.Value));
330 messages.Add(new ToolChatMessage(toolCall.Id, "bored"));
331 result = IsAsync
332 ? await client.CompleteChatAsync(messages, options)
333 : client.CompleteChat(messages, options);
334
335 Assert.That(result.Value.Content[0].Text.ToLowerInvariant(), Contains.Substring("bored"));
336 }
337}
338