-
Notifications
You must be signed in to change notification settings - Fork 1
Description
Thanks much for the article and code very helpful.
The optimizer produces different json with this code between release mode and debug mode. Debug mode works just fine with structured output however in release mode the json is incompatible with structured_output. It products json that utilizes $ref referencing nodes not at the top level. The following json is what is produced in release mode for one of my classes.
{
"type": "object",
"properties": {
"Name": {
"type": "string"
},
"seriesId": {
"type": "string"
},
"booksInSeries": {
"type": "array",
"items": {
"type": "object",
"properties": {
"BookTitle": {
"type": "string"
},
"Author": {
"type": "string"
},
"ISBN_10": {
"type": "string"
},
"ISBN_13": {
"type": "string"
},
"sysMynbIDAsString": {
"type": "string"
}
},
"additionalProperties": false,
"required": [
"BookTitle",
"Author",
"ISBN_10",
"ISBN_13",
"sysMynbIDAsString"
]
}
},
"booksMissingFromSeries": {
"type": "array",
"items": {
"$ref": "#/properties/booksInSeries/items"
}
}
},
"additionalProperties": false,
"required": [
"Name",
"seriesId",
"booksInSeries",
"booksMissingFromSeries"
]
}To rectify the problem I utilized newton soft and updated ProcessJsonObject I should say that AI did the updates, it was either meta, phind, or github copilot I ended up using all three before I found a solution that worked.
I forked the project and made my updates there in case you would like to reference.
https://github.com/bradyguyc/openai-structured-outputs-with-dotnet
Commit Message:
Update dependencies and refactor JSON schema handling
Updated StructuredOutputs.csproj to include new package references:
Microsoft.Extensions.Loggingversion9.0.0Newtonsoft.Jsonversion13.0.3Newtonsoft.Json.Schemaversion4.0.2-beta1
In StructuredOutputsExtensions.cs:
- Added new
usingdirectives. - Modified
StructuredOutputsTransformto accept a nullableJsonSchemaExporterContext. - Refactored
ProcessJsonObjectto simplifyadditionalPropertiesaddition, conditionally addrequiredfield, and handle nested objects in arrays. - Updated
CreateJsonSchemaFormatto accept an optionalILogger, useJSchemaGeneratorfor schema generation, transform the schema, and serialize it to a formatted JSON string.
No changes were made to spelling.dic.
`
using System.Diagnostics;
using System.Linq.Expressions;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Schema;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Schema;
using Newtonsoft.Json.Schema.Generation;
using OpenAI.Chat;
namespace MyNextBookBackEnd;
internal static class StructuredOutputsExtensions
{
public static Func<JsonSchemaExporterContext?, JsonNode, JsonNode> StructuredOutputsTransform = new((context, node) =>
{
static void ProcessJsonObject(JsonObject jsonObject)
{
if (jsonObject.ContainsKey("properties"))
{
jsonObject["additionalProperties"] = false;
var properties = jsonObject["properties"] as JsonObject;
if (properties != null)
{
foreach (var property in properties)
{
if (property.Value is JsonObject nestedObject)
{
ProcessJsonObject(nestedObject);
}
}
}
}
// Check for nested objects in arrays
if (jsonObject.ContainsKey("items"))
{
var items = jsonObject["items"] as JsonObject;
if (items != null)
{
ProcessJsonObject(items);
}
}
}
if (node is JsonObject rootObject)
{
ProcessJsonObject(rootObject);
}
return node;
});
public static T? CompleteChat<T>(this ChatClient chatClient, List<ChatMessage> messages, ChatCompletionOptions options)
{
ChatCompletion completion = chatClient.CompleteChat(messages, options);
return JsonSerializer.Deserialize<T>(completion.Content[0].Text);
}
public static ChatResponseFormat CreateJsonSchemaFormat<T>(
string jsonSchemaFormatName,
string? jsonSchemaFormatDescription = null,
bool? jsonSchemaIsStrict = null,
ILogger? logger = null)
{
JSchemaGenerator generator = new JSchemaGenerator
{
SchemaReferenceHandling = SchemaReferenceHandling.None,
};
JSchema schema = generator.Generate(typeof(T));
JsonNode? schemaNode = JsonNode.Parse(schema.ToString());
JsonSchemaExporterContext context = new JsonSchemaExporterContext(); // Create an empty context
schemaNode = StructuredOutputsTransform(context, schemaNode);
// Serialize the modified schema node to a formatted JSON string
string jsonString = schemaNode.ToJsonString(new JsonSerializerOptions { WriteIndented = true });
return ChatResponseFormat.CreateJsonSchemaFormat(
jsonSchemaFormatName,
jsonSchema: BinaryData.FromString(jsonString),
jsonSchemaFormatDescription: jsonSchemaFormatDescription,
jsonSchemaIsStrict: jsonSchemaIsStrict
);
}
}
`