Skip to content

Commit ccb7715

Browse files
committed
feat: add support for dynamic enum values
1 parent d954eae commit ccb7715

File tree

5 files changed

+138
-1
lines changed

5 files changed

+138
-1
lines changed

OpenAi.JsonSchema.Tests/FluentSchemaBuilderTests.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,44 @@ public void Test_array_anyof()
211211
Assert.NotNull(json);
212212
Helper.Assert(json);
213213
}
214+
215+
216+
[Fact]
217+
public void Test_dynamic_enum()
218+
{
219+
var generator = new DefaultSchemaGenerator();
220+
var options = new JsonSchemaOptions(SchemaDefaults.OpenAi, Helper.JsonOptionsSnakeCase);
221+
222+
var schema = generator.Build(options, _ => _
223+
.Object(_ => _
224+
.Property("answer", _ => _
225+
.Array(_ => _
226+
.AnyOf(
227+
_ => _.Object("Write markdown text.", _ => _
228+
.Property("$type", _ => _.Const("text"))
229+
.Property("content", _ => _.Value<string>())
230+
),
231+
_ => _.Object("Show a visually appearing widget.", _ => _
232+
.Property("$type", _ => _.Const("widget"))
233+
.Property("id", "Value form widgetId in context.", _ => _.Enum(["widget1", "widget2", "widget3"]))
234+
.Property("comment", "A user-facing `comment`", _ => _.Value<string>())
235+
),
236+
_ => _.Object("Add a reference to your statement.", _ => _
237+
.Property("$type", _ => _.Const("citation"))
238+
.Property("title", _ => _.Value<string>())
239+
.Property("url", "URL from context.", _ => _.Value<string>())
240+
)
241+
)
242+
)
243+
)
244+
)
245+
);
246+
247+
var json = schema.ToJson();
248+
output.WriteLine(json);
249+
Assert.NotNull(json);
250+
Helper.Assert(json);
251+
}
214252
}
215253

216254
public record FluentDocument(
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
{
2+
"type": "object",
3+
"properties": {
4+
"answer": {
5+
"type": "array",
6+
"items": {
7+
"anyOf": [
8+
{
9+
"type": "object",
10+
"description": "Write markdown text.",
11+
"properties": {
12+
"$type": {
13+
"type": "string",
14+
"const": "text"
15+
},
16+
"content": {
17+
"type": "string"
18+
}
19+
},
20+
"required": [
21+
"$type",
22+
"content"
23+
],
24+
"additionalProperties": false
25+
},
26+
{
27+
"type": "object",
28+
"description": "Show a visually appearing widget.",
29+
"properties": {
30+
"$type": {
31+
"type": "string",
32+
"const": "widget"
33+
},
34+
"id": {
35+
"type": "string",
36+
"description": "Value form widgetId in context.",
37+
"enum": [
38+
"widget1",
39+
"widget2",
40+
"widget3"
41+
]
42+
},
43+
"comment": {
44+
"type": "string",
45+
"description": "A user-facing \u0060comment\u0060"
46+
}
47+
},
48+
"required": [
49+
"$type",
50+
"id",
51+
"comment"
52+
],
53+
"additionalProperties": false
54+
},
55+
{
56+
"type": "object",
57+
"description": "Add a reference to your statement.",
58+
"properties": {
59+
"$type": {
60+
"type": "string",
61+
"const": "citation"
62+
},
63+
"title": {
64+
"type": "string"
65+
},
66+
"url": {
67+
"type": "string",
68+
"description": "URL from context."
69+
}
70+
},
71+
"required": [
72+
"$type",
73+
"title",
74+
"url"
75+
],
76+
"additionalProperties": false
77+
}
78+
]
79+
}
80+
}
81+
},
82+
"required": [
83+
"answer"
84+
],
85+
"additionalProperties": false
86+
}

OpenAi.JsonSchema/Fluent/FluentSchemaBuilder.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using OpenAi.JsonSchema.Generator.Abstractions;
22
using OpenAi.JsonSchema.Nodes;
3+
using System.Text.Json;
34

45

56
namespace OpenAi.JsonSchema.Fluent;
@@ -45,6 +46,11 @@ public SchemaNode Value(Type type)
4546
return Schema(Resolve(type));
4647
}
4748

49+
public SchemaNode Enum<T>(T[] values, bool nullable = false)
50+
{
51+
return SchemaEnumNode.Create(values, nullable, context.Options.JsonSerializerOptions);
52+
}
53+
4854
public SchemaNode Object(Action<IFluentObjectSchemaBuilder> properties)
4955
{
5056
var type = Resolve(typeof(object));

OpenAi.JsonSchema/Fluent/IFluentSchemaBuilder.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ public interface IFluentSchemaBuilder {
88
SchemaNode Value<T>();
99
SchemaNode Value(Type type);
1010

11+
public SchemaNode Enum<T>(T[] values, bool nullable = false);
12+
1113
SchemaNode Object(string description, Action<IFluentObjectSchemaBuilder> properties);
1214
SchemaNode Object(Action<IFluentObjectSchemaBuilder> properties);
1315

OpenAi.JsonSchema/Nodes/SchemaEnumNode.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,15 @@ public record SchemaEnumNode(string Type, string[] Values, bool Nullable) : Sche
1313
public static SchemaEnumNode Create(Type @enum, bool nullable, JsonSerializerOptions options)
1414
{
1515
var values = Enum.GetValues(@enum).Cast<object>().ToArray();
16+
return Create(values, nullable, options);
17+
}
18+
1619

20+
public static SchemaEnumNode Create<T>(T[] values, bool nullable, JsonSerializerOptions options)
21+
{
1722
var jsonValues = values.Select(value => JsonSerializer.Serialize(value, options)).ToArray();
1823

19-
var kind = jsonValues.Select(_ => JsonValue.Parse(_)!.GetValueKind()).FirstOrDefault();
24+
var kind = jsonValues.Select(_ => JsonNode.Parse(_)!.GetValueKind()).FirstOrDefault();
2025
var type = kind switch {
2126
JsonValueKind.String => "string",
2227
JsonValueKind.Number => "integer",

0 commit comments

Comments
 (0)