Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/upm-subtree-split.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ on:
jobs:
upm-subtree-split:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ internal AssistantResponse(
[JsonProperty("description")] string description,
[JsonProperty("model")] string model,
[JsonProperty("instructions")] string instructions,
[JsonProperty("tools")] IReadOnlyList<Tool> tools,
[JsonProperty("tools")] List<Tool> tools,
[JsonProperty("tool_resources")] ToolResources toolResources,
[JsonProperty("metadata")] Dictionary<string, string> metadata,
[JsonProperty("temperature")] float? temperature,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

using Newtonsoft.Json;
using OpenAI.Extensions;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Utilities.WebRequestRest;
Expand All @@ -10,7 +11,20 @@ namespace OpenAI.Assistants
{
public sealed class AssistantsEndpoint : OpenAIBaseEndpoint
{
internal AssistantsEndpoint(OpenAIClient client) : base(client) { }
internal AssistantsEndpoint(OpenAIClient client) : base(client)
{
var assistantHeaders = new Dictionary<string, string>();

foreach (var (key, value) in client.DefaultRequestHeaders)
{
assistantHeaders[key] = value;
}

assistantHeaders["OpenAI-Beta"] = "assistants=v2";
headers = assistantHeaders;
}

private readonly IReadOnlyDictionary<string, string> headers;

protected override string Root => "assistants";

Expand All @@ -22,7 +36,7 @@ internal AssistantsEndpoint(OpenAIClient client) : base(client) { }
/// <returns><see cref="ListResponse{AssistantResponse}"/></returns>
public async Task<ListResponse<AssistantResponse>> ListAssistantsAsync(ListQuery query = null, CancellationToken cancellationToken = default)
{
var response = await Rest.GetAsync(GetUrl(queryParameters: query), parameters: new RestParameters(client.DefaultRequestHeaders), cancellationToken);
var response = await Rest.GetAsync(GetUrl(queryParameters: query), parameters: new RestParameters(headers), cancellationToken);
response.Validate(EnableDebug);
return response.Deserialize<ListResponse<AssistantResponse>>(client);
}
Expand Down Expand Up @@ -58,7 +72,7 @@ public async Task<AssistantResponse> CreateAssistantAsync(CreateAssistantRequest
{
request ??= new CreateAssistantRequest();
var payload = JsonConvert.SerializeObject(request, OpenAIClient.JsonSerializationOptions);
var response = await Rest.PostAsync(GetUrl(), payload, new RestParameters(client.DefaultRequestHeaders), cancellationToken);
var response = await Rest.PostAsync(GetUrl(), payload, new RestParameters(headers), cancellationToken);
response.Validate(EnableDebug);
return response.Deserialize<AssistantResponse>(client);
}
Expand All @@ -71,7 +85,7 @@ public async Task<AssistantResponse> CreateAssistantAsync(CreateAssistantRequest
/// <returns><see cref="AssistantResponse"/>.</returns>
public async Task<AssistantResponse> RetrieveAssistantAsync(string assistantId, CancellationToken cancellationToken = default)
{
var response = await Rest.GetAsync(GetUrl($"/{assistantId}"), new RestParameters(client.DefaultRequestHeaders), cancellationToken);
var response = await Rest.GetAsync(GetUrl($"/{assistantId}"), new RestParameters(headers), cancellationToken);
response.Validate(EnableDebug);
return response.Deserialize<AssistantResponse>(client);
}
Expand All @@ -86,7 +100,7 @@ public async Task<AssistantResponse> RetrieveAssistantAsync(string assistantId,
public async Task<AssistantResponse> ModifyAssistantAsync(string assistantId, CreateAssistantRequest request, CancellationToken cancellationToken = default)
{
var payload = JsonConvert.SerializeObject(request, OpenAIClient.JsonSerializationOptions);
var response = await Rest.PostAsync(GetUrl($"/{assistantId}"), payload, new RestParameters(client.DefaultRequestHeaders), cancellationToken);
var response = await Rest.PostAsync(GetUrl($"/{assistantId}"), payload, new RestParameters(headers), cancellationToken);
response.Validate(EnableDebug);
return response.Deserialize<AssistantResponse>(client);
}
Expand All @@ -99,7 +113,7 @@ public async Task<AssistantResponse> ModifyAssistantAsync(string assistantId, Cr
/// <returns>True, if the assistant was deleted.</returns>
public async Task<bool> DeleteAssistantAsync(string assistantId, CancellationToken cancellationToken = default)
{
var response = await Rest.DeleteAsync(GetUrl($"/{assistantId}"), new RestParameters(client.DefaultRequestHeaders), cancellationToken);
var response = await Rest.DeleteAsync(GetUrl($"/{assistantId}"), new RestParameters(headers), cancellationToken);
response.Validate(EnableDebug);
var result = response.Deserialize<DeletedResponse>(client);
return result?.Deleted ?? false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public sealed class BatchErrors
{
[Preserve]
[JsonConstructor]
internal BatchErrors([JsonProperty("data")] IReadOnlyList<Error> errors)
internal BatchErrors([JsonProperty("data")] List<Error> errors)
{
Errors = errors;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ internal BatchResponse(
[JsonProperty("expired_at")] int? expiredAt,
[JsonProperty("cancelled_at")] int? cancelledAt,
[JsonProperty("request_counts")] RequestCounts requestCounts,
[JsonProperty("metadata")] IReadOnlyDictionary<string, object> metadata)
[JsonProperty("metadata")] Dictionary<string, object> metadata)
{
Id = id;
Object = @object;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ internal ChatResponse(
[JsonProperty("service_tier")] string serviceTier,
[JsonProperty("system_fingerprint")] string systemFingerprint,
[JsonProperty("usage")] Usage usage,
[JsonProperty("choices")] IReadOnlyList<Choice> choices)
[JsonProperty("choices")] List<Choice> choices)
{
Id = id;
Object = @object;
Expand Down
2 changes: 1 addition & 1 deletion OpenAI/Packages/com.openai.unity/Runtime/Chat/Delta.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public Delta(
[JsonProperty("content")] string content,
[JsonProperty("refusal")] string refusal,
[JsonProperty("name")] string name,
[JsonProperty("function_call")] IReadOnlyList<ToolCall> toolCalls)
[JsonProperty("function_call")] List<ToolCall> toolCalls)
{
Role = role;
Content = content;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@ namespace OpenAI
{
/// <summary>
/// Constrains the effort of reasoning for <see href="https://platform.openai.com/docs/guides/reasoning">Reasoning Models</see>.<br/>
/// Currently supported values are: Low, Medium, High. Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning response.
/// Currently supported values are: Minimal, Low, Medium, High. Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning response.
/// </summary>
/// <remarks>
/// <b>Reasoning models only!</b>
/// </remarks>
public enum ReasoningEffort
{
[EnumMember(Value = "minimal")]
Minimal = 1,
[EnumMember(Value = "low")]
Low = 1,
Low,
[EnumMember(Value = "medium")]
Medium,
[EnumMember(Value = "high")]
Expand Down
218 changes: 218 additions & 0 deletions OpenAI/Packages/com.openai.unity/Runtime/ConversationsEndpoint.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
// Licensed under the MIT License. See LICENSE in the project root for license information.

using Newtonsoft.Json;
using OpenAI.Extensions;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Utilities.WebRequestRest;

namespace OpenAI.Responses
{
public sealed class ConversationsEndpoint : OpenAIBaseEndpoint
{
public ConversationsEndpoint(OpenAIClient client) : base(client) { }

protected override string Root => "conversations";

/// <summary>
/// Create a conversation.
/// </summary>
/// <param name="request"><see cref="CreateConversationRequest"/>.</param>
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
/// <returns><see cref="Conversation"/>.</returns>
public async Task<Conversation> CreateConversationAsync(CreateConversationRequest request, CancellationToken cancellationToken = default)
{
var payload = JsonConvert.SerializeObject(request, OpenAIClient.JsonSerializationOptions);
var response = await Rest.PostAsync(GetUrl(), payload, new RestParameters(client.DefaultRequestHeaders), cancellationToken);
response.Validate(EnableDebug);
return response.Deserialize<Conversation>(client);
}

/// <summary>
/// Get a conversation.
/// </summary>
/// <param name="conversationId">The id of the conversation to retrieve.</param>
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
/// <returns><see cref="Conversation"/>.</returns>
public async Task<Conversation> GetConversationAsync(string conversationId, CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(conversationId))
{
throw new ArgumentNullException(nameof(conversationId));
}

var response = await Rest.GetAsync(GetUrl($"/{conversationId}"), new RestParameters(client.DefaultRequestHeaders), cancellationToken);
response.Validate(EnableDebug);
return response.Deserialize<Conversation>(client);
}

/// <summary>
/// Update a conversation.
/// </summary>
/// <param name="conversationId">
/// The id of the conversation to retrieve.
/// </param>
/// <param name="metadata">
/// 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,
/// and querying for objects via API or the dashboard.
/// Keys are strings with a maximum length of 64 characters.Values are strings with a maximum length of 512 characters.
/// </param>
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
/// <returns><see cref="Conversation"/>.</returns>
public async Task<Conversation> UpdateConversationAsync(string conversationId, IReadOnlyDictionary<string, string> metadata, CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(conversationId))
{
throw new ArgumentNullException(nameof(conversationId));
}

var payload = JsonConvert.SerializeObject(new { metadata }, OpenAIClient.JsonSerializationOptions);
var response = await Rest.PatchAsync(GetUrl($"/{conversationId}"), payload, new RestParameters(client.DefaultRequestHeaders), cancellationToken);
response.Validate(EnableDebug);
return response.Deserialize<Conversation>(client);
}

/// <summary>
/// Delete a conversation.
/// </summary>
/// <remarks>
/// Items in the conversation will not be deleted.
/// </remarks>
/// <param name="conversationId">The id of the conversation to retrieve.</param>
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
/// <returns>True, if the <see cref="Conversation"/> was deleted successfully, otherwise False.</returns>
public async Task<bool> DeleteConversationAsync(string conversationId, CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(conversationId))
{
throw new ArgumentNullException(nameof(conversationId));
}

var response = await Rest.DeleteAsync(GetUrl($"/{conversationId}"), new RestParameters(client.DefaultRequestHeaders), cancellationToken);
response.Validate(EnableDebug);
var result = response.Deserialize<DeletedResponse>(client);
return result.Deleted;
}

#region Conversation Items

/// <summary>
/// List all items for a conversation with the given ID.
/// </summary>
/// <param name="conversationId">The ID of the conversation to list items for.</param>
/// <param name="query">Optional, <see cref="ListQuery"/>.</param>
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
/// <returns><see cref="ListResponse{IResponseItem}"/>.</returns>
public async Task<ListResponse<IResponseItem>> ListConversationItemsAsync(string conversationId, ListQuery query = null, CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(conversationId))
{
throw new ArgumentNullException(nameof(conversationId));
}

var response = await Rest.GetAsync(GetUrl($"/{conversationId}/items", query), new RestParameters(client.DefaultRequestHeaders), cancellationToken);
response.Validate(EnableDebug);
return response.Deserialize<ListResponse<IResponseItem>>(client);
}

/// <summary>
/// Create items in a conversation with the given ID.
/// </summary>
/// <param name="conversationId">The ID of the conversation to add the item to.</param>
/// <param name="items">The items to add to the conversation. You may add up to 20 items at a time.</param>
/// <param name="include">Optional, Additional fields to include in the response.</param>
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
/// <returns><see cref="ListResponse{IResponseItem}"/>.</returns>
public async Task<ListResponse<IResponseItem>> CreateConversationItemsAsync(string conversationId, IEnumerable<IResponseItem> items, string[] include = null, CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(conversationId))
{
throw new ArgumentNullException(nameof(conversationId));
}

if (items == null)
{
throw new ArgumentNullException(nameof(items));
}

var payload = JsonConvert.SerializeObject(new { items }, OpenAIClient.JsonSerializationOptions);
Dictionary<string, string> query = null;

if (include is { Length: > 0 })
{
query = new Dictionary<string, string>
{
{ "include", string.Join(",", include) }
};
}

var response = await Rest.PostAsync(GetUrl($"/{conversationId}/items", query), payload, new RestParameters(client.DefaultRequestHeaders), cancellationToken);
response.Validate(EnableDebug);
return response.Deserialize<ListResponse<IResponseItem>>(client);
}

/// <summary>
/// Retrieve an item from a conversation.
/// </summary>
/// <param name="conversationId">The ID of the conversation that contains the item.</param>
/// <param name="itemId">The ID of the item to retrieve.</param>
/// <param name="include">Optional, Additional fields to include in the response.</param>
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
/// <returns><see cref="IResponseItem"/>.</returns>
public async Task<IResponseItem> GetConversationItemAsync(string conversationId, string itemId, string[] include = null, CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(conversationId))
{
throw new ArgumentNullException(nameof(conversationId));
}

if (string.IsNullOrWhiteSpace(itemId))
{
throw new ArgumentNullException(nameof(itemId));
}

Dictionary<string, string> query = null;

if (include is { Length: > 0 })
{
query = new Dictionary<string, string>
{
{ "include", string.Join(",", include) }
};
}

var response = await Rest.GetAsync(GetUrl($"/{conversationId}/items/{itemId}", query), new RestParameters(client.DefaultRequestHeaders), cancellationToken);
response.Validate(EnableDebug);
return response.Deserialize<IResponseItem>(client);
}

/// <summary>
/// Delete an item from a conversation with the given IDs.
/// </summary>
/// <param name="conversationId">The ID of the conversation that contains the item.</param>
/// <param name="itemId">The ID of the item to delete.</param>
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
/// <returns>Returns the updated <see cref="Conversation"/>>.</returns>
public async Task<Conversation> DeleteConversationItemAsync(string conversationId, string itemId, CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(conversationId))
{
throw new ArgumentNullException(nameof(conversationId));
}

if (string.IsNullOrWhiteSpace(itemId))
{
throw new ArgumentNullException(nameof(itemId));
}

var response = await Rest.DeleteAsync(GetUrl($"/{conversationId}/items/{itemId}"), new RestParameters(client.DefaultRequestHeaders), cancellationToken);
response.Validate(EnableDebug);
return response.Deserialize<Conversation>(client);
}

#endregion Conversation Items
}
}

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

Loading
Loading