3434from .tool_context import ToolContext
3535
3636
37+ def _merge_json_schema_descriptions (
38+ target : dict [str , Any ], source : dict [str , Any ]
39+ ) -> None :
40+ """Copies ``description`` values from ``source`` onto ``target`` in place.
41+
42+ Walks ``properties`` / ``items`` so nested object and list schemas keep the
43+ Field(description=...) metadata from the original Pydantic output schema.
44+ """
45+ source_props = source .get ('properties' )
46+ target_props = target .get ('properties' )
47+ if isinstance (source_props , dict ) and isinstance (target_props , dict ):
48+ for name , source_prop in source_props .items ():
49+ if name not in target_props or not isinstance (source_prop , dict ):
50+ continue
51+ target_prop = target_props [name ]
52+ if not isinstance (target_prop , dict ):
53+ continue
54+ description = source_prop .get ('description' )
55+ if isinstance (description , str ) and description :
56+ target_prop ['description' ] = description
57+ _merge_json_schema_descriptions (target_prop , source_prop )
58+
59+ source_items = source .get ('items' )
60+ target_items = target .get ('items' )
61+ if isinstance (source_items , dict ) and isinstance (target_items , dict ):
62+ description = source_items .get ('description' )
63+ if isinstance (description , str ) and description :
64+ target_items ['description' ] = description
65+ _merge_json_schema_descriptions (target_items , source_items )
66+
67+
68+ def _apply_descriptions_to_schema_properties (
69+ properties : dict [str , types .Schema ] | None ,
70+ model_fields : dict [str , Any ],
71+ ) -> None :
72+ """Sets Schema.description from Pydantic FieldInfo.description when present."""
73+ if not properties :
74+ return
75+ for name , field_info in model_fields .items ():
76+ prop = properties .get (name )
77+ description = getattr (field_info , 'description' , None )
78+ if prop is not None and isinstance (description , str ) and description :
79+ prop .description = description
80+
81+
3782class SetModelResponseTool (BaseTool ):
3883 """Internal tool used for output schema workaround.
3984
@@ -136,6 +181,55 @@ def set_model_response() -> str:
136181 description = self .func .__doc__ .strip () if self .func .__doc__ else '' ,
137182 )
138183
184+ def _preserve_output_schema_field_descriptions (
185+ self , function_decl : types .FunctionDeclaration
186+ ) -> None :
187+ """Restores Field(description=...) lost during function-declaration build.
188+
189+ ``build_function_declaration`` rebuilds parameters from ``inspect.Parameter``
190+ objects, which cannot carry Pydantic field descriptions. Re-apply them from
191+ the original ``output_schema`` so the model still sees the semantic hints.
192+ """
193+ if self ._is_basemodel :
194+ source_schema = self .output_schema .model_json_schema ()
195+ if function_decl .parameters_json_schema is not None :
196+ _merge_json_schema_descriptions (
197+ function_decl .parameters_json_schema , source_schema
198+ )
199+ elif function_decl .parameters is not None :
200+ _apply_descriptions_to_schema_properties (
201+ function_decl .parameters .properties ,
202+ self .output_schema .model_fields ,
203+ )
204+ return
205+
206+ if self ._is_list_of_basemodel :
207+ inner_type = get_list_inner_type (self .output_schema )
208+ if not is_basemodel_schema (inner_type ):
209+ return
210+ source_schema = {
211+ 'properties' : {
212+ 'items' : {
213+ 'type' : 'array' ,
214+ 'items' : inner_type .model_json_schema (),
215+ }
216+ }
217+ }
218+ if function_decl .parameters_json_schema is not None :
219+ _merge_json_schema_descriptions (
220+ function_decl .parameters_json_schema , source_schema
221+ )
222+ elif (
223+ function_decl .parameters is not None
224+ and function_decl .parameters .properties
225+ and 'items' in function_decl .parameters .properties
226+ ):
227+ items_schema = function_decl .parameters .properties ['items' ]
228+ if items_schema .items is not None :
229+ _apply_descriptions_to_schema_properties (
230+ items_schema .items .properties , inner_type .model_fields
231+ )
232+
139233 @override
140234 def _get_declaration (self ) -> Optional [types .FunctionDeclaration ]:
141235 """Gets the OpenAPI specification of this tool."""
@@ -146,6 +240,7 @@ def _get_declaration(self) -> Optional[types.FunctionDeclaration]:
146240 variant = self ._api_variant ,
147241 )
148242 )
243+ self ._preserve_output_schema_field_descriptions (function_decl )
149244 return function_decl
150245
151246 @override
0 commit comments