|
21 | 21 |
|
22 | 22 | import dspy |
23 | 23 | from dspy.adapters.types.tool import Tool |
24 | | -from dspy.adapters.utils import parse_value, translate_field_type |
| 24 | +from dspy.adapters.utils import _get_json_schema, parse_value, translate_field_type |
25 | 25 | from dspy.primitives.code_interpreter import SIMPLE_TYPES, CodeInterpreter, CodeInterpreterError, FinalOutput |
26 | 26 | from dspy.primitives.module import Module |
27 | 27 | from dspy.primitives.prediction import Prediction |
@@ -184,18 +184,28 @@ def _format_tool_docs(self, tools: dict[str, Tool]) -> str: |
184 | 184 |
|
185 | 185 | lines = ["\nAdditional tools available (use these instead of standard library equivalents):"] |
186 | 186 | for tool in tools.values(): |
187 | | - # Build signature string from Tool's args |
188 | 187 | params = [] |
189 | 188 | for arg_name, arg_schema in (tool.args or {}).items(): |
190 | | - arg_type = arg_schema.get("type", "Any") |
| 189 | + # Use the pydantic model title when available (e.g. "Profile" not "object") |
| 190 | + title = arg_schema.get("title") |
| 191 | + if title and arg_schema.get("type") == "object": |
| 192 | + arg_type = title |
| 193 | + else: |
| 194 | + arg_type = arg_schema.get("type", "Any") |
191 | 195 | params.append(f"{arg_name}: {arg_type}") |
192 | 196 | params_str = ", ".join(params) |
193 | 197 | sig_str = f"{tool.name}({params_str})" |
194 | 198 |
|
195 | | - # Get description with newlines escaped |
196 | 199 | desc = (tool.desc or "No description").replace("\n", " ") |
197 | 200 | lines.append(f"- `{sig_str}` - {desc}") |
198 | 201 |
|
| 202 | + # Show field details for object-typed params (pydantic models) |
| 203 | + for arg_name, arg_schema in (tool.args or {}).items(): |
| 204 | + props = arg_schema.get("properties") |
| 205 | + if props: |
| 206 | + brief = ", ".join(f"{n}: {s.get('type', 'any')}" for n, s in props.items()) |
| 207 | + lines.append(f" `{arg_name}` fields: {{{brief}}}") |
| 208 | + |
199 | 209 | return "\n".join(lines) |
200 | 210 |
|
201 | 211 | def _make_llm_tools(self, max_workers: int = 8) -> dict[str, Callable]: |
@@ -320,20 +330,17 @@ def _get_output_fields_info(self) -> list[dict]: |
320 | 330 | for name, field in self.signature.output_fields.items(): |
321 | 331 | annotation = getattr(field, "annotation", str) |
322 | 332 | field_info = {"name": name} |
323 | | - # Only include type for simple types that work in function signatures |
324 | | - # Complex types are provided via JSON schema for sandbox metadata. |
325 | 333 | if annotation in SIMPLE_TYPES: |
326 | 334 | field_info["type"] = annotation.__name__ |
327 | 335 | else: |
| 336 | + if isinstance(annotation, type) and issubclass(annotation, pydantic.BaseModel): |
| 337 | + field_info["model_type"] = annotation.__name__ |
328 | 338 | try: |
329 | | - field_info["json_schema"] = pydantic.TypeAdapter(annotation).json_schema() |
330 | | - except Exception: |
331 | | - # Some Python annotations are not representable as JSON schema. |
| 339 | + field_info["json_schema"] = _get_json_schema(annotation) |
| 340 | + except pydantic.PydanticSchemaGenerationError: |
332 | 341 | logger.debug( |
333 | 342 | "Skipping JSON schema for output field '%s' with annotation %r", |
334 | | - name, |
335 | | - annotation, |
336 | | - exc_info=True, |
| 343 | + name, annotation, exc_info=True, |
337 | 344 | ) |
338 | 345 | fields.append(field_info) |
339 | 346 | return fields |
|
0 commit comments