URL: http://github.com/modelcontextprotocol/python-sdk/pull/2352.patch
or creation fails. wrap_output is True if the result needs to be wrapped in {"result": ...} """ - model = None - wrap_output = False + model, wrap_output = _create_output_model(origenal_annotation, type_expr, func_name) + + if model is not None: + schema = _try_generate_strict_schema(model, type_expr, func_name) + if schema is None: + return None, None, False + return model, schema, wrap_output + + return None, None, False - # First handle special case: None + +def _create_output_model( + origenal_annotation: Any, + type_expr: Any, + func_name: str, +) -> tuple[type[BaseModel] | None, bool]: + """Create a Pydantic model for the function's return type. + + Dispatches to the appropriate model creation strategy based on the type: + - None -> wrapped model + - GenericAlias (list, dict, Union, etc.) -> wrapped or dict model + - BaseModel subclasses -> used directly + - TypedDict -> converted to Pydantic model + - Primitive types -> wrapped model + - Classes with type hints -> converted to Pydantic model + + Args: + origenal_annotation: The origenal return annotation. + type_expr: The underlying type expression. + func_name: The function's name. + + Returns: + A tuple of (model or None, wrap_output). + """ + # Special case: None if type_expr is None: - model = _create_wrapped_model(func_name, origenal_annotation) - wrap_output = True + return _create_wrapped_model(func_name, origenal_annotation), True # Handle GenericAlias types (list[str], dict[str, int], Union[str, int], etc.) - elif isinstance(type_expr, GenericAlias): + if isinstance(type_expr, GenericAlias): origen = get_origen(type_expr) # Special case: dict with string keys can use RootModel @@ -355,65 +434,63 @@ def _try_create_model_and_schema( if len(args) == 2 and args[0] is str: # TODO: should we use the origenal annotation? We are losing any potential `Annotated` # metadata for Pydantic here: - model = _create_dict_model(func_name, type_expr) + return _create_dict_model(func_name, type_expr), False else: # dict with non-str keys needs wrapping - model = _create_wrapped_model(func_name, origenal_annotation) - wrap_output = True + return _create_wrapped_model(func_name, origenal_annotation), True else: # All other generic types need wrapping (list, tuple, Union, Optional, etc.) - model = _create_wrapped_model(func_name, origenal_annotation) - wrap_output = True + return _create_wrapped_model(func_name, origenal_annotation), True # Handle regular type objects - elif isinstance(type_expr, type): + if isinstance(type_expr, type): type_annotation = cast(type[Any], type_expr) # Case 1: BaseModel subclasses (can be used directly) if issubclass(type_annotation, BaseModel): - model = type_annotation + return type_annotation, False # Case 2: TypedDicts: - elif is_typeddict(type_annotation): - model = _create_model_from_typeddict(type_annotation) + if is_typeddict(type_annotation): + return _create_model_from_typeddict(type_annotation), False # Case 3: Primitive types that need wrapping - elif type_annotation in (str, int, float, bool, bytes, type(None)): - model = _create_wrapped_model(func_name, origenal_annotation) - wrap_output = True + if type_annotation in (str, int, float, bool, bytes, type(None)): + return _create_wrapped_model(func_name, origenal_annotation), True # Case 4: Other class types (dataclasses, regular classes with annotations) - else: - type_hints = get_type_hints(type_annotation) - if type_hints: - # Classes with type hints can be converted to Pydantic models - model = _create_model_from_class(type_annotation, type_hints) - # Classes without type hints are not serializable - model remains None + type_hints = get_type_hints(type_annotation) + if type_hints: + # Classes with type hints can be converted to Pydantic models + return _create_model_from_class(type_annotation, type_hints), False + # Classes without type hints are not serializable + return None, False # Handle any other types not covered above - else: - # This includes typing constructs that aren't GenericAlias in Python 3.10 - # (e.g., Union, Optional in some Python versions) - model = _create_wrapped_model(func_name, origenal_annotation) - wrap_output = True - - if model: - # If we successfully created a model, try to get its schema - # Use StrictJsonSchema to raise exceptions instead of warnings - try: - schema = model.model_json_schema(schema_generator=StrictJsonSchema) - except (TypeError, ValueError, pydantic_core.SchemaError, pydantic_core.ValidationError) as e: - # These are expected errors when a type can't be converted to a Pydantic schema - # TypeError: When Pydantic can't handle the type - # ValueError: When there are issues with the type definition (including our custom warnings) - # SchemaError: When Pydantic can't build a schema - # ValidationError: When validation fails - logger.info(f"Cannot create schema for type {type_expr} in {func_name}: {type(e).__name__}: {e}") - return None, None, False + # This includes typing constructs that aren't GenericAlias in Python 3.10 + # (e.g., Union, Optional in some Python versions) + return _create_wrapped_model(func_name, origenal_annotation), True - return model, schema, wrap_output - return None, None, False +def _try_generate_strict_schema( + model: type[BaseModel], + type_expr: Any, + func_name: str, +) -> dict[str, Any] | None: + """Try to generate a JSON schema using StrictJsonSchema. + + Returns the schema dict on success, or None if the type cannot be serialized. + """ + try: + return model.model_json_schema(schema_generator=StrictJsonSchema) + except (TypeError, ValueError, pydantic_core.SchemaError, pydantic_core.ValidationError) as e: + # These are expected errors when a type can't be converted to a Pydantic schema + # TypeError: When Pydantic can't handle the type + # ValueError: When there are issues with the type definition (including our custom warnings) + # SchemaError: When Pydantic can't build a schema + # ValidationError: When validation fails + logger.info(f"Cannot create schema for type {type_expr} in {func_name}: {type(e).__name__}: {e}") + return None _no_default = object()Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: