From 7ee1c1047dd5abd74bef5901afbd9a75c566468a Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sun, 16 Jun 2024 12:39:15 -0400 Subject: [PATCH] com.openai.unity 8.0.3 (#254) - Fixed Thread.MessageResponse and Thread.RunStepResponse Delta objects not being properly populated - Added Thread.MessageDelta.PrintContent() - Added additional unit tests for delta objects --- Runtime/Threads/MessageDelta.cs | 13 ++++++++- Runtime/Threads/MessageResponse.cs | 25 +++++++++++++++-- Runtime/Threads/RunResponse.cs | 20 +++++++++++--- Runtime/Threads/RunStepResponse.cs | 24 ++++++++++++++--- Tests/TestFixture_03_Threads.cs | 43 ++++++++++++++++++++++++++++++ package.json | 2 +- 6 files changed, 117 insertions(+), 10 deletions(-) diff --git a/Runtime/Threads/MessageDelta.cs b/Runtime/Threads/MessageDelta.cs index 8ca989a8..ff8559e9 100644 --- a/Runtime/Threads/MessageDelta.cs +++ b/Runtime/Threads/MessageDelta.cs @@ -2,6 +2,7 @@ using Newtonsoft.Json; using System.Collections.Generic; +using System.Linq; using UnityEngine.Scripting; namespace OpenAI.Threads @@ -21,10 +22,20 @@ internal MessageDelta( [Preserve] [JsonProperty("role")] - public Role Role { get; private set; } + public Role Role { get; internal set; } [Preserve] [JsonProperty("content")] public IReadOnlyList Content { get; } + + /// + /// Formats all of the items into a single string, + /// putting each item on a new line. + /// + /// of all . + public string PrintContent() + => Content == null + ? string.Empty + : string.Join("\n", Content.Select(c => c?.ToString())); } } diff --git a/Runtime/Threads/MessageResponse.cs b/Runtime/Threads/MessageResponse.cs index 65e44f22..2c66adfa 100644 --- a/Runtime/Threads/MessageResponse.cs +++ b/Runtime/Threads/MessageResponse.cs @@ -213,13 +213,30 @@ internal void AppendFrom(MessageResponse other) { if (other == null) { return; } + if (!string.IsNullOrWhiteSpace(Id)) + { + if (Id != other.Id) + { + throw new InvalidOperationException("Attempting to append a different object than the original!"); + } + } + else + { + Id = other.Id; + } + + Object = other.Object; + if (other.Delta != null) { - if (Role == 0 && - other.Delta.Role > 0) + if (Role == 0 && other.Delta.Role > 0) { Role = other.Delta.Role; } + else if (other.Delta.Role == 0 && Role > 0) + { + other.Delta.Role = Role; + } if (other.Delta.Content != null) { @@ -227,10 +244,14 @@ internal void AppendFrom(MessageResponse other) content.AppendFrom(other.Delta.Content); } + Delta = other.Delta; + // bail early since we only care about the delta content return; } + Delta = null; + if (Role == 0 && other.Role > 0) { diff --git a/Runtime/Threads/RunResponse.cs b/Runtime/Threads/RunResponse.cs index 5ec366d8..2241755d 100644 --- a/Runtime/Threads/RunResponse.cs +++ b/Runtime/Threads/RunResponse.cs @@ -85,14 +85,14 @@ internal RunResponse( /// [Preserve] [JsonProperty("id")] - public string Id { get; } + public string Id { get; private set; } /// /// The object type, which is always run. /// [Preserve] [JsonProperty("object")] - public string Object { get; } + public string Object { get; private set; } /// /// The Unix timestamp (in seconds) for when the thread was created. @@ -326,7 +326,7 @@ public DateTime? CompletedAt /// which indicates the generation exceeded max_tokens or the conversation exceeded the max context length. /// [Preserve] - [JsonProperty("response_format")] + [JsonProperty("response_format", DefaultValueHandling = DefaultValueHandling.Ignore)] [JsonConverter(typeof(ResponseFormatConverter))] public ChatResponseFormat ResponseFormat { get; private set; } @@ -340,6 +340,20 @@ internal void AppendFrom(RunResponse other) { if (other is null) { return; } + if (!string.IsNullOrWhiteSpace(Id)) + { + if (Id != other.Id) + { + throw new InvalidOperationException("Attempting to append a different object than the original!"); + } + } + else + { + Id = other.Id; + } + + Object = other.Object; + if (other.Status > 0) { Status = other.Status; diff --git a/Runtime/Threads/RunStepResponse.cs b/Runtime/Threads/RunStepResponse.cs index 3593d1b8..9473ee38 100644 --- a/Runtime/Threads/RunStepResponse.cs +++ b/Runtime/Threads/RunStepResponse.cs @@ -64,15 +64,15 @@ internal RunStepResponse( /// [Preserve] [JsonProperty("id")] - public string Id { get; } + public string Id { get; private set; } [Preserve] [JsonProperty("object")] - public string Object { get; } + public string Object { get; private set; } [Preserve] [JsonProperty("delta", DefaultValueHandling = DefaultValueHandling.Ignore)] - public RunStepDelta Delta { get; } + public RunStepDelta Delta { get; private set; } /// /// The Unix timestamp (in seconds) for when the run step was created. @@ -225,6 +225,20 @@ internal void AppendFrom(RunStepResponse other) { if (other == null) { return; } + if (!string.IsNullOrWhiteSpace(Id)) + { + if (Id != other.Id) + { + throw new InvalidOperationException("Attempting to append a different object than the original!"); + } + } + else + { + Id = other.Id; + } + + Object = other.Object; + if (other.Delta != null) { if (other.Delta.StepDetails != null) @@ -239,10 +253,14 @@ internal void AppendFrom(RunStepResponse other) } } + Delta = other.Delta; + // don't update other fields if we are just appending Delta return; } + Delta = null; + if (other.CreatedAtUnixTimeSeconds.HasValue) { CreatedAtUnixTimeSeconds = other.CreatedAtUnixTimeSeconds; diff --git a/Tests/TestFixture_03_Threads.cs b/Tests/TestFixture_03_Threads.cs index 17c52282..d6f26df2 100644 --- a/Tests/TestFixture_03_Threads.cs +++ b/Tests/TestFixture_03_Threads.cs @@ -277,6 +277,49 @@ public async Task Test_03_03_01_CreateRun_Streaming() var run = await thread.CreateRunAsync(assistant, streamEvent => { Debug.Log(streamEvent.ToJsonString()); + + switch (streamEvent) + { + case RunResponse runEvent: + Assert.NotNull(runEvent); + break; + case RunStepResponse runStepEvent: + Assert.NotNull(runStepEvent); + switch (runStepEvent.Object) + { + case "thread.run.step.delta": + Assert.NotNull(runStepEvent.Delta); + break; + default: + Assert.IsNull(runStepEvent.Delta); + break; + } + break; + case ThreadResponse threadEvent: + Assert.NotNull(threadEvent); + break; + case MessageResponse messageEvent: + Assert.NotNull(messageEvent); + switch (messageEvent.Object) + { + case "thread.message.delta": + Assert.NotNull(messageEvent.Delta); + Debug.Log($"{messageEvent.Object}: \"{messageEvent.Delta.PrintContent()}\""); + break; + default: + Debug.Log($"{messageEvent.Object}: \"{messageEvent.PrintContent()}\""); + Assert.IsNull(messageEvent.Delta); + break; + } + break; + case Error errorEvent: + Assert.NotNull(errorEvent); + break; + //default: + // handle event not already processed by library + // var @event = JsonSerializer.Deserialize(streamEvent.ToJsonString()); + //break; + } }); Assert.IsNotNull(run); diff --git a/package.json b/package.json index 104a292f..711a8083 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "displayName": "OpenAI", "description": "A OpenAI package for the Unity Game Engine to use GPT-4, GPT-3.5, GPT-3 and Dall-E though their RESTful API (currently in beta).\n\nIndependently developed, this is not an official library and I am not affiliated with OpenAI.\n\nAn OpenAI API account is required.", "keywords": [], - "version": "8.0.2", + "version": "8.0.3", "unity": "2021.3", "documentationUrl": "https://github.com/RageAgainstThePixel/com.openai.unity#documentation", "changelogUrl": "https://github.com/RageAgainstThePixel/com.openai.unity/releases",