diff --git a/docs/index.md b/docs/index.md index 7540d5c6..87c592a4 100644 --- a/docs/index.md +++ b/docs/index.md @@ -227,4 +227,14 @@ double? priceInUsd = CreateSpeechRequestModel.Tts1Hd.TryGetPriceInUsd( Priority place for bugs: https://github.com/tryAGI/OpenAI/issues Priority place for ideas and general questions: https://github.com/tryAGI/OpenAI/discussions -Discord: https://discord.gg/Ca2xhfBf3v \ No newline at end of file +Discord: https://discord.gg/Ca2xhfBf3v + +## Acknowledgments + +![JetBrains logo](https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.png) + +This project is supported by JetBrains through the [Open Source Support Program](https://jb.gg/OpenSourceSupport). + +![CodeRabbit logo](https://opengraph.githubassets.com/1c51002d7d0bbe0c4fd72ff8f2e58192702f73a7037102f77e4dbb98ac00ea8f/marketplace/coderabbitai) + +This project is supported by CodeRabbit through the [Open Source Support Program](https://github.com/marketplace/coderabbitai). \ No newline at end of file diff --git a/docs/samples/Assistants.AssistantsWithVision.md b/docs/samples/Assistants.AssistantsWithVision.md index ea4c3446..cc896e11 100644 --- a/docs/samples/Assistants.AssistantsWithVision.md +++ b/docs/samples/Assistants.AssistantsWithVision.md @@ -46,32 +46,44 @@ ThreadObject thread = await api.Assistants.CreateThreadAsync(new CreateThreadReq ] }); -// AsyncResultCollection streamingUpdates = api.Assistants.CreateRunStreamingAsync( -// thread, -// assistant, -// new RunCreationOptions() -// { -// AdditionalInstructions = "When possible, try to sneak in puns if you're asked to compare things.", -// }); -// -// await foreach (StreamingUpdate streamingUpdate in streamingUpdates) -// { -// if (streamingUpdate.UpdateKind == StreamingUpdateReason.RunCreated) -// { -// Console.WriteLine($"--- Run started! ---"); -// } -// if (streamingUpdate is MessageContentUpdate contentUpdate) -// { -// Console.Write(contentUpdate.Text); -// } -// } - -RunObject response = await api.Assistants.CreateRunAsync( +var streamingUpdates = api.Assistants.CreateRunAsStreamAsync( threadId: thread.Id, assistantId: assistant.Id, instructions: "When possible, try to sneak in puns if you're asked to compare things."); -Console.WriteLine(response[0].Content); +await foreach (AssistantStreamEvent streamingUpdate in streamingUpdates) +{ + if (streamingUpdate.IsRun && streamingUpdate.Run.Value.IsValue1) // RunCreated + { + Console.WriteLine("--- Run started! ---"); + } + if (streamingUpdate is { IsMessage: true, Message: var messageStreamEvent } && + messageStreamEvent.Value is { IsValue3: true, Value3: var delta }) + { + foreach (var deltaVariation in delta.Data.Delta.Content ?? []) + { + if (deltaVariation.IsValue1) + { + Console.WriteLine(); + Console.WriteLine(deltaVariation.Value1.ImageFile?.FileId); + } + if (deltaVariation.IsValue2) + { + Console.Write(deltaVariation.Value2.Text?.Value); + } + if (deltaVariation.IsValue3) + { + Console.WriteLine(); + Console.WriteLine(deltaVariation.Value3.Refusal); + } + if (deltaVariation.IsValue4) + { + Console.WriteLine(); + Console.WriteLine(deltaVariation.Value4.ImageUrl?.Url); + } + } + } +} _ = await api.Files.DeleteFileAsync(pictureOfAppleFile.Id); _ = await api.Assistants.DeleteThreadAsync(thread.Id); diff --git a/docs/samples/Chat.FunctionCalling.md b/docs/samples/Chat.FunctionCalling.md new file mode 100644 index 00000000..04c9604f --- /dev/null +++ b/docs/samples/Chat.FunctionCalling.md @@ -0,0 +1,90 @@ +```csharp +using var api = GetAuthenticatedClient(); + +List messages = [ + "What's the weather like today?", +]; + +var service = new FunctionCallingService(); +IList tools = service.AsTools(); + +bool requiresAction; + +do +{ + requiresAction = false; + CreateChatCompletionResponse chatCompletion = await api.Chat.CreateChatCompletionAsync( + messages, + model: CreateChatCompletionRequestModel.Gpt4o, + tools: tools); + + switch (chatCompletion.Choices[0].FinishReason) + { + case CreateChatCompletionResponseChoiceFinishReason.Stop: + { + // Add the assistant message to the conversation history. + messages.Add(chatCompletion.Choices[0].Message.AsRequestMessage()); + break; + } + + case CreateChatCompletionResponseChoiceFinishReason.ToolCalls: + { + // First, add the assistant message with tool calls to the conversation history. + messages.Add(chatCompletion.Choices[0].Message.AsRequestMessage()); + + // Then, add a new tool message for each tool call that is resolved. + foreach (ChatCompletionMessageToolCall toolCall in chatCompletion.Choices[0].Message.ToolCalls ?? []) + { + var json = await service.CallAsync( + functionName: toolCall.Function.Name, + argumentsAsJson: toolCall.Function.Arguments); + messages.Add(json.AsToolMessage(toolCall.Id)); + } + + requiresAction = true; + break; + } + + case CreateChatCompletionResponseChoiceFinishReason.Length: + throw new NotImplementedException("Incomplete model output due to MaxTokens parameter or token limit exceeded."); + + case CreateChatCompletionResponseChoiceFinishReason.ContentFilter: + throw new NotImplementedException("Omitted content due to a content filter flag."); + + case CreateChatCompletionResponseChoiceFinishReason.FunctionCall: + throw new NotImplementedException("Deprecated in favor of tool calls."); + + default: + throw new NotImplementedException(chatCompletion.Choices[0].FinishReason.ToString()); + } +} while (requiresAction); + +foreach (ChatCompletionRequestMessage requestMessage in messages) +{ + if (requestMessage.System is { } systemMessage) + { + Console.WriteLine($"[SYSTEM]:"); + Console.WriteLine($"{systemMessage.Content.Value1}"); + Console.WriteLine(); + break; + } + else if (requestMessage.User is { } userMessage) + { + Console.WriteLine($"[USER]:"); + Console.WriteLine($"{userMessage.Content.Value1}"); + Console.WriteLine(); + } + else if (requestMessage.Assistant is { Content: not null } assistantMessage) + { + Console.WriteLine($"[ASSISTANT]:"); + Console.WriteLine($"{assistantMessage.Content?.Value1}"); + Console.WriteLine(); + } + else if (requestMessage.Tool is { } toolMessage) + { + // Do not print any tool messages; let the assistant summarize the tool results instead. + break; + + } +} +``` \ No newline at end of file diff --git a/docs/samples/Chat.FunctionCallingService.md b/docs/samples/Chat.FunctionCallingService.md new file mode 100644 index 00000000..e0600b2f --- /dev/null +++ b/docs/samples/Chat.FunctionCallingService.md @@ -0,0 +1,42 @@ +```csharp +public enum WeatherUnit +{ + Celsius, + Fahrenheit, +} + +[OpenAiTools(Strict = true)] +public interface IFunctionCallingService +{ + [Description("Get the user's current location")] + public Task GetCurrentLocation( + CancellationToken cancellationToken = default); + + [Description("Get the current weather in a given location")] + public Task GetCurrentWeatherAsync( + [Description("The city and state, e.g. Boston, MA")] + string location, + [Description("The temperature unit to use. Infer this from the specified location.")] + WeatherUnit unit = WeatherUnit.Celsius, + CancellationToken cancellationToken = default); +} + +public class FunctionCallingService : IFunctionCallingService +{ + public Task GetCurrentLocation( + CancellationToken cancellationToken = default) + { + // Call the location API here. + return Task.FromResult("San Francisco"); + } + + public Task GetCurrentWeatherAsync( + string location, + WeatherUnit unit = WeatherUnit.Celsius, + CancellationToken cancellationToken = default) + { + // Call the weather API here. + return Task.FromResult($"31 {unit:G}"); + } +} +``` \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index 14730a47..a50c6a99 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -7,6 +7,8 @@ nav: - SimpleChat: samples/Chat.SimpleChat.md - SimpleChatStreaming: samples/Chat.SimpleChatStreaming.md - ChatWithVision: samples/Chat.ChatWithVision.md + - FunctionCalling: samples/Chat.FunctionCalling.md + - FunctionCallingService: samples/Chat.FunctionCallingService.md - Assistants: - AssistantsWithVision: samples/Assistants.AssistantsWithVision.md - ListFiles: samples/Assistants.ListFiles.md diff --git a/src/helpers/GenerateDocs/Program.cs b/src/helpers/GenerateDocs/Program.cs index cc727541..877e9f34 100644 --- a/src/helpers/GenerateDocs/Program.cs +++ b/src/helpers/GenerateDocs/Program.cs @@ -15,12 +15,20 @@ { var code = await File.ReadAllTextAsync(path); - var start = code.IndexOf("\n {", StringComparison.Ordinal); - var end = code.IndexOf("\n }", StringComparison.Ordinal); - code = code.Substring(start + 4, end - start + 4); - - var lines = code.Split('\n')[1..^2]; - code = string.Join('\n', lines.Select(x => x.Length > 8 ? x[8..] : string.Empty)); + var startExample = code.IndexOf("// # START EXAMPLE #", StringComparison.Ordinal); + if (startExample == -1) + { + var start = code.IndexOf("\n {", StringComparison.Ordinal); + var end = code.IndexOf("\n }", StringComparison.Ordinal); + code = code.Substring(start + 4, end - start + 4); + + var lines = code.Split('\n')[1..^2]; + code = string.Join('\n', lines.Select(x => x.Length > 8 ? x[8..] : string.Empty)); + } + else + { + code = code[(startExample + "// # START EXAMPLE #".Length)..].Trim(); + } var newPath = Path.Combine(newDir, $"{Path.GetFileNameWithoutExtension(path).Replace("Examples.", string.Empty)}.md"); await File.WriteAllTextAsync(newPath, $@"```csharp diff --git a/src/tests/OpenAI.IntegrationTests/Examples/Examples.Chat.FunctionCalling.cs b/src/tests/OpenAI.IntegrationTests/Examples/Examples.Chat.FunctionCalling.cs new file mode 100644 index 00000000..bddcc1c2 --- /dev/null +++ b/src/tests/OpenAI.IntegrationTests/Examples/Examples.Chat.FunctionCalling.cs @@ -0,0 +1,98 @@ +namespace OpenAI.IntegrationTests.Examples; + +public partial class Examples +{ + [Test] + [Explicit] + public async Task FunctionCalling() + { + using var api = GetAuthenticatedClient(); + + List messages = [ + "What's the weather like today?", + ]; + + var service = new FunctionCallingService(); + IList tools = service.AsTools(); + + bool requiresAction; + + do + { + requiresAction = false; + CreateChatCompletionResponse chatCompletion = await api.Chat.CreateChatCompletionAsync( + messages, + model: CreateChatCompletionRequestModel.Gpt4o, + tools: tools); + + switch (chatCompletion.Choices[0].FinishReason) + { + case CreateChatCompletionResponseChoiceFinishReason.Stop: + { + // Add the assistant message to the conversation history. + messages.Add(chatCompletion.Choices[0].Message.AsRequestMessage()); + break; + } + + case CreateChatCompletionResponseChoiceFinishReason.ToolCalls: + { + // First, add the assistant message with tool calls to the conversation history. + messages.Add(chatCompletion.Choices[0].Message.AsRequestMessage()); + + // Then, add a new tool message for each tool call that is resolved. + foreach (ChatCompletionMessageToolCall toolCall in chatCompletion.Choices[0].Message.ToolCalls ?? []) + { + var json = await service.CallAsync( + functionName: toolCall.Function.Name, + argumentsAsJson: toolCall.Function.Arguments); + messages.Add(json.AsToolMessage(toolCall.Id)); + } + + requiresAction = true; + break; + } + + case CreateChatCompletionResponseChoiceFinishReason.Length: + throw new NotImplementedException("Incomplete model output due to MaxTokens parameter or token limit exceeded."); + + case CreateChatCompletionResponseChoiceFinishReason.ContentFilter: + throw new NotImplementedException("Omitted content due to a content filter flag."); + + case CreateChatCompletionResponseChoiceFinishReason.FunctionCall: + throw new NotImplementedException("Deprecated in favor of tool calls."); + + default: + throw new NotImplementedException(chatCompletion.Choices[0].FinishReason.ToString()); + } + } while (requiresAction); + + foreach (ChatCompletionRequestMessage requestMessage in messages) + { + if (requestMessage.System is { } systemMessage) + { + Console.WriteLine($"[SYSTEM]:"); + Console.WriteLine($"{systemMessage.Content.Value1}"); + Console.WriteLine(); + break; + } + else if (requestMessage.User is { } userMessage) + { + Console.WriteLine($"[USER]:"); + Console.WriteLine($"{userMessage.Content.Value1}"); + Console.WriteLine(); + } + else if (requestMessage.Assistant is { Content: not null } assistantMessage) + { + Console.WriteLine($"[ASSISTANT]:"); + Console.WriteLine($"{assistantMessage.Content?.Value1}"); + Console.WriteLine(); + } + else if (requestMessage.Tool is { } toolMessage) + { + // Do not print any tool messages; let the assistant summarize the tool results instead. + break; + + } + } + } +} diff --git a/src/tests/OpenAI.IntegrationTests/Examples/Examples.Chat.FunctionCallingService.cs b/src/tests/OpenAI.IntegrationTests/Examples/Examples.Chat.FunctionCallingService.cs new file mode 100644 index 00000000..cad7e2e0 --- /dev/null +++ b/src/tests/OpenAI.IntegrationTests/Examples/Examples.Chat.FunctionCallingService.cs @@ -0,0 +1,45 @@ +using DescriptionAttribute = System.ComponentModel.DescriptionAttribute; + +namespace OpenAI.IntegrationTests.Examples; + +// # START EXAMPLE # +public enum WeatherUnit +{ + Celsius, + Fahrenheit, +} + +[OpenAiTools(Strict = true)] +public interface IFunctionCallingService +{ + [Description("Get the user's current location")] + public Task GetCurrentLocation( + CancellationToken cancellationToken = default); + + [Description("Get the current weather in a given location")] + public Task GetCurrentWeatherAsync( + [Description("The city and state, e.g. Boston, MA")] + string location, + [Description("The temperature unit to use. Infer this from the specified location.")] + WeatherUnit unit = WeatherUnit.Celsius, + CancellationToken cancellationToken = default); +} + +public class FunctionCallingService : IFunctionCallingService +{ + public Task GetCurrentLocation( + CancellationToken cancellationToken = default) + { + // Call the location API here. + return Task.FromResult("San Francisco"); + } + + public Task GetCurrentWeatherAsync( + string location, + WeatherUnit unit = WeatherUnit.Celsius, + CancellationToken cancellationToken = default) + { + // Call the weather API here. + return Task.FromResult($"31 {unit:G}"); + } +} \ No newline at end of file diff --git a/src/tests/OpenAI.IntegrationTests/Extended/Chat/Example03_FunctionCalling.cs b/src/tests/OpenAI.IntegrationTests/Extended/Chat/Example03_FunctionCalling.cs deleted file mode 100644 index 9f725a41..00000000 --- a/src/tests/OpenAI.IntegrationTests/Extended/Chat/Example03_FunctionCalling.cs +++ /dev/null @@ -1,186 +0,0 @@ -using NUnit.Framework; -using OpenAI.Chat; -using System; -using System.Collections.Generic; -using System.Text.Json; - -namespace OpenAI.Examples; - -public partial class ChatExamples -{ - #region - private static string GetCurrentLocation() - { - // Call the location API here. - return "San Francisco"; - } - - private static string GetCurrentWeather(string location, string unit = "celsius") - { - // Call the weather API here. - return $"31 {unit}"; - } - #endregion - - #region - private static readonly ChatTool getCurrentLocationTool = ChatTool.CreateFunctionTool( - functionName: nameof(GetCurrentLocation), - functionDescription: "Get the user's current location" - ); - - private static readonly ChatTool getCurrentWeatherTool = ChatTool.CreateFunctionTool( - functionName: nameof(GetCurrentWeather), - functionDescription: "Get the current weather in a given location", - functionParameters: BinaryData.FromString(""" - { - "type": "object", - "properties": { - "location": { - "type": "string", - "description": "The city and state, e.g. Boston, MA" - }, - "unit": { - "type": "string", - "enum": [ "celsius", "fahrenheit" ], - "description": "The temperature unit to use. Infer this from the specified location." - } - }, - "required": [ "location" ] - } - """) - ); - #endregion - - [Test] - public void Example03_FunctionCalling() - { - ChatClient client = new("gpt-4-turbo", Environment.GetEnvironmentVariable("OPENAI_API_KEY")); - - #region - List messages = [ - new UserChatMessage("What's the weather like today?"), - ]; - - ChatCompletionOptions options = new() - { - Tools = { getCurrentLocationTool, getCurrentWeatherTool }, - }; - #endregion - - #region - bool requiresAction; - - do - { - requiresAction = false; - ChatCompletion chatCompletion = client.CompleteChat(messages, options); - - switch (chatCompletion.FinishReason) - { - case ChatFinishReason.Stop: - { - // Add the assistant message to the conversation history. - messages.Add(new AssistantChatMessage(chatCompletion)); - break; - } - - case ChatFinishReason.ToolCalls: - { - // First, add the assistant message with tool calls to the conversation history. - messages.Add(new AssistantChatMessage(chatCompletion)); - - // Then, add a new tool message for each tool call that is resolved. - foreach (ChatToolCall toolCall in chatCompletion.ToolCalls) - { - switch (toolCall.FunctionName) - { - case nameof(GetCurrentLocation): - { - string toolResult = GetCurrentLocation(); - messages.Add(new ToolChatMessage(toolCall.Id, toolResult)); - break; - } - - case nameof(GetCurrentWeather): - { - // The arguments that the model wants to use to call the function are specified as a - // stringified JSON object based on the schema defined in the tool definition. Note that - // the model may hallucinate arguments too. Consequently, it is important to do the - // appropriate parsing and validation before calling the function. - using JsonDocument argumentsJson = JsonDocument.Parse(toolCall.FunctionArguments); - bool hasLocation = argumentsJson.RootElement.TryGetProperty("location", out JsonElement location); - bool hasUnit = argumentsJson.RootElement.TryGetProperty("unit", out JsonElement unit); - - if (!hasLocation) - { - throw new ArgumentNullException(nameof(location), "The location argument is required."); - } - - string toolResult = hasUnit - ? GetCurrentWeather(location.GetString(), unit.GetString()) - : GetCurrentWeather(location.GetString()); - messages.Add(new ToolChatMessage(toolCall.Id, toolResult)); - break; - } - - default: - { - // Handle other unexpected calls. - throw new NotImplementedException(); - } - } - } - - requiresAction = true; - break; - } - - case ChatFinishReason.Length: - throw new NotImplementedException("Incomplete model output due to MaxTokens parameter or token limit exceeded."); - - case ChatFinishReason.ContentFilter: - throw new NotImplementedException("Omitted content due to a content filter flag."); - - case ChatFinishReason.FunctionCall: - throw new NotImplementedException("Deprecated in favor of tool calls."); - - default: - throw new NotImplementedException(chatCompletion.FinishReason.ToString()); - } - } while (requiresAction); - #endregion - - #region - foreach (ChatMessage requestMessage in messages) - { - switch (requestMessage) - { - case SystemChatMessage systemMessage: - Console.WriteLine($"[SYSTEM]:"); - Console.WriteLine($"{systemMessage.Content[0].Text}"); - Console.WriteLine(); - break; - - case UserChatMessage userMessage: - Console.WriteLine($"[USER]:"); - Console.WriteLine($"{userMessage.Content[0].Text}"); - Console.WriteLine(); - break; - - case AssistantChatMessage assistantMessage when assistantMessage.Content.Count > 0: - Console.WriteLine($"[ASSISTANT]:"); - Console.WriteLine($"{assistantMessage.Content[0].Text}"); - Console.WriteLine(); - break; - - case ToolChatMessage: - // Do not print any tool messages; let the assistant summarize the tool results instead. - break; - - default: - break; - } - } - #endregion - } -} diff --git a/src/tests/OpenAI.IntegrationTests/Extended/Chat/Example03_FunctionCallingAsync.cs b/src/tests/OpenAI.IntegrationTests/Extended/Chat/Example03_FunctionCallingAsync.cs deleted file mode 100644 index e66464be..00000000 --- a/src/tests/OpenAI.IntegrationTests/Extended/Chat/Example03_FunctionCallingAsync.cs +++ /dev/null @@ -1,146 +0,0 @@ -using NUnit.Framework; -using OpenAI.Chat; -using System; -using System.Collections.Generic; -using System.Text.Json; -using System.Threading.Tasks; - -namespace OpenAI.Examples; - -public partial class ChatExamples -{ - // See Example03_FunctionCalling.cs for the tool and function definitions. - - [Test] - public async Task Example03_FunctionCallingAsync() - { - ChatClient client = new("gpt-4-turbo", Environment.GetEnvironmentVariable("OPENAI_API_KEY")); - - #region - List messages = [ - new UserChatMessage("What's the weather like today?"), - ]; - - ChatCompletionOptions options = new() - { - Tools = { getCurrentLocationTool, getCurrentWeatherTool }, - }; - #endregion - - #region - bool requiresAction; - - do - { - requiresAction = false; - ChatCompletion chatCompletion = await client.CompleteChatAsync(messages, options); - - switch (chatCompletion.FinishReason) - { - case ChatFinishReason.Stop: - { - // Add the assistant message to the conversation history. - messages.Add(new AssistantChatMessage(chatCompletion)); - break; - } - - case ChatFinishReason.ToolCalls: - { - // First, add the assistant message with tool calls to the conversation history. - messages.Add(new AssistantChatMessage(chatCompletion)); - - // Then, add a new tool message for each tool call that is resolved. - foreach (ChatToolCall toolCall in chatCompletion.ToolCalls) - { - switch (toolCall.FunctionName) - { - case nameof(GetCurrentLocation): - { - string toolResult = GetCurrentLocation(); - messages.Add(new ToolChatMessage(toolCall.Id, toolResult)); - break; - } - - case nameof(GetCurrentWeather): - { - // The arguments that the model wants to use to call the function are specified as a - // stringified JSON object based on the schema defined in the tool definition. Note that - // the model may hallucinate arguments too. Consequently, it is important to do the - // appropriate parsing and validation before calling the function. - using JsonDocument argumentsJson = JsonDocument.Parse(toolCall.FunctionArguments); - bool hasLocation = argumentsJson.RootElement.TryGetProperty("location", out JsonElement location); - bool hasUnit = argumentsJson.RootElement.TryGetProperty("unit", out JsonElement unit); - - if (!hasLocation) - { - throw new ArgumentNullException(nameof(location), "The location argument is required."); - } - - string toolResult = hasUnit - ? GetCurrentWeather(location.GetString(), unit.GetString()) - : GetCurrentWeather(location.GetString()); - messages.Add(new ToolChatMessage(toolCall.Id, toolResult)); - break; - } - - default: - { - // Handle other unexpected calls. - throw new NotImplementedException(); - } - } - } - - requiresAction = true; - break; - } - - case ChatFinishReason.Length: - throw new NotImplementedException("Incomplete model output due to MaxTokens parameter or token limit exceeded."); - - case ChatFinishReason.ContentFilter: - throw new NotImplementedException("Omitted content due to a content filter flag."); - - case ChatFinishReason.FunctionCall: - throw new NotImplementedException("Deprecated in favor of tool calls."); - - default: - throw new NotImplementedException(chatCompletion.FinishReason.ToString()); - } - } while (requiresAction); - #endregion - - #region - foreach (ChatMessage requestMessage in messages) - { - switch (requestMessage) - { - case SystemChatMessage systemMessage: - Console.WriteLine($"[SYSTEM]:"); - Console.WriteLine($"{systemMessage.Content[0].Text}"); - Console.WriteLine(); - break; - - case UserChatMessage userMessage: - Console.WriteLine($"[USER]:"); - Console.WriteLine($"{userMessage.Content[0].Text}"); - Console.WriteLine(); - break; - - case AssistantChatMessage assistantMessage when assistantMessage.Content.Count > 0: - Console.WriteLine($"[ASSISTANT]:"); - Console.WriteLine($"{assistantMessage.Content[0].Text}"); - Console.WriteLine(); - break; - - case ToolChatMessage: - // Do not print any tool messages; let the assistant summarize the tool results instead. - break; - - default: - break; - } - } - #endregion - } -} diff --git a/src/tests/OpenAI.IntegrationTests/Tools/Tests.Tools.cs b/src/tests/OpenAI.IntegrationTests/Tools/Tests.Tools.cs index ca841773..cd55437d 100755 --- a/src/tests/OpenAI.IntegrationTests/Tools/Tests.Tools.cs +++ b/src/tests/OpenAI.IntegrationTests/Tools/Tests.Tools.cs @@ -18,7 +18,7 @@ public async Task WeatherTools(CustomProvider customProvider) var messages = new List { "You are a helpful weather assistant.".AsSystemMessage(), - "What is the current temperature in Dubai, UAE in Celsius?".AsUserMessage(), + "What is the current temperature in Dubai, UAE in Celsius?", }; try