@@ -44,7 +44,7 @@ def my_tool(param1: str, param2: int = 42) -> dict:
4444import functools
4545import inspect
4646import logging
47- from copy import deepcopy
47+ from copy import copy
4848from typing import (
4949 Annotated ,
5050 Any ,
@@ -65,6 +65,7 @@ def my_tool(param1: str, param2: int = 42) -> dict:
6565import docstring_parser
6666from pydantic import BaseModel , Field , create_model
6767from pydantic .fields import FieldInfo
68+ from pydantic_core import PydanticUndefined
6869from typing_extensions import override
6970
7071from ..interrupt import InterruptException
@@ -130,34 +131,32 @@ def _extract_annotated_metadata(
130131 if get_origin (annotation ) is Annotated :
131132 args = get_args (annotation )
132133 actual_type = args [0 ]
133-
134- # Look through metadata for FieldInfo and string descriptions
135134 for meta in args [1 :]:
136135 if isinstance (meta , FieldInfo ):
137136 field_info = meta
138137 elif isinstance (meta , str ):
139138 description = meta
140139
141- # Determine Final Description
142- # Priority: 1. Annotated string, 2. FieldInfo description, 3. Docstring, 4. Fallback
143- final_description = description
144-
145- if final_description is None :
146- if field_info and field_info .description :
147- final_description = field_info .description
148- else :
149- final_description = self .param_descriptions .get (param_name )
150-
151- if final_description is None :
152- final_description = f"Parameter { param_name } "
140+ # Final description — always a string, never None
141+ final_description = (
142+ description
143+ if description is not None
144+ else (
145+ field_info .description
146+ if field_info and field_info .description is not None
147+ else self .param_descriptions .get (param_name ) or f"Parameter { param_name } "
148+ )
149+ )
153150
154- # Create Final FieldInfo with proper default handling
151+ # Build final FieldInfo
155152 if field_info :
156- final_field = deepcopy (field_info )
153+ final_field = copy (field_info )
157154 final_field .description = final_description
158155
159- # Function signature default takes priority
160- if param_default is not ...:
156+ # ONLY override default if Field has no default AND signature has one.
157+ # Pydantic uses `PydanticUndefined` to signify no default was provided,
158+ # which is distinct from an explicit default of `None`.
159+ if field_info .default is PydanticUndefined and param_default is not ...:
161160 final_field .default = param_default
162161 else :
163162 final_field = Field (default = param_default , description = final_description )
0 commit comments