Skip to content

Commit 105abde

Browse files
committed
Add prompt parameter support for stored prompts in Responses API
- Add ResponsePrompt class with Id, Version, and Variables properties - Add Prompt property to ResponseCreationOptions - Include proper JSON serialization/deserialization support - Add unit tests for serialization and deserialization - Add sync and async examples showing usage - Follows existing codebase patterns and conventions Resolves #500
1 parent 5f13e29 commit 105abde

File tree

6 files changed

+250
-1
lines changed

6 files changed

+250
-1
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using NUnit.Framework;
2+
using OpenAI.Responses;
3+
using System;
4+
5+
namespace OpenAI.Examples;
6+
7+
public partial class ResponseExamples
8+
{
9+
[Test]
10+
public void Example03_StoredPrompts()
11+
{
12+
OpenAIResponseClient client = new(model: "gpt-4o", apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY"));
13+
14+
// Create options using a stored prompt
15+
ResponseCreationOptions options = new()
16+
{
17+
Prompt = new ResponsePrompt
18+
{
19+
Id = "your-stored-prompt-id",
20+
Version = "v1.0"
21+
}
22+
};
23+
24+
// Add variables to substitute in the prompt template
25+
options.Prompt.Variables["location"] = "San Francisco";
26+
options.Prompt.Variables["unit"] = "celsius";
27+
28+
OpenAIResponse response = client.CreateResponse([], options);
29+
30+
Console.WriteLine($"[ASSISTANT]: {response.GetOutputText()}");
31+
}
32+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using NUnit.Framework;
2+
using OpenAI.Responses;
3+
using System;
4+
using System.Threading.Tasks;
5+
6+
namespace OpenAI.Examples;
7+
8+
public partial class ResponseExamples
9+
{
10+
[Test]
11+
public async Task Example03_StoredPromptsAsync()
12+
{
13+
OpenAIResponseClient client = new(model: "gpt-4o", apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY"));
14+
15+
// Create options using a stored prompt
16+
ResponseCreationOptions options = new()
17+
{
18+
Prompt = new ResponsePrompt
19+
{
20+
Id = "your-stored-prompt-id",
21+
Version = "v1.0"
22+
}
23+
};
24+
25+
// Add variables to substitute in the prompt template
26+
options.Prompt.Variables["location"] = "San Francisco";
27+
options.Prompt.Variables["unit"] = "celsius";
28+
29+
OpenAIResponse response = await client.CreateResponseAsync([], options);
30+
31+
Console.WriteLine($"[ASSISTANT]: {response.GetOutputText()}");
32+
}
33+
}

src/Custom/Responses/ResponseCreationOptions.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,19 @@ public partial class ResponseCreationOptions
3434
// CUSTOM: Made internal. This value comes from a parameter on the client method.
3535
internal bool? Stream { get; set; }
3636

37+
// CUSTOM: Added prompt parameter support for stored prompts.
38+
[CodeGenMember("Prompt")]
39+
public ResponsePrompt Prompt { get; set; }
40+
41+
// CUSTOM: Added public default constructor now that there are no required properties.
42+
public ResponseCreationOptions()
43+
{
44+
Input = new ChangeTrackingList<ResponseItem>();
45+
Metadata = new ChangeTrackingDictionary<string, string>();
46+
Tools = new ChangeTrackingList<ResponseTool>();
47+
Include = new ChangeTrackingList<InternalCreateResponsesRequestIncludable>();
48+
}
49+
3750
// CUSTOM: Renamed.
3851
[CodeGenMember("User")]
3952
public string EndUserId { get; set; }
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
using System;
2+
using System.ClientModel.Primitives;
3+
using System.Collections.Generic;
4+
using System.Text.Json;
5+
6+
namespace OpenAI.Responses;
7+
8+
public partial class ResponsePrompt : IJsonModel<ResponsePrompt>
9+
{
10+
void IJsonModel<ResponsePrompt>.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options)
11+
=> CustomSerializationHelpers.SerializeInstance(this, SerializeResponsePrompt, writer, options);
12+
13+
ResponsePrompt IJsonModel<ResponsePrompt>.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options)
14+
=> CustomSerializationHelpers.DeserializeNewInstance(this, DeserializeResponsePrompt, ref reader, options);
15+
16+
BinaryData IPersistableModel<ResponsePrompt>.Write(ModelReaderWriterOptions options)
17+
=> CustomSerializationHelpers.SerializeInstance(this, options);
18+
19+
ResponsePrompt IPersistableModel<ResponsePrompt>.Create(BinaryData data, ModelReaderWriterOptions options)
20+
=> CustomSerializationHelpers.DeserializeNewInstance(this, DeserializeResponsePrompt, data, options);
21+
22+
string IPersistableModel<ResponsePrompt>.GetFormatFromOptions(ModelReaderWriterOptions options) => "J";
23+
24+
internal static void SerializeResponsePrompt(ResponsePrompt instance, Utf8JsonWriter writer, ModelReaderWriterOptions options)
25+
{
26+
writer.WriteStartObject();
27+
28+
if (instance.Id != null)
29+
{
30+
writer.WritePropertyName("id");
31+
writer.WriteStringValue(instance.Id);
32+
}
33+
34+
if (instance.Version != null)
35+
{
36+
writer.WritePropertyName("version");
37+
writer.WriteStringValue(instance.Version);
38+
}
39+
40+
if (instance.Variables != null && instance.Variables.Count > 0)
41+
{
42+
writer.WritePropertyName("variables");
43+
writer.WriteStartObject();
44+
foreach (var variable in instance.Variables)
45+
{
46+
writer.WritePropertyName(variable.Key);
47+
JsonSerializer.Serialize(writer, variable.Value, options.Format == "W" ? null : (JsonSerializerOptions)null);
48+
}
49+
writer.WriteEndObject();
50+
}
51+
52+
writer.WriteEndObject();
53+
}
54+
55+
internal static ResponsePrompt DeserializeResponsePrompt(JsonElement element, ModelReaderWriterOptions options = null)
56+
{
57+
if (element.ValueKind == JsonValueKind.Null)
58+
{
59+
return null;
60+
}
61+
62+
string id = null;
63+
string version = null;
64+
Dictionary<string, object> variables = new Dictionary<string, object>();
65+
66+
foreach (var property in element.EnumerateObject())
67+
{
68+
if (property.NameEquals("id"))
69+
{
70+
id = property.Value.GetString();
71+
}
72+
else if (property.NameEquals("version"))
73+
{
74+
version = property.Value.GetString();
75+
}
76+
else if (property.NameEquals("variables"))
77+
{
78+
foreach (var variable in property.Value.EnumerateObject())
79+
{
80+
variables[variable.Name] = JsonSerializer.Deserialize<object>(variable.Value.GetRawText());
81+
}
82+
}
83+
}
84+
85+
var result = new ResponsePrompt();
86+
result.Id = id;
87+
result.Version = version;
88+
89+
if (variables.Count > 0)
90+
{
91+
foreach (var variable in variables)
92+
{
93+
result.Variables[variable.Key] = variable.Value;
94+
}
95+
}
96+
97+
return result;
98+
}
99+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System.Collections.Generic;
2+
3+
namespace OpenAI.Responses;
4+
5+
[CodeGenType("ResponsesPrompt")]
6+
public partial class ResponsePrompt
7+
{
8+
[CodeGenMember("Id")]
9+
public string Id { get; set; }
10+
11+
[CodeGenMember("Version")]
12+
public string Version { get; set; }
13+
14+
[CodeGenMember("Variables")]
15+
public IDictionary<string, object> Variables { get; }
16+
17+
public ResponsePrompt()
18+
{
19+
Variables = new Dictionary<string, object>();
20+
}
21+
}

tests/Responses/ResponsesTests.cs

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,7 @@ public async Task FileInputFromIdWorks()
530530
BinaryData.FromBytes(File.ReadAllBytes(filePath)),
531531
"test_favorite_foods.pdf",
532532
FileUploadPurpose.UserData);
533-
Validate(newFileToUse);
533+
Validate(newFileToUse);
534534

535535
ResponseItem messageItem = ResponseItem.CreateUserMessageItem(
536536
[
@@ -905,4 +905,55 @@ public async Task CanCancelBackgroundResponses()
905905
false);
906906

907907
private static OpenAIResponseClient GetTestClient(string overrideModel = null) => GetTestClient<OpenAIResponseClient>(TestScenario.Responses, overrideModel);
908+
909+
[Test]
910+
public void ResponsePromptSerializationWorks()
911+
{
912+
ResponsePrompt prompt = new ResponsePrompt
913+
{
914+
Id = "test-prompt-id",
915+
Version = "v1.0"
916+
};
917+
prompt.Variables["location"] = "San Francisco";
918+
prompt.Variables["unit"] = "celsius";
919+
920+
string json = BinaryData.FromObjectAsJson(prompt).ToString();
921+
JsonDocument document = JsonDocument.Parse(json);
922+
923+
Assert.IsTrue(document.RootElement.TryGetProperty("id", out JsonElement idElement));
924+
Assert.AreEqual("test-prompt-id", idElement.GetString());
925+
926+
Assert.IsTrue(document.RootElement.TryGetProperty("version", out JsonElement versionElement));
927+
Assert.AreEqual("v1.0", versionElement.GetString());
928+
929+
Assert.IsTrue(document.RootElement.TryGetProperty("variables", out JsonElement variablesElement));
930+
Assert.IsTrue(variablesElement.TryGetProperty("location", out JsonElement locationElement));
931+
Assert.AreEqual("San Francisco", locationElement.GetString());
932+
933+
Assert.IsTrue(variablesElement.TryGetProperty("unit", out JsonElement unitElement));
934+
Assert.AreEqual("celsius", unitElement.GetString());
935+
}
936+
937+
[Test]
938+
public void ResponsePromptDeserializationWorks()
939+
{
940+
string json = """
941+
{
942+
"id": "test-prompt-id",
943+
"version": "v1.0",
944+
"variables": {
945+
"location": "San Francisco",
946+
"unit": "celsius"
947+
}
948+
}
949+
""";
950+
951+
ResponsePrompt prompt = BinaryData.FromString(json).ToObjectFromJson<ResponsePrompt>();
952+
953+
Assert.AreEqual("test-prompt-id", prompt.Id);
954+
Assert.AreEqual("v1.0", prompt.Version);
955+
Assert.AreEqual(2, prompt.Variables.Count);
956+
Assert.AreEqual("San Francisco", prompt.Variables["location"].ToString());
957+
Assert.AreEqual("celsius", prompt.Variables["unit"].ToString());
958+
}
908959
}

0 commit comments

Comments
 (0)