11import asyncio
22import inspect
3- from typing import TYPE_CHECKING , Any , Callable , Optional , get_origin , get_type_hints
3+ from typing import TYPE_CHECKING , Any , Callable , Optional , Tuple , Type , get_origin , get_type_hints
44
55from jsonschema import ValidationError , validate
66from pydantic import BaseModel , TypeAdapter , create_model
99
1010if TYPE_CHECKING :
1111 import mcp
12+ from langchain .tools import BaseTool
1213
14+ _TYPE_MAPPING = {"string" : str , "integer" : int , "number" : float , "boolean" : bool , "array" : list , "object" : dict }
1315
1416class Tool :
1517 """Tool class.
@@ -94,7 +96,7 @@ def _parse_function(self, func: Callable, arg_desc: Optional[dict[str, str]] = N
9496 origin = get_origin (v ) or v
9597 if isinstance (origin , type ) and issubclass (origin , BaseModel ):
9698 # Get json schema, and replace $ref with the actual schema
97- v_json_schema = resolve_json_schema_reference (v .model_json_schema ())
99+ v_json_schema = _resolve_json_schema_reference (v .model_json_schema ())
98100 args [k ] = v_json_schema
99101 else :
100102 args [k ] = TypeAdapter (v ).json_schema ()
@@ -172,6 +174,21 @@ def from_mcp_tool(cls, session: "mcp.client.session.ClientSession", tool: "mcp.t
172174
173175 return convert_mcp_tool (session , tool )
174176
177+ @classmethod
178+ def from_langchain (cls , tool : "BaseTool" ) -> "Tool" :
179+ """
180+ Build a DSPy tool from a LangChain tool.
181+
182+ Args:
183+ tool: The LangChain tool to convert.
184+
185+ Returns:
186+ A Tool object.
187+ """
188+ from dspy .utils .langchain_tool import convert_langchain_tool
189+
190+ return convert_langchain_tool (tool )
191+
175192 def __repr__ (self ):
176193 return f"Tool(name={ self .name } , desc={ self .desc } , args={ self .args } )"
177194
@@ -181,7 +198,7 @@ def __str__(self):
181198 return f"{ self .name } { desc } { arg_desc } "
182199
183200
184- def resolve_json_schema_reference (schema : dict ) -> dict :
201+ def _resolve_json_schema_reference (schema : dict ) -> dict :
185202 """Recursively resolve json model schema, expanding all references."""
186203
187204 # If there are no definitions to resolve, return the main schema
@@ -205,3 +222,35 @@ def resolve_refs(obj: Any) -> Any:
205222 # Remove the $defs key as it's no longer needed
206223 resolved_schema .pop ("$defs" , None )
207224 return resolved_schema
225+
226+
227+ def convert_input_schema_to_tool_args (
228+ schema : dict [str , Any ],
229+ ) -> Tuple [dict [str , Any ], dict [str , Type ], dict [str , str ]]:
230+ """Convert an input json schema to tool arguments compatible with DSPy Tool.
231+
232+ Args:
233+ schema: An input json schema describing the tool's input parameters
234+
235+ Returns:
236+ A tuple of (args, arg_types, arg_desc) for DSPy Tool definition.
237+ """
238+ args , arg_types , arg_desc = {}, {}, {}
239+ properties = schema .get ("properties" , None )
240+ if properties is None :
241+ return args , arg_types , arg_desc
242+
243+ required = schema .get ("required" , [])
244+
245+ defs = schema .get ("$defs" , {})
246+
247+ for name , prop in properties .items ():
248+ if len (defs ) > 0 :
249+ prop = _resolve_json_schema_reference ({"$defs" : defs , ** prop })
250+ args [name ] = prop
251+ arg_types [name ] = _TYPE_MAPPING .get (prop .get ("type" ), Any )
252+ arg_desc [name ] = prop .get ("description" , "No description provided." )
253+ if name in required :
254+ arg_desc [name ] += " (Required)"
255+
256+ return args , arg_types , arg_desc
0 commit comments