from ast import literal_eval from typing import Any, Literal, Optional, Type from pydantic import BaseModel, Field, create_model def json_schema_to_model(tool_dict: dict[str, Any]) -> Type[BaseModel]: """ Converts a JSON schema to a Pydantic BaseModel class. Args: json_schema: The JSON schema to convert. Returns: A Pydantic BaseModel class. """ # Extract the model name from the schema title. model_name = tool_dict["name"] schema = tool_dict["parameters"] # Extract the field definitions from the schema properties. field_definitions = { name: json_schema_to_pydantic_field(name, prop, schema.get("required", [])) for name, prop in schema.get("properties", {}).items() } # Create the BaseModel class using create_model(). return create_model(model_name, **field_definitions) def json_schema_to_pydantic_field( name: str, json_schema: dict[str, Any], required: list[str] ) -> Any: """ Converts a JSON schema property to a Pydantic field definition. Args: name: The field name. json_schema: The JSON schema property. Returns: A Pydantic field definition. """ # Get the field type. type_ = json_schema_to_pydantic_type(json_schema) # Get the field description. description = json_schema.get("description") # Get the field examples. examples = json_schema.get("examples") # Create a Field object with the type, description, and examples. # The 'required' flag will be set later when creating the model. return ( type_, Field( description=description, examples=examples, default=... if name in required else None, ), ) def json_schema_to_pydantic_type(json_schema: dict[str, Any]) -> Any: """ Converts a JSON schema type to a Pydantic type. Args: json_schema: The JSON schema to convert. Returns: A Pydantic type. """ type_ = json_schema.get("type") if type_ == "string" or type_ == "str": return str elif type_ == "integer" or type_ == "int": return int elif type_ == "number" or type_ == "float": return float elif type_ == "boolean" or type_ == "bool": return bool elif type_ == "array" or type_ == "list": items_schema = json_schema.get("items") if items_schema: item_type = json_schema_to_pydantic_type(items_schema) return list[item_type] else: return list elif type_ == "object": # Handle nested models. properties = json_schema.get("properties") if properties: nested_model = json_schema_to_model(json_schema) return nested_model else: return dict elif type_ == "null": return Optional[Any] # Use Optional[Any] for nullable fields elif type_ == "literal": enum = json_schema.get("enum") if enum is None: raise ValueError("Enum values must be provided for 'literal' type.") return Literal[literal_eval(enum)] elif type_ == "optional": inner_schema = json_schema.get("items", {"type": "string"}) inner_type = json_schema_to_pydantic_type(inner_schema) return Optional[inner_type] else: raise ValueError(f"Unsupported JSON schema type: {type_}")