Skip to content

Commit

Permalink
[.Net] add output schema to generateReplyOption (#3450)
Browse files Browse the repository at this point in the history
* add output schema

* fix format
  • Loading branch information
LittleLittleCloud authored Aug 30, 2024
1 parent 5861bd9 commit 084532e
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 51 deletions.
77 changes: 40 additions & 37 deletions dotnet/sample/AutoGen.OpenAI.Sample/Structural_Output.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@
using Json.Schema;
using Json.Schema.Generation;
using OpenAI;
using OpenAI.Chat;

namespace AutoGen.OpenAI.Sample;

internal class Structural_Output
public class Structural_Output
{
public static async Task RunAsync()
{
Expand All @@ -23,24 +22,25 @@ public static async Task RunAsync()

var schemaBuilder = new JsonSchemaBuilder().FromType<Person>();
var schema = schemaBuilder.Build();

var personSchemaFormat = ChatResponseFormat.CreateJsonSchemaFormat(
name: "Person",
jsonSchema: BinaryData.FromObjectAsJson(schema),
description: "Person schema");

var openAIClient = new OpenAIClient(apiKey);
var openAIClientAgent = new OpenAIChatAgent(
chatClient: openAIClient.GetChatClient(model),
name: "assistant",
systemMessage: "You are a helpful assistant",
responseFormat: personSchemaFormat) // structural output by passing schema to response format
systemMessage: "You are a helpful assistant")
.RegisterMessageConnector()
.RegisterPrintMessage();
#endregion create_agent

#region chat_with_agent
var reply = await openAIClientAgent.SendAsync("My name is John, I am 25 years old, and I live in Seattle. I like to play soccer and read books.");
var prompt = new TextMessage(Role.User, """
My name is John, I am 25 years old, and I live in Seattle. I like to play soccer and read books.
""");
var reply = await openAIClientAgent.GenerateReplyAsync(
messages: [prompt],
options: new GenerateReplyOptions
{
OutputSchema = schema,
});

var person = JsonSerializer.Deserialize<Person>(reply.GetContent());
Console.WriteLine($"Name: {person.Name}");
Expand All @@ -60,31 +60,34 @@ public static async Task RunAsync()
person.City.Should().Be("Seattle");
person.Hobbies.Count.Should().Be(2);
}
}

#region person_class
public class Person
{
[JsonPropertyName("name")]
[Description("Name of the person")]
[Required]
public string Name { get; set; }

[JsonPropertyName("age")]
[Description("Age of the person")]
[Required]
public int Age { get; set; }

[JsonPropertyName("city")]
[Description("City of the person")]
public string? City { get; set; }

[JsonPropertyName("address")]
[Description("Address of the person")]
public string? Address { get; set; }

[JsonPropertyName("hobbies")]
[Description("Hobbies of the person")]
public List<string>? Hobbies { get; set; }

#region person_class
[Title("Person")]
public class Person
{
[JsonPropertyName("name")]
[Description("Name of the person")]
[Required]
public string Name { get; set; }

[JsonPropertyName("age")]
[Description("Age of the person")]
[Required]
public int Age { get; set; }

[JsonPropertyName("city")]
[Description("City of the person")]
public string? City { get; set; }

[JsonPropertyName("address")]
[Description("Address of the person")]
public string? Address { get; set; }

[JsonPropertyName("hobbies")]
[Description("Hobbies of the person")]
public List<string>? Hobbies { get; set; }
}
#endregion person_class

}
#endregion person_class
27 changes: 14 additions & 13 deletions dotnet/sample/AutoGen.OpenAI.Sample/Use_Json_Mode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using AutoGen.Core;
using AutoGen.OpenAI;
using AutoGen.OpenAI.Extension;
using FluentAssertions;
using OpenAI;
using OpenAI.Chat;

namespace AutoGen.BasicSample;
namespace AutoGen.OpenAI.Sample;

public class Use_Json_Mode
{
Expand Down Expand Up @@ -50,18 +49,20 @@ public static async Task RunAsync()
person.Age.Should().Be(25);
person.Address.Should().BeNullOrEmpty();
}
}

#region person_class
public class Person
{
[JsonPropertyName("name")]
public string Name { get; set; }

[JsonPropertyName("age")]
public int Age { get; set; }
#region person_class
public class Person
{
[JsonPropertyName("name")]
public string Name { get; set; }

[JsonPropertyName("age")]
public int Age { get; set; }

[JsonPropertyName("address")]
public string Address { get; set; }
[JsonPropertyName("address")]
public string Address { get; set; }
}
#endregion person_class
}
#endregion person_class

7 changes: 7 additions & 0 deletions dotnet/src/AutoGen.Core/Agent/IAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Json.Schema;

namespace AutoGen.Core;

Expand Down Expand Up @@ -42,6 +43,7 @@ public GenerateReplyOptions(GenerateReplyOptions other)
this.MaxToken = other.MaxToken;
this.StopSequence = other.StopSequence?.Select(s => s)?.ToArray();
this.Functions = other.Functions?.Select(f => f)?.ToArray();
this.OutputSchema = other.OutputSchema;
}

public float? Temperature { get; set; }
Expand All @@ -51,4 +53,9 @@ public GenerateReplyOptions(GenerateReplyOptions other)
public string[]? StopSequence { get; set; }

public FunctionContract[]? Functions { get; set; }

/// <summary>
/// Structural schema for the output. This property only applies to certain LLMs.
/// </summary>
public JsonSchema? OutputSchema { get; set; }
}
9 changes: 9 additions & 0 deletions dotnet/src/AutoGen.OpenAI/Agent/OpenAIChatAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using AutoGen.OpenAI.Extension;
using global::OpenAI;
using global::OpenAI.Chat;
using Json.Schema;

namespace AutoGen.OpenAI;

Expand Down Expand Up @@ -179,6 +180,14 @@ private ChatCompletionOptions CreateChatCompletionsOptions(GenerateReplyOptions?
}
}

if (options?.OutputSchema is not null)
{
option.ResponseFormat = ChatResponseFormat.CreateJsonSchemaFormat(
name: options.OutputSchema.GetTitle() ?? throw new ArgumentException("Output schema must have a title"),
jsonSchema: BinaryData.FromObjectAsJson(options.OutputSchema),
description: options.OutputSchema.GetDescription());
}

return option;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\sample\AutoGen.OpenAI.Sample\AutoGen.OpenAI.Sample.csproj" />
<ProjectReference Include="..\..\src\AutoGen.OpenAI\AutoGen.OpenAI.csproj" />
<ProjectReference Include="..\..\src\AutoGen.SourceGenerator\AutoGen.SourceGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
<ProjectReference Include="..\AutoGen.Test.Share\AutoGen.Tests.Share.csproj" />
Expand Down
1 change: 0 additions & 1 deletion dotnet/test/AutoGen.OpenAI.Tests/OpenAIChatAgentTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,6 @@ public async Task ItCreateOpenAIChatAgentWithChatCompletionOptionAsync()
respond.GetContent()?.Should().NotBeNullOrEmpty();
}


private OpenAIClient CreateOpenAIClientFromAzureOpenAI()
{
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new Exception("Please set AZURE_OPENAI_ENDPOINT environment variable.");
Expand Down
48 changes: 48 additions & 0 deletions dotnet/test/AutoGen.OpenAI.Tests/OpenAISampleTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// OpenAISampleTest.cs

using System;
using System.IO;
using System.Threading.Tasks;
using AutoGen.OpenAI.Sample;
using AutoGen.Tests;
using Xunit.Abstractions;

namespace AutoGen.OpenAI.Tests;

public class OpenAISampleTest
{
private readonly ITestOutputHelper _output;

public OpenAISampleTest(ITestOutputHelper output)
{
_output = output;
Console.SetOut(new ConsoleWriter(_output));
}

[ApiKeyFact("OPENAI_API_KEY")]
public async Task Structural_OutputAsync()
{
await Structural_Output.RunAsync();
}

[ApiKeyFact("OPENAI_API_KEY")]
public async Task Use_Json_ModeAsync()
{
await Use_Json_Mode.RunAsync();
}

public class ConsoleWriter : StringWriter
{
private ITestOutputHelper output;
public ConsoleWriter(ITestOutputHelper output)
{
this.output = output;
}

public override void WriteLine(string? m)
{
output.WriteLine(m);
}
}
}

0 comments on commit 084532e

Please sign in to comment.