From 5215c12de40534b202aee9dbded3843b9bafbc9c Mon Sep 17 00:00:00 2001 From: Yair Sadan Date: Tue, 8 Jul 2025 16:41:06 +0300 Subject: [PATCH 1/3] Add ResponsePrompt and ResponseCreationOptions with serialization support - Implement ResponsePrompt class with properties for Id, Version, and Variables. - Add serialization and deserialization methods for ResponsePrompt. - Introduce ResponseCreationOptions class to support stored prompts. - Create example tests for serialization and deserialization of ResponsePrompt. - Add basic functionality tests for ResponsePrompt integration. --- TestResponsePrompt.cs | 58 +++++++ examples/Responses/Example03_StoredPrompts.cs | 35 ++++ .../Responses/Example03_StoredPromptsAsync.cs | 36 ++++ .../Responses/ResponseCreationOptions.cs | 13 ++ .../Responses/ResponsePrompt.Serialization.cs | 154 ++++++++++++++++++ src/Custom/Responses/ResponsePrompt.cs | 21 +++ temp-test/Program.cs | 33 ++++ tests/Responses/ResponsesTests.cs | 51 ++++++ 8 files changed, 401 insertions(+) create mode 100644 TestResponsePrompt.cs create mode 100644 examples/Responses/Example03_StoredPrompts.cs create mode 100644 examples/Responses/Example03_StoredPromptsAsync.cs create mode 100644 src/Custom/Responses/ResponsePrompt.Serialization.cs create mode 100644 src/Custom/Responses/ResponsePrompt.cs create mode 100644 temp-test/Program.cs diff --git a/TestResponsePrompt.cs b/TestResponsePrompt.cs new file mode 100644 index 00000000..5684f289 --- /dev/null +++ b/TestResponsePrompt.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using OpenAI.Responses; + +class TestResponsePrompt +{ + static void Main(string[] args) + { + Console.WriteLine("Testing ResponsePrompt implementation..."); + + // Test 1: Basic serialization + var prompt = new ResponsePrompt("test-prompt-id", "v1.0"); + var json = JsonSerializer.Serialize(prompt); + Console.WriteLine($"Serialized ResponsePrompt: {json}"); + + // Test 2: With variables + var variables = new Dictionary + { + ["name"] = "John", + ["age"] = 30, + ["isActive"] = true + }; + + var promptWithVars = new ResponsePrompt("test-prompt-id", "v1.0", variables); + var jsonWithVars = JsonSerializer.Serialize(promptWithVars); + Console.WriteLine($"Serialized ResponsePrompt with variables: {jsonWithVars}"); + + // Test 3: Deserialization + var deserializedPrompt = JsonSerializer.Deserialize(jsonWithVars); + Console.WriteLine($"Deserialized - ID: {deserializedPrompt.Id}, Version: {deserializedPrompt.Version}"); + Console.WriteLine($"Variables count: {deserializedPrompt.Variables?.Count ?? 0}"); + + if (deserializedPrompt.Variables != null) + { + foreach (var kvp in deserializedPrompt.Variables) + { + Console.WriteLine($" {kvp.Key}: {kvp.Value} (Type: {kvp.Value?.GetType().Name ?? "null"})"); + } + } + + // Test 4: ResponseCreationOptions with prompt + var options = new ResponseCreationOptions + { + Model = "gpt-4", + Prompt = promptWithVars + }; + + var optionsJson = JsonSerializer.Serialize(options); + Console.WriteLine($"Serialized ResponseCreationOptions: {optionsJson}"); + + var deserializedOptions = JsonSerializer.Deserialize(optionsJson); + Console.WriteLine($"Deserialized options - Model: {deserializedOptions.Model}"); + Console.WriteLine($"Prompt ID: {deserializedOptions.Prompt?.Id}"); + + Console.WriteLine("All tests completed successfully!"); + } +} diff --git a/examples/Responses/Example03_StoredPrompts.cs b/examples/Responses/Example03_StoredPrompts.cs new file mode 100644 index 00000000..5927541a --- /dev/null +++ b/examples/Responses/Example03_StoredPrompts.cs @@ -0,0 +1,35 @@ +using NUnit.Framework; +using OpenAI.Responses; +using System; +using System.Collections.Generic; + +namespace OpenAI.Examples; + +public partial class ResponseExamples +{ + [Test] + public void Example03_StoredPrompts() + { + OpenAIResponseClient client = new(model: "gpt-4o", apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")); + + // Create options using a stored prompt + ResponseCreationOptions options = new() + { + Prompt = new ResponsePrompt + { + Id = "your-stored-prompt-id", + Version = "v1.0" + } + }; + + // Add variables to substitute in the prompt template + options.Prompt.Variables["location"] = "San Francisco"; + options.Prompt.Variables["unit"] = "celsius"; + + // Use stored prompt with variables + IEnumerable inputItems = Array.Empty(); + OpenAIResponse response = client.CreateResponse(inputItems, options); + + Console.WriteLine($"[ASSISTANT]: {response.GetOutputText()}"); + } +} diff --git a/examples/Responses/Example03_StoredPromptsAsync.cs b/examples/Responses/Example03_StoredPromptsAsync.cs new file mode 100644 index 00000000..a8196678 --- /dev/null +++ b/examples/Responses/Example03_StoredPromptsAsync.cs @@ -0,0 +1,36 @@ +using NUnit.Framework; +using OpenAI.Responses; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace OpenAI.Examples; + +public partial class ResponseExamples +{ + [Test] + public async Task Example03_StoredPromptsAsync() + { + OpenAIResponseClient client = new(model: "gpt-4o", apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")); + + // Create options using a stored prompt + ResponseCreationOptions options = new() + { + Prompt = new ResponsePrompt + { + Id = "your-stored-prompt-id", + Version = "v1.0" + } + }; + + // Add variables to substitute in the prompt template + options.Prompt.Variables["location"] = "San Francisco"; + options.Prompt.Variables["unit"] = "celsius"; + + // Use stored prompt with variables + IEnumerable inputItems = Array.Empty(); + OpenAIResponse response = await client.CreateResponseAsync(inputItems, options); + + Console.WriteLine($"[ASSISTANT]: {response.GetOutputText()}"); + } +} diff --git a/src/Custom/Responses/ResponseCreationOptions.cs b/src/Custom/Responses/ResponseCreationOptions.cs index 082ff383..b0344bd5 100644 --- a/src/Custom/Responses/ResponseCreationOptions.cs +++ b/src/Custom/Responses/ResponseCreationOptions.cs @@ -34,6 +34,19 @@ public partial class ResponseCreationOptions // CUSTOM: Made internal. This value comes from a parameter on the client method. internal bool? Stream { get; set; } + // CUSTOM: Added prompt parameter support for stored prompts. + [CodeGenMember("Prompt")] + public ResponsePrompt Prompt { get; set; } + + // CUSTOM: Added public default constructor now that there are no required properties. + public ResponseCreationOptions() + { + Input = new ChangeTrackingList(); + Metadata = new ChangeTrackingDictionary(); + Tools = new ChangeTrackingList(); + Include = new ChangeTrackingList(); + } + // CUSTOM: Renamed. [CodeGenMember("User")] public string EndUserId { get; set; } diff --git a/src/Custom/Responses/ResponsePrompt.Serialization.cs b/src/Custom/Responses/ResponsePrompt.Serialization.cs new file mode 100644 index 00000000..24976c3e --- /dev/null +++ b/src/Custom/Responses/ResponsePrompt.Serialization.cs @@ -0,0 +1,154 @@ +using System; +using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.Text.Json; + +namespace OpenAI.Responses; + +public partial class ResponsePrompt : IJsonModel +{ + void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) + => CustomSerializationHelpers.SerializeInstance(this, SerializeResponsePrompt, writer, options); + + ResponsePrompt IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + => CustomSerializationHelpers.DeserializeNewInstance(this, DeserializeResponsePrompt, ref reader, options); + + BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) + => CustomSerializationHelpers.SerializeInstance(this, options); + + ResponsePrompt IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) + => CustomSerializationHelpers.DeserializeNewInstance(this, DeserializeResponsePrompt, data, options); + + string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) => "J"; + + internal static void SerializeResponsePrompt(ResponsePrompt instance, Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + writer.WriteStartObject(); + + if (instance.Id != null) + { + writer.WritePropertyName("id"); + writer.WriteStringValue(instance.Id); + } + + if (instance.Version != null) + { + writer.WritePropertyName("version"); + writer.WriteStringValue(instance.Version); + } + + if (instance.Variables != null && instance.Variables.Count > 0) + { + writer.WritePropertyName("variables"); + writer.WriteStartObject(); + foreach (var variable in instance.Variables) + { + writer.WritePropertyName(variable.Key); + if (variable.Value is JsonElement element) + { +#if NET6_0_OR_GREATER + writer.WriteRawValue(element.GetRawText()); +#else + using JsonDocument document = JsonDocument.Parse(element.GetRawText()); + JsonSerializer.Serialize(writer, document.RootElement); +#endif + } + else if (variable.Value != null) + { + // Handle primitive types directly + switch (variable.Value) + { + case string str: + writer.WriteStringValue(str); + break; + case int intVal: + writer.WriteNumberValue(intVal); + break; + case long longVal: + writer.WriteNumberValue(longVal); + break; + case float floatVal: + writer.WriteNumberValue(floatVal); + break; + case double doubleVal: + writer.WriteNumberValue(doubleVal); + break; + case decimal decimalVal: + writer.WriteNumberValue(decimalVal); + break; + case bool boolVal: + writer.WriteBooleanValue(boolVal); + break; + default: + // For other types, write as string value + writer.WriteStringValue(variable.Value.ToString()); + break; + } + } + else + { + writer.WriteNullValue(); + } + } + writer.WriteEndObject(); + } + + writer.WriteEndObject(); + } + + internal static ResponsePrompt DeserializeResponsePrompt(JsonElement element, ModelReaderWriterOptions options = null) + { + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + + string id = null; + string version = null; + Dictionary variables = new Dictionary(); + + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("id")) + { + id = property.Value.GetString(); + } + else if (property.NameEquals("version")) + { + version = property.Value.GetString(); + } + else if (property.NameEquals("variables")) + { + foreach (var variable in property.Value.EnumerateObject()) + { + // Handle different JSON value types appropriately + object value = variable.Value.ValueKind switch + { + JsonValueKind.String => variable.Value.GetString(), + JsonValueKind.Number => variable.Value.TryGetInt32(out int intVal) ? intVal : variable.Value.GetDouble(), + JsonValueKind.True => true, + JsonValueKind.False => false, + JsonValueKind.Null => null, + _ => variable.Value.GetRawText() // For objects/arrays, store as raw JSON string + }; + variables[variable.Name] = value; + } + } + } + + var result = new ResponsePrompt(); + result.Id = id; + result.Version = version; + + // Add variables to the existing dictionary (Variables property is get-only) + if (variables.Count > 0) + { + foreach (var variable in variables) + { + result.Variables[variable.Key] = variable.Value; + } + } + + return result; + } +} diff --git a/src/Custom/Responses/ResponsePrompt.cs b/src/Custom/Responses/ResponsePrompt.cs new file mode 100644 index 00000000..f27e26a3 --- /dev/null +++ b/src/Custom/Responses/ResponsePrompt.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; + +namespace OpenAI.Responses; + +[CodeGenType("ResponsesPrompt")] +public partial class ResponsePrompt +{ + [CodeGenMember("Id")] + public string Id { get; set; } + + [CodeGenMember("Version")] + public string Version { get; set; } + + [CodeGenMember("Variables")] + public IDictionary Variables { get; } + + public ResponsePrompt() + { + Variables = new Dictionary(); + } +} diff --git a/temp-test/Program.cs b/temp-test/Program.cs new file mode 100644 index 00000000..d38d7c17 --- /dev/null +++ b/temp-test/Program.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using OpenAI.Responses; + +Console.WriteLine("Testing ResponsePrompt implementation..."); + +// Test 1: Basic prompt without variables +var basicPrompt = new ResponsePrompt() +{ + Id = "test-prompt-id", + Version = "v1.0" +}; + +var basicJson = JsonSerializer.Serialize(basicPrompt); +Console.WriteLine($"Basic prompt JSON: {basicJson}"); + +var basicDeserialized = JsonSerializer.Deserialize(basicJson); +Console.WriteLine($"Basic deserialized - ID: {basicDeserialized?.Id}, Version: {basicDeserialized?.Version}"); + +// Test 2: ResponseCreationOptions integration +var options = new ResponseCreationOptions +{ + Prompt = basicPrompt +}; + +var optionsJson = JsonSerializer.Serialize(options); +Console.WriteLine($"Options JSON contains prompt: {optionsJson.Contains("test-prompt-id")}"); + +var deserializedOptions = JsonSerializer.Deserialize(optionsJson); +Console.WriteLine($"Options deserialized prompt ID: {deserializedOptions?.Prompt?.Id}"); + +Console.WriteLine("Basic functionality test completed!"); diff --git a/tests/Responses/ResponsesTests.cs b/tests/Responses/ResponsesTests.cs index 95ac6f28..5c0baa8e 100644 --- a/tests/Responses/ResponsesTests.cs +++ b/tests/Responses/ResponsesTests.cs @@ -905,4 +905,55 @@ public async Task CanCancelBackgroundResponses() false); private static OpenAIResponseClient GetTestClient(string overrideModel = null) => GetTestClient(TestScenario.Responses, overrideModel); + + [Test] + public void ResponsePromptSerializationWorks() + { + ResponsePrompt prompt = new ResponsePrompt + { + Id = "test-prompt-id", + Version = "v1.0" + }; + prompt.Variables["location"] = "San Francisco"; + prompt.Variables["unit"] = "celsius"; + + string json = BinaryData.FromObjectAsJson(prompt).ToString(); + JsonDocument document = JsonDocument.Parse(json); + + Assert.IsTrue(document.RootElement.TryGetProperty("id", out JsonElement idElement)); + Assert.AreEqual("test-prompt-id", idElement.GetString()); + + Assert.IsTrue(document.RootElement.TryGetProperty("version", out JsonElement versionElement)); + Assert.AreEqual("v1.0", versionElement.GetString()); + + Assert.IsTrue(document.RootElement.TryGetProperty("variables", out JsonElement variablesElement)); + Assert.IsTrue(variablesElement.TryGetProperty("location", out JsonElement locationElement)); + Assert.AreEqual("San Francisco", locationElement.GetString()); + + Assert.IsTrue(variablesElement.TryGetProperty("unit", out JsonElement unitElement)); + Assert.AreEqual("celsius", unitElement.GetString()); + } + + [Test] + public void ResponsePromptDeserializationWorks() + { + string json = """ + { + "id": "test-prompt-id", + "version": "v1.0", + "variables": { + "location": "San Francisco", + "unit": "celsius" + } + } + """; + + ResponsePrompt prompt = BinaryData.FromString(json).ToObjectFromJson(); + + Assert.AreEqual("test-prompt-id", prompt.Id); + Assert.AreEqual("v1.0", prompt.Version); + Assert.AreEqual(2, prompt.Variables.Count); + Assert.AreEqual("San Francisco", prompt.Variables["location"].ToString()); + Assert.AreEqual("celsius", prompt.Variables["unit"].ToString()); + } } \ No newline at end of file From 720cf9a3f5a79a556659ba67a71e9bf0a3ab6da3 Mon Sep 17 00:00:00 2001 From: Yair Sadan Date: Tue, 8 Jul 2025 16:42:25 +0300 Subject: [PATCH 2/3] Delete temp-test/Program.cs --- temp-test/Program.cs | 33 --------------------------------- 1 file changed, 33 deletions(-) delete mode 100644 temp-test/Program.cs diff --git a/temp-test/Program.cs b/temp-test/Program.cs deleted file mode 100644 index d38d7c17..00000000 --- a/temp-test/Program.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text.Json; -using OpenAI.Responses; - -Console.WriteLine("Testing ResponsePrompt implementation..."); - -// Test 1: Basic prompt without variables -var basicPrompt = new ResponsePrompt() -{ - Id = "test-prompt-id", - Version = "v1.0" -}; - -var basicJson = JsonSerializer.Serialize(basicPrompt); -Console.WriteLine($"Basic prompt JSON: {basicJson}"); - -var basicDeserialized = JsonSerializer.Deserialize(basicJson); -Console.WriteLine($"Basic deserialized - ID: {basicDeserialized?.Id}, Version: {basicDeserialized?.Version}"); - -// Test 2: ResponseCreationOptions integration -var options = new ResponseCreationOptions -{ - Prompt = basicPrompt -}; - -var optionsJson = JsonSerializer.Serialize(options); -Console.WriteLine($"Options JSON contains prompt: {optionsJson.Contains("test-prompt-id")}"); - -var deserializedOptions = JsonSerializer.Deserialize(optionsJson); -Console.WriteLine($"Options deserialized prompt ID: {deserializedOptions?.Prompt?.Id}"); - -Console.WriteLine("Basic functionality test completed!"); From 0b504c9ba202c4269b16013d48837b7612fd1e4d Mon Sep 17 00:00:00 2001 From: Yair Sadan Date: Tue, 8 Jul 2025 16:43:24 +0300 Subject: [PATCH 3/3] Delete TestResponsePrompt.cs --- TestResponsePrompt.cs | 58 ------------------------------------------- 1 file changed, 58 deletions(-) delete mode 100644 TestResponsePrompt.cs diff --git a/TestResponsePrompt.cs b/TestResponsePrompt.cs deleted file mode 100644 index 5684f289..00000000 --- a/TestResponsePrompt.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text.Json; -using OpenAI.Responses; - -class TestResponsePrompt -{ - static void Main(string[] args) - { - Console.WriteLine("Testing ResponsePrompt implementation..."); - - // Test 1: Basic serialization - var prompt = new ResponsePrompt("test-prompt-id", "v1.0"); - var json = JsonSerializer.Serialize(prompt); - Console.WriteLine($"Serialized ResponsePrompt: {json}"); - - // Test 2: With variables - var variables = new Dictionary - { - ["name"] = "John", - ["age"] = 30, - ["isActive"] = true - }; - - var promptWithVars = new ResponsePrompt("test-prompt-id", "v1.0", variables); - var jsonWithVars = JsonSerializer.Serialize(promptWithVars); - Console.WriteLine($"Serialized ResponsePrompt with variables: {jsonWithVars}"); - - // Test 3: Deserialization - var deserializedPrompt = JsonSerializer.Deserialize(jsonWithVars); - Console.WriteLine($"Deserialized - ID: {deserializedPrompt.Id}, Version: {deserializedPrompt.Version}"); - Console.WriteLine($"Variables count: {deserializedPrompt.Variables?.Count ?? 0}"); - - if (deserializedPrompt.Variables != null) - { - foreach (var kvp in deserializedPrompt.Variables) - { - Console.WriteLine($" {kvp.Key}: {kvp.Value} (Type: {kvp.Value?.GetType().Name ?? "null"})"); - } - } - - // Test 4: ResponseCreationOptions with prompt - var options = new ResponseCreationOptions - { - Model = "gpt-4", - Prompt = promptWithVars - }; - - var optionsJson = JsonSerializer.Serialize(options); - Console.WriteLine($"Serialized ResponseCreationOptions: {optionsJson}"); - - var deserializedOptions = JsonSerializer.Deserialize(optionsJson); - Console.WriteLine($"Deserialized options - Model: {deserializedOptions.Model}"); - Console.WriteLine($"Prompt ID: {deserializedOptions.Prompt?.Id}"); - - Console.WriteLine("All tests completed successfully!"); - } -}