mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-06 02:48:13 +00:00
Merge e5bd410251 into 82d8b3797c
This commit is contained in:
commit
05e070d8b6
2 changed files with 107 additions and 15 deletions
|
|
@ -1015,6 +1015,20 @@ def _convert_schema_types(schema, depth=0):
|
|||
"minProperties",
|
||||
"maxProperties",
|
||||
}
|
||||
# Constraint keywords that apply to primitive types and should move
|
||||
# into the anyOf branch with the type. Without this they are left on
|
||||
# the parent next to anyOf and dropped by _filter_anyof_fields.
|
||||
# multipleOf is not copied on purpose: filter_schema_fields drops it
|
||||
# from the branch anyway, so carrying it would only imply it survives.
|
||||
scalar_constraint_fields: Final = {
|
||||
"enum",
|
||||
"pattern",
|
||||
"minLength",
|
||||
"maxLength",
|
||||
"minimum",
|
||||
"maximum",
|
||||
"format",
|
||||
}
|
||||
|
||||
any_of: Final[list[dict[str, object]]] = []
|
||||
for t in type_val:
|
||||
|
|
@ -1025,23 +1039,31 @@ def _convert_schema_types(schema, depth=0):
|
|||
any_of.append({"type": "null"})
|
||||
continue
|
||||
|
||||
# For object/array types, include type-specific fields
|
||||
if t in ("object", "array"):
|
||||
item_schema: dict[str, object] = {"type": t}
|
||||
# Move type-specific fields into this anyOf item
|
||||
for field in type_specific_fields:
|
||||
if field in schema:
|
||||
item_schema[field] = deepcopy(schema[field])
|
||||
any_of.append(item_schema)
|
||||
else:
|
||||
# For primitive types, only include the type
|
||||
any_of.append({"type": t})
|
||||
item_schema: dict[str, object] = {"type": t}
|
||||
# For object/array types, move the type-specific fields into
|
||||
# the branch; for primitives, carry the scalar constraint
|
||||
# keywords so they survive _filter_anyof_fields.
|
||||
branch_fields = type_specific_fields if t in ("object", "array") else scalar_constraint_fields
|
||||
for field in branch_fields:
|
||||
if field not in schema:
|
||||
continue
|
||||
if field == "enum":
|
||||
# enums only land on the string branch, string values
|
||||
# only: a mixed enum like ["a", 1] must not put 1 on
|
||||
# the string branch. _fix_enum_types drops the rest.
|
||||
if t == "string" and isinstance(schema["enum"], list):
|
||||
string_values = [v for v in schema["enum"] if isinstance(v, str)]
|
||||
if string_values:
|
||||
item_schema["enum"] = string_values
|
||||
continue
|
||||
item_schema[field] = deepcopy(schema[field])
|
||||
any_of.append(item_schema)
|
||||
|
||||
# Remove type-specific fields from parent if we moved them into anyOf
|
||||
# Remove the fields we moved into anyOf branches from the parent
|
||||
has_object_or_array: Final = any(t in ("object", "array") for t in type_val if isinstance(t, str))
|
||||
if has_object_or_array:
|
||||
for field in type_specific_fields:
|
||||
schema.pop(field, None)
|
||||
parent_fields = type_specific_fields if has_object_or_array else scalar_constraint_fields
|
||||
for field in parent_fields:
|
||||
schema.pop(field, None)
|
||||
|
||||
schema["anyOf"] = any_of
|
||||
schema.pop("type")
|
||||
|
|
|
|||
|
|
@ -1755,3 +1755,73 @@ def test_get_vertex_ai_lyria_model_info_is_none_for_non_lyria_speech_models(mode
|
|||
assert get_vertex_ai_lyria_model_info(model=model) is None
|
||||
|
||||
|
||||
|
||||
def test_type_array_keeps_scalar_constraints_on_anyof_branches():
|
||||
"""A nullable type array must not lose enum/min-max/pattern/format (issue #43325)."""
|
||||
from litellm.llms.vertex_ai.common_utils import _build_vertex_schema
|
||||
|
||||
schema = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": {"type": ["string", "null"], "enum": ["open", "closed"], "maxLength": 6},
|
||||
"priority": {"type": ["integer", "null"], "minimum": 1, "maximum": 5},
|
||||
"code": {"type": ["string", "null"], "pattern": "^[A-Z]+$", "format": "date-time"},
|
||||
},
|
||||
"required": ["status"],
|
||||
}
|
||||
|
||||
assert _build_vertex_schema(schema) == {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": {
|
||||
"anyOf": [
|
||||
{"type": "string", "enum": ["open", "closed"], "maxLength": 6},
|
||||
{"type": "null"},
|
||||
]
|
||||
},
|
||||
"priority": {
|
||||
"anyOf": [
|
||||
{"type": "integer", "minimum": 1, "maximum": 5},
|
||||
{"type": "null"},
|
||||
]
|
||||
},
|
||||
"code": {
|
||||
"anyOf": [
|
||||
{"type": "string", "pattern": "^[A-Z]+$", "format": "date-time"},
|
||||
{"type": "null"},
|
||||
]
|
||||
},
|
||||
},
|
||||
"required": ["status"],
|
||||
}
|
||||
|
||||
|
||||
def test_type_array_mixed_enum_only_string_values_on_string_branch():
|
||||
"""A mixed-type enum must not leak non-string values onto the string branch."""
|
||||
from litellm.llms.vertex_ai.common_utils import _build_vertex_schema
|
||||
|
||||
schema = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"mixed": {"type": ["string", "integer"], "enum": ["a", 1]},
|
||||
"numbers": {"type": ["string", "integer"], "enum": [1, 2]},
|
||||
},
|
||||
}
|
||||
|
||||
assert _build_vertex_schema(schema) == {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"mixed": {
|
||||
"anyOf": [
|
||||
{"type": "string", "enum": ["a"]},
|
||||
{"type": "integer"},
|
||||
]
|
||||
},
|
||||
"numbers": {
|
||||
"anyOf": [
|
||||
{"type": "string"},
|
||||
{"type": "integer"},
|
||||
]
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue