Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 9 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@

## Bugs Fixed

- ([#72](https://github.com/openai/openai-dotnet/issues/72)) Fixed `filename` request encoding in operations using `multipart/form-data`, including `files` and `audio`
- ([#72](https://github.com/openai/openai-dotnet/issues/72)) Fixed `filename` request encoding in operations using `multipart/form-data`, including `files` and `audio` (commit_hash)
- ([#79](https://github.com/openai/openai-dotnet/issues/79)) Fixed hard-coded `user` role for caller-created Assistants API messages on threads (commit_hash)
- Fixed non-streaming Assistants API run step details not reporting code interpreter logs when present

## Breaking Changes

**Assistants (beta)**:
- `AssistantClient.CreateMessage()` and the explicit constructor for `ThreadInitializationMessage` now require a `MessageRole` parameter. This properly enables the ability to create an Assistant message representing conversation history on a new thread.

## 2.0.0-beta.5 (2024-06-14)

Expand All @@ -22,7 +29,7 @@

## Breaking Changes

**Assistants**:
**Assistants (beta)**:
- `InputQuote` is removed from Assistants `TextAnnotation` and `TextAnnotationUpdate`, per [openai/openai-openapi@dd73070b](https://github.com/openai/openai-openapi/commit/dd73070b1d507645d24c249a63ebebd3ec38c0cb) ([1af6569](https://github.com/openai/openai-dotnet/commit/1af6569e2ceae9d840b8826e42d7e3b2569b43f6))

## Other Changes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,7 @@ public async Task Example01_RetrievalAugmentedGenerationAsync()
// Now we'll create a thread with a user query about the data already associated with the assistant, then run it
ThreadCreationOptions threadOptions = new()
{
InitialMessages =
{
new ThreadInitializationMessage(new List<MessageContent>()
{
MessageContent.FromText("How well did product 113045 sell in February? Graph its trend over time."),
}),
},
InitialMessages = { "How well did product 113045 sell in February? Graph its trend over time." }
};

ThreadRun threadRun = await assistantClient.CreateThreadAndRunAsync(assistant.Id, threadOptions);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ public async Task Example02b_FunctionCallingStreaming()
AssistantThread thread = await client.CreateThreadAsync();
ThreadMessage message = await client.CreateMessageAsync(
thread,
MessageRole.User,
[
"What's the weather in San Francisco today and the likelihood it'll rain?"
]);
Expand Down
1 change: 1 addition & 0 deletions examples/Assistants/Example05_AssistantsWithVision.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public void Example05_AssistantsWithVision()
InitialMessages =
{
new ThreadInitializationMessage(
MessageRole.User,
[
"Hello, assistant! Please compare these two images for me:",
MessageContent.FromImageFileId(pictureOfAppleFile.Id),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public async Task Example05_AssistantsWithVisionAsync()
InitialMessages =
{
new ThreadInitializationMessage(
MessageRole.User,
[
"Hello, assistant! Please compare these two images for me:",
MessageContent.FromImageFileId(pictureOfAppleFile.Id),
Expand Down
8 changes: 6 additions & 2 deletions src/Custom/Assistants/AssistantClient.Convenience.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,27 +99,31 @@ public virtual ClientResult<bool> DeleteThread(AssistantThread thread)
/// Creates a new <see cref="ThreadMessage"/> on an existing <see cref="AssistantThread"/>.
/// </summary>
/// <param name="thread"> The thread to associate the new message with. </param>
/// <param name="role"> The role to associate with the new message. </param>
/// <param name="content"> The collection of <see cref="MessageContent"/> items for the message. </param>
/// <param name="options"> Additional options to apply to the new message. </param>
/// <returns> A new <see cref="ThreadMessage"/>. </returns>
public virtual Task<ClientResult<ThreadMessage>> CreateMessageAsync(
AssistantThread thread,
MessageRole role,
IEnumerable<MessageContent> content,
MessageCreationOptions options = null)
=> CreateMessageAsync(thread?.Id, content, options);
=> CreateMessageAsync(thread?.Id, role, content, options);

/// <summary>
/// Creates a new <see cref="ThreadMessage"/> on an existing <see cref="AssistantThread"/>.
/// </summary>
/// <param name="thread"> The thread to associate the new message with. </param>
/// <param name="role"> The role to associate with the new message. </param>
/// <param name="content"> The collection of <see cref="MessageContent"/> items for the message. </param>
/// <param name="options"> Additional options to apply to the new message. </param>
/// <returns> A new <see cref="ThreadMessage"/>. </returns>
public virtual ClientResult<ThreadMessage> CreateMessage(
AssistantThread thread,
MessageRole role,
IEnumerable<MessageContent> content,
MessageCreationOptions options = null)
=> CreateMessage(thread?.Id, content, options);
=> CreateMessage(thread?.Id, role, content, options);

/// <summary>
/// Returns a collection of <see cref="ThreadMessage"/> instances from an existing <see cref="AssistantThread"/>.
Expand Down
6 changes: 6 additions & 0 deletions src/Custom/Assistants/AssistantClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -280,18 +280,21 @@ public virtual ClientResult<bool> DeleteThread(string threadId, CancellationToke
/// Creates a new <see cref="ThreadMessage"/> on an existing <see cref="AssistantThread"/>.
/// </summary>
/// <param name="threadId"> The ID of the thread to associate the new message with. </param>
/// <param name="role"> The role to associate with the new message. </param>
/// <param name="content"> The collection of <see cref="MessageContent"/> items for the message. </param>
/// <param name="options"> Additional options to apply to the new message. </param>
/// <param name="cancellationToken">A token that can be used to cancel this method call.</param>
/// <returns> A new <see cref="ThreadMessage"/>. </returns>
public virtual async Task<ClientResult<ThreadMessage>> CreateMessageAsync(
string threadId,
MessageRole role,
IEnumerable<MessageContent> content,
MessageCreationOptions options = null,
CancellationToken cancellationToken = default)
{
Argument.AssertNotNullOrEmpty(threadId, nameof(threadId));
options ??= new();
options.Role = role;
options.Content.Clear();
foreach (MessageContent contentItem in content)
{
Expand All @@ -307,18 +310,21 @@ public virtual async Task<ClientResult<ThreadMessage>> CreateMessageAsync(
/// Creates a new <see cref="ThreadMessage"/> on an existing <see cref="AssistantThread"/>.
/// </summary>
/// <param name="threadId"> The ID of the thread to associate the new message with. </param>
/// <param name="role"> The role to associate with the new message. </param>
/// <param name="content"> The collection of <see cref="MessageContent"/> items for the message. </param>
/// <param name="options"> Additional options to apply to the new message. </param>
/// <param name="cancellationToken">A token that can be used to cancel this method call.</param>
/// <returns> A new <see cref="ThreadMessage"/>. </returns>
public virtual ClientResult<ThreadMessage> CreateMessage(
string threadId,
MessageRole role,
IEnumerable<MessageContent> content,
MessageCreationOptions options = null,
CancellationToken cancellationToken = default)
{
Argument.AssertNotNullOrEmpty(threadId, nameof(threadId));
options ??= new();
options.Role = role;
options.Content.Clear();
foreach (MessageContent contentItem in content)
{
Expand Down
1 change: 0 additions & 1 deletion src/Custom/Assistants/AssistantCreationOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,5 @@ public AssistantCreationOptions()
{
Metadata = new ChangeTrackingDictionary<string, string>();
Tools = new ChangeTrackingList<ToolDefinition>();
ToolResources = new();
}
}
7 changes: 0 additions & 7 deletions src/Custom/Assistants/Internal/GeneratorStubs.Internal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -207,13 +207,6 @@ internal readonly partial struct InternalListRunStepsResponseObject {}
[CodeGenModel("RunStepDetailsToolCallsFileSearchObject")]
internal partial class InternalRunStepFileSearchToolCallDetails { }

[CodeGenModel("RunStepDetailsToolCallsCodeOutputLogsObject")]
internal partial class InternalRunStepDetailsToolCallsCodeOutputLogsObject
{
[CodeGenMember("Logs")]
internal string InternalLogs { get; }
}

[CodeGenModel("RunTruncationStrategyType")]
internal readonly partial struct InternalRunTruncationStrategyType { }

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
namespace OpenAI.Assistants
{
/// <summary> Text output from the Code Interpreter tool call as part of a run step. </summary>
[CodeGenModel("RunStepDetailsToolCallsCodeOutputLogsObject")]
internal partial class InternalRunStepCodeInterpreterLogOutput : RunStepCodeInterpreterOutput
{
/// <summary> The text output from the Code Interpreter tool call. </summary>
Expand Down
5 changes: 5 additions & 0 deletions src/Custom/Assistants/MessageCreationOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ namespace OpenAI.Assistants;
[CodeGenSerialization(nameof(Content), SerializationValueHook=nameof(SerializeContent))]
public partial class MessageCreationOptions
{
// CUSTOM: role is hidden, as this required property is promoted to a method parameter

[CodeGenMember("Role")]
internal MessageRole Role { get; set; }

// CUSTOM: content is hidden to allow the promotion of required request information into top-level
// method signatures.

Expand Down
2 changes: 1 addition & 1 deletion src/Custom/Assistants/RunStepCodeInterpreterOutput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ public abstract partial class RunStepCodeInterpreterOutput
/// <inheritdoc cref="InternalRunStepDetailsToolCallsCodeOutputImageObject.FileId"/>
public string ImageFileId => AsInternalImage?.FileId;
/// <inheritdoc cref="InternalRunStepCodeInterpreterLogOutput.Logs"/>
public string Logs => AsInternalLogs?.Logs;
public string Logs => AsInternalLogs?.InternalLogs;

private InternalRunStepDetailsToolCallsCodeOutputImageObject AsInternalImage => this as InternalRunStepDetailsToolCallsCodeOutputImageObject;
private InternalRunStepCodeInterpreterLogOutput AsInternalLogs => this as InternalRunStepCodeInterpreterLogOutput;
Expand Down
15 changes: 9 additions & 6 deletions src/Custom/Assistants/ThreadInitializationMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,24 @@ public partial class ThreadInitializationMessage : MessageCreationOptions
/// <param name="content">
/// The content items that should be included in the message, added to the thread being created.
/// </param>
public ThreadInitializationMessage(IEnumerable<MessageContent> content) : base(content)
{ }
public ThreadInitializationMessage(MessageRole role, IEnumerable<MessageContent> content) : base(content)
{
Role = role;
}

internal ThreadInitializationMessage(MessageCreationOptions baseOptions)
: base(baseOptions.Role, baseOptions.Content, baseOptions.Attachments, baseOptions.Metadata, null)
{ }

/// <summary>
/// Implicitly creates a new instance of <see cref="ThreadInitializationMessage"/> from a single item of plain text
/// content.
/// content, assuming the role of <see cref="MessageRole.User"/>.
/// </summary>
/// <remarks>
/// Using a <see cref="string"/> in the position of a <see cref="ThreadInitializationMessage"/> is equivalent to
/// using the <see cref="ThreadInitializationMessage(IEnumerable{MessageContent})"/> constructor with a single
/// <see cref="MessageContent.FromText(string)"/> content instance.
/// using the <see cref="ThreadInitializationMessage(MessageRole,IEnumerable{MessageContent})"/> constructor with
/// <see cref="MessageRole.User"/> and a single <see cref="MessageContent.FromText(string)"/> content instance.
/// </remarks>
public static implicit operator ThreadInitializationMessage(string initializationMessage) => new([initializationMessage]);
public static implicit operator ThreadInitializationMessage(string initializationMessage)
=> new(MessageRole.User, [initializationMessage]);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@

namespace OpenAI.Assistants
{
internal partial class InternalRunStepDetailsToolCallsCodeOutputLogsObject : IJsonModel<InternalRunStepDetailsToolCallsCodeOutputLogsObject>
internal partial class InternalRunStepCodeInterpreterLogOutput : IJsonModel<InternalRunStepCodeInterpreterLogOutput>
{
void IJsonModel<InternalRunStepDetailsToolCallsCodeOutputLogsObject>.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options)
void IJsonModel<InternalRunStepCodeInterpreterLogOutput>.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options)
{
var format = options.Format == "W" ? ((IPersistableModel<InternalRunStepDetailsToolCallsCodeOutputLogsObject>)this).GetFormatFromOptions(options) : options.Format;
var format = options.Format == "W" ? ((IPersistableModel<InternalRunStepCodeInterpreterLogOutput>)this).GetFormatFromOptions(options) : options.Format;
if (format != "J")
{
throw new FormatException($"The model {nameof(InternalRunStepDetailsToolCallsCodeOutputLogsObject)} does not support writing '{format}' format.");
throw new FormatException($"The model {nameof(InternalRunStepCodeInterpreterLogOutput)} does not support writing '{format}' format.");
}

writer.WriteStartObject();
Expand All @@ -43,19 +43,19 @@ void IJsonModel<InternalRunStepDetailsToolCallsCodeOutputLogsObject>.Write(Utf8J
writer.WriteEndObject();
}

InternalRunStepDetailsToolCallsCodeOutputLogsObject IJsonModel<InternalRunStepDetailsToolCallsCodeOutputLogsObject>.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options)
InternalRunStepCodeInterpreterLogOutput IJsonModel<InternalRunStepCodeInterpreterLogOutput>.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options)
{
var format = options.Format == "W" ? ((IPersistableModel<InternalRunStepDetailsToolCallsCodeOutputLogsObject>)this).GetFormatFromOptions(options) : options.Format;
var format = options.Format == "W" ? ((IPersistableModel<InternalRunStepCodeInterpreterLogOutput>)this).GetFormatFromOptions(options) : options.Format;
if (format != "J")
{
throw new FormatException($"The model {nameof(InternalRunStepDetailsToolCallsCodeOutputLogsObject)} does not support reading '{format}' format.");
throw new FormatException($"The model {nameof(InternalRunStepCodeInterpreterLogOutput)} does not support reading '{format}' format.");
}

using JsonDocument document = JsonDocument.ParseValue(ref reader);
return DeserializeInternalRunStepDetailsToolCallsCodeOutputLogsObject(document.RootElement, options);
return DeserializeInternalRunStepCodeInterpreterLogOutput(document.RootElement, options);
}

internal static InternalRunStepDetailsToolCallsCodeOutputLogsObject DeserializeInternalRunStepDetailsToolCallsCodeOutputLogsObject(JsonElement element, ModelReaderWriterOptions options = null)
internal static InternalRunStepCodeInterpreterLogOutput DeserializeInternalRunStepCodeInterpreterLogOutput(JsonElement element, ModelReaderWriterOptions options = null)
{
options ??= ModelSerializationExtensions.WireOptions;

Expand Down Expand Up @@ -85,44 +85,44 @@ internal static InternalRunStepDetailsToolCallsCodeOutputLogsObject DeserializeI
}
}
serializedAdditionalRawData = rawDataDictionary;
return new InternalRunStepDetailsToolCallsCodeOutputLogsObject(type, serializedAdditionalRawData, logs);
return new InternalRunStepCodeInterpreterLogOutput(type, serializedAdditionalRawData, logs);
}

BinaryData IPersistableModel<InternalRunStepDetailsToolCallsCodeOutputLogsObject>.Write(ModelReaderWriterOptions options)
BinaryData IPersistableModel<InternalRunStepCodeInterpreterLogOutput>.Write(ModelReaderWriterOptions options)
{
var format = options.Format == "W" ? ((IPersistableModel<InternalRunStepDetailsToolCallsCodeOutputLogsObject>)this).GetFormatFromOptions(options) : options.Format;
var format = options.Format == "W" ? ((IPersistableModel<InternalRunStepCodeInterpreterLogOutput>)this).GetFormatFromOptions(options) : options.Format;

switch (format)
{
case "J":
return ModelReaderWriter.Write(this, options);
default:
throw new FormatException($"The model {nameof(InternalRunStepDetailsToolCallsCodeOutputLogsObject)} does not support writing '{options.Format}' format.");
throw new FormatException($"The model {nameof(InternalRunStepCodeInterpreterLogOutput)} does not support writing '{options.Format}' format.");
}
}

InternalRunStepDetailsToolCallsCodeOutputLogsObject IPersistableModel<InternalRunStepDetailsToolCallsCodeOutputLogsObject>.Create(BinaryData data, ModelReaderWriterOptions options)
InternalRunStepCodeInterpreterLogOutput IPersistableModel<InternalRunStepCodeInterpreterLogOutput>.Create(BinaryData data, ModelReaderWriterOptions options)
{
var format = options.Format == "W" ? ((IPersistableModel<InternalRunStepDetailsToolCallsCodeOutputLogsObject>)this).GetFormatFromOptions(options) : options.Format;
var format = options.Format == "W" ? ((IPersistableModel<InternalRunStepCodeInterpreterLogOutput>)this).GetFormatFromOptions(options) : options.Format;

switch (format)
{
case "J":
{
using JsonDocument document = JsonDocument.Parse(data);
return DeserializeInternalRunStepDetailsToolCallsCodeOutputLogsObject(document.RootElement, options);
return DeserializeInternalRunStepCodeInterpreterLogOutput(document.RootElement, options);
}
default:
throw new FormatException($"The model {nameof(InternalRunStepDetailsToolCallsCodeOutputLogsObject)} does not support reading '{options.Format}' format.");
throw new FormatException($"The model {nameof(InternalRunStepCodeInterpreterLogOutput)} does not support reading '{options.Format}' format.");
}
}

string IPersistableModel<InternalRunStepDetailsToolCallsCodeOutputLogsObject>.GetFormatFromOptions(ModelReaderWriterOptions options) => "J";
string IPersistableModel<InternalRunStepCodeInterpreterLogOutput>.GetFormatFromOptions(ModelReaderWriterOptions options) => "J";

internal static new InternalRunStepDetailsToolCallsCodeOutputLogsObject FromResponse(PipelineResponse response)
internal static new InternalRunStepCodeInterpreterLogOutput FromResponse(PipelineResponse response)
{
using var document = JsonDocument.Parse(response.Content);
return DeserializeInternalRunStepDetailsToolCallsCodeOutputLogsObject(document.RootElement);
return DeserializeInternalRunStepCodeInterpreterLogOutput(document.RootElement);
}

internal override BinaryContent ToBinaryContent()
Expand Down
29 changes: 29 additions & 0 deletions src/Generated/Models/InternalRunStepCodeInterpreterLogOutput.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// <auto-generated/>

#nullable disable

using System;
using System.Collections.Generic;

namespace OpenAI.Assistants
{
internal partial class InternalRunStepCodeInterpreterLogOutput : RunStepCodeInterpreterOutput
{
internal InternalRunStepCodeInterpreterLogOutput(string internalLogs)
{
Argument.AssertNotNull(internalLogs, nameof(internalLogs));

Type = "logs";
InternalLogs = internalLogs;
}

internal InternalRunStepCodeInterpreterLogOutput(string type, IDictionary<string, BinaryData> serializedAdditionalRawData, string internalLogs) : base(type, serializedAdditionalRawData)
{
InternalLogs = internalLogs;
}

internal InternalRunStepCodeInterpreterLogOutput()
{
}
}
}
Loading