Spaces:
Running
Running
Optimize _make_optional_parameter_nullable and _adjust_union_types functions (#1321)
Browse files- src/fastmcp/utilities/openapi.py +42 -28
src/fastmcp/utilities/openapi.py
CHANGED
|
@@ -1122,16 +1122,17 @@ def _make_optional_parameter_nullable(schema: dict[str, Any]) -> dict[str, Any]:
|
|
| 1122 |
|
| 1123 |
if isinstance(original_type, str):
|
| 1124 |
# Single type - make it a union with null
|
| 1125 |
-
|
| 1126 |
-
|
| 1127 |
nested_non_nullable_schema = {
|
| 1128 |
"type": original_type,
|
| 1129 |
}
|
|
|
|
| 1130 |
|
| 1131 |
-
#
|
| 1132 |
-
|
| 1133 |
if original_type == "array":
|
| 1134 |
-
|
|
|
|
| 1135 |
"items",
|
| 1136 |
"prefixItems",
|
| 1137 |
"unevaluatedItems",
|
|
@@ -1141,17 +1142,10 @@ def _make_optional_parameter_nullable(schema: dict[str, Any]) -> dict[str, Any]:
|
|
| 1141 |
"minItems",
|
| 1142 |
"maxItems",
|
| 1143 |
"uniqueItems",
|
| 1144 |
-
|
| 1145 |
-
if array_property in nullable_schema:
|
| 1146 |
-
nested_non_nullable_schema[array_property] = nullable_schema[
|
| 1147 |
-
array_property
|
| 1148 |
-
]
|
| 1149 |
-
del nullable_schema[array_property]
|
| 1150 |
-
|
| 1151 |
-
# If the original type is an object, move the object-specific properties into the now-nested schema
|
| 1152 |
-
# https://json-schema.org/understanding-json-schema/reference/object
|
| 1153 |
elif original_type == "object":
|
| 1154 |
-
|
|
|
|
| 1155 |
"properties",
|
| 1156 |
"patternProperties",
|
| 1157 |
"additionalProperties",
|
|
@@ -1160,17 +1154,18 @@ def _make_optional_parameter_nullable(schema: dict[str, Any]) -> dict[str, Any]:
|
|
| 1160 |
"propertyNames",
|
| 1161 |
"minProperties",
|
| 1162 |
"maxProperties",
|
| 1163 |
-
|
| 1164 |
-
if object_property in nullable_schema:
|
| 1165 |
-
nested_non_nullable_schema[object_property] = nullable_schema[
|
| 1166 |
-
object_property
|
| 1167 |
-
]
|
| 1168 |
-
del nullable_schema[object_property]
|
| 1169 |
|
| 1170 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1171 |
|
| 1172 |
-
|
| 1173 |
-
del nullable_schema["type"]
|
| 1174 |
return nullable_schema
|
| 1175 |
|
| 1176 |
return schema
|
|
@@ -1394,12 +1389,31 @@ def _adjust_union_types(
|
|
| 1394 |
) -> dict[str, Any] | list[Any]:
|
| 1395 |
"""Recursively replace 'oneOf' with 'anyOf' in schema to handle overlapping unions."""
|
| 1396 |
if isinstance(schema, dict):
|
| 1397 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1398 |
result = schema.copy()
|
| 1399 |
-
if
|
| 1400 |
result["anyOf"] = result.pop("oneOf")
|
| 1401 |
-
|
| 1402 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1403 |
return result
|
| 1404 |
elif isinstance(schema, list):
|
| 1405 |
return [_adjust_union_types(item) for item in schema]
|
|
|
|
| 1122 |
|
| 1123 |
if isinstance(original_type, str):
|
| 1124 |
# Single type - make it a union with null
|
| 1125 |
+
# Optimize: avoid full schema copy by building directly
|
|
|
|
| 1126 |
nested_non_nullable_schema = {
|
| 1127 |
"type": original_type,
|
| 1128 |
}
|
| 1129 |
+
nullable_schema = {}
|
| 1130 |
|
| 1131 |
+
# Define type-specific properties that should move to nested schema
|
| 1132 |
+
type_specific_properties = set()
|
| 1133 |
if original_type == "array":
|
| 1134 |
+
# https://json-schema.org/understanding-json-schema/reference/array
|
| 1135 |
+
type_specific_properties = {
|
| 1136 |
"items",
|
| 1137 |
"prefixItems",
|
| 1138 |
"unevaluatedItems",
|
|
|
|
| 1142 |
"minItems",
|
| 1143 |
"maxItems",
|
| 1144 |
"uniqueItems",
|
| 1145 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1146 |
elif original_type == "object":
|
| 1147 |
+
# https://json-schema.org/understanding-json-schema/reference/object
|
| 1148 |
+
type_specific_properties = {
|
| 1149 |
"properties",
|
| 1150 |
"patternProperties",
|
| 1151 |
"additionalProperties",
|
|
|
|
| 1154 |
"propertyNames",
|
| 1155 |
"minProperties",
|
| 1156 |
"maxProperties",
|
| 1157 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1158 |
|
| 1159 |
+
# Efficiently distribute properties without copying the entire schema
|
| 1160 |
+
for key, value in schema.items():
|
| 1161 |
+
if key == "type":
|
| 1162 |
+
continue # Already handled
|
| 1163 |
+
elif key in type_specific_properties:
|
| 1164 |
+
nested_non_nullable_schema[key] = value
|
| 1165 |
+
else:
|
| 1166 |
+
nullable_schema[key] = value
|
| 1167 |
|
| 1168 |
+
nullable_schema["anyOf"] = [nested_non_nullable_schema, {"type": "null"}]
|
|
|
|
| 1169 |
return nullable_schema
|
| 1170 |
|
| 1171 |
return schema
|
|
|
|
| 1389 |
) -> dict[str, Any] | list[Any]:
|
| 1390 |
"""Recursively replace 'oneOf' with 'anyOf' in schema to handle overlapping unions."""
|
| 1391 |
if isinstance(schema, dict):
|
| 1392 |
+
# Optimize: only copy if we need to modify something
|
| 1393 |
+
has_one_of = "oneOf" in schema
|
| 1394 |
+
needs_recursive_processing = False
|
| 1395 |
+
|
| 1396 |
+
# Check if we need recursive processing
|
| 1397 |
+
for v in schema.values():
|
| 1398 |
+
if isinstance(v, dict | list):
|
| 1399 |
+
needs_recursive_processing = True
|
| 1400 |
+
break
|
| 1401 |
+
|
| 1402 |
+
# If nothing to change, return original
|
| 1403 |
+
if not has_one_of and not needs_recursive_processing:
|
| 1404 |
+
return schema
|
| 1405 |
+
|
| 1406 |
+
# Work on a copy only when modification is needed
|
| 1407 |
result = schema.copy()
|
| 1408 |
+
if has_one_of:
|
| 1409 |
result["anyOf"] = result.pop("oneOf")
|
| 1410 |
+
|
| 1411 |
+
# Only recurse where needed
|
| 1412 |
+
if needs_recursive_processing:
|
| 1413 |
+
for k, v in result.items():
|
| 1414 |
+
if isinstance(v, dict | list):
|
| 1415 |
+
result[k] = _adjust_union_types(v)
|
| 1416 |
+
|
| 1417 |
return result
|
| 1418 |
elif isinstance(schema, list):
|
| 1419 |
return [_adjust_union_types(item) for item in schema]
|