mirror of
https://github.com/meta-llama/llama-stack.git
synced 2025-12-04 02:03:44 +00:00
need to fix default:
Signed-off-by: Sébastien Han <seb@redhat.com>
This commit is contained in:
parent
8df9340dd3
commit
1f388377b2
2 changed files with 95 additions and 611 deletions
598
docs/static/deprecated-llama-stack-spec.yaml
vendored
598
docs/static/deprecated-llama-stack-spec.yaml
vendored
File diff suppressed because it is too large
Load diff
|
|
@ -10,7 +10,6 @@ FastAPI-based OpenAPI generator for Llama Stack.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import importlib
|
import importlib
|
||||||
import json
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
|
@ -177,21 +176,23 @@ def _fix_ref_references(openapi_schema: dict[str, Any]) -> dict[str, Any]:
|
||||||
def _fix_schema_issues(openapi_schema: dict[str, Any]) -> dict[str, Any]:
|
def _fix_schema_issues(openapi_schema: dict[str, Any]) -> dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
Fix common schema issues that cause OpenAPI validation problems.
|
Fix common schema issues that cause OpenAPI validation problems.
|
||||||
This includes converting exclusiveMinimum numbers to minimum values and fixing string fields with null defaults.
|
This includes converting exclusiveMinimum numbers to minimum values and fixing invalid None defaults.
|
||||||
"""
|
"""
|
||||||
if "components" not in openapi_schema or "schemas" not in openapi_schema["components"]:
|
if "components" not in openapi_schema or "schemas" not in openapi_schema["components"]:
|
||||||
return openapi_schema
|
return openapi_schema
|
||||||
|
|
||||||
schemas = openapi_schema["components"]["schemas"]
|
schemas = openapi_schema["components"]["schemas"]
|
||||||
|
|
||||||
# Fix exclusiveMinimum issues
|
# Fix exclusiveMinimum issues and invalid None defaults
|
||||||
for _, schema_def in schemas.items():
|
for schema_name, schema_def in schemas.items():
|
||||||
_fix_exclusive_minimum_in_schema(schema_def)
|
if isinstance(schema_def, dict):
|
||||||
|
_fix_exclusive_minimum_in_schema(schema_def)
|
||||||
|
_fix_none_defaults_in_schema(schema_def, schema_name)
|
||||||
|
|
||||||
return openapi_schema
|
return openapi_schema
|
||||||
|
|
||||||
|
|
||||||
def validate_openapi_schema(schema: dict[str, Any], schema_name: str = "OpenAPI schema") -> bool:
|
def validate_openapi_schema(schema: dict[str, Any] | None, schema_name: str = "OpenAPI schema") -> bool:
|
||||||
"""
|
"""
|
||||||
Validate an OpenAPI schema using openapi-spec-validator.
|
Validate an OpenAPI schema using openapi-spec-validator.
|
||||||
|
|
||||||
|
|
@ -205,6 +206,26 @@ def validate_openapi_schema(schema: dict[str, Any], schema_name: str = "OpenAPI
|
||||||
Raises:
|
Raises:
|
||||||
OpenAPIValidationError: If validation fails
|
OpenAPIValidationError: If validation fails
|
||||||
"""
|
"""
|
||||||
|
if schema is None:
|
||||||
|
print(f"❌ {schema_name} is None")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Ensure required OpenAPI structure exists
|
||||||
|
if "paths" not in schema:
|
||||||
|
schema["paths"] = {}
|
||||||
|
if "components" not in schema:
|
||||||
|
schema["components"] = {}
|
||||||
|
if not isinstance(schema["components"], dict):
|
||||||
|
schema["components"] = {}
|
||||||
|
if "schemas" not in schema["components"]:
|
||||||
|
schema["components"]["schemas"] = {}
|
||||||
|
if not isinstance(schema["components"]["schemas"], dict):
|
||||||
|
schema["components"]["schemas"] = {}
|
||||||
|
|
||||||
|
# Ensure info section exists
|
||||||
|
if "info" not in schema:
|
||||||
|
schema["info"] = {"title": "API", "version": "1.0.0"}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
validate_spec(schema)
|
validate_spec(schema)
|
||||||
print(f"✅ {schema_name} is valid")
|
print(f"✅ {schema_name} is valid")
|
||||||
|
|
@ -240,6 +261,41 @@ def _fix_exclusive_minimum_in_schema(obj: Any) -> None:
|
||||||
_fix_exclusive_minimum_in_schema(item)
|
_fix_exclusive_minimum_in_schema(item)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: handle this in the Classes
|
||||||
|
def _fix_none_defaults_in_schema(obj: Any, path: str = "") -> None:
|
||||||
|
"""
|
||||||
|
Recursively fix invalid None defaults in schema objects.
|
||||||
|
Removes default values that are None to prevent discriminator validation errors and empty defaults in YAML.
|
||||||
|
"""
|
||||||
|
if isinstance(obj, dict):
|
||||||
|
# Remove None defaults - they cause issues with discriminator validation and create empty defaults in YAML
|
||||||
|
# For optional fields (int | None), None defaults are redundant and create empty "default:" in YAML
|
||||||
|
if "default" in obj and obj["default"] is None:
|
||||||
|
del obj["default"]
|
||||||
|
|
||||||
|
# Recursively check all nested schemas
|
||||||
|
for key, value in obj.items():
|
||||||
|
if key in ("properties", "items", "additionalProperties", "allOf", "anyOf", "oneOf"):
|
||||||
|
if isinstance(value, dict):
|
||||||
|
for sub_key, sub_value in value.items():
|
||||||
|
if isinstance(sub_value, dict):
|
||||||
|
new_path = f"{path}.{sub_key}" if path else sub_key
|
||||||
|
_fix_none_defaults_in_schema(sub_value, new_path)
|
||||||
|
elif isinstance(value, list):
|
||||||
|
for i, item in enumerate(value):
|
||||||
|
if isinstance(item, dict):
|
||||||
|
new_path = f"{path}[{i}]" if path else f"[{i}]"
|
||||||
|
_fix_none_defaults_in_schema(item, new_path)
|
||||||
|
elif isinstance(value, dict):
|
||||||
|
new_path = f"{path}.{key}" if path else key
|
||||||
|
_fix_none_defaults_in_schema(value, new_path)
|
||||||
|
elif isinstance(value, list):
|
||||||
|
for i, item in enumerate(value):
|
||||||
|
if isinstance(item, dict):
|
||||||
|
new_path = f"{path}.{key}[{i}]" if path else f"{key}[{i}]"
|
||||||
|
_fix_none_defaults_in_schema(item, new_path)
|
||||||
|
|
||||||
|
|
||||||
def _get_path_version(path: str) -> str | None:
|
def _get_path_version(path: str) -> str | None:
|
||||||
"""
|
"""
|
||||||
Determine the API version of a path based on its prefix.
|
Determine the API version of a path based on its prefix.
|
||||||
|
|
@ -409,19 +465,28 @@ def _filter_schema(
|
||||||
|
|
||||||
filtered_schema["paths"] = filtered_paths
|
filtered_schema["paths"] = filtered_paths
|
||||||
|
|
||||||
|
# Ensure components structure exists
|
||||||
|
if "components" not in filtered_schema:
|
||||||
|
filtered_schema["components"] = {}
|
||||||
|
|
||||||
# Filter schemas/components if requested
|
# Filter schemas/components if requested
|
||||||
if filter_schemas and "components" in filtered_schema and "schemas" in filtered_schema["components"]:
|
if filter_schemas and "schemas" in filtered_schema.get("components", {}):
|
||||||
referenced_schemas = _find_schemas_referenced_by_paths(filtered_paths, openapi_schema)
|
try:
|
||||||
filtered_schema["components"]["schemas"] = {
|
referenced_schemas = _find_schemas_referenced_by_paths(filtered_paths, openapi_schema)
|
||||||
name: schema
|
filtered_schema["components"]["schemas"] = {
|
||||||
for name, schema in filtered_schema["components"]["schemas"].items()
|
name: schema
|
||||||
if name in referenced_schemas
|
for name, schema in filtered_schema["components"]["schemas"].items()
|
||||||
}
|
if name in referenced_schemas
|
||||||
|
}
|
||||||
|
except Exception:
|
||||||
|
# If schema reference finding fails, keep all schemas
|
||||||
|
pass
|
||||||
|
elif "schemas" not in filtered_schema["components"]:
|
||||||
|
# Ensure schemas section exists even if empty
|
||||||
|
filtered_schema["components"]["schemas"] = {}
|
||||||
|
|
||||||
# Preserve $defs section if it exists
|
# Preserve $defs section if it exists
|
||||||
if "components" in openapi_schema and "$defs" in openapi_schema["components"]:
|
if "components" in openapi_schema and "$defs" in openapi_schema.get("components", {}):
|
||||||
if "components" not in filtered_schema:
|
|
||||||
filtered_schema["components"] = {}
|
|
||||||
filtered_schema["components"]["$defs"] = openapi_schema["components"]["$defs"]
|
filtered_schema["components"]["$defs"] = openapi_schema["components"]["$defs"]
|
||||||
|
|
||||||
return filtered_schema
|
return filtered_schema
|
||||||
|
|
@ -448,12 +513,17 @@ def _find_schemas_referenced_by_paths(filtered_paths: dict[str, Any], openapi_sc
|
||||||
referenced_schemas.update(_find_schema_refs_in_object(operation))
|
referenced_schemas.update(_find_schema_refs_in_object(operation))
|
||||||
|
|
||||||
# Also check the responses section for schema references
|
# Also check the responses section for schema references
|
||||||
if "components" in openapi_schema and "responses" in openapi_schema["components"]:
|
components = openapi_schema.get("components")
|
||||||
referenced_schemas.update(_find_schema_refs_in_object(openapi_schema["components"]["responses"]))
|
if components and isinstance(components, dict) and "responses" in components:
|
||||||
|
referenced_schemas.update(_find_schema_refs_in_object(components["responses"]))
|
||||||
|
|
||||||
# Also include schemas that are referenced by other schemas (transitive references)
|
# Also include schemas that are referenced by other schemas (transitive references)
|
||||||
# This ensures we include all dependencies
|
# This ensures we include all dependencies
|
||||||
all_schemas = openapi_schema.get("components", {}).get("schemas", {})
|
all_schemas = {}
|
||||||
|
if components and isinstance(components, dict):
|
||||||
|
all_schemas = components.get("schemas", {})
|
||||||
|
if not isinstance(all_schemas, dict):
|
||||||
|
all_schemas = {}
|
||||||
additional_schemas = set()
|
additional_schemas = set()
|
||||||
|
|
||||||
for schema_name in referenced_schemas:
|
for schema_name in referenced_schemas:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue