Skip to content

Commit 6ad58ba

Browse files
committed
Stop suppressing existing format handling
1 parent e838066 commit 6ad58ba

File tree

2 files changed

+4
-65
lines changed

2 files changed

+4
-65
lines changed

src/Libraries/Microsoft.Extensions.AI.Abstractions/Utilities/AIJsonUtilities.Schema.Create.cs

Lines changed: 1 addition & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ public static partial class AIJsonUtilities
4141
private const string AdditionalPropertiesPropertyName = "additionalProperties";
4242
private const string DefaultPropertyName = "default";
4343
private const string RefPropertyName = "$ref";
44-
private const string FormatPropertyName = "format";
4544
#if NET
45+
private const string FormatPropertyName = "format";
4646
private const string ContentEncodingPropertyName = "contentEncoding";
4747
private const string ContentMediaTypePropertyName = "contentMediaType";
4848
private const string MinLengthStringPropertyName = "minLength";
@@ -58,10 +58,6 @@ public static partial class AIJsonUtilities
5858
/// <summary>The uri used when populating the $schema keyword in created schemas.</summary>
5959
private const string SchemaKeywordUri = "https://json-schema.org/draft/2020-12/schema";
6060

61-
// List of keywords used by JsonSchemaExporter but explicitly disallowed by some AI vendors.
62-
// cf. https://platform.openai.com/docs/guides/structured-outputs#some-type-specific-keywords-are-not-yet-supported
63-
private static readonly string[] _schemaKeywordsDisallowedByAIVendors = ["minLength", "maxLength", "pattern", "format"];
64-
6561
/// <summary>
6662
/// Determines a JSON schema for the provided method.
6763
/// </summary>
@@ -296,12 +292,6 @@ JsonNode TransformSchemaNode(JsonSchemaExporterContext schemaExporterContext, Js
296292
objSchema.InsertAtStart(TypePropertyName, new JsonArray { (JsonNode)"string", (JsonNode)"null" });
297293
}
298294

299-
// Filter potentially disallowed keywords.
300-
foreach (string keyword in _schemaKeywordsDisallowedByAIVendors)
301-
{
302-
_ = objSchema.Remove(keyword);
303-
}
304-
305295
// Some consumers of the JSON schema, including Ollama as of v0.3.13, don't understand
306296
// schemas with "type": [...], and only understand "type" being a single value.
307297
// In certain configurations STJ represents .NET numeric types as ["string", "number"], which will then lead to an error.
@@ -334,7 +324,6 @@ JsonNode TransformSchemaNode(JsonSchemaExporterContext schemaExporterContext, Js
334324
ConvertSchemaToObject(ref schema).InsertAtStart(SchemaPropertyName, (JsonNode)SchemaKeywordUri);
335325
}
336326

337-
ApplyDataTypeFormats(parameterName, ref schema, ctx);
338327
ApplyDataAnnotations(parameterName, ref schema, ctx);
339328

340329
// Finally, apply any user-defined transformations if specified.
@@ -365,43 +354,6 @@ static JsonObject ConvertSchemaToObject(ref JsonNode schema)
365354
}
366355
}
367356

368-
static void ApplyDataTypeFormats(string? parameterName, ref JsonNode schema, AIJsonSchemaCreateContext ctx)
369-
{
370-
Type t = ctx.TypeInfo.Type;
371-
372-
if (Nullable.GetUnderlyingType(t) is { } underlyingType)
373-
{
374-
t = underlyingType;
375-
}
376-
377-
if (t == typeof(DateTime) || t == typeof(DateTimeOffset))
378-
{
379-
ConvertSchemaToObject(ref schema)[FormatPropertyName] = "date-time";
380-
}
381-
#if NET
382-
else if (t == typeof(DateOnly))
383-
{
384-
ConvertSchemaToObject(ref schema)[FormatPropertyName] = "date";
385-
}
386-
else if (t == typeof(TimeOnly))
387-
{
388-
ConvertSchemaToObject(ref schema)[FormatPropertyName] = "time";
389-
}
390-
#endif
391-
else if (t == typeof(TimeSpan))
392-
{
393-
ConvertSchemaToObject(ref schema)[FormatPropertyName] = "duration";
394-
}
395-
else if (t == typeof(Guid))
396-
{
397-
ConvertSchemaToObject(ref schema)[FormatPropertyName] = "uuid";
398-
}
399-
else if (t == typeof(Uri))
400-
{
401-
ConvertSchemaToObject(ref schema)[FormatPropertyName] = "uri";
402-
}
403-
}
404-
405357
void ApplyDataAnnotations(string? parameterName, ref JsonNode schema, AIJsonSchemaCreateContext ctx)
406358
{
407359
if (ctx.GetCustomAttribute<DisplayNameAttribute>() is { } displayNameAttribute)
@@ -613,10 +565,6 @@ static JsonArray CreateJsonArray(object?[] values, JsonSerializerOptions seriali
613565
obj[FormatPropertyName] ??= "time";
614566
break;
615567

616-
case DataType.Duration:
617-
obj[FormatPropertyName] ??= "duration";
618-
break;
619-
620568
case DataType.EmailAddress:
621569
obj[FormatPropertyName] ??= "email";
622570
break;

test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Utilities/AIJsonUtilitiesTests.cs

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,7 @@ public static void CreateJsonSchema_ValidateWithTestData(ITestData testData)
405405
JsonElement schema = AIJsonUtilities.CreateJsonSchema(testData.Type, serializerOptions: options, inferenceOptions: createOptions);
406406
JsonNode? schemaAsNode = JsonSerializer.SerializeToNode(schema, options);
407407

408+
// NOTE: This is not validating the schemas match, only that they have the same top-level kind.
408409
Assert.NotNull(schemaAsNode);
409410
Assert.Equal(testData.ExpectedJsonSchema.GetValueKind(), schemaAsNode.GetValueKind());
410411

@@ -474,7 +475,7 @@ public static void CreateJsonSchema_IncorporatesTypesAndAnnotations()
474475
"TimeSpanProp": {
475476
"$comment": "Represents a System.TimeSpan value.",
476477
"type": "string",
477-
"format": "duration"
478+
"pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$"
478479
},
479480
"GuidProp": {
480481
"type": "string",
@@ -579,13 +580,6 @@ public static void CreateJsonSchema_IncorporatesTypesAndAnnotations()
579580
],
580581
"format": "time"
581582
},
582-
"DataTypeDurationProp": {
583-
"type": [
584-
"string",
585-
"null"
586-
],
587-
"format": "duration"
588-
},
589583
"DataTypeEmailProp": {
590584
"type": [
591585
"string",
@@ -703,7 +697,7 @@ public static void CreateJsonSchema_IncorporatesTypesAndAnnotations()
703697
"TimeSpanProp": {
704698
"$comment": "Represents a System.TimeSpan value.",
705699
"type": "string",
706-
"format": "duration"
700+
"pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$"
707701
},
708702
"GuidProp": {
709703
"type": "string",
@@ -776,9 +770,6 @@ private sealed class CreateJsonSchema_IncorporatesTypesAndAnnotations_Type
776770
[DataType(DataType.Time)]
777771
public string? DataTypeTimeProp { get; set; }
778772

779-
[DataType(DataType.Duration)]
780-
public string? DataTypeDurationProp { get; set; }
781-
782773
[DataType(DataType.EmailAddress)]
783774
public string? DataTypeEmailProp { get; set; }
784775

0 commit comments

Comments
 (0)