@@ -282,6 +282,23 @@ def get_response(
282282 # Disable litellm debug messages
283283 litellm .set_verbose = False
284284
285+ # Format tools if provided
286+ formatted_tools = None
287+ if tools :
288+ formatted_tools = []
289+ for tool in tools :
290+ if callable (tool ):
291+ tool_def = self ._generate_tool_definition (tool .__name__ )
292+ elif isinstance (tool , str ):
293+ tool_def = self ._generate_tool_definition (tool )
294+ else :
295+ continue
296+
297+ if tool_def :
298+ formatted_tools .append (tool_def )
299+ if not formatted_tools :
300+ formatted_tools = None
301+
285302 # Build messages list
286303 messages = []
287304 if system_prompt :
@@ -340,6 +357,7 @@ def get_response(
340357 messages = messages ,
341358 temperature = temperature ,
342359 stream = False , # force non-streaming
360+ tools = formatted_tools ,
343361 ** {k :v for k ,v in kwargs .items () if k != 'reasoning_steps' }
344362 )
345363 reasoning_content = resp ["choices" ][0 ]["message" ].get ("provider_specific_fields" , {}).get ("reasoning_content" )
@@ -371,6 +389,7 @@ def get_response(
371389 for chunk in litellm .completion (
372390 model = self .model ,
373391 messages = messages ,
392+ tools = formatted_tools ,
374393 temperature = temperature ,
375394 stream = True ,
376395 ** kwargs
@@ -385,6 +404,7 @@ def get_response(
385404 for chunk in litellm .completion (
386405 model = self .model ,
387406 messages = messages ,
407+ tools = formatted_tools ,
388408 temperature = temperature ,
389409 stream = True ,
390410 ** kwargs
@@ -398,6 +418,7 @@ def get_response(
398418 final_response = litellm .completion (
399419 model = self .model ,
400420 messages = messages ,
421+ tools = formatted_tools ,
401422 temperature = temperature ,
402423 stream = False , # No streaming for tool call check
403424 ** kwargs
@@ -1386,4 +1407,117 @@ async def aresponse(
13861407
13871408 except Exception as error :
13881409 display_error (f"Error in response_async: { str (error )} " )
1389- raise
1410+ raise
1411+
1412+ def _generate_tool_definition (self , function_name : str ) -> Optional [Dict ]:
1413+ """Generate a tool definition from a function name."""
1414+ logging .debug (f"Attempting to generate tool definition for: { function_name } " )
1415+
1416+ # First try to get the tool definition if it exists
1417+ tool_def_name = f"{ function_name } _definition"
1418+ tool_def = globals ().get (tool_def_name )
1419+ logging .debug (f"Looking for { tool_def_name } in globals: { tool_def is not None } " )
1420+
1421+ if not tool_def :
1422+ import __main__
1423+ tool_def = getattr (__main__ , tool_def_name , None )
1424+ logging .debug (f"Looking for { tool_def_name } in __main__: { tool_def is not None } " )
1425+
1426+ if tool_def :
1427+ logging .debug (f"Found tool definition: { tool_def } " )
1428+ return tool_def
1429+
1430+ # Try to find the function
1431+ func = globals ().get (function_name )
1432+ logging .debug (f"Looking for { function_name } in globals: { func is not None } " )
1433+
1434+ if not func :
1435+ import __main__
1436+ func = getattr (__main__ , function_name , None )
1437+ logging .debug (f"Looking for { function_name } in __main__: { func is not None } " )
1438+
1439+ if not func or not callable (func ):
1440+ logging .debug (f"Function { function_name } not found or not callable" )
1441+ return None
1442+
1443+ import inspect
1444+ # Handle Langchain and CrewAI tools
1445+ if inspect .isclass (func ) and hasattr (func , 'run' ) and not hasattr (func , '_run' ):
1446+ original_func = func
1447+ func = func .run
1448+ function_name = original_func .__name__
1449+ elif inspect .isclass (func ) and hasattr (func , '_run' ):
1450+ original_func = func
1451+ func = func ._run
1452+ function_name = original_func .__name__
1453+
1454+ sig = inspect .signature (func )
1455+ logging .debug (f"Function signature: { sig } " )
1456+
1457+ # Skip self, *args, **kwargs
1458+ parameters_list = []
1459+ for name , param in sig .parameters .items ():
1460+ if name == "self" :
1461+ continue
1462+ if param .kind in (inspect .Parameter .VAR_POSITIONAL , inspect .Parameter .VAR_KEYWORD ):
1463+ continue
1464+ parameters_list .append ((name , param ))
1465+
1466+ parameters = {
1467+ "type" : "object" ,
1468+ "properties" : {},
1469+ "required" : []
1470+ }
1471+
1472+ # Parse docstring for parameter descriptions
1473+ docstring = inspect .getdoc (func )
1474+ logging .debug (f"Function docstring: { docstring } " )
1475+
1476+ param_descriptions = {}
1477+ if docstring :
1478+ import re
1479+ param_section = re .split (r'\s*Args:\s*' , docstring )
1480+ logging .debug (f"Param section split: { param_section } " )
1481+ if len (param_section ) > 1 :
1482+ param_lines = param_section [1 ].split ('\n ' )
1483+ for line in param_lines :
1484+ line = line .strip ()
1485+ if line and ':' in line :
1486+ param_name , param_desc = line .split (':' , 1 )
1487+ param_descriptions [param_name .strip ()] = param_desc .strip ()
1488+
1489+ logging .debug (f"Parameter descriptions: { param_descriptions } " )
1490+
1491+ for name , param in parameters_list :
1492+ param_type = "string" # Default type
1493+ if param .annotation != inspect .Parameter .empty :
1494+ if param .annotation == int :
1495+ param_type = "integer"
1496+ elif param .annotation == float :
1497+ param_type = "number"
1498+ elif param .annotation == bool :
1499+ param_type = "boolean"
1500+ elif param .annotation == list :
1501+ param_type = "array"
1502+ elif param .annotation == dict :
1503+ param_type = "object"
1504+
1505+ parameters ["properties" ][name ] = {
1506+ "type" : param_type ,
1507+ "description" : param_descriptions .get (name , "Parameter description not available" )
1508+ }
1509+
1510+ if param .default == inspect .Parameter .empty :
1511+ parameters ["required" ].append (name )
1512+
1513+ logging .debug (f"Generated parameters: { parameters } " )
1514+ tool_def = {
1515+ "type" : "function" ,
1516+ "function" : {
1517+ "name" : function_name ,
1518+ "description" : docstring .split ('\n \n ' )[0 ] if docstring else "No description available" ,
1519+ "parameters" : parameters
1520+ }
1521+ }
1522+ logging .debug (f"Generated tool definition: { tool_def } " )
1523+ return tool_def
0 commit comments