Skip to content

Commit

Permalink
OpenAI-DotNet 7.7.1 (#247)
Browse files Browse the repository at this point in the history
- More Function utilities and invoking methods
  - Added FunctionPropertyAttribute to help better inform the feature how to format the Function json
  - Added FromFunc<,> overloads for convenance
  - Fixed invoke args sometimes being casting to wrong type
  - Added additional protections for static and instanced function calls
  - Added additional tool utilities:
    - Tool.ClearRegisteredTools
    - Tool.IsToolRegistered(Tool) - Tool.TryRegisterTool(Tool)
  - Improved memory usage and performance by propertly disposing http content and response objects
  - Updated debug output to be formatted to json for easier reading and debugging
  • Loading branch information
StephenHodgson authored Feb 25, 2024
1 parent 192c765 commit d3e59d1
Show file tree
Hide file tree
Showing 38 changed files with 1,170 additions and 479 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

namespace OpenAI.Tests
{
internal class TestFixture_00_00_Authentication
internal class TestFixture_00_01_Authentication
{
[SetUp]
public void Setup()
Expand Down
40 changes: 0 additions & 40 deletions OpenAI-DotNet-Tests/TestFixture_00_02_Extensions.cs

This file was deleted.

78 changes: 78 additions & 0 deletions OpenAI-DotNet-Tests/TestFixture_00_02_Tools.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Licensed under the MIT License. See LICENSE in the project root for license information.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Threading.Tasks;
using NUnit.Framework;
using OpenAI.Images;
using OpenAI.Tests.Weather;

namespace OpenAI.Tests
{
internal class TestFixture_00_02_Tools : AbstractTestFixture
{
[Test]
public void Test_01_GetTools()
{
var tools = Tool.GetAllAvailableTools(forceUpdate: true, clearCache: true).ToList();
Assert.IsNotNull(tools);
Assert.IsNotEmpty(tools);
tools.Add(Tool.GetOrCreateTool(OpenAIClient.ImagesEndPoint, nameof(ImagesEndpoint.GenerateImageAsync)));
var json = JsonSerializer.Serialize(tools, new JsonSerializerOptions(OpenAIClient.JsonSerializationOptions)
{
WriteIndented = true
});
Console.WriteLine(json);
}

[Test]
public async Task Test_02_Tool_Funcs()
{
var tools = new List<Tool>
{
Tool.FromFunc("test_func", Function),
Tool.FromFunc<string, string, string>("test_func_with_args", FunctionWithArgs),
Tool.FromFunc("test_func_weather", () => WeatherService.GetCurrentWeatherAsync("my location", WeatherService.WeatherUnit.Celsius))
};

var json = JsonSerializer.Serialize(tools, new JsonSerializerOptions(OpenAIClient.JsonSerializationOptions)
{
WriteIndented = true
});
Console.WriteLine(json);
Assert.IsNotNull(tools);
var tool = tools[0];
Assert.IsNotNull(tool);
var result = tool.InvokeFunction<string>();
Assert.AreEqual("success", result);
var toolWithArgs = tools[1];
Assert.IsNotNull(toolWithArgs);
toolWithArgs.Function.Arguments = new JsonObject
{
["arg1"] = "arg1",
["arg2"] = "arg2"
};
var resultWithArgs = toolWithArgs.InvokeFunction<string>();
Assert.AreEqual("arg1 arg2", resultWithArgs);

var toolWeather = tools[2];
Assert.IsNotNull(toolWeather);
var resultWeather = await toolWeather.InvokeFunctionAsync();
Assert.IsFalse(string.IsNullOrWhiteSpace(resultWeather));
Console.WriteLine(resultWeather);
}

private string Function()
{
return "success";
}

private string FunctionWithArgs(string arg1, string arg2)
{
return $"{arg1} {arg2}";
}
}
}
19 changes: 11 additions & 8 deletions OpenAI-DotNet-Tests/TestFixture_03_Chat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ public async Task Test_02_01_GetChatToolCompletion()

var messages = new List<Message>
{
new(Role.System, "You are a helpful weather assistant."),
new(Role.System, "You are a helpful weather assistant.\n\r- Always prompt the user for their location."),
new(Role.User, "What's the weather like today?"),
};

Expand All @@ -142,12 +142,13 @@ public async Task Test_02_01_GetChatToolCompletion()
Console.WriteLine($"{message.Role}: {message.Content}");
}

var tools = Tool.GetAllAvailableTools(false);
var tools = Tool.GetAllAvailableTools(false, forceUpdate: true, clearCache: true);
var chatRequest = new ChatRequest(messages, tools: tools, toolChoice: "auto");
var response = await OpenAIClient.ChatEndpoint.GetCompletionAsync(chatRequest);
Assert.IsNotNull(response);
Assert.IsNotNull(response.Choices);
Assert.IsTrue(response.Choices.Count == 1);
Assert.IsTrue(response.FirstChoice.FinishReason == "stop");
messages.Add(response.FirstChoice.Message);

Console.WriteLine($"{response.FirstChoice.Message.Role}: {response.FirstChoice} | Finish Reason: {response.FirstChoice.FinishReason}");
Expand All @@ -163,7 +164,7 @@ public async Task Test_02_01_GetChatToolCompletion()
Assert.IsTrue(response.Choices.Count == 1);
messages.Add(response.FirstChoice.Message);

if (!string.IsNullOrEmpty(response.ToString()))
if (response.FirstChoice.FinishReason == "stop")
{
Console.WriteLine($"{response.FirstChoice.Message.Role}: {response.FirstChoice} | Finish Reason: {response.FirstChoice.FinishReason}");

Expand Down Expand Up @@ -198,7 +199,7 @@ public async Task Test_02_02_GetChatToolCompletion_Streaming()
Assert.IsNotNull(OpenAIClient.ChatEndpoint);
var messages = new List<Message>
{
new(Role.System, "You are a helpful weather assistant."),
new(Role.System, "You are a helpful weather assistant.\n\r- Always prompt the user for their location."),
new(Role.User, "What's the weather like today?"),
};

Expand Down Expand Up @@ -281,11 +282,11 @@ public async Task Test_02_03_ChatCompletion_Multiple_Tools_Streaming()
Assert.IsNotNull(OpenAIClient.ChatEndpoint);
var messages = new List<Message>
{
new(Role.System, "You are a helpful weather assistant. Use the appropriate unit based on geographical location."),
new(Role.System, "You are a helpful weather assistant.\n\r - Use the appropriate unit based on geographical location."),
new(Role.User, "What's the weather like today in Los Angeles, USA and Tokyo, Japan?"),
};

var tools = Tool.GetAllAvailableTools(false);
var tools = Tool.GetAllAvailableTools(false, forceUpdate: true, clearCache: true);
var chatRequest = new ChatRequest(messages, model: "gpt-4-turbo-preview", tools: tools, toolChoice: "auto");
var response = await OpenAIClient.ChatEndpoint.StreamCompletionAsync(chatRequest, partialResponse =>
{
Expand All @@ -294,6 +295,7 @@ public async Task Test_02_03_ChatCompletion_Multiple_Tools_Streaming()
Assert.NotZero(partialResponse.Choices.Count);
});

Assert.IsTrue(response.FirstChoice.FinishReason == "tool_calls");
messages.Add(response.FirstChoice.Message);

var toolCalls = response.FirstChoice.Message.ToolCalls;
Expand Down Expand Up @@ -328,12 +330,13 @@ public async Task Test_02_04_GetChatToolForceCompletion()
Console.WriteLine($"{message.Role}: {message.Content}");
}

var tools = Tool.GetAllAvailableTools(false);
var tools = Tool.GetAllAvailableTools(false, forceUpdate: true, clearCache: true);
var chatRequest = new ChatRequest(messages, tools: tools);
var response = await OpenAIClient.ChatEndpoint.GetCompletionAsync(chatRequest);
Assert.IsNotNull(response);
Assert.IsNotNull(response.Choices);
Assert.IsTrue(response.Choices.Count == 1);
Assert.IsTrue(response.FirstChoice.FinishReason == "stop");
messages.Add(response.FirstChoice.Message);

Console.WriteLine($"{response.FirstChoice.Message.Role}: {response.FirstChoice} | Finish Reason: {response.FirstChoice.FinishReason}");
Expand Down Expand Up @@ -422,7 +425,7 @@ public async Task Test_04_01_GetChatLogProbs()
new(Role.Assistant, "The Los Angeles Dodgers won the World Series in 2020."),
new(Role.User, "Where was it played?"),
};
var chatRequest = new ChatRequest(messages, Model.GPT3_5_Turbo, topLogProbs: 1);
var chatRequest = new ChatRequest(messages, topLogProbs: 1);
var response = await OpenAIClient.ChatEndpoint.GetCompletionAsync(chatRequest);
Assert.IsNotNull(response);
Assert.IsNotNull(response.Choices);
Expand Down
11 changes: 3 additions & 8 deletions OpenAI-DotNet-Tests/TestFixture_12_Threads.cs
Original file line number Diff line number Diff line change
Expand Up @@ -381,14 +381,9 @@ public async Task Test_07_01_SubmitToolOutput()
Assert.IsTrue(toolCall.FunctionCall.Name.Contains(nameof(WeatherService.GetCurrentWeatherAsync)));
Assert.IsNotNull(toolCall.FunctionCall.Arguments);
Console.WriteLine($"tool call arguments: {toolCall.FunctionCall.Arguments}");
var toolOutputs = await testAssistant.GetToolOutputsAsync(run.RequiredAction.SubmitToolOutputs.ToolCalls);

foreach (var toolOutput in toolOutputs)
{
Console.WriteLine($"tool call output: {toolOutput.Output}");
}

run = await run.SubmitToolOutputsAsync(toolOutputs);
var toolOutput = await testAssistant.GetToolOutputAsync(toolCall);
Console.WriteLine($"tool call output: {toolOutput.Output}");
run = await run.SubmitToolOutputsAsync(toolOutput);
// waiting while run in Queued and InProgress
run = await run.WaitForStatusChangeAsync();
Assert.AreEqual(RunStatus.Completed, run.Status);
Expand Down
8 changes: 4 additions & 4 deletions OpenAI-DotNet/Assistants/AssistantExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public static async Task<AssistantResponse> ModifyAsync(this AssistantResponse a
/// </summary>
/// <param name="assistant"><see cref="AssistantResponse"/>.</param>
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
/// <returns>True, if the assistant was successfully deleted.</returns>
/// <returns>True, if the <see cref="assistant"/> was successfully deleted.</returns>
public static async Task<bool> DeleteAsync(this AssistantResponse assistant, CancellationToken cancellationToken = default)
=> await assistant.Client.AssistantsEndpoint.DeleteAssistantAsync(assistant.Id, cancellationToken).ConfigureAwait(false);

Expand All @@ -58,7 +58,7 @@ public static async Task<ListResponse<AssistantFileResponse>> ListFilesAsync(thi
=> await assistant.Client.AssistantsEndpoint.ListFilesAsync(assistant.Id, query, cancellationToken).ConfigureAwait(false);

/// <summary>
/// Attach a file to the assistant.
/// Attach a file to the <see cref="assistant"/>.
/// </summary>
/// <param name="assistant"><see cref="AssistantResponse"/>.</param>
/// <param name="file">
Expand All @@ -71,7 +71,7 @@ public static async Task<AssistantFileResponse> AttachFileAsync(this AssistantRe
=> await assistant.Client.AssistantsEndpoint.AttachFileAsync(assistant.Id, file, cancellationToken).ConfigureAwait(false);

/// <summary>
/// Uploads a new file at the specified path and attaches it to the assistant.
/// Uploads a new file at the specified <see cref="filePath"/> and attaches it to the <see cref="assistant"/>.
/// </summary>
/// <param name="assistant"><see cref="AssistantResponse"/>.</param>
/// <param name="filePath">The local file path to upload.</param>
Expand Down Expand Up @@ -162,7 +162,7 @@ public static async Task<bool> DeleteFileAsync(this AssistantFileResponse file,
}

/// <summary>
/// Removes and Deletes a file from the assistant.
/// Removes and Deletes a file from the <see cref="assistant"/>.
/// </summary>
/// <param name="assistant"><see cref="AssistantResponse"/>.</param>
/// <param name="fileId">The ID of the file to delete.</param>
Expand Down
Loading

0 comments on commit d3e59d1

Please sign in to comment.