Skip to content

Commit

Permalink
com.openai.unity 7.0.0 (#135)
Browse files Browse the repository at this point in the history
- Added AssistantsEndpoint
- Added ThreadsEndpoint
  • Loading branch information
StephenHodgson authored Nov 30, 2023
1 parent d5dc83f commit 7794d14
Show file tree
Hide file tree
Showing 99 changed files with 4,623 additions and 44 deletions.
528 changes: 526 additions & 2 deletions Documentation~/README.md

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions Runtime/Assistants.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

160 changes: 160 additions & 0 deletions Runtime/Assistants/AssistantExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
// Licensed under the MIT License. See LICENSE in the project root for license information.

using OpenAI.Files;
using OpenAI.Threads;
using System.Threading.Tasks;
using System.Threading;

namespace OpenAI.Assistants
{
public static class AssistantExtensions
{
/// <summary>
/// Modify the assistant.
/// </summary>
/// <param name="assistant"><see cref="AssistantResponse"/>.</param>
/// <param name="request"><see cref="CreateAssistantRequest"/>.</param>
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
/// <returns><see cref="AssistantResponse"/>.</returns>
public static async Task<AssistantResponse> ModifyAsync(this AssistantResponse assistant, CreateAssistantRequest request, CancellationToken cancellationToken = default)
{
request = new CreateAssistantRequest(assistant: assistant, model: request.Model, name: request.Name, description: request.Description, instructions: request.Instructions, tools: request.Tools, files: request.FileIds, metadata: request.Metadata);
return await assistant.Client.AssistantsEndpoint.ModifyAssistantAsync(assistantId: assistant.Id, request: request, cancellationToken: cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
}

/// <summary>
/// Delete the assistant.
/// </summary>
/// <param name="assistant"><see cref="AssistantResponse"/>.</param>
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
/// <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);

/// <summary>
/// Create a thread and run it.
/// </summary>
/// <param name="assistant"><see cref="AssistantResponse"/>.</param>
/// <param name="request">Optional, <see cref="CreateThreadRequest"/>.</param>
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
/// <returns><see cref="RunResponse"/>.</returns>
public static async Task<RunResponse> CreateThreadAndRunAsync(this AssistantResponse assistant, CreateThreadRequest request = null, CancellationToken cancellationToken = default)
=> await assistant.Client.ThreadsEndpoint.CreateThreadAndRunAsync(new CreateThreadAndRunRequest(assistant.Id, createThreadRequest: request), cancellationToken);

#region Files

/// <summary>
/// Returns a list of assistant files.
/// </summary>
/// <param name="assistant"><see cref="AssistantResponse"/>.</param>
/// <param name="query"><see cref="ListQuery"/>.</param>
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
/// <returns><see cref="ListResponse{AssistantFile}"/>.</returns>
public static async Task<ListResponse<AssistantFileResponse>> ListFilesAsync(this AssistantResponse assistant, ListQuery query = null, CancellationToken cancellationToken = default)
=> await assistant.Client.AssistantsEndpoint.ListFilesAsync(assistant.Id, query, cancellationToken);

/// <summary>
/// Attach a file to the <see cref="assistant"/>.
/// </summary>
/// <param name="assistant"><see cref="AssistantResponse"/>.</param>
/// <param name="file">
/// A <see cref="FileResponse"/> (with purpose="assistants") that the assistant should use.
/// Useful for tools like retrieval and code_interpreter that can access files.
/// </param>
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
/// <returns><see cref="AssistantFileResponse"/>.</returns>
public static async Task<AssistantFileResponse> AttachFileAsync(this AssistantResponse assistant, FileResponse file, CancellationToken cancellationToken = default)
=> await assistant.Client.AssistantsEndpoint.AttachFileAsync(assistant.Id, file, cancellationToken);

/// <summary>
/// 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>
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
/// <returns><see cref="AssistantFileResponse"/>.</returns>
public static async Task<AssistantFileResponse> UploadFileAsync(this AssistantResponse assistant, string filePath, CancellationToken cancellationToken = default)
{
var file = await assistant.Client.FilesEndpoint.UploadFileAsync(new FileUploadRequest(filePath, "assistant"), uploadProgress: null, cancellationToken);
return await assistant.AttachFileAsync(file, cancellationToken);
}

/// <summary>
/// Retrieves the <see cref="AssistantFileResponse"/>.
/// </summary>
/// <param name="assistant"><see cref="AssistantResponse"/>.</param>
/// <param name="fileId">The ID of the file we're getting.</param>
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
/// <returns><see cref="AssistantFileResponse"/>.</returns>
public static async Task<AssistantFileResponse> RetrieveFileAsync(this AssistantResponse assistant, string fileId, CancellationToken cancellationToken = default)
=> await assistant.Client.AssistantsEndpoint.RetrieveFileAsync(assistant.Id, fileId, cancellationToken);

// TODO 400 bad request errors. Likely OpenAI bug downloading assistant file content.
///// <summary>
///// Downloads the <see cref="assistantFile"/> to the specified <see cref="directory"/>.
///// </summary>
///// <param name="assistantFile"><see cref="AssistantFileResponse"/>.</param>
///// <param name="directory">The directory to download the file into.</param>
///// <param name="deleteCachedFile">Optional, delete the cached file. Defaults to false.</param>
///// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
///// <returns>The full path of the downloaded file.</returns>
//public static async Task<string> DownloadFileAsync(this AssistantFileResponse assistantFile, string directory, bool deleteCachedFile = false, CancellationToken cancellationToken = default)
// => await assistantFile.Client.FilesEndpoint.DownloadFileAsync(assistantFile.Id, directory, deleteCachedFile, cancellationToken);

/// <summary>
/// Remove AssistantFile.
/// </summary>
/// <remarks>
/// Note that removing an AssistantFile does not delete the original File object,
/// it simply removes the association between that File and the Assistant.
/// To delete a File, use <see cref="DeleteFileAsync(AssistantFileResponse,CancellationToken)"/>.
/// </remarks>
/// <param name="file"><see cref="AssistantResponse"/>.</param>
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
/// <returns>True, if file was removed.</returns>
public static async Task<bool> RemoveFileAsync(this AssistantFileResponse file, CancellationToken cancellationToken = default)
=> await file.Client.AssistantsEndpoint.RemoveFileAsync(file.AssistantId, file.Id, cancellationToken);

/// <summary>
/// Remove AssistantFile.
/// </summary>
/// <remarks>
/// Note that removing an AssistantFile does not delete the original File object,
/// it simply removes the association between that File and the Assistant.
/// To delete a File, use <see cref="DeleteFileAsync(AssistantFileResponse,CancellationToken)"/>.
/// </remarks>
/// <param name="assistant"><see cref="AssistantResponse"/>.</param>
/// <param name="fileId">The ID of the file to remove.</param>
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
/// <returns>True, if file was removed.</returns>
public static async Task<bool> RemoveFileAsync(this AssistantResponse assistant, string fileId, CancellationToken cancellationToken = default)
=> await assistant.Client.AssistantsEndpoint.RemoveFileAsync(assistant.Id, fileId, cancellationToken);

/// <summary>
/// Removes and Deletes a file from the assistant.
/// </summary>
/// <param name="file"><see cref="AssistantResponse"/>.</param>
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
/// <returns>True, if the file was successfully removed from the assistant and deleted.</returns>
public static async Task<bool> DeleteFileAsync(this AssistantFileResponse file, CancellationToken cancellationToken = default)
{
var isRemoved = await file.RemoveFileAsync(cancellationToken);
return isRemoved && await file.Client.FilesEndpoint.DeleteFileAsync(file.Id, cancellationToken);
}

/// <summary>
/// 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>
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
/// <returns>True, if the file was successfully removed from the assistant and deleted.</returns>
public static async Task<bool> DeleteFileAsync(this AssistantResponse assistant, string fileId, CancellationToken cancellationToken = default)
{
var isRemoved = await assistant.Client.AssistantsEndpoint.RemoveFileAsync(assistant.Id, fileId, cancellationToken);
return isRemoved && await assistant.Client.FilesEndpoint.DeleteFileAsync(fileId, cancellationToken);
}

#endregion Files
}
}
11 changes: 11 additions & 0 deletions Runtime/Assistants/AssistantExtensions.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

67 changes: 67 additions & 0 deletions Runtime/Assistants/AssistantFileResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Licensed under the MIT License. See LICENSE in the project root for license information.

using System;
using Newtonsoft.Json;
using UnityEngine.Scripting;

namespace OpenAI.Assistants
{
/// <summary>
/// File attached to an assistant.
/// </summary>
[Preserve]
public sealed class AssistantFileResponse : BaseResponse
{
[Preserve]
[JsonConstructor]
public AssistantFileResponse(
string id,
string @object,
int createdAtUnixTimeSeconds,
string assistantId)
{
Id = id;
Object = @object;
CreatedAtUnixTimeSeconds = createdAtUnixTimeSeconds;
AssistantId = assistantId;
}

/// <summary>
/// The identifier, which can be referenced in API endpoints.
/// </summary>
[Preserve]
[JsonProperty("id")]
public string Id { get; private set; }

/// <summary>
/// The object type, which is always assistant.file.
/// </summary>
[Preserve]
[JsonProperty("object")]
public string Object { get; private set; }

/// <summary>
/// The Unix timestamp (in seconds) for when the assistant file was created.
/// </summary>
[Preserve]
[JsonProperty("created_at")]
public int CreatedAtUnixTimeSeconds { get; private set; }

[Preserve]
[JsonIgnore]
public DateTime CreatedAt => DateTimeOffset.FromUnixTimeSeconds(CreatedAtUnixTimeSeconds).DateTime;

/// <summary>
/// The assistant ID that the file is attached to.
/// </summary>
[Preserve]
[JsonProperty("assistant_id")]
public string AssistantId { get; private set; }

[Preserve]
public static implicit operator string(AssistantFileResponse file) => file?.ToString();

[Preserve]
public override string ToString() => Id;
}
}
11 changes: 11 additions & 0 deletions Runtime/Assistants/AssistantFileResponse.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

133 changes: 133 additions & 0 deletions Runtime/Assistants/AssistantResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// Licensed under the MIT License. See LICENSE in the project root for license information.

using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using UnityEngine.Scripting;

namespace OpenAI.Assistants
{
/// <summary>
/// Purpose-built AI that uses OpenAI’s models and calls tools.
/// </summary>
[Preserve]
public sealed class AssistantResponse : BaseResponse
{
[Preserve]
[JsonConstructor]
public AssistantResponse(
string id,
string @object,
int createdAtUnixTimeSeconds,
string name,
string description,
string model,
string instructions,
IReadOnlyList<Tool> tools,
IReadOnlyList<string> fileIds,
IReadOnlyDictionary<string, string> metadata)
{
Id = id;
Object = @object;
CreatedAtUnixTimeSeconds = createdAtUnixTimeSeconds;
Name = name;
Description = description;
Model = model;
Instructions = instructions;
Tools = tools;
FileIds = fileIds;
Metadata = metadata;
}

/// <summary>
/// The identifier, which can be referenced in API endpoints.
/// </summary>
[Preserve]
[JsonProperty("id")]
public string Id { get; }

/// <summary>
/// The object type, which is always assistant.
/// </summary>
[Preserve]
[JsonProperty("object")]
public string Object { get; }

/// <summary>
/// The Unix timestamp (in seconds) for when the assistant was created.
/// </summary>
[Preserve]
[JsonProperty("created_at")]
public int CreatedAtUnixTimeSeconds { get; }

[Preserve]
[JsonIgnore]
public DateTime CreatedAt => DateTimeOffset.FromUnixTimeSeconds(CreatedAtUnixTimeSeconds).DateTime;

/// <summary>
/// The name of the assistant.
/// The maximum length is 256 characters.
/// </summary>
[Preserve]
[JsonProperty("name")]
public string Name { get; }

/// <summary>
/// The description of the assistant.
/// The maximum length is 512 characters.
/// </summary>
[Preserve]
[JsonProperty("description")]
public string Description { get; }

/// <summary>
/// ID of the model to use.
/// You can use the List models API to see all of your available models,
/// or see our Model overview for descriptions of them.
/// </summary>
[Preserve]
[JsonProperty("model")]
public string Model { get; }

/// <summary>
/// The system instructions that the assistant uses.
/// The maximum length is 32768 characters.
/// </summary>
[Preserve]
[JsonProperty("instructions")]
public string Instructions { get; }

/// <summary>
/// A list of tool enabled on the assistant.
/// There can be a maximum of 128 tools per assistant.
/// Tools can be of types 'code_interpreter', 'retrieval', or 'function'.
/// </summary>
[Preserve]
[JsonProperty("tools")]
public IReadOnlyList<Tool> Tools { get; }

/// <summary>
/// A list of file IDs attached to this assistant.
/// There can be a maximum of 20 files attached to the assistant.
/// Files are ordered by their creation date in ascending order.
/// </summary>
[Preserve]
[JsonProperty("file_ids")]
public IReadOnlyList<string> FileIds { get; }

/// <summary>
/// Set of 16 key-value pairs that can be attached to an object.
/// This can be useful for storing additional information about the object in a structured format.
/// Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.
/// </summary>
[Preserve]
[JsonProperty("metadata")]
public IReadOnlyDictionary<string, string> Metadata { get; }

[Preserve]
public static implicit operator string(AssistantResponse assistant) => assistant?.Id;

[Preserve]
public override string ToString() => Id;
}
}
Loading

0 comments on commit 7794d14

Please sign in to comment.