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/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