From dd442cc43eefecc73590eeb6d51be80cec42214b Mon Sep 17 00:00:00 2001 From: cthomas Date: Sat, 1 Mar 2025 19:05:11 -0800 Subject: [PATCH] fix: process pydantic args in reverse order to handle deps (#1172) --- letta/functions/helpers.py | 28 +++++++++++----------------- letta/functions/schema_generator.py | 3 ++- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/letta/functions/helpers.py b/letta/functions/helpers.py index 524f50f6..03b27e40 100644 --- a/letta/functions/helpers.py +++ b/letta/functions/helpers.py @@ -572,31 +572,25 @@ def generate_model_from_args_json_schema(schema: Dict[str, Any]) -> Type[BaseMod Returns: A Pydantic model class """ - # First create any nested models from $defs + # First create any nested models from $defs in reverse order to handle dependencies nested_models = {} if "$defs" in schema: - for name, model_schema in schema["$defs"].items(): - # Create field definitions for the nested model - fields = {} - for field_name, field_schema in model_schema["properties"].items(): - field_type = _get_field_type(field_schema) - required = field_name in model_schema.get("required", []) - description = field_schema.get("description", "") # Get description or empty string - fields[field_name] = (field_type, Field(..., description=description) if required else Field(None, description=description)) + for name, model_schema in reversed(list(schema.get("$defs", {}).items())): + nested_models[name] = _create_model_from_schema(name, model_schema, nested_models) - # Create the nested model - nested_models[name] = create_model(name, **fields) + # Create and return the main model + return _create_model_from_schema(schema.get("title", "DynamicModel"), schema, nested_models) - # Create the main model fields + +def _create_model_from_schema(name: str, model_schema: Dict[str, Any], nested_models: Dict[str, Type[BaseModel]] = None) -> Type[BaseModel]: fields = {} - for field_name, field_schema in schema["properties"].items(): + for field_name, field_schema in model_schema["properties"].items(): field_type = _get_field_type(field_schema, nested_models) - required = field_name in schema.get("required", []) + required = field_name in model_schema.get("required", []) description = field_schema.get("description", "") # Get description or empty string fields[field_name] = (field_type, Field(..., description=description) if required else Field(None, description=description)) - # Create and return the main model - return create_model(schema.get("title", "DynamicModel"), **fields) + return create_model(name, **fields) def _get_field_type(field_schema: Dict[str, Any], nested_models: Dict[str, Type[BaseModel]] = None) -> Any: @@ -613,7 +607,7 @@ def _get_field_type(field_schema: Dict[str, Any], nested_models: Dict[str, Type[ item_type = field_schema["items"].get("$ref", "").split("/")[-1] if item_type and nested_models and item_type in nested_models: return List[nested_models[item_type]] - return List[_get_field_type(field_schema["items"])] + return List[_get_field_type(field_schema["items"], nested_models)] elif field_schema.get("type") == "object": if "$ref" in field_schema: ref_type = field_schema["$ref"].split("/")[-1] diff --git a/letta/functions/schema_generator.py b/letta/functions/schema_generator.py index 8273b4bb..5a2cb115 100644 --- a/letta/functions/schema_generator.py +++ b/letta/functions/schema_generator.py @@ -234,7 +234,8 @@ def pydantic_model_to_json_schema(model: Type[BaseModel]) -> dict: if "description" not in prop: raise ValueError(f"Property {prop} lacks a 'description' key") - + if "type" not in prop and "$ref" in prop: + prop["type"] = "object" return { "type": "string" if prop["type"] == "string" else prop["type"], "description": prop["description"],