diff --git a/autogen/coding/local_commandline_code_executor.py b/autogen/coding/local_commandline_code_executor.py index 620b359a4ae..2280f7f030d 100644 --- a/autogen/coding/local_commandline_code_executor.py +++ b/autogen/coding/local_commandline_code_executor.py @@ -221,7 +221,12 @@ def _setup_functions(self) -> None: cmd = [py_executable, "-m", "pip", "install"] + required_packages try: result = subprocess.run( - cmd, cwd=self._work_dir, capture_output=True, text=True, timeout=float(self._timeout) + cmd, + cwd=self._work_dir, + capture_output=True, + text=True, + timeout=float(self._timeout), + encoding="utf-8", ) except subprocess.TimeoutExpired as e: raise ValueError("Pip install timed out") from e @@ -303,7 +308,13 @@ def _execute_code_dont_check_setup(self, code_blocks: List[CodeBlock]) -> Comman try: result = subprocess.run( - cmd, cwd=self._work_dir, capture_output=True, text=True, timeout=float(self._timeout), env=env + cmd, + cwd=self._work_dir, + capture_output=True, + text=True, + timeout=float(self._timeout), + env=env, + encoding="utf-8", ) except subprocess.TimeoutExpired: logs_all += "\n" + TIMEOUT_MSG diff --git a/dotnet/sample/AutoGen.OpenAI.Sample/Structural_Output.cs b/dotnet/sample/AutoGen.OpenAI.Sample/Structural_Output.cs index e562d7223a6..e83be0082ba 100644 --- a/dotnet/sample/AutoGen.OpenAI.Sample/Structural_Output.cs +++ b/dotnet/sample/AutoGen.OpenAI.Sample/Structural_Output.cs @@ -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() { @@ -23,24 +22,25 @@ public static async Task RunAsync() var schemaBuilder = new JsonSchemaBuilder().FromType(); 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(reply.GetContent()); Console.WriteLine($"Name: {person.Name}"); @@ -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? 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? Hobbies { get; set; } + } + #endregion person_class + } -#endregion person_class diff --git a/dotnet/sample/AutoGen.OpenAI.Sample/Use_Json_Mode.cs b/dotnet/sample/AutoGen.OpenAI.Sample/Use_Json_Mode.cs index 392796d819f..4e5247d93ce 100644 --- a/dotnet/sample/AutoGen.OpenAI.Sample/Use_Json_Mode.cs +++ b/dotnet/sample/AutoGen.OpenAI.Sample/Use_Json_Mode.cs @@ -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 { @@ -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 + diff --git a/dotnet/src/AutoGen.Core/Agent/IAgent.cs b/dotnet/src/AutoGen.Core/Agent/IAgent.cs index 34a31055d1b..f2b8ce67d01 100644 --- a/dotnet/src/AutoGen.Core/Agent/IAgent.cs +++ b/dotnet/src/AutoGen.Core/Agent/IAgent.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Json.Schema; namespace AutoGen.Core; @@ -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; } @@ -51,4 +53,9 @@ public GenerateReplyOptions(GenerateReplyOptions other) public string[]? StopSequence { get; set; } public FunctionContract[]? Functions { get; set; } + + /// + /// Structural schema for the output. This property only applies to certain LLMs. + /// + public JsonSchema? OutputSchema { get; set; } } diff --git a/dotnet/src/AutoGen.OpenAI/Agent/OpenAIChatAgent.cs b/dotnet/src/AutoGen.OpenAI/Agent/OpenAIChatAgent.cs index 1ae1e45db15..44711c5e428 100644 --- a/dotnet/src/AutoGen.OpenAI/Agent/OpenAIChatAgent.cs +++ b/dotnet/src/AutoGen.OpenAI/Agent/OpenAIChatAgent.cs @@ -10,6 +10,7 @@ using AutoGen.OpenAI.Extension; using global::OpenAI; using global::OpenAI.Chat; +using Json.Schema; namespace AutoGen.OpenAI; @@ -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; } diff --git a/dotnet/test/AutoGen.OpenAI.Tests/AutoGen.OpenAI.Tests.csproj b/dotnet/test/AutoGen.OpenAI.Tests/AutoGen.OpenAI.Tests.csproj index a6495fc4487..d1e48686007 100644 --- a/dotnet/test/AutoGen.OpenAI.Tests/AutoGen.OpenAI.Tests.csproj +++ b/dotnet/test/AutoGen.OpenAI.Tests/AutoGen.OpenAI.Tests.csproj @@ -12,6 +12,7 @@ + diff --git a/dotnet/test/AutoGen.OpenAI.Tests/OpenAIChatAgentTest.cs b/dotnet/test/AutoGen.OpenAI.Tests/OpenAIChatAgentTest.cs index bcbfee6e208..04f4d3d4d39 100644 --- a/dotnet/test/AutoGen.OpenAI.Tests/OpenAIChatAgentTest.cs +++ b/dotnet/test/AutoGen.OpenAI.Tests/OpenAIChatAgentTest.cs @@ -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."); diff --git a/dotnet/test/AutoGen.OpenAI.Tests/OpenAISampleTest.cs b/dotnet/test/AutoGen.OpenAI.Tests/OpenAISampleTest.cs new file mode 100644 index 00000000000..6376c4ff498 --- /dev/null +++ b/dotnet/test/AutoGen.OpenAI.Tests/OpenAISampleTest.cs @@ -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); + } + } +} diff --git a/samples/apps/autogen-studio/autogenstudio/datamodel.py b/samples/apps/autogen-studio/autogenstudio/datamodel.py index 6c6dc567a80..92d60cf5c52 100644 --- a/samples/apps/autogen-studio/autogenstudio/datamodel.py +++ b/samples/apps/autogen-studio/autogenstudio/datamodel.py @@ -16,7 +16,19 @@ Enum as SqlEnum, ) -SQLModel.model_config["protected_namespaces"] = () +# added for python3.11 and sqlmodel 0.0.22 incompatibility +if hasattr(SQLModel, "model_config"): + SQLModel.model_config["protected_namespaces"] = () +elif hasattr(SQLModel, "Config"): + + class CustomSQLModel(SQLModel): + class Config: + protected_namespaces = () + + SQLModel = CustomSQLModel +else: + print("Warning: Unable to set protected_namespaces.") + # pylint: disable=protected-access