Skip to content

Commit 7c5ac89

Browse files
committed
Enhance RLM and PythonInterpreter to support Pydantic model integration, including improved handling of model types in tool parameters and outputs. Update JSON schema generation and add tests for model serialization and attribute access in the sandbox environment.
1 parent cc314f7 commit 7c5ac89

5 files changed

Lines changed: 667 additions & 71 deletions

File tree

‎dspy/predict/rlm.py‎

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
import dspy
2323
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
2525
from dspy.primitives.code_interpreter import SIMPLE_TYPES, CodeInterpreter, CodeInterpreterError, FinalOutput
2626
from dspy.primitives.module import Module
2727
from dspy.primitives.prediction import Prediction
@@ -184,18 +184,28 @@ def _format_tool_docs(self, tools: dict[str, Tool]) -> str:
184184

185185
lines = ["\nAdditional tools available (use these instead of standard library equivalents):"]
186186
for tool in tools.values():
187-
# Build signature string from Tool's args
188187
params = []
189188
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")
191195
params.append(f"{arg_name}: {arg_type}")
192196
params_str = ", ".join(params)
193197
sig_str = f"{tool.name}({params_str})"
194198

195-
# Get description with newlines escaped
196199
desc = (tool.desc or "No description").replace("\n", " ")
197200
lines.append(f"- `{sig_str}` - {desc}")
198201

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+
199209
return "\n".join(lines)
200210

201211
def _make_llm_tools(self, max_workers: int = 8) -> dict[str, Callable]:
@@ -320,20 +330,17 @@ def _get_output_fields_info(self) -> list[dict]:
320330
for name, field in self.signature.output_fields.items():
321331
annotation = getattr(field, "annotation", str)
322332
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.
325333
if annotation in SIMPLE_TYPES:
326334
field_info["type"] = annotation.__name__
327335
else:
336+
if isinstance(annotation, type) and issubclass(annotation, pydantic.BaseModel):
337+
field_info["model_type"] = annotation.__name__
328338
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:
332341
logger.debug(
333342
"Skipping JSON schema for output field '%s' with annotation %r",
334-
name,
335-
annotation,
336-
exc_info=True,
343+
name, annotation, exc_info=True,
337344
)
338345
fields.append(field_info)
339346
return fields

0 commit comments

Comments
 (0)