Home

Awesome

OpenAI

Discord openupm openupm

Based on OpenAI-DotNet

A OpenAI package for the Unity to use though their RESTful API. Independently developed, this is not an official library and I am not affiliated with OpenAI. An OpenAI API account is required.

All copyrights, trademarks, logos, and assets are the property of their respective owners.

Installing

Requires Unity 2021.3 LTS or higher.

The recommended installation method is though the unity package manager and OpenUPM.

Via Unity Package Manager and OpenUPM

scoped-registries

Via Unity Package Manager and Git url


Documentation

Check out our new api docs!

https://rageagainstthepixel.github.io/OpenAI-DotNet

Table of Contents

Authentication

There are 4 ways to provide your API keys, in order of precedence:

[!WARNING] We recommended using the environment variables to load the API key instead of having it hard coded in your source. It is not recommended use this method in production, but only for accepting user credentials, local testing and quick start scenarios.

  1. Pass keys directly with constructor :warning:
  2. Unity Scriptable Object :warning:
  3. Load key from configuration file
  4. Use System Environment Variables

You use the OpenAIAuthentication when you initialize the API as shown:

Pass keys directly with constructor

[!WARNING] We recommended using the environment variables to load the API key instead of having it hard coded in your source. It is not recommended use this method in production, but only for accepting user credentials, local testing and quick start scenarios.

var api = new OpenAIClient("sk-apiKey");

Or create a OpenAIAuthentication object manually

var api = new OpenAIClient(new OpenAIAuthentication("sk-apiKey", "org-yourOrganizationId", "proj_yourProjectId"));

Unity Scriptable Object

You can save the key directly into a scriptable object that is located in the Assets/Resources folder.

You can create a new one by using the context menu of the project pane and creating a new OpenAIConfiguration scriptable object.

[!WARNING] Beware checking this file into source control, as other people will be able to see your API key. It is recommended to use the OpenAI-DotNet-Proxy and authenticate users with your preferred OAuth provider.

Create new OpenAIConfiguration

Load key from configuration file

Attempts to load api keys from a configuration file, by default .openai in the current directory, optionally traversing up the directory tree or in the user's home directory.

To create a configuration file, create a new text file named .openai and containing the line:

[!NOTE] Organization and project id entries are optional.

Json format
{
  "apiKey": "sk-aaaabbbbbccccddddd",
  "organizationId": "org-yourOrganizationId",
  "projectId": "proj_yourProjectId"
}
Deprecated format
OPENAI_API_KEY=sk-aaaabbbbbccccddddd
OPENAI_ORGANIZATION_ID=org-yourOrganizationId
OPENAI_PROJECT_ID=proj_yourProjectId

You can also load the configuration file directly with known path by calling static methods in OpenAIAuthentication:

var api = new OpenAIClient(new OpenAIAuthentication().LoadFromDirectory("path/to/your/directory"));
var api = new OpenAIClient(new OpenAIAuthentication().LoadFromPath("path/to/your/file.json"));

Use System Environment Variables

Use your system's environment variables specify an api key and organization to use.

var api = new OpenAIClient(new OpenAIAuthentication().LoadFromEnvironment());

Azure OpenAI

You can also choose to use Microsoft's Azure OpenAI deployments as well.

You can find the required information in the Azure Playground by clicking the View Code button and view a URL like this:

https://{your-resource-name}.openai.azure.com/openai/deployments/{deployment-id}/chat/completions?api-version={api-version}

To setup the client to use your deployment, you'll need to pass in OpenAISettings into the client constructor.

var auth = new OpenAIAuthentication("sk-apiKey");
var settings = new OpenAISettings(resourceName: "your-resource-name", deploymentId: "deployment-id", apiVersion: "api-version");
var api = new OpenAIClient(auth, settings);

Azure Active Directory Authentication

Authenticate with MSAL as usual and get access token, then use the access token when creating your OpenAIAuthentication. Then be sure to set useAzureActiveDirectory to true when creating your OpenAISettings.

Tutorial: Desktop app that calls web APIs: Acquire a token

// get your access token using any of the MSAL methods
var accessToken = result.AccessToken;
var auth = new OpenAIAuthentication(accessToken);
var settings = new OpenAISettings(resourceName: "your-resource", deploymentId: "deployment-id", apiVersion: "api-version", useActiveDirectoryAuthentication: true);
var api = new OpenAIClient(auth, settings);

OpenAI API Proxy

NuGet version (OpenAI-DotNet-Proxy)

Using either the OpenAI-DotNet or com.openai.unity packages directly in your front-end app may expose your API keys and other sensitive information. To mitigate this risk, it is recommended to set up an intermediate API that makes requests to OpenAI on behalf of your front-end app. This library can be utilized for both front-end and intermediary host configurations, ensuring secure communication with the OpenAI API.

Front End Example

In the front end example, you will need to securely authenticate your users using your preferred OAuth provider. Once the user is authenticated, exchange your custom auth token with your API key on the backend.

Follow these steps:

  1. Setup a new project using either the OpenAI-DotNet or com.openai.unity packages.
  2. Authenticate users with your OAuth provider.
  3. After successful authentication, create a new OpenAIAuthentication object and pass in the custom token with the prefix sess-.
  4. Create a new OpenAISettings object and specify the domain where your intermediate API is located.
  5. Pass your new auth and settings objects to the OpenAIClient constructor when you create the client instance.

Here's an example of how to set up the front end:

var authToken = await LoginAsync();
var auth = new OpenAIAuthentication($"sess-{authToken}");
var settings = new OpenAISettings(domain: "api.your-custom-domain.com");
var api = new OpenAIClient(auth, settings);

This setup allows your front end application to securely communicate with your backend that will be using the OpenAI-DotNet-Proxy, which then forwards requests to the OpenAI API. This ensures that your OpenAI API keys and other sensitive information remain secure throughout the process.

Back End Example

In this example, we demonstrate how to set up and use OpenAIProxy in a new ASP.NET Core web app. The proxy server will handle authentication and forward requests to the OpenAI API, ensuring that your API keys and other sensitive information remain secure.

  1. Create a new ASP.NET Core minimal web API project.
  2. Add the OpenAI-DotNet nuget package to your project.
    • Powershell install: Install-Package OpenAI-DotNet-Proxy
    • Dotnet install: dotnet add package OpenAI-DotNet-Proxy
    • Manually editing .csproj: <PackageReference Include="OpenAI-DotNet-Proxy" />
  3. Create a new class that inherits from AbstractAuthenticationFilter and override the ValidateAuthentication method. This will implement the IAuthenticationFilter that you will use to check user session token against your internal server.
  4. In Program.cs, create a new proxy web application by calling OpenAIProxy.CreateWebApplication method, passing your custom AuthenticationFilter as a type argument.
  5. Create OpenAIAuthentication and OpenAIClientSettings as you would normally with your API keys, org id, or Azure settings.
public partial class Program
{
    private class AuthenticationFilter : AbstractAuthenticationFilter
    {
        public override async Task ValidateAuthenticationAsync(IHeaderDictionary request)
        {
            await Task.CompletedTask; // remote resource call to verify token

            // You will need to implement your own class to properly test
            // custom issued tokens you've setup for your end users.
            if (!request.Authorization.ToString().Contains(TestUserToken))
            {
                throw new AuthenticationException("User is not authorized");
            }
        }
    }

    public static void Main(string[] args)
    {
        var auth = OpenAIAuthentication.LoadFromEnv();
        var settings = new OpenAIClientSettings(/* your custom settings if using Azure OpenAI */);
        using var openAIClient = new OpenAIClient(auth, settings);
        OpenAIProxy.CreateWebApplication<AuthenticationFilter>(args, openAIClient).Run();
    }
}

Once you have set up your proxy server, your end users can now make authenticated requests to your proxy api instead of directly to the OpenAI API. The proxy server will handle authentication and forward requests to the OpenAI API, ensuring that your API keys and other sensitive information remain secure.


Models

List and describe the various models available in the API. You can refer to the Models documentation to understand what models are available and the differences between them.

Also checkout model endpoint compatibility to understand which models work with which endpoints.

To specify a custom model not pre-defined in this library:

var model = new Model("model-id");

The Models API is accessed via OpenAIClient.ModelsEndpoint

List models

Lists the currently available models, and provides basic information about each one such as the owner and availability.

var api = new OpenAIClient();
var models = await api.ModelsEndpoint.GetModelsAsync();

foreach (var model in models)
{
    Debug.Log(model.ToString());
}

Retrieve model

Retrieves a model instance, providing basic information about the model such as the owner and permissions.

var api = new OpenAIClient();
var model = await api.ModelsEndpoint.GetModelDetailsAsync("gpt-4o");
Debug.Log(model.ToString());

Delete Fine Tuned Model

Delete a fine-tuned model. You must have the Owner role in your organization.

var api = new OpenAIClient();
var isDeleted = await api.ModelsEndpoint.DeleteFineTuneModelAsync("your-fine-tuned-model");
Assert.IsTrue(isDeleted);

Realtime

[!WARNING] Beta Feature. API subject to breaking changes.

The Realtime API enables you to build low-latency, multi-modal conversational experiences. It currently supports text and audio as both input and output, as well as function calling.

The Assistants API is accessed via OpenAIClient.RealtimeEndpoint

Create Realtime Session

Here is a simple example of how to create a realtime session and to send and receive messages from the model.

var api = new OpenAIClient();
var cancellationTokenSource = new CancellationTokenSource();
var tools = new List<Tool>
{
    Tool.FromFunc("goodbye", () =>
    {
        cancellationTokenSource.Cancel();
        return "Goodbye!";
    })
};
var options = new Options(Model.GPT4oRealtime, tools: tools);
using var session = await api.RealtimeEndpoint.CreateSessionAsync(options);
var responseTask = session.ReceiveUpdatesAsync<IServerEvent>(ServerEvents, cancellationTokenSource.Token);
await session.SendAsync(new ConversationItemCreateRequest("Hello!"));
await session.SendAsync(new CreateResponseRequest());
await session.SendAsync(new InputAudioBufferAppendRequest(new ReadOnlyMemory<byte>(new byte[1024 * 4])), cancellationTokenSource.Token);
await session.SendAsync(new ConversationItemCreateRequest("GoodBye!"));
await session.SendAsync(new CreateResponseRequest());
await responseTask;

void ServerEvents(IServerEvent @event)
{
    switch (@event)
    {
        case ResponseAudioTranscriptResponse transcriptResponse:
            Debug.Log(transcriptResponse.ToString());
            break;
        case ResponseFunctionCallArgumentsResponse functionCallResponse:
            if (functionCallResponse.IsDone)
            {
                ToolCall toolCall = functionCallResponse;
                toolCall.InvokeFunction();
            }

            break;
    }
}

Client Events

The library implements IClientEvent interface for outgoing client sent events.

Sending Client Events

You can send client events at any time to the server by calling the RealtimeSession.SendAsync method on the session object. The send call will return a IServerEvent handle that best represents the appropriate response from the server for that event. This is useful if you want to handle server responses in a more granular way.

Ideally though, you may want to handle all server responses with RealtimeSession.ReceiveUpdatesAsync.

[!NOTE] The server will not send a confirmation response to the InputAudioBufferAppendRequest event.

[!IMPORTANT] You will also need to send CreateResponseRequest to trigger the model to generate a response.

var serverEvent = await session.SendAsync(new ConversationItemCreateRequest("Hello!"));
Debug.Log(serverEvent.ToJsonString());
serverEvent = await session.SendAsync(new CreateResponseRequest());
Debug.Log(serverEvent.ToJsonString());

Server Events

The library implements IServerEvent interface for incoming server sent events.

Receiving Server Events

To receive server events, you will need to call the RealtimeSession.ReceiveUpdatesAsync method on the session object. This method will return a Task that will complete when the session is closed or when the cancellation token is triggered. Ideally this method should be called once and runs for the duration of the session.

[!NOTE] You can also get sent IClientEvent callbacks as well by using the IRealtimeEvent interface instead of IServerEvent.

await session.ReceiveUpdatesAsync<IServerEvent>(ServerEvents, cancellationTokenSource.Token);

void ServerEvents(IServerEvent @event)
{
    switch (@event)
    {
        case RealtimeEventError error:
            // raised anytime an error occurs
            break;
        case SessionResponse sessionResponse:
            // raised when a session is created or updated
            break;
        case RealtimeConversationResponse conversationResponse:
            // raised when a new conversation is created
            break;
        case ConversationItemCreatedResponse conversationItemCreated:
            // raised when a new conversation item is created
            break;
        case ConversationItemInputAudioTranscriptionResponse conversationItemTranscription:
            // raised when the input audio transcription is completed or failed
            break;
        case ConversationItemTruncatedResponse conversationItemTruncated:
            // raised when a conversation item is truncated
            break;
        case ConversationItemDeletedResponse conversationItemDeleted:
            // raised when a conversation item is deleted
            break;
        case InputAudioBufferCommittedResponse committedResponse:
            // raised when an input audio buffer is committed
            break;
        case InputAudioBufferClearedResponse clearedResponse:
            // raised when an input audio buffer is cleared
            break;
        case InputAudioBufferStartedResponse startedResponse:
            // raised when speech is detected in the audio buffer
            break;
        case InputAudioBufferStoppedResponse stoppedResponse:
            // raised when speech stops in the audio buffer
            break;
        case RealtimeResponse realtimeResponse:
            // raised when a response is created or done
            break;
        case ResponseOutputItemResponse outputItemResponse:
            // raised when a response output item is added or done
            break;
        case ResponseContentPartResponse contentPartResponse:
            // raised when a response content part is added or done
            break;
        case ResponseTextResponse textResponse:
            // raised when a response text is updated or done
            break;
        case ResponseAudioTranscriptResponse transcriptResponse:
            // raised when a response audio transcript is updated or done
            break;
        case ResponseFunctionCallArgumentsResponse functionCallResponse:
            // raised when a response function call arguments are updated or done
            break;
        case RateLimitsResponse rateLimitsResponse:
            // raised when rate limits are updated
            break;
    }
}

Assistants

[!WARNING] Beta Feature. API subject to breaking changes.

Build assistants that can call models and use tools to perform tasks.

The Assistants API is accessed via OpenAIClient.AssistantsEndpoint

List Assistants

Returns a list of assistants.

var api = new OpenAIClient();
var assistantsList = await api.AssistantsEndpoint.ListAssistantsAsync();

foreach (var assistant in assistantsList.Items)
{
    Debug.Log($"{assistant} -> {assistant.CreatedAt}");
}

Create Assistant

Create an assistant with a model and instructions.

var api = new OpenAIClient();
var request = new CreateAssistantRequest(Model.GPT4o);
var assistant = await api.AssistantsEndpoint.CreateAssistantAsync(request);

Retrieve Assistant

Retrieves an assistant.

var api = new OpenAIClient();
var assistant = await api.AssistantsEndpoint.RetrieveAssistantAsync("assistant-id");
Debug.Log($"{assistant} -> {assistant.CreatedAt}");

Modify Assistant

Modifies an assistant.

var api = new OpenAIClient();
var createRequest = new CreateAssistantRequest(Model.GPT4_Turbo);
var assistant = await api.AssistantsEndpoint.CreateAssistantAsync(createRequest);
var modifyRequest = new CreateAssistantRequest(Model.GPT4o);
var modifiedAssistant = await api.AssistantsEndpoint.ModifyAssistantAsync(assistant.Id, modifyRequest);
// OR AssistantExtension for easier use!
var modifiedAssistantEx = await assistant.ModifyAsync(modifyRequest);

Delete Assistant

Delete an assistant.

var api = new OpenAIClient();
var isDeleted = await api.AssistantsEndpoint.DeleteAssistantAsync("assistant-id");
// OR AssistantExtension for easier use!
var isDeleted = await assistant.DeleteAsync();
Assert.IsTrue(isDeleted);

Assistant Streaming

[!NOTE] Assistant stream events can be easily added to existing thread calls by passing Func<IServerSentEvent, Task> streamEventHandler callback to any existing method that supports streaming.

Threads

Create Threads that Assistants can interact with.

The Threads API is accessed via OpenAIClient.ThreadsEndpoint

Create Thread

Create a thread.

var api = new OpenAIClient();
var thread = await api.ThreadsEndpoint.CreateThreadAsync();
Debug.Log($"Create thread {thread.Id} -> {thread.CreatedAt}");
Create Thread and Run

Create a thread and run it in one request.

See also: Thread Runs

var api = new OpenAIClient();
var assistant = await api.AssistantsEndpoint.CreateAssistantAsync(
    new CreateAssistantRequest(
        name: "Math Tutor",
        instructions: "You are a personal math tutor. Answer questions briefly, in a sentence or less.",
        model: Model.GPT4o));
var messages = new List<Message> { "I need to solve the equation `3x + 11 = 14`. Can you help me?" };
var threadRequest = new CreateThreadRequest(messages);
var run = await assistant.CreateThreadAndRunAsync(threadRequest);
Debug.Log($"Created thread and run: {run.ThreadId} -> {run.Id} -> {run.CreatedAt}");
Create Thread and Run Streaming

Create a thread and run it in one request while streaming events.

var api = new OpenAIClient();
var tools = new List<Tool>
{
    Tool.GetOrCreateTool(typeof(WeatherService), nameof(WeatherService.GetCurrentWeatherAsync))
};
var assistantRequest = new CreateAssistantRequest(tools: tools, instructions: "You are a helpful weather assistant. Use the appropriate unit based on geographical location.");
var assistant = await api.AssistantsEndpoint.CreateAssistantAsync(assistantRequest);
ThreadResponse thread = null;
async Task StreamEventHandler(IServerSentEvent streamEvent)
{
    switch (streamEvent)
    {
        case ThreadResponse threadResponse:
            thread = threadResponse;
            break;
        case RunResponse runResponse:
            if (runResponse.Status == RunStatus.RequiresAction)
            {
                var toolOutputs = await assistant.GetToolOutputsAsync(runResponse);

                foreach (var toolOutput in toolOutputs)
                {
                    Debug.Log($"Tool Output: {toolOutput}");
                }

                await runResponse.SubmitToolOutputsAsync(toolOutputs, StreamEventHandler);
            }
            break;
        default:
            Debug.Log(streamEvent.ToJsonString());
            break;
    }
}

var run = await assistant.CreateThreadAndRunAsync("I'm in Kuala-Lumpur, please tell me what's the temperature now?", StreamEventHandler);
run = await run.WaitForStatusChangeAsync();
var messages = await thread.ListMessagesAsync();
foreach (var response in messages.Items.Reverse())
{
    Debug.Log($"{response.Role}: {response.PrintContent()}");
}
Retrieve Thread

Retrieves a thread.

var api = new OpenAIClient();
var thread = await api.ThreadsEndpoint.RetrieveThreadAsync("thread-id");
// OR if you simply wish to get the latest state of a thread
thread = await thread.UpdateAsync();
Debug.Log($"Retrieve thread {thread.Id} -> {thread.CreatedAt}");
Modify Thread

Modifies a thread.

Note: Only the metadata can be modified.

var api = new OpenAIClient();
var thread = await api.ThreadsEndpoint.CreateThreadAsync();
var metadata = new Dictionary<string, string>
{
    { "key", "custom thread metadata" }
}
thread = await api.ThreadsEndpoint.ModifyThreadAsync(thread.Id, metadata);
// OR use extension method for convenience!
thread = await thread.ModifyAsync(metadata);
Debug.Log($"Modify thread {thread.Id} -> {thread.Metadata["key"]}");
Delete Thread

Delete a thread.

var api = new OpenAIClient();
var isDeleted = await api.ThreadsEndpoint.DeleteThreadAsync("thread-id");
// OR use extension method for convenience!
var isDeleted = await thread.DeleteAsync();
Assert.IsTrue(isDeleted);
Thread Messages

Create messages within threads.

List Thread Messages

Returns a list of messages for a given thread.

var api = new OpenAIClient();
var messageList = await api.ThreadsEndpoint.ListMessagesAsync("thread-id");
// OR use extension method for convenience!
var messageList = await thread.ListMessagesAsync();

foreach (var message in messageList.Items)
{
    Debug.Log($"{message.Id}: {message.Role}: {message.PrintContent()}");
}
Create Thread Message

Create a message.

var api = new OpenAIClient();
var thread = await api.ThreadsEndpoint.CreateThreadAsync();
var request = new CreateMessageRequest("Hello world!");
var message = await api.ThreadsEndpoint.CreateMessageAsync(thread.Id, request);
// OR use extension method for convenience!
var message = await thread.CreateMessageAsync("Hello World!");
Debug.Log($"{message.Id}: {message.Role}: {message.PrintContent()}");
Retrieve Thread Message

Retrieve a message.

var api = new OpenAIClient();
var message = await api.ThreadsEndpoint.RetrieveMessageAsync("thread-id", "message-id");
// OR use extension methods for convenience!
var message = await thread.RetrieveMessageAsync("message-id");
var message = await message.UpdateAsync();
Debug.Log($"{message.Id}: {message.Role}: {message.PrintContent()}");
Modify Thread Message

Modify a message.

Note: Only the message metadata can be modified.

var api = new OpenAIClient();
var metadata = new Dictionary<string, string>
{
    { "key", "custom message metadata" }
};
var message = await api.ThreadsEndpoint.ModifyMessageAsync("thread-id", "message-id", metadata);
// OR use extension method for convenience!
var message = await message.ModifyAsync(metadata);
Debug.Log($"Modify message metadata: {message.Id} -> {message.Metadata["key"]}");
Thread Runs

Represents an execution run on a thread.

List Thread Runs

Returns a list of runs belonging to a thread.

var api = new OpenAIClient();
var runList = await api.ThreadsEndpoint.ListRunsAsync("thread-id");
// OR use extension method for convenience!
var runList = await thread.ListRunsAsync();

foreach (var run in runList.Items)
{
    Debug.Log($"[{run.Id}] {run.Status} | {run.CreatedAt}");
}
Create Thread Run

Create a run.

var api = new OpenAIClient();
var assistant = await api.AssistantsEndpoint.CreateAssistantAsync(
    new CreateAssistantRequest(
        name: "Math Tutor",
        instructions: "You are a personal math tutor. Answer questions briefly, in a sentence or less.",
        model: Model.GPT4o));
var thread = await api.ThreadsEndpoint.CreateThreadAsync();
var message = await thread.CreateMessageAsync("I need to solve the equation `3x + 11 = 14`. Can you help me?");
var run = await thread.CreateRunAsync(assistant);
Debug.Log($"[{run.Id}] {run.Status} | {run.CreatedAt}");
Create Thread Run Streaming

Create a run and stream the events.

var api = new OpenAIClient();
var assistant = await api.AssistantsEndpoint.CreateAssistantAsync(
    new CreateAssistantRequest(
        name: "Math Tutor",
        instructions: "You are a personal math tutor. Answer questions briefly, in a sentence or less. Your responses should be formatted in JSON.",
        model: Model.GPT4o,
        responseFormat: ChatResponseFormat.Json));
var thread = await api.ThreadsEndpoint.CreateThreadAsync();
var message = await thread.CreateMessageAsync("I need to solve the equation `3x + 11 = 14`. Can you help me?");
var run = await thread.CreateRunAsync(assistant, async streamEvent =>
{
    Debug.Log(streamEvent.ToJsonString());
    await Task.CompletedTask;
});
var messages = await thread.ListMessagesAsync();

foreach (var response in messages.Items.Reverse())
{
    Debug.Log($"{response.Role}: {response.PrintContent()}");
}
Retrieve Thread Run

Retrieves a run.

var api = new OpenAIClient();
var run = await api.ThreadsEndpoint.RetrieveRunAsync("thread-id", "run-id");
// OR use extension method for convenience!
var run = await thread.RetrieveRunAsync("run-id");
var run = await run.UpdateAsync();
Debug.Log($"[{run.Id}] {run.Status} | {run.CreatedAt}");
Modify Thread Run

Modifies a run.

Note: Only the metadata can be modified.

var api = new OpenAIClient();
var metadata = new Dictionary<string, string>
{
    { "key", "custom run metadata" }
};
var run = await api.ThreadsEndpoint.ModifyRunAsync("thread-id", "run-id", metadata);
// OR use extension method for convenience!
var run = await run.ModifyAsync(metadata);
Debug.Log($"Modify run {run.Id} -> {run.Metadata["key"]}");
Thread Submit Tool Outputs to Run

When a run has the status: requires_action and required_action.type is submit_tool_outputs, this endpoint can be used to submit the outputs from the tool calls once they're all completed. All outputs must be submitted in a single request.

[!NOTE] See Create Thread and Run Streaming example on how to stream tool output events.

var api = new OpenAIClient();
var tools = new List<Tool>
{
    // Use a predefined tool
    Tool.Retrieval, Tool.CodeInterpreter,
    // Or create a tool from a type and the name of the method you want to use for function calling
    Tool.GetOrCreateTool(typeof(WeatherService), nameof(WeatherService.GetCurrentWeatherAsync)),
    // Pass in an instance of an object to call a method on it
    Tool.GetOrCreateTool(api.ImagesEndPoint, nameof(ImagesEndpoint.GenerateImageAsync)),
    // Define func<,> callbacks
    Tool.FromFunc("name_of_func", () => { /* callback function */ }),
    Tool.FromFunc<T1,T2,TResult>("func_with_multiple_params", (t1, t2) => { /* logic that calculates return value */ return tResult; })
};
var assistantRequest = new CreateAssistantRequest(tools: tools, instructions: "You are a helpful weather assistant. Use the appropriate unit based on geographical location.");
var testAssistant = await api.AssistantsEndpoint.CreateAssistantAsync(assistantRequest);
var run = await testAssistant.CreateThreadAndRunAsync("I'm in Kuala-Lumpur, please tell me what's the temperature now?");
// waiting while run is Queued and InProgress
run = await run.WaitForStatusChangeAsync();

// Invoke all of the tool call functions and return the tool outputs.
var toolOutputs = await testAssistant.GetToolOutputsAsync(run.RequiredAction.SubmitToolOutputs.ToolCalls);

foreach (var toolOutput in toolOutputs)
{
    Debug.Log($"tool call output: {toolOutput.Output}");
}
// submit the tool outputs
run = await run.SubmitToolOutputsAsync(toolOutputs);
// waiting while run in Queued and InProgress
run = await run.WaitForStatusChangeAsync();
var messages = await run.ListMessagesAsync();

foreach (var message in messages.Items.OrderBy(response => response.CreatedAt))
{
    Debug.Log($"{message.Role}: {message.PrintContent()}");
}
Thread Structured Outputs

Structured Outputs is the evolution of JSON mode. While both ensure valid JSON is produced, only Structured Outputs ensure schema adherence.

[!IMPORTANT]

First define the structure of your responses. These will be used as your schema. These are the objects you'll deserialize to, so be sure to use standard Json object models.

public class MathResponse
{
    [JsonProperty("steps")]
    public IReadOnlyList<MathStep> Steps { get; private set; }

    [JsonProperty("final_answer")]
    public string FinalAnswer { get; private set; }
}

public class MathStep
{
    [JsonProperty("explanation")]
    public string Explanation { get; private set; }

    [JsonProperty("output")]
    public string Output { get; private set; }
}

To use, simply specify the MathResponse type as a generic constraint in either CreateAssistantAsync, CreateRunAsync, or CreateThreadAndRunAsync.

var api = new OpenAIClient();
var assistant = await api.AssistantsEndpoint.CreateAssistantAsync<MathResponse>(
    new CreateAssistantRequest(
        name: "Math Tutor",
        instructions: "You are a helpful math tutor. Guide the user through the solution step by step.",
        model: "gpt-4o-2024-08-06"));
ThreadResponse thread = null;

try
{
    async Task StreamEventHandler(IServerSentEvent @event)
    {
        try
        {
            switch (@event)
            {
                case MessageResponse message:
                    if (message.Status != MessageStatus.Completed)
                    {
                        Debug.Log(@event.ToJsonString());
                        break;
                    }

                    var mathResponse = message.FromSchema<MathResponse>();

                    for (var i = 0; i < mathResponse.Steps.Count; i++)
                    {
                        var step = mathResponse.Steps[i];
                        Debug.Log($"Step {i}: {step.Explanation}");
                        Debug.Log($"Result: {step.Output}");
                    }

                    Debug.Log($"Final Answer: {mathResponse.FinalAnswer}");
                    break;
                default:
                    Debug.Log(@event.ToJsonString());
                    break;
            }
        }
        catch (Exception e)
        {
            Debug.Log(e);
            throw;
        }

        await Task.CompletedTask;
    }

    var run = await assistant.CreateThreadAndRunAsync("how can I solve 8x + 7 = -23", StreamEventHandler);
    thread = await run.GetThreadAsync();
    run = await run.WaitForStatusChangeAsync();
    Debug.Log($"Created thread and run: {run.ThreadId} -> {run.Id} -> {run.CreatedAt}");
    var messages = await thread.ListMessagesAsync();

    foreach (var response in messages.Items.OrderBy(response => response.CreatedAt))
    {
        Debug.Log($"{response.Role}: {response.PrintContent()}");
    }
}
finally
{
    await assistant.DeleteAsync(deleteToolResources: thread == null);

    if (thread != null)
    {
        var isDeleted = await thread.DeleteAsync(deleteToolResources: true);
    }
}

You can also manually create json schema json string as well, but you will be responsible for deserializing your response data:

var api = new OpenAIClient();
var mathSchema = new JsonSchema("math_response", @"
{
  ""type"": ""object"",
  ""properties"": {
    ""steps"": {
      ""type"": ""array"",
      ""items"": {
        ""type"": ""object"",
        ""properties"": {
          ""explanation"": {
            ""type"": ""string""
          },
          ""output"": {
            ""type"": ""string""
          }
        },
        ""required"": [
          ""explanation"",
          ""output""
        ],
        ""additionalProperties"": false
      }
    },
    ""final_answer"": {
      ""type"": ""string""
    }
  },
  ""required"": [
    ""steps"",
    ""final_answer""
  ],
  ""additionalProperties"": false
}");
var assistant = await api.AssistantsEndpoint.CreateAssistantAsync(
    new CreateAssistantRequest(
        name: "Math Tutor",
        instructions: "You are a helpful math tutor. Guide the user through the solution step by step.",
        model: "gpt-4o-2024-08-06",
        jsonSchema: mathSchema));
ThreadResponse thread = null;

try
{
    var run = await assistant.CreateThreadAndRunAsync("how can I solve 8x + 7 = -23",
        async @event =>
        {
            Debug.Log(@event.ToJsonString());
            await Task.CompletedTask;
        });
    thread = await run.GetThreadAsync();
    run = await run.WaitForStatusChangeAsync();
    Debug.Log($"Created thread and run: {run.ThreadId} -> {run.Id} -> {run.CreatedAt}");
    var messages = await thread.ListMessagesAsync();

    foreach (var response in messages.Items)
    {
        Debug.Log($"{response.Role}: {response.PrintContent()}");
    }
}
finally
{
    await assistant.DeleteAsync(deleteToolResources: thread == null);

    if (thread != null)
    {
        var isDeleted = await thread.DeleteAsync(deleteToolResources: true);
        Assert.IsTrue(isDeleted);
    }
}
List Thread Run Steps

Returns a list of run steps belonging to a run.

var api = new OpenAIClient();
var runStepList = await api.ThreadsEndpoint.ListRunStepsAsync("thread-id", "run-id");
// OR use extension method for convenience!
var runStepList = await run.ListRunStepsAsync();

foreach (var runStep in runStepList.Items)
{
    Debug.Log($"[{runStep.Id}] {runStep.Status} {runStep.CreatedAt} -> {runStep.ExpiresAt}");
}
Retrieve Thread Run Step

Retrieves a run step.

var api = new OpenAIClient();
var runStep = await api.ThreadsEndpoint.RetrieveRunStepAsync("thread-id", "run-id", "step-id");
// OR use extension method for convenience!
var runStep = await run.RetrieveRunStepAsync("step-id");
var runStep = await runStep.UpdateAsync();
Debug.Log($"[{runStep.Id}] {runStep.Status} {runStep.CreatedAt} -> {runStep.ExpiresAt}");
Cancel Thread Run

Cancels a run that is in_progress.

var api = new OpenAIClient();
var isCancelled = await api.ThreadsEndpoint.CancelRunAsync("thread-id", "run-id");
// OR use extension method for convenience!
var isCancelled = await run.CancelAsync();
Assert.IsTrue(isCancelled);

Vector Stores

Vector stores are used to store files for use by the file_search tool.

The Vector Stores API is accessed via OpenAIClient.VectorStoresEndpoint

List Vector Stores

Returns a list of vector stores.

var api = new OpenAIClient();
var vectorStores = await api.VectorStoresEndpoint.ListVectorStoresAsync();

foreach (var vectorStore in vectorStores.Items)
{
    Debug.Log(vectorStore);
}
Create Vector Store

Create a vector store.

var api = new OpenAIClient();
var createVectorStoreRequest = new CreateVectorStoreRequest("test-vector-store");
var vectorStore = await api.VectorStoresEndpoint.CreateVectorStoreAsync(createVectorStoreRequest);
Debug.Log(vectorStore);
Retrieve Vector Store

Retrieves a vector store.

var api = new OpenAIClient();
var vectorStore = await api.VectorStoresEndpoint.GetVectorStoreAsync("vector-store-id");
Debug.Log(vectorStore);
Modify Vector Store

Modifies a vector store.

var api = new OpenAIClient();
var metadata = new Dictionary<string, object> { { "Test", DateTime.UtcNow } };
var vectorStore = await api.VectorStoresEndpoint.ModifyVectorStoreAsync("vector-store-id", metadata: metadata);
Debug.Log(vectorStore);
Delete Vector Store

Delete a vector store.

var api = new OpenAIClient();
var isDeleted = await api.VectorStoresEndpoint.DeleteVectorStoreAsync("vector-store-id");
Assert.IsTrue(isDeleted);
Vector Store Files

Vector store files represent files inside a vector store.

List Vector Store Files

Returns a list of vector store files.

var api = new OpenAIClient();
var files = await api.VectorStoresEndpoint.ListVectorStoreFilesAsync("vector-store-id");

foreach (var file in vectorStoreFiles.Items)
{
    Debug.Log(file);
}
Create Vector Store File

Create a vector store file by attaching a file to a vector store.

var api = new OpenAIClient();
var file = await api.VectorStoresEndpoint.CreateVectorStoreFileAsync("vector-store-id", "file-id", new ChunkingStrategy(ChunkingStrategyType.Static));
Debug.Log(file);
Retrieve Vector Store File

Retrieves a vector store file.

var api = new OpenAIClient();
var file = await api.VectorStoresEndpoint.GetVectorStoreFileAsync("vector-store-id", "vector-store-file-id");
Debug.Log(file);
Delete Vector Store File

Delete a vector store file. This will remove the file from the vector store but the file itself will not be deleted. To delete the file, use the delete file endpoint.

var api = new OpenAIClient();
var isDeleted = await api.VectorStoresEndpoint.DeleteVectorStoreFileAsync("vector-store-id", vectorStoreFile);
Assert.IsTrue(isDeleted);
Vector Store File Batches

Vector store files represent files inside a vector store.

Create Vector Store File Batch

Create a vector store file batch.

var api = new OpenAIClient();
var files = new List<string> { "file_id_1","file_id_2" };
var vectorStoreFileBatch = await api.VectorStoresEndpoint.CreateVectorStoreFileBatchAsync("vector-store-id", files);
Debug.Log(vectorStoreFileBatch);
Retrieve Vector Store File Batch

Retrieves a vector store file batch.

var api = new OpenAIClient();
var vectorStoreFileBatch = await api.VectorStoresEndpoint.GetVectorStoreFileBatchAsync("vector-store-id", "vector-store-file-batch-id");
// you can also use convenience methods!
vectorStoreFileBatch = await vectorStoreFileBatch.UpdateAsync();
vectorStoreFileBatch = await vectorStoreFileBatch.WaitForStatusChangeAsync();
List Files In Vector Store Batch

Returns a list of vector store files in a batch.

var api = new OpenAIClient();
var files = await api.VectorStoresEndpoint.ListVectorStoreBatchFilesAsync("vector-store-id", "vector-store-file-batch-id");

foreach (var file in files.Items)
{
    Debug.Log(file);
}
Cancel Vector Store File Batch

Cancel a vector store file batch. This attempts to cancel the processing of files in this batch as soon as possible.

var api = new OpenAIClient();
var isCancelled = await api.VectorStoresEndpoint.CancelVectorStoreFileBatchAsync("vector-store-id", "vector-store-file-batch-id");

Chat

Given a chat conversation, the model will return a chat completion response.

The Chat API is accessed via OpenAIClient.ChatEndpoint

Chat Completions

Creates a completion for the chat message

var api = new OpenAIClient();
var messages = new List<Message>
{
    new Message(Role.System, "You are a helpful assistant."),
    new Message(Role.User, "Who won the world series in 2020?"),
    new Message(Role.Assistant, "The Los Angeles Dodgers won the World Series in 2020."),
    new Message(Role.User, "Where was it played?"),
};
var chatRequest = new ChatRequest(messages, Model.GPT4o);
var response = await api.ChatEndpoint.GetCompletionAsync(chatRequest);
var choice = response.FirstChoice;
Debug.Log($"[{choice.Index}] {choice.Message.Role}: {choice.Message} | Finish Reason: {choice.FinishReason}");

Chat Streaming

var api = new OpenAIClient();
var messages = new List<Message>
{
    new Message(Role.System, "You are a helpful assistant."),
    new Message(Role.User, "Who won the world series in 2020?"),
    new Message(Role.Assistant, "The Los Angeles Dodgers won the World Series in 2020."),
    new Message(Role.User, "Where was it played?"),
};
var chatRequest = new ChatRequest(messages);
var response = await api.ChatEndpoint.StreamCompletionAsync(chatRequest, async partialResponse =>
{
    Debug.Log(partialResponse.FirstChoice.Delta.ToString());
    await Task.CompletedTask;
});
var choice = response.FirstChoice;
Debug.Log($"[{choice.Index}] {choice.Message.Role}: {choice.Message} | Finish Reason: {choice.FinishReason}");

Chat Tools

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

foreach (var message in messages)
{
    Debug.Log($"{message.Role}: {message}");
}

// Define the tools that the assistant is able to use:
// 1. Get a list of all the static methods decorated with FunctionAttribute
var tools = Tool.GetAllAvailableTools(includeDefaults: false, forceUpdate: true, clearCache: true);
// 2. Define a custom list of tools:
var tools = new List<Tool>
{
    Tool.GetOrCreateTool(objectInstance, "TheNameOfTheMethodToCall"),
    Tool.FromFunc("a_custom_name_for_your_function", ()=> { /* Some logic to run */ })
};
var chatRequest = new ChatRequest(messages, tools: tools, toolChoice: "auto");
var response = await api.ChatEndpoint.GetCompletionAsync(chatRequest);
messages.Add(response.FirstChoice.Message);

Debug.Log($"{response.FirstChoice.Message.Role}: {response.FirstChoice} | Finish Reason: {response.FirstChoice.FinishReason}");

var locationMessage = new Message(Role.User, "I'm in Glasgow, Scotland");
messages.Add(locationMessage);
Debug.Log($"{locationMessage.Role}: {locationMessage.Content}");
chatRequest = new ChatRequest(messages, tools: tools, toolChoice: "auto");
response = await api.ChatEndpoint.GetCompletionAsync(chatRequest);

messages.Add(response.FirstChoice.Message);

if (response.FirstChoice.FinishReason == "stop")
{
    Debug.Log($"{response.FirstChoice.Message.Role}: {response.FirstChoice} | Finish Reason: {response.FirstChoice.FinishReason}");

    var unitMessage = new Message(Role.User, "Fahrenheit");
    messages.Add(unitMessage);
    Debug.Log($"{unitMessage.Role}: {unitMessage.Content}");
    chatRequest = new ChatRequest(messages, tools: tools, toolChoice: "auto");
    response = await api.ChatEndpoint.GetCompletionAsync(chatRequest);
}

// iterate over all tool calls and invoke them
foreach (var toolCall in response.FirstChoice.Message.ToolCalls)
{
    Debug.Log($"{response.FirstChoice.Message.Role}: {toolCall.Function.Name} | Finish Reason: {response.FirstChoice.FinishReason}");
    Debug.Log($"{toolCall.Function.Arguments}");
    // Invokes function to get a generic json result to return for tool call.
    var functionResult = await toolCall.InvokeFunctionAsync();
    // If you know the return type and do additional processing you can use generic overload
    var functionResult = await toolCall.InvokeFunctionAsync<string>();
    messages.Add(new Message(toolCall, functionResult));
    Debug.Log($"{Role.Tool}: {functionResult}");
}
// System: You are a helpful weather assistant.
// User: What's the weather like today?
// Assistant: Sure, may I know your current location? | Finish Reason: stop
// User: I'm in Glasgow, Scotland
// Assistant: GetCurrentWeather | Finish Reason: tool_calls
// {
//   "location": "Glasgow, Scotland",
//   "unit": "celsius"
// }
// Tool: The current weather in Glasgow, Scotland is 39°C.

Chat Vision

[!WARNING] Beta Feature. API subject to breaking changes.

var api = new OpenAIClient();
var messages = new List<Message>
{
    new Message(Role.System, "You are a helpful assistant."),
    new Message(Role.User, new List<Content>
    {
        "What's in this image?",
        new ImageUrl("https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg", ImageDetail.Low)
    })
};
var chatRequest = new ChatRequest(messages, model: Model.GPT4o);
var response = await api.ChatEndpoint.GetCompletionAsync(chatRequest);
Debug.Log($"{response.FirstChoice.Message.Role}: {response.FirstChoice.Message.Content} | Finish Reason: {response.FirstChoice.FinishDetails}");

You can even pass in a Texture2D!

var api = new OpenAIClient();
var messages = new List<Message>
{
    new Message(Role.System, "You are a helpful assistant."),
    new Message(Role.User, new List<Content>
    {
        "What's in this image?",
        texture
    })
};
var chatRequest = new ChatRequest(messages, model: Model.GPT4o);
var result = await api.ChatEndpoint.GetCompletionAsync(chatRequest);
Debug.Log($"{result.FirstChoice.Message.Role}: {result.FirstChoice} | Finish Reason: {result.FirstChoice.FinishDetails}");

Chat Audio

var api = new OpenAIClient();
var messages = new List<Message>
{
    new Message(Role.System, "You are a helpful assistant."),
    new Message(Role.User, "Is a golden retriever a good family dog?")
};
var chatRequest = new ChatRequest(messages, Model.GPT4oAudio, audioConfig: Voice.Alloy);
var response = await api.ChatEndpoint.GetCompletionAsync(chatRequest);
Debug.Log($"{response.FirstChoice.Message.Role}: {response.FirstChoice} | Finish Reason: {response.FirstChoice.FinishDetails}");
audioSource.PlayOneShot(response.FirstChoice.Message.AudioOutput.AudioClip);

Chat Structured Outputs

The evolution of Json Mode. While both ensure valid JSON is produced, only Structured Outputs ensure schema adherence.

[!IMPORTANT]

First define the structure of your responses. These will be used as your schema. These are the objects you'll deserialize to, so be sure to use standard Json object models.

public class MathResponse
{
    [JsonProperty("steps")]
    public IReadOnlyList<MathStep> Steps { get; private set; }

    [JsonProperty("final_answer")]
    public string FinalAnswer { get; private set; }
}

public class MathStep
{
    [JsonProperty("explanation")]
    public string Explanation { get; private set; }

    [JsonProperty("output")]
    public string Output { get; private set; }
}

To use, simply specify the MathResponse type as a generic constraint when requesting a completion.

var api = new OpenAIClient();
var messages = new List<Message>
{
    new(Role.System, "You are a helpful math tutor. Guide the user through the solution step by step."),
    new(Role.User, "how can I solve 8x + 7 = -23")
};

var chatRequest = new ChatRequest(messages, model: "gpt-4o-2024-08-06");
var (mathResponse, chatResponse) = await api.ChatEndpoint.GetCompletionAsync<MathResponse>(chatRequest);

for (var i = 0; i < mathResponse.Steps.Count; i++)
{
    var step = mathResponse.Steps[i];
    Debug.Log($"Step {i}: {step.Explanation}");
    Debug.Log($"Result: {step.Output}");
}

Debug.Log($"Final Answer: {mathResponse.FinalAnswer}");
chatResponse.GetUsage();

Chat Json Mode

[!IMPORTANT]

var messages = new List<Message>
{
    new Message(Role.System, "You are a helpful assistant designed to output JSON."),
    new Message(Role.User, "Who won the world series in 2020?"),
};
var chatRequest = new ChatRequest(messages, Model.GPT4o, responseFormat: ChatResponseFormat.Json);
var response = await api.ChatEndpoint.GetCompletionAsync(chatRequest);

foreach (var choice in response.Choices)
{
    Debug.Log($"[{choice.Index}] {choice.Message.Role}: {choice} | Finish Reason: {choice.FinishReason}");
}

response.GetUsage();

Audio

Converts audio into text.

The Audio API is accessed via OpenAIClient.AudioEndpoint

Create Speech

Generates audio from the input text.

var api = new OpenAIClient();
var request = new SpeechRequest("Hello world!");
var (path, clip) = await api.AudioEndpoint.CreateSpeechAsync(request);
audioSource.PlayOneShot(clip);
Debug.Log(path);
[Stream Speech]

Generate streamed audio from the input text.

var api = new OpenAIClient();
var request = new SpeechRequest("Hello world!");
var (path, clip) = await api.AudioEndpoint.CreateSpeechStreamAsync(request, partialClip => audioSource.PlayOneShot(partialClip));
Debug.Log(path);

Create Transcription

Transcribes audio into the input language.

var api = new OpenAIClient();
var request = new AudioTranscriptionRequest(audioClip, language: "en");
var result = await api.AudioEndpoint.CreateTranscriptionAsync(request);
Debug.Log(result);

You can also get detailed information using verbose_json to get timestamp granularities:

var api = new OpenAIClient();
using var request = new AudioTranscriptionRequest(transcriptionAudio, responseFormat: AudioResponseFormat.Verbose_Json, timestampGranularity: TimestampGranularity.Word, temperature: 0.1f, language: "en");
var response = await api.AudioEndpoint.CreateTranscriptionTextAsync(request);

foreach (var word in response.Words)
{
    Debug.Log($"[{word.Start}-{word.End}] \"{word.Word}\"");
}

Create Translation

Translates audio into into English.

var api = new OpenAIClient();
var request = new AudioTranslationRequest(audioClip);
var result = await api.AudioEndpoint.CreateTranslationAsync(request);
Debug.Log(result);

Images

Given a prompt and/or an input image, the model will generate a new image.

The Images API is accessed via OpenAIClient.ImagesEndpoint

Create Image

Creates an image given a prompt.

var api = new OpenAIClient();
var request = new ImageGenerationRequest("A house riding a velociraptor", Models.Model.DallE_3);
var imageResults = await api.ImagesEndPoint.GenerateImageAsync(request);

foreach (var result in imageResults)
{
    Debug.Log(result.ToString());
    Assert.IsNotNull(result.Texture);
}

Edit Image

Creates an edited or extended image given an original image and a prompt.

var api = new OpenAIClient();
var request = new ImageEditRequest(Path.GetFullPath(imageAssetPath), Path.GetFullPath(maskAssetPath), "A sunlit indoor lounge area with a pool containing a flamingo", size: ImageSize.Small);
var imageResults = await api.ImagesEndPoint.CreateImageEditAsync(request);

foreach (var result in imageResults)
{
    Debug.Log(result.ToString());
    Assert.IsNotNull(result.Texture);
}

Create Image Variation

Creates a variation of a given image.

var api = new OpenAIClient();
var request = new ImageVariationRequest(imageTexture, size: ImageSize.Small);
var imageResults = await api.ImagesEndPoint.CreateImageVariationAsync(request);

foreach (var result in imageResults)
{
    Debug.Log(result.ToString());
    Assert.IsNotNull(result.Texture);
}

Alternatively, the endpoint can directly take a Texture2D with Read/Write enabled and Compression set to None.

var api = new OpenAIClient();
var request = new ImageVariationRequest(imageTexture, size: ImageSize.Small);
var imageResults = await api.ImagesEndPoint.CreateImageVariationAsync(request);

foreach (var result in imageResults)
{
    Debug.Log(result.ToString());
    Assert.IsNotNull(result.Texture);
}

Files

Files are used to upload documents that can be used with features like Fine-tuning.

The Files API is accessed via OpenAIClient.FilesEndpoint

List Files

Returns a list of files that belong to the user's organization.

var api = new OpenAIClient();
var fileList = await api.FilesEndpoint.ListFilesAsync();

foreach (var file in fileList)
{
    Debug.Log($"{file.Id} -> {file.Object}: {file.FileName} | {file.Size} bytes");
}

Upload File

Upload a file that can be used across various endpoints. The size of all the files uploaded by one organization can be up to 100 GB.

The size of individual files can be a maximum of 512 MB. See the Assistants Tools guide to learn more about the types of files supported. The Fine-tuning API only supports .jsonl files.

var api = new OpenAIClient();
var file = await api.FilesEndpoint.UploadFileAsync("path/to/your/file.jsonl", FilePurpose.FineTune);
Debug.Log(file.Id);

Delete File

Delete a file.

var api = new OpenAIClient();
var isDeleted = await api.FilesEndpoint.DeleteFileAsync(fileId);
Assert.IsTrue(isDeleted);

Retrieve File Info

Returns information about a specific file.

var api = new OpenAIClient();
var file = await api.FilesEndpoint.GetFileInfoAsync(fileId);
Debug.Log($"{file.Id} -> {file.Object}: {file.FileName} | {file.Size} bytes");

Download File Content

Downloads the file content to the specified directory.

var api = new OpenAIClient();
var downloadedFilePath = await api.FilesEndpoint.DownloadFileAsync(fileId);
Debug.Log(downloadedFilePath);
Assert.IsTrue(File.Exists(downloadedFilePath));

Fine Tuning

Manage fine-tuning jobs to tailor a model to your specific training data.

Related guide: Fine-tune models

The Files API is accessed via OpenAIClient.FineTuningEndpoint

Create Fine Tune Job

Creates a job that fine-tunes a specified model from a given dataset.

Response includes details of the enqueued job including job status and the name of the fine-tuned models once complete.

var api = new OpenAIClient();
var fileId = "file-abc123";
var request = new CreateFineTuneRequest(fileId);
var job = await api.FineTuningEndpoint.CreateJobAsync(Model.GPT3_5_Turbo, request);
Debug.Log($"Started {job.Id} | Status: {job.Status}");

List Fine Tune Jobs

List your organization's fine-tuning jobs.

var api = new OpenAIClient();
var jobList = await api.FineTuningEndpoint.ListJobsAsync();

foreach (var job in jobList.Items.OrderByDescending(job => job.CreatedAt)))
{
    Debug.Log($"{job.Id} -> {job.CreatedAt} | {job.Status}");
}

Retrieve Fine Tune Job Info

Gets info about the fine-tune job.

var api = new OpenAIClient();
var job = await api.FineTuningEndpoint.GetJobInfoAsync(fineTuneJob);
Debug.Log($"{job.Id} -> {job.CreatedAt} | {job.Status}");

Cancel Fine Tune Job

Immediately cancel a fine-tune job.

var api = new OpenAIClient();
var isCancelled = await api.FineTuningEndpoint.CancelFineTuneJobAsync(fineTuneJob);
Assert.IsTrue(isCancelled);

List Fine Tune Job Events

Get status updates for a fine-tuning job.

var api = new OpenAIClient();
var eventList = await api.FineTuningEndpoint.ListJobEventsAsync(fineTuneJob);
Debug.Log($"{fineTuneJob.Id} -> status: {fineTuneJob.Status} | event count: {eventList.Events.Count}");

foreach (var @event in eventList.Items.OrderByDescending(@event => @event.CreatedAt))
{
    Debug.Log($"  {@event.CreatedAt} [{@event.Level}] {@event.Message}");
}

Batches

Create large batches of API requests for asynchronous processing. The Batch API returns completions within 24 hours for a 50% discount.

The Batches API is accessed via OpenAIClient.BatchesEndpoint

List Batches

List your organization's batches.

var api = new OpenAIClient();
var batches = await api.BatchEndpoint.ListBatchesAsync();

foreach (var batch in listResponse.Items)
{
    Debug.Log(batch);
}

Create Batch

Creates and executes a batch from an uploaded file of requests

var api = new OpenAIClient();
var batchRequest = new CreateBatchRequest("file-id", Endpoint.ChatCompletions);
var batch = await api.BatchEndpoint.CreateBatchAsync(batchRequest);

Retrieve Batch

Retrieves a batch.

var api = new OpenAIClient();
var batch = await api.BatchEndpoint.RetrieveBatchAsync("batch-id");
// you can also use convenience methods!
batch = await batch.UpdateAsync();
batch = await batch.WaitForStatusChangeAsync();

Cancel Batch

Cancels an in-progress batch. The batch will be in status cancelling for up to 10 minutes, before changing to cancelled, where it will have partial results (if any) available in the output file.

var api = new OpenAIClient();
var isCancelled = await api.BatchEndpoint.CancelBatchAsync(batch);
Assert.IsTrue(isCancelled);

Embeddings

Get a vector representation of a given input that can be easily consumed by machine learning models and algorithms.

Related guide: Embeddings

The Edits API is accessed via OpenAIClient.EmbeddingsEndpoint

Create Embeddings

Creates an embedding vector representing the input text.

var api = new OpenAIClient();
var response = await api.EmbeddingsEndpoint.CreateEmbeddingAsync("The food was delicious and the waiter...", Models.Embedding_Ada_002);
Debug.Log(response);

Moderations

Given a input text, outputs if the model classifies it as violating OpenAI's content policy.

Related guide: Moderations

The Moderations API can be accessed via OpenAIClient.ModerationsEndpoint

Create Moderation

Classifies if text violates OpenAI's Content Policy.

var api = new OpenAIClient();
var isViolation = await api.ModerationsEndpoint.GetModerationAsync("I want to kill them.");
Assert.IsTrue(isViolation);

Additionally you can also get the scores of a given input.

var api = new OpenAIClient();
var response = await api.ModerationsEndpoint.CreateModerationAsync(new ModerationsRequest("I love you"));
Assert.IsNotNull(response);
Debug.Log(response.Results?[0]?.Scores?.ToString());