55using  System . ComponentModel ; 
66using  System . Diagnostics ; 
77using  System . Diagnostics . CodeAnalysis ; 
8- using  System . Linq ; 
98using  System . Reflection ; 
109using  System . Runtime . CompilerServices ; 
1110using  System . Text . Json ; 
@@ -40,7 +39,7 @@ public static partial class AIJsonUtilities
4039    private  const  string  DefaultPropertyName  =  "default" ; 
4140    private  const  string  RefPropertyName  =  "$ref" ; 
4241
43-     /// <summary>The uri used when populating the $schema keyword in inferred  schemas.</summary> 
42+     /// <summary>The uri used when populating the $schema keyword in created  schemas.</summary> 
4443    private  const  string  SchemaKeywordUri  =  "https://json-schema.org/draft/2020-12/schema" ; 
4544
4645    // List of keywords used by JsonSchemaExporter but explicitly disallowed by some AI vendors. 
@@ -54,7 +53,7 @@ public static partial class AIJsonUtilities
5453    /// <param name="title">The title keyword used by the method schema.</param> 
5554    /// <param name="description">The description keyword used by the method schema.</param> 
5655    /// <param name="serializerOptions">The options used to extract the schema from the specified type.</param> 
57-     /// <param name="inferenceOptions">The options controlling schema inference .</param> 
56+     /// <param name="inferenceOptions">The options controlling schema creation .</param> 
5857    /// <returns>A JSON schema document encoded as a <see cref="JsonElement"/>.</returns> 
5958    /// <exception cref="ArgumentNullException"><paramref name="method"/> is <see langword="null"/>.</exception> 
6059    public  static   JsonElement  CreateFunctionJsonSchema ( 
@@ -106,13 +105,13 @@ public static JsonElement CreateFunctionJsonSchema(
106105                inferenceOptions ) ; 
107106
108107            parameterSchemas . Add ( parameter . Name ,  parameterSchema ) ; 
109-             if  ( ! parameter . IsOptional   ||   inferenceOptions . RequireAllProperties ) 
108+             if  ( ! parameter . IsOptional ) 
110109            { 
111110                ( requiredProperties  ??=  [ ] ) . Add ( ( JsonNode ) parameter . Name ) ; 
112111            } 
113112        } 
114113
115-         JsonObject  schema  =  new ( ) ; 
114+         JsonNode  schema  =  new   JsonObject ( ) ; 
116115        if  ( inferenceOptions . IncludeSchemaKeyword ) 
117116        { 
118117            schema [ SchemaPropertyName ]  =  SchemaKeywordUri ; 
@@ -136,7 +135,13 @@ public static JsonElement CreateFunctionJsonSchema(
136135            schema [ RequiredPropertyName ]  =  requiredProperties ; 
137136        } 
138137
139-         return  JsonSerializer . SerializeToElement ( schema ,  JsonContext . Default . JsonNode ) ; 
138+         // Finally, apply any schema transformations if specified. 
139+         if  ( inferenceOptions . TransformOptions  is  {  }  options ) 
140+         { 
141+             schema  =  TransformSchema ( schema ,  options ) ; 
142+         } 
143+ 
144+         return  JsonSerializer . SerializeToElement ( schema ,  JsonContextNoIndentation . Default . JsonNode ) ; 
140145    } 
141146
142147    /// <summary>Creates a JSON schema for the specified type.</summary> 
@@ -145,7 +150,7 @@ public static JsonElement CreateFunctionJsonSchema(
145150    /// <param name="hasDefaultValue"><see langword="true"/> if the parameter is optional; otherwise, <see langword="false"/>.</param> 
146151    /// <param name="defaultValue">The default value of the optional parameter, if applicable.</param> 
147152    /// <param name="serializerOptions">The options used to extract the schema from the specified type.</param> 
148-     /// <param name="inferenceOptions">The options controlling schema inference .</param> 
153+     /// <param name="inferenceOptions">The options controlling schema creation .</param> 
149154    /// <returns>A <see cref="JsonElement"/> representing the schema.</returns> 
150155    public  static   JsonElement  CreateJsonSchema ( 
151156        Type ?  type , 
@@ -158,7 +163,14 @@ public static JsonElement CreateJsonSchema(
158163        serializerOptions  ??=  DefaultOptions ; 
159164        inferenceOptions  ??=  AIJsonSchemaCreateOptions . Default ; 
160165        JsonNode  schema  =  CreateJsonSchemaCore ( type ,  parameterName :  null ,  description ,  hasDefaultValue ,  defaultValue ,  serializerOptions ,  inferenceOptions ) ; 
161-         return  JsonSerializer . SerializeToElement ( schema ,  JsonContext . Default . JsonNode ) ; 
166+ 
167+         // Finally, apply any schema transformations if specified. 
168+         if  ( inferenceOptions . TransformOptions  is  {  }  options ) 
169+         { 
170+             schema  =  TransformSchema ( schema ,  options ) ; 
171+         } 
172+ 
173+         return  JsonSerializer . SerializeToElement ( schema ,  JsonContextNoIndentation . Default . JsonNode ) ; 
162174    } 
163175
164176    /// <summary>Gets the default JSON schema to be used by types or functions.</summary> 
@@ -203,25 +215,11 @@ private static JsonNode CreateJsonSchemaCore(
203215
204216            if  ( hasDefaultValue ) 
205217            { 
206-                 if  ( inferenceOptions . RequireAllProperties ) 
207-                 { 
208-                     // Default values are only used in the context of optional parameters. 
209-                     // Do not include a default keyword (since certain AI vendors don't support it) 
210-                     // and instead embed its JSON in the description as a hint to the LLM. 
211-                     string  defaultValueJson  =  defaultValue  is  not null 
212-                         ?  JsonSerializer . Serialize ( defaultValue ,  serializerOptions . GetTypeInfo ( defaultValue . GetType ( ) ) ) 
213-                         :  "null" ; 
214- 
215-                     description  =  CreateDescriptionWithDefaultValue ( description ,  defaultValueJson ) ; 
216-                 } 
217-                 else 
218-                 { 
219-                     JsonNode ?  defaultValueNode  =  defaultValue  is  not null 
220-                         ?  JsonSerializer . SerializeToNode ( defaultValue ,  serializerOptions . GetTypeInfo ( defaultValue . GetType ( ) ) ) 
221-                         :  null ; 
218+                 JsonNode ?  defaultValueNode  =  defaultValue  is  not null 
219+                     ?  JsonSerializer . SerializeToNode ( defaultValue ,  serializerOptions . GetTypeInfo ( defaultValue . GetType ( ) ) ) 
220+                     :  null ; 
222221
223-                     ( schemaObj  ??=  [ ] ) [ DefaultPropertyName ]  =  defaultValueNode ; 
224-                 } 
222+                 ( schemaObj  ??=  [ ] ) [ DefaultPropertyName ]  =  defaultValueNode ; 
225223            } 
226224
227225            if  ( description  is  not null ) 
@@ -271,41 +269,11 @@ JsonNode TransformSchemaNode(JsonSchemaExporterContext schemaExporterContext, Js
271269                } 
272270
273271                // Include the type keyword in enum types 
274-                 if  ( inferenceOptions . IncludeTypeInEnumSchemas   &&   ctx . TypeInfo . Type . IsEnum  &&  objSchema . ContainsKey ( EnumPropertyName )  &&  ! objSchema . ContainsKey ( TypePropertyName ) ) 
272+                 if  ( ctx . TypeInfo . Type . IsEnum  &&  objSchema . ContainsKey ( EnumPropertyName )  &&  ! objSchema . ContainsKey ( TypePropertyName ) ) 
275273                { 
276274                    objSchema . InsertAtStart ( TypePropertyName ,  "string" ) ; 
277275                } 
278276
279-                 // Disallow additional properties in object schemas 
280-                 if  ( inferenceOptions . DisallowAdditionalProperties  && 
281-                     objSchema . ContainsKey ( PropertiesPropertyName )  && 
282-                     ! objSchema . ContainsKey ( AdditionalPropertiesPropertyName ) ) 
283-                 { 
284-                     objSchema . Add ( AdditionalPropertiesPropertyName ,  ( JsonNode ) false ) ; 
285-                 } 
286- 
287-                 // Mark all properties as required 
288-                 if  ( inferenceOptions . RequireAllProperties  && 
289-                     objSchema . TryGetPropertyValue ( PropertiesPropertyName ,  out  JsonNode ?  properties )  && 
290-                     properties  is  JsonObject  propertiesObj ) 
291-                 { 
292-                     _  =  objSchema . TryGetPropertyValue ( RequiredPropertyName ,  out  JsonNode ?  required ) ; 
293-                     if  ( required  is  not JsonArray  {  }  requiredArray  ||  requiredArray . Count  !=  propertiesObj . Count ) 
294-                     { 
295-                         requiredArray  =  [ .. propertiesObj . Select ( prop =>  ( JsonNode ) prop . Key ) ] ; 
296-                         objSchema [ RequiredPropertyName ]  =  requiredArray ; 
297-                     } 
298-                 } 
299- 
300-                 // Strip default keywords and embed in description where required 
301-                 if  ( inferenceOptions . RequireAllProperties  && 
302-                     objSchema . TryGetPropertyValue ( DefaultPropertyName ,  out  JsonNode ?  defaultValue ) ) 
303-                 { 
304-                     _  =  objSchema . Remove ( DefaultPropertyName ) ; 
305-                     string  defaultValueJson  =  defaultValue ? . ToJsonString ( )  ??  "null" ; 
306-                     localDescription  =  CreateDescriptionWithDefaultValue ( localDescription ,  defaultValueJson ) ; 
307-                 } 
308- 
309277                // Filter potentially disallowed keywords. 
310278                foreach  ( string  keyword  in  _schemaKeywordsDisallowedByAIVendors ) 
311279                { 
@@ -328,20 +296,8 @@ JsonNode TransformSchemaNode(JsonSchemaExporterContext schemaExporterContext, Js
328296
329297            if  ( ctx . Path . IsEmpty  &&  hasDefaultValue ) 
330298            { 
331-                 // Add root-level default value metadata 
332-                 if  ( inferenceOptions . RequireAllProperties ) 
333-                 { 
334-                     // Default values are only used in the context of optional parameters. 
335-                     // Do not include a default keyword (since certain AI vendors don't support it) 
336-                     // and instead embed its JSON in the description as a hint to the LLM. 
337-                     string  defaultValueJson  =  JsonSerializer . Serialize ( defaultValue ,  ctx . TypeInfo ) ; 
338-                     localDescription  =  CreateDescriptionWithDefaultValue ( localDescription ,  defaultValueJson ) ; 
339-                 } 
340-                 else 
341-                 { 
342-                     JsonNode ?  defaultValueNode  =  JsonSerializer . SerializeToNode ( defaultValue ,  ctx . TypeInfo ) ; 
343-                     ConvertSchemaToObject ( ref  schema ) [ DefaultPropertyName ]  =  defaultValueNode ; 
344-                 } 
299+                 JsonNode ?  defaultValueNode  =  JsonSerializer . SerializeToNode ( defaultValue ,  ctx . TypeInfo ) ; 
300+                 ConvertSchemaToObject ( ref  schema ) [ DefaultPropertyName ]  =  defaultValueNode ; 
345301            } 
346302
347303            if  ( localDescription  is  not null ) 
@@ -423,7 +379,7 @@ private static void InsertAtStart(this JsonObject jsonObject, string key, JsonNo
423379        jsonObject . Insert ( 0 ,  key ,  value ) ; 
424380#else
425381        jsonObject . Remove ( key ) ; 
426-         var  copiedEntries  =  jsonObject . ToArray ( ) ; 
382+         var  copiedEntries  =  System . Linq . Enumerable . ToArray ( jsonObject ) ; 
427383        jsonObject . Clear ( ) ; 
428384
429385        jsonObject . Add ( key ,  value ) ; 
@@ -434,13 +390,6 @@ private static void InsertAtStart(this JsonObject jsonObject, string key, JsonNo
434390#endif
435391    } 
436392
437-     private  static   string  CreateDescriptionWithDefaultValue ( string ?  existingDescription ,  string  defaultValueJson ) 
438-     { 
439-         return  existingDescription  is  null 
440-             ?  $ "Default value: { defaultValueJson } "
441-             :  $ "{ existingDescription }  (Default value: { defaultValueJson } )"; 
442-     } 
443- 
444393    private  static   JsonElement  ParseJsonElement ( ReadOnlySpan < byte >  utf8Json ) 
445394    { 
446395        Utf8JsonReader  reader  =  new ( utf8Json ) ; 
0 commit comments