fix: convert anyOf with const values to enum types in OpenAPI schema

Add a post-processing step that converts anyOf schemas containing
multiple const string values into proper enum types. This fixes the
Schema/EnumDescriptionNotValid error from Stainless by ensuring enum
schemas are properly formatted instead of using anyOf with const values.

Signed-off-by: Sébastien Han <seb@redhat.com>
This commit is contained in:
Sébastien Han 2025-11-13 13:31:55 +01:00
parent 769cfe4654
commit 912ee24bdf
No known key found for this signature in database
6 changed files with 434 additions and 480 deletions

View file

@ -2434,17 +2434,15 @@ paths:
in: query
required: false
schema:
anyOf:
- const: completed
type: string
- const: in_progress
type: string
- const: cancelled
type: string
- const: failed
type: string
- type: 'null'
title: Filter
type: string
enum:
- completed
- in_progress
- cancelled
- failed
default: completed
nullable: true
- name: limit
in: query
required: false
@ -7849,15 +7847,13 @@ components:
OpenAIResponseInputMessageContentImage:
properties:
detail:
anyOf:
- type: string
const: low
- type: string
const: high
- type: string
const: auto
title: string
title: Detail
default: auto
type: string
enum:
- low
- high
- auto
type:
type: string
const: input_image
@ -8000,17 +7996,14 @@ components:
OpenAIResponseInputToolWebSearch:
properties:
type:
anyOf:
- type: string
const: web_search
- type: string
const: web_search_preview
- type: string
const: web_search_preview_2025_03_11
- type: string
const: web_search_2025_08_26
title: string
title: Type
default: web_search
type: string
enum:
- web_search
- web_search_preview
- web_search_preview_2025_03_11
- web_search_2025_08_26
search_context_size:
anyOf:
- type: string
@ -8112,16 +8105,14 @@ components:
title: list[OpenAIResponseOutputMessageContentOutputText | OpenAIResponseContentPartRefusal]
title: string | list[OpenAIResponseInputMessageContentText | OpenAIResponseInputMessageContentImage | OpenAIResponseInputMessageContentFile] | list[OpenAIResponseOutputMessageContentOutputText | OpenAIResponseContentPartRefusal]
role:
anyOf:
- type: string
const: system
- type: string
const: developer
- type: string
const: user
- type: string
const: assistant
title: string
title: Role
type: string
enum:
- system
- developer
- user
- assistant
default: system
type:
type: string
const: message
@ -8183,16 +8174,14 @@ components:
title: list[OpenAIResponseOutputMessageContentOutputText | OpenAIResponseContentPartRefusal]
title: string | list[OpenAIResponseInputMessageContentText | OpenAIResponseInputMessageContentImage | OpenAIResponseInputMessageContentFile] | list[OpenAIResponseOutputMessageContentOutputText | OpenAIResponseContentPartRefusal]
role:
anyOf:
- type: string
const: system
- type: string
const: developer
- type: string
const: user
- type: string
const: assistant
title: string
title: Role
type: string
enum:
- system
- developer
- user
- assistant
default: system
type:
type: string
const: message
@ -8740,14 +8729,13 @@ components:
OpenAIResponseTextFormat:
properties:
type:
anyOf:
- type: string
const: text
- type: string
const: json_schema
- type: string
const: json_object
title: string
title: Type
type: string
enum:
- text
- json_schema
- json_object
default: text
name:
anyOf:
- type: string
@ -10006,16 +9994,14 @@ components:
type: string
title: Vector Store Id
status:
anyOf:
- type: string
const: completed
- type: string
const: in_progress
- type: string
const: cancelled
- type: string
const: failed
title: string
title: Status
type: string
enum:
- completed
- in_progress
- cancelled
- failed
default: completed
file_counts:
$ref: '#/components/schemas/VectorStoreFileCounts'
type: object
@ -10099,12 +10085,12 @@ components:
VectorStoreFileLastError:
properties:
code:
anyOf:
- type: string
const: server_error
- type: string
const: rate_limit_exceeded
title: string
title: Code
type: string
enum:
- server_error
- rate_limit_exceeded
default: server_error
message:
type: string
title: Message
@ -10149,16 +10135,14 @@ components:
- type: 'null'
title: VectorStoreFileLastError
status:
anyOf:
- type: string
const: completed
- type: string
const: in_progress
- type: string
const: cancelled
- type: string
const: failed
title: string
title: Status
type: string
enum:
- completed
- in_progress
- cancelled
- failed
default: completed
usage_bytes:
type: integer
title: Usage Bytes
@ -11425,16 +11409,13 @@ components:
title: VectorStoreChunkingStrategyStatic
title: VectorStoreChunkingStrategyAuto | VectorStoreChunkingStrategyStatic
VectorStoreFileStatus:
anyOf:
- const: completed
type: string
- const: in_progress
type: string
- const: cancelled
type: string
- const: failed
type: string
title: string
type: string
enum:
- completed
- in_progress
- cancelled
- failed
default: completed
OpenAIResponseInputMessageContent:
discriminator:
mapping:
@ -11523,16 +11504,14 @@ components:
title: list[OpenAIResponseOutputMessageContentOutputText | OpenAIResponseContentPartRefusal]
title: string | list[OpenAIResponseInputMessageContentText | OpenAIResponseInputMessageContentImage | OpenAIResponseInputMessageContentFile] | list[OpenAIResponseOutputMessageContentOutputText | OpenAIResponseContentPartRefusal]
role:
anyOf:
- const: system
type: string
- const: developer
type: string
- const: user
type: string
- const: assistant
type: string
title: string
title: Role
type: string
enum:
- system
- developer
- user
- assistant
default: system
type:
const: message
default: message

View file

@ -3671,15 +3671,13 @@ components:
OpenAIResponseInputMessageContentImage:
properties:
detail:
anyOf:
- type: string
const: low
- type: string
const: high
- type: string
const: auto
title: string
title: Detail
default: auto
type: string
enum:
- low
- high
- auto
type:
type: string
const: input_image
@ -3822,17 +3820,14 @@ components:
OpenAIResponseInputToolWebSearch:
properties:
type:
anyOf:
- type: string
const: web_search
- type: string
const: web_search_preview
- type: string
const: web_search_preview_2025_03_11
- type: string
const: web_search_2025_08_26
title: string
title: Type
default: web_search
type: string
enum:
- web_search
- web_search_preview
- web_search_preview_2025_03_11
- web_search_2025_08_26
search_context_size:
anyOf:
- type: string
@ -3934,16 +3929,14 @@ components:
title: list[OpenAIResponseOutputMessageContentOutputText | OpenAIResponseContentPartRefusal]
title: string | list[OpenAIResponseInputMessageContentText | OpenAIResponseInputMessageContentImage | OpenAIResponseInputMessageContentFile] | list[OpenAIResponseOutputMessageContentOutputText | OpenAIResponseContentPartRefusal]
role:
anyOf:
- type: string
const: system
- type: string
const: developer
- type: string
const: user
- type: string
const: assistant
title: string
title: Role
type: string
enum:
- system
- developer
- user
- assistant
default: system
type:
type: string
const: message
@ -4005,16 +3998,14 @@ components:
title: list[OpenAIResponseOutputMessageContentOutputText | OpenAIResponseContentPartRefusal]
title: string | list[OpenAIResponseInputMessageContentText | OpenAIResponseInputMessageContentImage | OpenAIResponseInputMessageContentFile] | list[OpenAIResponseOutputMessageContentOutputText | OpenAIResponseContentPartRefusal]
role:
anyOf:
- type: string
const: system
- type: string
const: developer
- type: string
const: user
- type: string
const: assistant
title: string
title: Role
type: string
enum:
- system
- developer
- user
- assistant
default: system
type:
type: string
const: message
@ -4735,14 +4726,13 @@ components:
OpenAIResponseTextFormat:
properties:
type:
anyOf:
- type: string
const: text
- type: string
const: json_schema
- type: string
const: json_object
title: string
title: Type
type: string
enum:
- text
- json_schema
- json_object
default: text
name:
anyOf:
- type: string
@ -6001,16 +5991,14 @@ components:
type: string
title: Vector Store Id
status:
anyOf:
- type: string
const: completed
- type: string
const: in_progress
- type: string
const: cancelled
- type: string
const: failed
title: string
title: Status
type: string
enum:
- completed
- in_progress
- cancelled
- failed
default: completed
file_counts:
$ref: '#/components/schemas/VectorStoreFileCounts'
type: object
@ -6094,12 +6082,12 @@ components:
VectorStoreFileLastError:
properties:
code:
anyOf:
- type: string
const: server_error
- type: string
const: rate_limit_exceeded
title: string
title: Code
type: string
enum:
- server_error
- rate_limit_exceeded
default: server_error
message:
type: string
title: Message
@ -6144,16 +6132,14 @@ components:
- type: 'null'
title: VectorStoreFileLastError
status:
anyOf:
- type: string
const: completed
- type: string
const: in_progress
- type: string
const: cancelled
- type: string
const: failed
title: string
title: Status
type: string
enum:
- completed
- in_progress
- cancelled
- failed
default: completed
usage_bytes:
type: integer
title: Usage Bytes
@ -7519,16 +7505,13 @@ components:
title: VectorStoreChunkingStrategyStatic
title: VectorStoreChunkingStrategyAuto | VectorStoreChunkingStrategyStatic
VectorStoreFileStatus:
anyOf:
- const: completed
type: string
- const: in_progress
type: string
- const: cancelled
type: string
- const: failed
type: string
title: string
type: string
enum:
- completed
- in_progress
- cancelled
- failed
default: completed
OpenAIResponseInputMessageContent:
discriminator:
mapping:
@ -7617,16 +7600,14 @@ components:
title: list[OpenAIResponseOutputMessageContentOutputText | OpenAIResponseContentPartRefusal]
title: string | list[OpenAIResponseInputMessageContentText | OpenAIResponseInputMessageContentImage | OpenAIResponseInputMessageContentFile] | list[OpenAIResponseOutputMessageContentOutputText | OpenAIResponseContentPartRefusal]
role:
anyOf:
- const: system
type: string
- const: developer
type: string
- const: user
type: string
- const: assistant
type: string
title: string
title: Role
type: string
enum:
- system
- developer
- user
- assistant
default: system
type:
const: message
default: message

View file

@ -3238,15 +3238,13 @@ components:
OpenAIResponseInputMessageContentImage:
properties:
detail:
anyOf:
- type: string
const: low
- type: string
const: high
- type: string
const: auto
title: string
title: Detail
default: auto
type: string
enum:
- low
- high
- auto
type:
type: string
const: input_image
@ -3389,17 +3387,14 @@ components:
OpenAIResponseInputToolWebSearch:
properties:
type:
anyOf:
- type: string
const: web_search
- type: string
const: web_search_preview
- type: string
const: web_search_preview_2025_03_11
- type: string
const: web_search_2025_08_26
title: string
title: Type
default: web_search
type: string
enum:
- web_search
- web_search_preview
- web_search_preview_2025_03_11
- web_search_2025_08_26
search_context_size:
anyOf:
- type: string
@ -3501,16 +3496,14 @@ components:
title: list[OpenAIResponseOutputMessageContentOutputText | OpenAIResponseContentPartRefusal]
title: string | list[OpenAIResponseInputMessageContentText | OpenAIResponseInputMessageContentImage | OpenAIResponseInputMessageContentFile] | list[OpenAIResponseOutputMessageContentOutputText | OpenAIResponseContentPartRefusal]
role:
anyOf:
- type: string
const: system
- type: string
const: developer
- type: string
const: user
- type: string
const: assistant
title: string
title: Role
type: string
enum:
- system
- developer
- user
- assistant
default: system
type:
type: string
const: message
@ -4058,14 +4051,13 @@ components:
OpenAIResponseTextFormat:
properties:
type:
anyOf:
- type: string
const: text
- type: string
const: json_schema
- type: string
const: json_object
title: string
title: Type
type: string
enum:
- text
- json_schema
- json_object
default: text
name:
anyOf:
- type: string
@ -5317,16 +5309,14 @@ components:
type: string
title: Vector Store Id
status:
anyOf:
- type: string
const: completed
- type: string
const: in_progress
- type: string
const: cancelled
- type: string
const: failed
title: string
title: Status
type: string
enum:
- completed
- in_progress
- cancelled
- failed
default: completed
file_counts:
$ref: '#/components/schemas/VectorStoreFileCounts'
type: object
@ -5410,12 +5400,12 @@ components:
VectorStoreFileLastError:
properties:
code:
anyOf:
- type: string
const: server_error
- type: string
const: rate_limit_exceeded
title: string
title: Code
type: string
enum:
- server_error
- rate_limit_exceeded
default: server_error
message:
type: string
title: Message
@ -5460,16 +5450,14 @@ components:
- type: 'null'
title: VectorStoreFileLastError
status:
anyOf:
- type: string
const: completed
- type: string
const: in_progress
- type: string
const: cancelled
- type: string
const: failed
title: string
title: Status
type: string
enum:
- completed
- in_progress
- cancelled
- failed
default: completed
usage_bytes:
type: integer
title: Usage Bytes
@ -6223,16 +6211,13 @@ components:
title: VectorStoreChunkingStrategyStatic
title: VectorStoreChunkingStrategyAuto | VectorStoreChunkingStrategyStatic
VectorStoreFileStatus:
anyOf:
- const: completed
type: string
- const: in_progress
type: string
- const: cancelled
type: string
- const: failed
type: string
title: string
type: string
enum:
- completed
- in_progress
- cancelled
- failed
default: completed
OpenAIResponseInputMessageContent:
discriminator:
mapping:
@ -6321,16 +6306,14 @@ components:
title: list[OpenAIResponseOutputMessageContentOutputText | OpenAIResponseContentPartRefusal]
title: string | list[OpenAIResponseInputMessageContentText | OpenAIResponseInputMessageContentImage | OpenAIResponseInputMessageContentFile] | list[OpenAIResponseOutputMessageContentOutputText | OpenAIResponseContentPartRefusal]
role:
anyOf:
- const: system
type: string
- const: developer
type: string
- const: user
type: string
- const: assistant
type: string
title: string
title: Role
type: string
enum:
- system
- developer
- user
- assistant
default: system
type:
const: message
default: message

View file

@ -844,17 +844,15 @@ paths:
in: query
required: false
schema:
anyOf:
- const: completed
type: string
- const: in_progress
type: string
- const: cancelled
type: string
- const: failed
type: string
- type: 'null'
title: Filter
type: string
enum:
- completed
- in_progress
- cancelled
- failed
default: completed
nullable: true
- name: limit
in: query
required: false
@ -5626,15 +5624,13 @@ components:
OpenAIResponseInputMessageContentImage:
properties:
detail:
anyOf:
- type: string
const: low
- type: string
const: high
- type: string
const: auto
title: string
title: Detail
default: auto
type: string
enum:
- low
- high
- auto
type:
type: string
const: input_image
@ -5777,17 +5773,14 @@ components:
OpenAIResponseInputToolWebSearch:
properties:
type:
anyOf:
- type: string
const: web_search
- type: string
const: web_search_preview
- type: string
const: web_search_preview_2025_03_11
- type: string
const: web_search_2025_08_26
title: string
title: Type
default: web_search
type: string
enum:
- web_search
- web_search_preview
- web_search_preview_2025_03_11
- web_search_2025_08_26
search_context_size:
anyOf:
- type: string
@ -5889,16 +5882,14 @@ components:
title: list[OpenAIResponseOutputMessageContentOutputText | OpenAIResponseContentPartRefusal]
title: string | list[OpenAIResponseInputMessageContentText | OpenAIResponseInputMessageContentImage | OpenAIResponseInputMessageContentFile] | list[OpenAIResponseOutputMessageContentOutputText | OpenAIResponseContentPartRefusal]
role:
anyOf:
- type: string
const: system
- type: string
const: developer
- type: string
const: user
- type: string
const: assistant
title: string
title: Role
type: string
enum:
- system
- developer
- user
- assistant
default: system
type:
type: string
const: message
@ -5960,16 +5951,14 @@ components:
title: list[OpenAIResponseOutputMessageContentOutputText | OpenAIResponseContentPartRefusal]
title: string | list[OpenAIResponseInputMessageContentText | OpenAIResponseInputMessageContentImage | OpenAIResponseInputMessageContentFile] | list[OpenAIResponseOutputMessageContentOutputText | OpenAIResponseContentPartRefusal]
role:
anyOf:
- type: string
const: system
- type: string
const: developer
- type: string
const: user
- type: string
const: assistant
title: string
title: Role
type: string
enum:
- system
- developer
- user
- assistant
default: system
type:
type: string
const: message
@ -6517,14 +6506,13 @@ components:
OpenAIResponseTextFormat:
properties:
type:
anyOf:
- type: string
const: text
- type: string
const: json_schema
- type: string
const: json_object
title: string
title: Type
type: string
enum:
- text
- json_schema
- json_object
default: text
name:
anyOf:
- type: string
@ -7774,16 +7762,14 @@ components:
type: string
title: Vector Store Id
status:
anyOf:
- type: string
const: completed
- type: string
const: in_progress
- type: string
const: cancelled
- type: string
const: failed
title: string
title: Status
type: string
enum:
- completed
- in_progress
- cancelled
- failed
default: completed
file_counts:
$ref: '#/components/schemas/VectorStoreFileCounts'
type: object
@ -7867,12 +7853,12 @@ components:
VectorStoreFileLastError:
properties:
code:
anyOf:
- type: string
const: server_error
- type: string
const: rate_limit_exceeded
title: string
title: Code
type: string
enum:
- server_error
- rate_limit_exceeded
default: server_error
message:
type: string
title: Message
@ -7917,16 +7903,14 @@ components:
- type: 'null'
title: VectorStoreFileLastError
status:
anyOf:
- type: string
const: completed
- type: string
const: in_progress
- type: string
const: cancelled
- type: string
const: failed
title: string
title: Status
type: string
enum:
- completed
- in_progress
- cancelled
- failed
default: completed
usage_bytes:
type: integer
title: Usage Bytes
@ -9063,16 +9047,13 @@ components:
title: VectorStoreChunkingStrategyStatic
title: VectorStoreChunkingStrategyAuto | VectorStoreChunkingStrategyStatic
VectorStoreFileStatus:
anyOf:
- const: completed
type: string
- const: in_progress
type: string
- const: cancelled
type: string
- const: failed
type: string
title: string
type: string
enum:
- completed
- in_progress
- cancelled
- failed
default: completed
OpenAIResponseInputMessageContent:
discriminator:
mapping:
@ -9161,16 +9142,14 @@ components:
title: list[OpenAIResponseOutputMessageContentOutputText | OpenAIResponseContentPartRefusal]
title: string | list[OpenAIResponseInputMessageContentText | OpenAIResponseInputMessageContentImage | OpenAIResponseInputMessageContentFile] | list[OpenAIResponseOutputMessageContentOutputText | OpenAIResponseContentPartRefusal]
role:
anyOf:
- const: system
type: string
- const: developer
type: string
- const: user
type: string
- const: assistant
type: string
title: string
title: Role
type: string
enum:
- system
- developer
- user
- assistant
default: system
type:
const: message
default: message

View file

@ -2400,17 +2400,15 @@ paths:
in: query
required: false
schema:
anyOf:
- const: completed
type: string
- const: in_progress
type: string
- const: cancelled
type: string
- const: failed
type: string
- type: 'null'
title: Filter
type: string
enum:
- completed
- in_progress
- cancelled
- failed
default: completed
nullable: true
- name: limit
in: query
required: false
@ -7815,15 +7813,13 @@ components:
OpenAIResponseInputMessageContentImage:
properties:
detail:
anyOf:
- type: string
const: low
- type: string
const: high
- type: string
const: auto
title: string
title: Detail
default: auto
type: string
enum:
- low
- high
- auto
type:
type: string
const: input_image
@ -7966,17 +7962,14 @@ components:
OpenAIResponseInputToolWebSearch:
properties:
type:
anyOf:
- type: string
const: web_search
- type: string
const: web_search_preview
- type: string
const: web_search_preview_2025_03_11
- type: string
const: web_search_2025_08_26
title: string
title: Type
default: web_search
type: string
enum:
- web_search
- web_search_preview
- web_search_preview_2025_03_11
- web_search_2025_08_26
search_context_size:
anyOf:
- type: string
@ -8078,16 +8071,14 @@ components:
title: list[OpenAIResponseOutputMessageContentOutputText | OpenAIResponseContentPartRefusal]
title: string | list[OpenAIResponseInputMessageContentText | OpenAIResponseInputMessageContentImage | OpenAIResponseInputMessageContentFile] | list[OpenAIResponseOutputMessageContentOutputText | OpenAIResponseContentPartRefusal]
role:
anyOf:
- type: string
const: system
- type: string
const: developer
- type: string
const: user
- type: string
const: assistant
title: string
title: Role
type: string
enum:
- system
- developer
- user
- assistant
default: system
type:
type: string
const: message
@ -8149,16 +8140,14 @@ components:
title: list[OpenAIResponseOutputMessageContentOutputText | OpenAIResponseContentPartRefusal]
title: string | list[OpenAIResponseInputMessageContentText | OpenAIResponseInputMessageContentImage | OpenAIResponseInputMessageContentFile] | list[OpenAIResponseOutputMessageContentOutputText | OpenAIResponseContentPartRefusal]
role:
anyOf:
- type: string
const: system
- type: string
const: developer
- type: string
const: user
- type: string
const: assistant
title: string
title: Role
type: string
enum:
- system
- developer
- user
- assistant
default: system
type:
type: string
const: message
@ -8706,14 +8695,13 @@ components:
OpenAIResponseTextFormat:
properties:
type:
anyOf:
- type: string
const: text
- type: string
const: json_schema
- type: string
const: json_object
title: string
title: Type
type: string
enum:
- text
- json_schema
- json_object
default: text
name:
anyOf:
- type: string
@ -9972,16 +9960,14 @@ components:
type: string
title: Vector Store Id
status:
anyOf:
- type: string
const: completed
- type: string
const: in_progress
- type: string
const: cancelled
- type: string
const: failed
title: string
title: Status
type: string
enum:
- completed
- in_progress
- cancelled
- failed
default: completed
file_counts:
$ref: '#/components/schemas/VectorStoreFileCounts'
type: object
@ -10065,12 +10051,12 @@ components:
VectorStoreFileLastError:
properties:
code:
anyOf:
- type: string
const: server_error
- type: string
const: rate_limit_exceeded
title: string
title: Code
type: string
enum:
- server_error
- rate_limit_exceeded
default: server_error
message:
type: string
title: Message
@ -10115,16 +10101,14 @@ components:
- type: 'null'
title: VectorStoreFileLastError
status:
anyOf:
- type: string
const: completed
- type: string
const: in_progress
- type: string
const: cancelled
- type: string
const: failed
title: string
title: Status
type: string
enum:
- completed
- in_progress
- cancelled
- failed
default: completed
usage_bytes:
type: integer
title: Usage Bytes
@ -11391,16 +11375,13 @@ components:
title: VectorStoreChunkingStrategyStatic
title: VectorStoreChunkingStrategyAuto | VectorStoreChunkingStrategyStatic
VectorStoreFileStatus:
anyOf:
- const: completed
type: string
- const: in_progress
type: string
- const: cancelled
type: string
- const: failed
type: string
title: string
type: string
enum:
- completed
- in_progress
- cancelled
- failed
default: completed
OpenAIResponseInputMessageContent:
discriminator:
mapping:
@ -11489,16 +11470,14 @@ components:
title: list[OpenAIResponseOutputMessageContentOutputText | OpenAIResponseContentPartRefusal]
title: string | list[OpenAIResponseInputMessageContentText | OpenAIResponseInputMessageContentImage | OpenAIResponseInputMessageContentFile] | list[OpenAIResponseOutputMessageContentOutputText | OpenAIResponseContentPartRefusal]
role:
anyOf:
- const: system
type: string
- const: developer
type: string
- const: user
type: string
- const: assistant
type: string
title: string
title: Role
type: string
enum:
- system
- developer
- user
- assistant
default: system
type:
const: message
default: message

View file

@ -1031,6 +1031,10 @@ def _fix_path_parameters(openapi_schema: dict[str, Any]) -> dict[str, Any]:
def _fix_schema_issues(openapi_schema: dict[str, Any]) -> dict[str, Any]:
"""Fix common schema issues: exclusiveMinimum, null defaults, and add titles to unions."""
# Convert anyOf with const values to enums across the entire schema
_convert_anyof_const_to_enum(openapi_schema)
# Fix other schema issues and add titles to unions
if "components" in openapi_schema and "schemas" in openapi_schema["components"]:
for schema_name, schema_def in openapi_schema["components"]["schemas"].items():
_fix_schema_recursive(schema_def)
@ -1189,6 +1193,55 @@ def _add_titles_to_unions(obj: Any, parent_key: str | None = None) -> None:
_add_titles_to_unions(item, parent_key)
def _convert_anyof_const_to_enum(obj: Any) -> None:
"""Convert anyOf with multiple const string values to a proper enum."""
if isinstance(obj, dict):
if "anyOf" in obj:
any_of = obj["anyOf"]
if isinstance(any_of, list):
# Check if all items are const string values
const_values = []
has_null = False
can_convert = True
for item in any_of:
if isinstance(item, dict):
if item.get("type") == "null":
has_null = True
elif item.get("type") == "string" and "const" in item:
const_values.append(item["const"])
else:
# Not a simple const pattern, skip conversion for this anyOf
can_convert = False
break
# If we have const values and they're all strings, convert to enum
if can_convert and const_values and len(const_values) == len(any_of) - (1 if has_null else 0):
# Convert to enum
obj["type"] = "string"
obj["enum"] = const_values
# Preserve default if present, otherwise try to get from first const item
if "default" not in obj:
for item in any_of:
if isinstance(item, dict) and "const" in item:
obj["default"] = item["const"]
break
# Remove anyOf
del obj["anyOf"]
# Handle nullable
if has_null:
obj["nullable"] = True
# Remove title if it's just "string"
if obj.get("title") == "string":
del obj["title"]
# Recursively process all values
for value in obj.values():
_convert_anyof_const_to_enum(value)
elif isinstance(obj, list):
for item in obj:
_convert_anyof_const_to_enum(item)
def _fix_schema_recursive(obj: Any) -> None:
"""Recursively fix schema issues: exclusiveMinimum and null defaults."""
if isinstance(obj, dict):