Skip to content

Commit

Permalink
com.openai.unity 8.0.2 (#251)
Browse files Browse the repository at this point in the history
- Fixed Thread.Message.Attachement serialization
- Fixed CreateAssistantRequest to properly copy all override assistant properties
- Fixed some objects that are chunked, were not properly being appended to the final object
- Added FileSearchOptions to Tool.FileSearch
- Added some additional constructor overloads for CodeInterpreterResources
- Added some additional constructor overloads for VectorStoreRequest
- Thread.DeleteAsync and Assistant.DeleteAsync now fetch the latest before deleting when deleteToolResources is also requested
- Refactored the way Function handles reflected invocations for both synchronous and asynchronous calls
  - Function.InvokeAsync will now properly also call synchronous invocations in the tool call collection
- Refactored Threads/Assistant Unit Tests
  • Loading branch information
StephenHodgson authored Jun 15, 2024
1 parent 7070d1f commit c081574
Show file tree
Hide file tree
Showing 37 changed files with 745 additions and 512 deletions.
6 changes: 5 additions & 1 deletion Documentation~/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@ The recommended installation method is though the unity package manager and [Ope

---

## Documentation
## [Documentation](https://rageagainstthepixel.github.io/OpenAI-DotNet)

> Check out our new api docs!
<https://rageagainstthepixel.github.io/OpenAI-DotNet> :new:

### Table of Contents

Expand Down
40 changes: 23 additions & 17 deletions Runtime/Assistants/AssistantExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,18 @@ public static class AssistantExtensions
public static async Task<AssistantResponse> ModifyAsync(this AssistantResponse assistant, CreateAssistantRequest request, CancellationToken cancellationToken = default)
=> await assistant.Client.AssistantsEndpoint.ModifyAssistantAsync(
assistantId: assistant.Id,
request: new CreateAssistantRequest(
assistant: assistant,
model: request.Model,
name: request.Name,
description: request.Description,
instructions: request.Instructions,
toolResources: request.ToolResources,
tools: request.Tools,
metadata: request.Metadata,
temperature: request.Temperature,
topP: request.TopP,
responseFormat: request.ResponseFormat),
request: request ?? new CreateAssistantRequest(assistant),
cancellationToken: cancellationToken);

/// <summary>
/// Get the latest status of the assistant.
/// </summary>
/// <param name="assistant"><see cref="AssistantResponse"/>.</param>
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
/// <returns><see cref="AssistantResponse"/>.</returns>
public static async Task<AssistantResponse> UpdateAsync(this AssistantResponse assistant, CancellationToken cancellationToken = default)
=> await assistant.Client.AssistantsEndpoint.RetrieveAssistantAsync(assistant, cancellationToken);

/// <summary>
/// Delete the assistant.
/// </summary>
Expand All @@ -47,6 +45,11 @@ public static async Task<AssistantResponse> ModifyAsync(this AssistantResponse a
/// <returns>True, if the <see cref="assistant"/> was successfully deleted.</returns>
public static async Task<bool> DeleteAsync(this AssistantResponse assistant, bool deleteToolResources = false, CancellationToken cancellationToken = default)
{
if (deleteToolResources)
{
assistant = await assistant.UpdateAsync(cancellationToken);
}

var deleteTasks = new List<Task<bool>> { assistant.Client.AssistantsEndpoint.DeleteAssistantAsync(assistant.Id, cancellationToken) };

if (deleteToolResources && assistant.ToolResources?.FileSearch?.VectorStoreIds is { Count: > 0 })
Expand Down Expand Up @@ -80,15 +83,16 @@ public static async Task<RunResponse> CreateThreadAndRunAsync(this AssistantResp
/// <param name="assistant"><see cref="AssistantResponse"/>.</param>
/// <param name="toolCall"><see cref="ToolCall"/>.</param>
/// <returns>Tool output result as <see cref="string"/>.</returns>
/// <remarks>Only call this directly on your <see cref="ToolCall"/> if you know the method is synchronous.</remarks>
public static string InvokeToolCall(this AssistantResponse assistant, ToolCall toolCall)
{
if (toolCall.Type != "function")
if (!toolCall.IsFunction)
{
throw new InvalidOperationException($"Cannot invoke built in tool {toolCall.Type}");
}

var tool = assistant.Tools.FirstOrDefault(tool => tool.Type == "function" && tool.Function.Name == toolCall.FunctionCall.Name) ??
throw new InvalidOperationException($"Failed to find a valid tool for [{toolCall.Id}] {toolCall.Type}");
var tool = assistant.Tools.FirstOrDefault(tool => tool.IsFunction && tool.Function.Name == toolCall.FunctionCall.Name) ??
throw new InvalidOperationException($"Failed to find a valid tool for [{toolCall.Id}] {toolCall.FunctionCall.Name}");
tool.Function.Arguments = toolCall.FunctionCall.Arguments;
return tool.InvokeFunction();
}
Expand All @@ -102,13 +106,13 @@ public static string InvokeToolCall(this AssistantResponse assistant, ToolCall t
/// <returns>Tool output result as <see cref="string"/>.</returns>
public static async Task<string> InvokeToolCallAsync(this AssistantResponse assistant, ToolCall toolCall, CancellationToken cancellationToken = default)
{
if (toolCall.Type != "function")
if (!toolCall.IsFunction)
{
throw new InvalidOperationException($"Cannot invoke built in tool {toolCall.Type}");
}

var tool = assistant.Tools.FirstOrDefault(tool => tool.Type == "function" && tool.Function.Name == toolCall.FunctionCall.Name) ??
throw new InvalidOperationException($"Failed to find a valid tool for [{toolCall.Id}] {toolCall.Type}");
throw new InvalidOperationException($"Failed to find a valid tool for [{toolCall.Id}] {toolCall.FunctionCall.Name}");
tool.Function.Arguments = toolCall.FunctionCall.Arguments;
return await tool.InvokeFunctionAsync(cancellationToken);
}
Expand All @@ -119,6 +123,7 @@ public static async Task<string> InvokeToolCallAsync(this AssistantResponse assi
/// <param name="assistant"><see cref="AssistantResponse"/>.</param>
/// <param name="toolCall"><see cref="ToolCall"/>.</param>
/// <returns><see cref="ToolOutput"/>.</returns>
/// <remarks>Only call this directly on your <see cref="ToolCall"/> if you know the method is synchronous.</remarks>
public static ToolOutput GetToolOutput(this AssistantResponse assistant, ToolCall toolCall)
=> new(toolCall.Id, assistant.InvokeToolCall(toolCall));

Expand All @@ -128,6 +133,7 @@ public static ToolOutput GetToolOutput(this AssistantResponse assistant, ToolCal
/// <param name="assistant"><see cref="AssistantResponse"/>.</param>
/// <param name="toolCalls">A collection of <see cref="ToolCall"/>s.</param>
/// <returns>A collection of <see cref="ToolOutput"/>s.</returns>
[Obsolete("Use GetToolOutputsAsync instead.")]
public static IReadOnlyList<ToolOutput> GetToolOutputs(this AssistantResponse assistant, IEnumerable<ToolCall> toolCalls)
=> toolCalls.Select(assistant.GetToolOutput).ToList();

Expand Down
22 changes: 11 additions & 11 deletions Runtime/Assistants/CreateAssistantRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public CreateAssistantRequest(
IReadOnlyDictionary<string, string> metadata = null,
double? temperature = null,
double? topP = null,
ChatResponseFormat responseFormat = ChatResponseFormat.Auto)
ChatResponseFormat? responseFormat = null)
: this(
string.IsNullOrWhiteSpace(model) ? assistant.Model : model,
string.IsNullOrWhiteSpace(name) ? assistant.Name : name,
Expand All @@ -89,22 +89,22 @@ public CreateAssistantRequest(
tools ?? assistant.Tools,
toolResources ?? assistant.ToolResources,
metadata ?? assistant.Metadata,
temperature,
topP,
responseFormat)
temperature ?? assistant.Temperature,
topP ?? assistant.TopP,
responseFormat ?? assistant.ResponseFormat)
{
}

[Obsolete("use new .ctr")]
public CreateAssistantRequest(
AssistantResponse assistant,
string model = null,
string name = null,
string description = null,
string instructions = null,
IEnumerable<Tool> tools = null,
IEnumerable<string> files = null,
IReadOnlyDictionary<string, string> metadata = null)
string model,
string name,
string description,
string instructions,
IEnumerable<Tool> tools,
IEnumerable<string> files,
IReadOnlyDictionary<string, string> metadata)
{
}

Expand Down
6 changes: 3 additions & 3 deletions Runtime/Batch/BatchExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public static class BatchExtensions
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
/// <returns><see cref="BatchResponse"/>.</returns>
public static async Task<BatchResponse> UpdateAsync(this BatchResponse batchResponse, CancellationToken cancellationToken = default)
=> await batchResponse.Client.BatchEndpoint.RetrieveBatchAsync(batchResponse.Id, cancellationToken).ConfigureAwait(false);
=> await batchResponse.Client.BatchEndpoint.RetrieveBatchAsync(batchResponse.Id, cancellationToken);

/// <summary>
/// Waits for <see cref="BatchResponse.Status"/> to change.
Expand All @@ -34,9 +34,9 @@ public static async Task<BatchResponse> WaitForStatusChangeAsync(this BatchRespo
BatchResponse result;
do
{
await Task.Delay(pollingInterval ?? 500, chainedCts.Token).ConfigureAwait(false);
await Task.Delay(pollingInterval ?? 500, chainedCts.Token).ConfigureAwait(true);
cancellationToken.ThrowIfCancellationRequested();
result = await batchResponse.UpdateAsync(cancellationToken: chainedCts.Token).ConfigureAwait(false);
result = await batchResponse.UpdateAsync(cancellationToken: chainedCts.Token);
} while (result.Status is BatchStatus.NotStarted or BatchStatus.InProgress or BatchStatus.Cancelling);
return result;
}
Expand Down
6 changes: 5 additions & 1 deletion Runtime/Batch/BatchResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ internal BatchResponse(
public string Endpoint { get; }

/// <summary>
/// Errors that occured during the batch job.
/// Errors that occurred during the batch job.
/// </summary>
[Preserve]
[JsonProperty("errors")]
Expand Down Expand Up @@ -202,6 +202,7 @@ public DateTime? FailedAt
[JsonProperty("expired_at")]
public int? ExpiredAtUnixTimeSeconds { get; }

[Preserve]
[JsonIgnore]
public DateTime? ExpiredAt
=> ExpiredAtUnixTimeSeconds.HasValue
Expand All @@ -215,6 +216,7 @@ public DateTime? ExpiredAt
[JsonProperty("cancelled_at")]
public int? CancelledAtUnixTimeSeconds { get; }

[Preserve]
[JsonIgnore]
public DateTime? CancelledAt
=> CancelledAtUnixTimeSeconds.HasValue
Expand All @@ -237,8 +239,10 @@ public DateTime? CancelledAt
[JsonProperty("metadata")]
public IReadOnlyDictionary<string, object> Metadata { get; }

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

[Preserve]
public static implicit operator string(BatchResponse response) => response?.ToString();
}
}
2 changes: 1 addition & 1 deletion Runtime/Batch/CreateBatchRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public sealed class CreateBatchRequest
private const string DefaultCompletionWindow = "24h";

/// <summary>
///
/// Constructor.
/// </summary>
/// <param name="inputFileId">
/// The ID of an uploaded file that contains requests for the new batch.
Expand Down
8 changes: 8 additions & 0 deletions Runtime/Batch/Endpoint.cs
Original file line number Diff line number Diff line change
@@ -1,21 +1,29 @@
// Licensed under the MIT License. See LICENSE in the project root for license information.

using UnityEngine.Scripting;

namespace OpenAI.Batch
{
[Preserve]
public sealed class Endpoint
{
public const string ChatCompletions = "/v1/chat/completions";
public const string Embeddings = "/v1/embeddings";
public const string Completions = "/v1/completions";

[Preserve]
public Endpoint(string endpoint) => Value = endpoint;

[Preserve]
public string Value { get; }

[Preserve]
public override string ToString() => Value;

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

[Preserve]
public static implicit operator Endpoint(string endpoint) => new(endpoint);
}
}
29 changes: 17 additions & 12 deletions Runtime/Chat/Choice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ public Choice() { }
/// A chat completion message generated by the model.
/// </summary>
[Preserve]
[JsonProperty("message")]
[JsonProperty("message", DefaultValueHandling = DefaultValueHandling.Ignore)]
public Message Message { get; private set; }

/// <summary>
/// A chat completion delta generated by streamed model responses.
/// </summary>
[Preserve]
[JsonProperty("delta")]
[JsonProperty("delta", DefaultValueHandling = DefaultValueHandling.Ignore)]
public Delta Delta { get; private set; }

/// <summary>
Expand All @@ -34,25 +34,25 @@ public Choice() { }
/// tool_calls if the model called a tool, or function_call (deprecated) if the model called a function.
/// </summary>
[Preserve]
[JsonProperty("finish_reason")]
[JsonProperty("finish_reason", DefaultValueHandling = DefaultValueHandling.Ignore)]
public string FinishReason { get; private set; }

[Preserve]
[JsonProperty("finish_details")]
[JsonProperty("finish_details", DefaultValueHandling = DefaultValueHandling.Ignore)]
public FinishDetails FinishDetails { get; private set; }

/// <summary>
/// The index of the choice in the list of choices.
/// </summary>
[Preserve]
[JsonProperty("index")]
[JsonProperty("index", DefaultValueHandling = DefaultValueHandling.Ignore)]
public int? Index { get; private set; }

/// <summary>
/// Log probability information for the choice.
/// </summary>
[Preserve]
[JsonProperty("logprobs")]
[JsonProperty("logprobs", DefaultValueHandling = DefaultValueHandling.Ignore)]
public LogProbs LogProbs { get; private set; }

[Preserve]
Expand All @@ -64,14 +64,19 @@ public Choice() { }
[Preserve]
public void AppendFrom(Choice other)
{
Index = other?.Index ?? 0;
if (other == null) { return; }

if (other?.Message != null)
if (other.Index.HasValue)
{
Index = other.Index.Value;
}

if (other.Message != null)
{
Message = other.Message;
}

if (other?.Delta != null)
if (other.Delta != null)
{
if (Message == null)
{
Expand All @@ -83,17 +88,17 @@ public void AppendFrom(Choice other)
}
}

if (other?.LogProbs != null)
if (other.LogProbs != null)
{
LogProbs = other.LogProbs;
}

if (!string.IsNullOrWhiteSpace(other?.FinishReason))
if (!string.IsNullOrWhiteSpace(other.FinishReason))
{
FinishReason = other.FinishReason;
}

if (other?.FinishDetails != null)
if (other.FinishDetails != null)
{
FinishDetails = other.FinishDetails;
}
Expand Down
11 changes: 8 additions & 3 deletions Runtime/Common/Annotation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ internal Annotation(
}

[Preserve]
[JsonProperty("index")]
[JsonProperty("index", DefaultValueHandling = DefaultValueHandling.Ignore)]
public int? Index { get; private set; }

[Preserve]
Expand All @@ -53,14 +53,14 @@ internal Annotation(
/// Generated when the assistant uses the 'retrieval' tool to search files.
/// </summary>
[Preserve]
[JsonProperty("file_citation")]
[JsonProperty("file_citation", DefaultValueHandling = DefaultValueHandling.Ignore)]
public FileCitation FileCitation { get; private set; }

/// <summary>
/// A URL for the file that's generated when the assistant used the 'code_interpreter' tool to generate a file.
/// </summary>
[Preserve]
[JsonProperty("file_path")]
[JsonProperty("file_path", DefaultValueHandling = DefaultValueHandling.Ignore)]
public FilePath FilePath { get; private set; }

[Preserve]
Expand All @@ -75,6 +75,11 @@ public void AppendFrom(Annotation other)
{
if (other == null) { return; }

if (other.Index.HasValue)
{
Index = other.Index.Value;
}

if (!string.IsNullOrWhiteSpace(other.Text))
{
Text += other.Text;
Expand Down
Loading

0 comments on commit c081574

Please sign in to comment.