Skip to content

Commit

Permalink
OpenAI-DotNet 8.0.3 (#332)
Browse files Browse the repository at this point in the history
- Fixed Thread.MessageResponse and Thread.RunStepResponse Delta objects not being properly populated
- Added Thread.MessageDelta.PrintContent()
- Added additional unit tests for delta objects
  • Loading branch information
StephenHodgson authored Jun 16, 2024
1 parent 8294a63 commit 3b0f89a
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 5 deletions.
45 changes: 45 additions & 0 deletions OpenAI-DotNet-Tests/TestFixture_03_Threads.cs
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,51 @@ public async Task Test_03_03_01_CreateRun_Streaming()
var run = await thread.CreateRunAsync(assistant, streamEvent =>
{
Console.WriteLine(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);
Console.WriteLine($"{messageEvent.Object}: \"{messageEvent.Delta.PrintContent()}\"");
break;
default:
Console.WriteLine($"{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<T>(streamEvent.ToJsonString());
//break;
}
});

Assert.IsNotNull(run);
Expand Down
6 changes: 5 additions & 1 deletion OpenAI-DotNet/OpenAI-DotNet.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,12 @@ More context [on Roger Pincombe's blog](https://rogerpincombe.com/openai-dotnet-
<AssemblyOriginatorKeyFile>OpenAI-DotNet.pfx</AssemblyOriginatorKeyFile>
<IncludeSymbols>True</IncludeSymbols>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
<Version>8.0.2</Version>
<Version>8.0.3</Version>
<PackageReleaseNotes>
Version 8.0.3
- Fixed Thread.MessageResponse and Thread.RunStepResponse Delta stream event objects not being properly populated
- Added Thread.MessageDelta.PrintContent()
- Added additional unit tests for delta objects
Version 8.0.2
- Fixed Thread.Message.Attachement serialization
- Fixed CreateAssistantRequest to properly copy all override assistant properties
Expand Down
13 changes: 12 additions & 1 deletion OpenAI-DotNet/Threads/MessageDelta.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed under the MIT License. See LICENSE in the project root for license information.

using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;

namespace OpenAI.Threads
Expand All @@ -9,10 +10,20 @@ public sealed class MessageDelta
{
[JsonInclude]
[JsonPropertyName("role")]
public Role Role { get; private set; }
public Role Role { get; internal set; }

[JsonInclude]
[JsonPropertyName("content")]
public IReadOnlyList<Content> Content { get; private set; }

/// <summary>
/// Formats all of the <see cref="Content"/> items into a single string,
/// putting each item on a new line.
/// </summary>
/// <returns><see cref="string"/> of all <see cref="Content"/>.</returns>
public string PrintContent()
=> Content == null
? string.Empty
: string.Join("\n", Content.Select(c => c?.ToString()));
}
}
25 changes: 23 additions & 2 deletions OpenAI-DotNet/Threads/MessageResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -181,24 +181,45 @@ 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)
{
content ??= new List<Content>();
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)
{
Expand Down
15 changes: 15 additions & 0 deletions OpenAI-DotNet/Threads/RunResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ public IReadOnlyList<Tool> Tools
[JsonInclude]
[JsonPropertyName("response_format")]
[JsonConverter(typeof(ResponseFormatConverter))]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public ChatResponseFormat ResponseFormat { get; private set; }

public static implicit operator string(RunResponse run) => run?.ToString();
Expand All @@ -291,6 +292,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;
Expand Down
20 changes: 19 additions & 1 deletion OpenAI-DotNet/Threads/RunStepResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public RunStepResponse() { }
[JsonInclude]
[JsonPropertyName("delta")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public RunStepDelta Delta { get; }
public RunStepDelta Delta { get; private set; }

/// <summary>
/// The Unix timestamp (in seconds) for when the run step was created.
Expand Down Expand Up @@ -191,6 +191,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)
Expand All @@ -205,10 +219,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;
Expand Down
3 changes: 3 additions & 0 deletions OpenAI-DotNet/Threads/ThreadsEndpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,7 @@ private async Task<RunResponse> StreamRunAsync(string endpoint, StringContent pa
case "thread.run.cancelled":
case "thread.run.expired":
var partialRun = sseResponse.Deserialize<RunResponse>(ssEvent, client);

if (run == null)
{
run = partialRun;
Expand All @@ -452,6 +453,7 @@ private async Task<RunResponse> StreamRunAsync(string endpoint, StringContent pa
case "thread.run.step.cancelled":
case "thread.run.step.expired":
var partialRunStep = sseResponse.Deserialize<RunStepResponse>(ssEvent, client);

if (runStep == null)
{
runStep = partialRunStep;
Expand All @@ -469,6 +471,7 @@ private async Task<RunResponse> StreamRunAsync(string endpoint, StringContent pa
case "thread.message.completed":
case "thread.message.incomplete":
var partialMessage = sseResponse.Deserialize<MessageResponse>(ssEvent, client);

if (message == null)
{
message = partialMessage;
Expand Down

0 comments on commit 3b0f89a

Please sign in to comment.