Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2.0.0-beta.9: Structured Outputs changes #180

Merged
merged 1 commit into from
Aug 23, 2024
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
80 changes: 65 additions & 15 deletions api/OpenAI.netstandard2.0.cs

Large diffs are not rendered by default.

59 changes: 59 additions & 0 deletions examples/Chat/Example07_StructuredOutputs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using NUnit.Framework;
using OpenAI.Chat;
using System;
using System.Text.Json;

namespace OpenAI.Examples;

public partial class ChatExamples
{
[Test]
public void Example07_StructuredOutputs()
{
ChatClient client = new("gpt-4o-mini", Environment.GetEnvironmentVariable("OPENAI_API_KEY"));

ChatCompletionOptions options = new()
{
ResponseFormat = ChatResponseFormat.CreateJsonSchemaFormat(
name: "math_reasoning",
jsonSchema: BinaryData.FromString("""
{
"type": "object",
"properties": {
"steps": {
"type": "array",
"items": {
"type": "object",
"properties": {
"explanation": { "type": "string" },
"output": { "type": "string" }
},
"required": ["explanation", "output"],
"additionalProperties": false
}
},
"final_answer": { "type": "string" }
},
"required": ["steps", "final_answer"],
"additionalProperties": false
}
"""),
strictSchemaEnabled: true)
};

ChatCompletion chatCompletion = client.CompleteChat(
["How can I solve 8x + 7 = -23?"],
options);

using JsonDocument structuredJson = JsonDocument.Parse(chatCompletion.ToString());

Console.WriteLine($"Final answer: {structuredJson.RootElement.GetProperty("final_answer").GetString()}");
Console.WriteLine("Reasoning steps:");

foreach (JsonElement stepElement in structuredJson.RootElement.GetProperty("steps").EnumerateArray())
{
Console.WriteLine($" - Explanation: {stepElement.GetProperty("explanation").GetString()}");
Console.WriteLine($" Output: {stepElement.GetProperty("output")}");
}
}
}
60 changes: 60 additions & 0 deletions examples/Chat/Example07_StructuredOutputsAsync.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using NUnit.Framework;
using OpenAI.Chat;
using System;
using System.Text.Json;
using System.Threading.Tasks;

namespace OpenAI.Examples;

public partial class ChatExamples
{
[Test]
public async Task Example07_StructuredOutputsAsync()
{
ChatClient client = new("gpt-4o-mini", Environment.GetEnvironmentVariable("OPENAI_API_KEY"));

ChatCompletionOptions options = new()
{
ResponseFormat = ChatResponseFormat.CreateJsonSchemaFormat(
name: "math_reasoning",
jsonSchema: BinaryData.FromString("""
{
"type": "object",
"properties": {
"steps": {
"type": "array",
"items": {
"type": "object",
"properties": {
"explanation": { "type": "string" },
"output": { "type": "string" }
},
"required": ["explanation", "output"],
"additionalProperties": false
}
},
"final_answer": { "type": "string" }
},
"required": ["steps", "final_answer"],
"additionalProperties": false
}
"""),
strictSchemaEnabled: true)
};

ChatCompletion chatCompletion = await client.CompleteChatAsync(
["How can I solve 8x + 7 = -23?"],
options);

using JsonDocument structuredJson = JsonDocument.Parse(chatCompletion.ToString());

Console.WriteLine($"Final answer: {structuredJson.RootElement.GetProperty("final_answer").GetString()}");
Console.WriteLine("Reasoning steps:");

foreach (JsonElement stepElement in structuredJson.RootElement.GetProperty("steps").EnumerateArray())
{
Console.WriteLine($" - Explanation: {stepElement.GetProperty("explanation").GetString()}");
Console.WriteLine($" Output: {stepElement.GetProperty("output")}");
}
}
}
36 changes: 36 additions & 0 deletions src/Custom/Administration/Internal/GeneratorStubs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
namespace OpenAI.Administration;

[CodeGenModel("AuditLogActorServiceAccount")] internal partial class InternalAuditLogActorServiceAccount { }
[CodeGenModel("AuditLogActorUser")] internal partial class InternalAuditLogActorUser { }
[CodeGenModel("AuditLogActorApiKey")] internal partial class InternalAuditLogActorApiKey { }
[CodeGenModel("AuditLogActorSession")] internal partial class InternalAuditLogActorSession { }
[CodeGenModel("AuditLogActor")] internal partial class InternalAuditLogActor { }
[CodeGenModel("AuditLog")] internal partial class InternalAuditLog { }
[CodeGenModel("ListAuditLogsResponse")] internal partial class InternalListAuditLogsResponse { }
[CodeGenModel("Invite")] internal partial class InternalInvite { }
[CodeGenModel("InviteListResponse")] internal partial class InternalInviteListResponse { }
[CodeGenModel("InviteRequest")] internal partial class InternalInviteRequest { }
[CodeGenModel("InviteDeleteResponse")] internal partial class InternalInviteDeleteResponse { }
[CodeGenModel("User")] internal partial class InternalUser { }
[CodeGenModel("UserListResponse")] internal partial class InternalUserListResponse { }
[CodeGenModel("UserRoleUpdateRequest")] internal partial class InternalUserRoleUpdateRequest { }
[CodeGenModel("UserDeleteResponse")] internal partial class InternalUserDeleteResponse { }
[CodeGenModel("Project")] internal partial class InternalProject { }
[CodeGenModel("ProjectListResponse")] internal partial class InternalProjectListResponse { }
[CodeGenModel("ProjectCreateRequest")] internal partial class InternalProjectCreateRequest { }
[CodeGenModel("ProjectUpdateRequest")] internal partial class InternalProjectUpdateRequest { }
[CodeGenModel("DefaultProjectErrorResponse")] internal partial class InternalDefaultProjectErrorResponse { }
[CodeGenModel("ProjectUser")] internal partial class InternalProjectUser { }
[CodeGenModel("ProjectUserListResponse")] internal partial class InternalProjectUserListResponse { }
[CodeGenModel("ProjectUserCreateRequest")] internal partial class InternalProjectUserCreateRequest { }
[CodeGenModel("ProjectUserUpdateRequest")] internal partial class InternalProjectUserUpdateRequest { }
[CodeGenModel("ProjectUserDeleteResponse")] internal partial class InternalProjectUserDeleteResponse { }
[CodeGenModel("ProjectServiceAccount")] internal partial class InternalProjectServiceAccount { }
[CodeGenModel("ProjectServiceAccountListResponse")] internal partial class InternalProjectServiceAccountListResponse { }
[CodeGenModel("ProjectServiceAccountCreateRequest")] internal partial class InternalProjectServiceAccountCreateRequest { }
[CodeGenModel("ProjectServiceAccountCreateResponse")] internal partial class InternalProjectServiceAccountCreateResponse { }
[CodeGenModel("ProjectServiceAccountApiKey")] internal partial class InternalProjectServiceAccountApiKey { }
[CodeGenModel("ProjectServiceAccountDeleteResponse")] internal partial class InternalProjectServiceAccountDeleteResponse { }
[CodeGenModel("ProjectApiKey")] internal partial class InternalProjectApiKey { }
[CodeGenModel("ProjectApiKeyListResponse")] internal partial class InternalProjectApiKeyListResponse { }
[CodeGenModel("ProjectApiKeyDeleteResponse")] internal partial class InternalProjectApiKeyDeleteResponse { }
77 changes: 31 additions & 46 deletions src/Custom/Assistants/AssistantResponseFormat.Serialization.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System;
using System.ClientModel;
using System.ClientModel.Primitives;
using System.Collections.Generic;
using System.Text.Json;

namespace OpenAI.Assistants;
Expand All @@ -9,10 +9,33 @@ namespace OpenAI.Assistants;
[CodeGenSuppress("global::System.ClientModel.Primitives.IJsonModel<OpenAI.Assistants.AssistantResponseFormat>.Create", typeof(Utf8JsonReader), typeof(ModelReaderWriterOptions))]
[CodeGenSuppress("global::System.ClientModel.Primitives.IPersistableModel<OpenAI.Assistants.AssistantResponseFormat>.Write", typeof(ModelReaderWriterOptions))]
[CodeGenSuppress("global::System.ClientModel.Primitives.IPersistableModel<OpenAI.Assistants.AssistantResponseFormat>.Create", typeof(BinaryData), typeof(ModelReaderWriterOptions))]
[CodeGenSuppress("global::System.ClientModel.Primitives.IPersistableModel<OpenAI.Assistants.AssistantResponseFormat>.GetFormatFromOptions", typeof(ModelReaderWriterOptions))]
public partial class AssistantResponseFormat : IJsonModel<AssistantResponseFormat>
{
internal static void SerializeAssistantResponseFormat(AssistantResponseFormat instance, Utf8JsonWriter writer, ModelReaderWriterOptions options = null)
{
throw new InvalidOperationException();
}

internal static AssistantResponseFormat DeserializeAssistantResponseFormat(JsonElement element, ModelReaderWriterOptions options = null)
{
return element.ValueKind switch
{
JsonValueKind.String => InternalAssistantResponseFormatPlainTextNoObject.DeserializeInternalAssistantResponseFormatPlainTextNoObject(element, options),
JsonValueKind.Object when element.TryGetProperty("type", out JsonElement discriminatorElement)
=> discriminatorElement.GetString() switch
{
"json_object" => InternalAssistantResponseFormatJsonObject.DeserializeInternalAssistantResponseFormatJsonObject(element, options),
"json_schema" => InternalAssistantResponseFormatJsonSchema.DeserializeInternalAssistantResponseFormatJsonSchema(element, options),
"text" => InternalAssistantResponseFormatText.DeserializeInternalAssistantResponseFormatText(element, options),
_ => null,
},
_ => null,
};
}

void IJsonModel<AssistantResponseFormat>.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options)
=> CustomSerializationHelpers.SerializeInstance(this, SerializeAssistantResponseFormat, writer, options);
=> CustomSerializationHelpers.SerializeInstance(this, SerializeAssistantResponseFormat, writer, options);

AssistantResponseFormat IJsonModel<AssistantResponseFormat>.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options)
=> CustomSerializationHelpers.DeserializeNewInstance(this, DeserializeAssistantResponseFormat, ref reader, options);
Expand All @@ -23,53 +46,15 @@ BinaryData IPersistableModel<AssistantResponseFormat>.Write(ModelReaderWriterOpt
AssistantResponseFormat IPersistableModel<AssistantResponseFormat>.Create(BinaryData data, ModelReaderWriterOptions options)
=> CustomSerializationHelpers.DeserializeNewInstance(this, DeserializeAssistantResponseFormat, data, options);

internal static void SerializeAssistantResponseFormat(AssistantResponseFormat formatInstance, Utf8JsonWriter writer, ModelReaderWriterOptions options)
string IPersistableModel<AssistantResponseFormat>.GetFormatFromOptions(ModelReaderWriterOptions options) => "J";

internal static AssistantResponseFormat FromResponse(PipelineResponse response)
{
if (formatInstance._plainTextValue is not null)
{
writer.WriteStringValue(formatInstance._plainTextValue);
}
else
{
writer.WriteStartObject();
writer.WritePropertyName("type"u8);
writer.WriteStringValue(formatInstance._objectType);
writer.WriteSerializedAdditionalRawData(formatInstance.SerializedAdditionalRawData, options);
writer.WriteEndObject();
}
throw new InvalidOperationException();
}

internal static AssistantResponseFormat DeserializeAssistantResponseFormat(JsonElement element, ModelReaderWriterOptions options = null)
internal virtual BinaryContent ToBinaryContent()
{
options ??= ModelSerializationExtensions.WireOptions;

string plainTextValue = null;
string objectType = null;
IDictionary<string, BinaryData> rawDataDictionary = new ChangeTrackingDictionary<string, BinaryData>();

if (element.ValueKind == JsonValueKind.Null)
{
return null;
}
else if (element.ValueKind == JsonValueKind.String)
{
plainTextValue = element.GetString();
}
else
{
foreach (var property in element.EnumerateObject())
{
if (property.NameEquals("type"u8))
{
objectType = property.Value.GetString();
continue;
}
if (true)
{
rawDataDictionary.Add(property.Name, BinaryData.FromString(property.Value.GetRawText()));
}
}
}
return new AssistantResponseFormat(plainTextValue, objectType, rawDataDictionary);
return BinaryContent.Create(this, ModelSerializationExtensions.WireOptions);
}
}
Loading
Loading