diff --git a/OpenAI.Utilities/FunctionCalling/PropertyDefinitionGenerator.cs b/OpenAI.Utilities/FunctionCalling/PropertyDefinitionGenerator.cs new file mode 100644 index 00000000..48398b93 --- /dev/null +++ b/OpenAI.Utilities/FunctionCalling/PropertyDefinitionGenerator.cs @@ -0,0 +1,91 @@ +using System.Reflection; +using System.Text.Json.Serialization; +using OpenAI.ObjectModels.SharedModels; + +namespace OpenAI.Utilities.FunctionCalling; + +public class PropertyDefinitionGenerator +{ + public static PropertyDefinition GenerateFromType(Type type) + { + if (type == null) + throw new ArgumentNullException(nameof(type)); + + if (type.IsPrimitive || type == typeof(string) || type == typeof(DateTime)) + { + return GeneratePrimitiveDefinition(type); + } + else if (type.IsEnum) + { + return GenerateEnumDefinition(type); + } + else if (type.IsArray || (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>))) + { + return GenerateArrayDefinition(type); + } + else + { + return GenerateObjectDefinition(type); + } + } + + private static PropertyDefinition GeneratePrimitiveDefinition(Type type) + { + if (type == typeof(string)) + return PropertyDefinition.DefineString(); + else if (type == typeof(int) || type == typeof(long)) + return PropertyDefinition.DefineInteger(); + else if (type == typeof(float) || type == typeof(double) || type == typeof(decimal)) + return PropertyDefinition.DefineNumber(); + else if (type == typeof(bool)) + return PropertyDefinition.DefineBoolean(); + else if (type == typeof(DateTime)) + return PropertyDefinition.DefineString("ISO 8601 date-time string"); + else + throw new ArgumentException($"Unsupported primitive type: {type.Name}"); + } + + private static PropertyDefinition GenerateEnumDefinition(Type type) + { + var enumValues = Enum.GetNames(type); + return PropertyDefinition.DefineEnum(new List(enumValues), $"Enum of type {type.Name}"); + } + + private static PropertyDefinition GenerateArrayDefinition(Type type) + { + Type elementType = type.IsArray ? type.GetElementType() : type.GetGenericArguments()[0]; + return PropertyDefinition.DefineArray(GenerateFromType(elementType)); + } + + private static PropertyDefinition GenerateObjectDefinition(Type type) + { + var properties = new Dictionary(); + var required = new List(); + + foreach (var prop in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) + { + string propertyName = GetJsonPropertyName(prop); + properties[propertyName] = GenerateFromType(prop.PropertyType); + + // You might want to customize this logic based on your needs + if (!prop.PropertyType.IsValueType && Nullable.GetUnderlyingType(prop.PropertyType) == null) + { + required.Add(propertyName); + } + } + + return PropertyDefinition.DefineObject( + properties, + required, + false, // Set additionalProperties to false by default + $"Object of type {type.Name}", + null + ); + } + + private static string GetJsonPropertyName(PropertyInfo prop) + { + var jsonPropertyNameAttribute = prop.GetCustomAttribute(); + return jsonPropertyNameAttribute != null ? jsonPropertyNameAttribute.Name : prop.Name; + } +} \ No newline at end of file diff --git a/OpenAI.Utilities/OpenAI.Utilities.csproj b/OpenAI.Utilities/OpenAI.Utilities.csproj index 8d335c6f..736519c4 100644 --- a/OpenAI.Utilities/OpenAI.Utilities.csproj +++ b/OpenAI.Utilities/OpenAI.Utilities.csproj @@ -9,7 +9,7 @@ https://openai.com/ OpenAI-Betalgo.png true - 8.0.1 + 8.1.0 Tolga Kayhan, Betalgo Betalgo Up Ltd. Utility tools for Betalgo.OpenAI @@ -40,8 +40,8 @@ - - + + diff --git a/OpenAI.UtilitiesPlayground/Program.cs b/OpenAI.UtilitiesPlayground/Program.cs index 8b7d2fbc..e096b2b4 100644 --- a/OpenAI.UtilitiesPlayground/Program.cs +++ b/OpenAI.UtilitiesPlayground/Program.cs @@ -24,7 +24,8 @@ //await EmbeddingTestHelpers.ExerciseEmbeddingTools(sdk); -await FunctionCallingTestHelpers.ExerciseFunctionCalling(sdk); +//await FunctionCallingTestHelpers.ExerciseFunctionCalling(sdk); +await JsonSchemaResponseTypeTestHelpers.RunChatWithJsonSchemaResponseFormat2(sdk); Console.WriteLine("Press any key to exit..."); Console.ReadKey(); \ No newline at end of file diff --git a/OpenAI.UtilitiesPlayground/TestHelpers/JsonSchemaResponseTypeTestHelpers.cs b/OpenAI.UtilitiesPlayground/TestHelpers/JsonSchemaResponseTypeTestHelpers.cs new file mode 100644 index 00000000..68c3e664 --- /dev/null +++ b/OpenAI.UtilitiesPlayground/TestHelpers/JsonSchemaResponseTypeTestHelpers.cs @@ -0,0 +1,88 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using OpenAI.Interfaces; +using OpenAI.ObjectModels; +using OpenAI.ObjectModels.RequestModels; +using OpenAI.Utilities.FunctionCalling; + +namespace OpenAI.UtilitiesPlayground.TestHelpers; + +public static class JsonSchemaResponseTypeTestHelpers +{ + public static async Task RunChatWithJsonSchemaResponseFormat2(IOpenAIService sdk) + { + Console.WriteLine("Chat Completion Testing is starting:"); + try + { + var completionResult = await sdk.ChatCompletion.CreateCompletion(new() + { + Messages = new List + { + ChatMessage.FromSystem("You are a helpful math tutor. Guide the user through the solution step by step."), + ChatMessage.FromUser("how can I solve 8x + 7 = -23") + }, + Model = "gpt-4o-2024-08-06", + ResponseFormat = new() + { + Type = StaticValues.CompletionStatics.ResponseFormat.JsonSchema, + JsonSchema = new() + { + Name = "math_response", + Strict = true, + Schema = PropertyDefinitionGenerator.GenerateFromType(typeof(MathResponse)) + } + } + }); + + if (completionResult.Successful) + { + var response =JsonSerializer.Deserialize(completionResult.Choices.First().Message.Content!); + foreach (var responseStep in response?.Steps!) + { + Console.WriteLine(responseStep.Explanation); + Console.WriteLine(responseStep.Output); + } + + Console.WriteLine("Final:" + response.FinalAnswer); + + } + else + { + if (completionResult.Error == null) + { + throw new("Unknown Error"); + } + + Console.WriteLine($"{completionResult.Error.Code}: {completionResult.Error.Message}"); + } + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } + } + + public class MathResponse + { + public MathResponse() + { + Steps = new(); + } + + [JsonPropertyName("steps")] + public List Steps { get; set; } + + [JsonPropertyName("final_answer")] + public string FinalAnswer { get; set; } + } + + public class Step + { + [JsonPropertyName("explanation")] + public string Explanation { get; set; } + + [JsonPropertyName("output")] + public string Output { get; set; } + } +} \ No newline at end of file