praisonaiagents 0.0.112__py3-none-any.whl → 0.0.113__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -364,6 +364,66 @@ class LLM:
364
364
 
365
365
  return messages, original_prompt
366
366
 
367
+ def _format_tools_for_litellm(self, tools: Optional[List[Any]]) -> Optional[List[Dict]]:
368
+ """Format tools for LiteLLM - handles all tool formats.
369
+
370
+ Supports:
371
+ - Pre-formatted OpenAI tools (dicts with type='function')
372
+ - Lists of pre-formatted tools
373
+ - Callable functions
374
+ - String function names
375
+
376
+ Args:
377
+ tools: List of tools in various formats
378
+
379
+ Returns:
380
+ List of formatted tools or None
381
+ """
382
+ if not tools:
383
+ return None
384
+
385
+ formatted_tools = []
386
+ for tool in tools:
387
+ # Check if the tool is already in OpenAI format (e.g. from MCP.to_openai_tool())
388
+ if isinstance(tool, dict) and 'type' in tool and tool['type'] == 'function':
389
+ # Validate nested dictionary structure before accessing
390
+ if 'function' in tool and isinstance(tool['function'], dict) and 'name' in tool['function']:
391
+ logging.debug(f"Using pre-formatted OpenAI tool: {tool['function']['name']}")
392
+ formatted_tools.append(tool)
393
+ else:
394
+ logging.debug(f"Skipping malformed OpenAI tool: missing function or name")
395
+ # Handle lists of tools (e.g. from MCP.to_openai_tool())
396
+ elif isinstance(tool, list):
397
+ for subtool in tool:
398
+ if isinstance(subtool, dict) and 'type' in subtool and subtool['type'] == 'function':
399
+ # Validate nested dictionary structure before accessing
400
+ if 'function' in subtool and isinstance(subtool['function'], dict) and 'name' in subtool['function']:
401
+ logging.debug(f"Using pre-formatted OpenAI tool from list: {subtool['function']['name']}")
402
+ formatted_tools.append(subtool)
403
+ else:
404
+ logging.debug(f"Skipping malformed OpenAI tool in list: missing function or name")
405
+ elif callable(tool):
406
+ tool_def = self._generate_tool_definition(tool)
407
+ if tool_def:
408
+ formatted_tools.append(tool_def)
409
+ elif isinstance(tool, str):
410
+ tool_def = self._generate_tool_definition(tool)
411
+ if tool_def:
412
+ formatted_tools.append(tool_def)
413
+ else:
414
+ logging.debug(f"Skipping tool of unsupported type: {type(tool)}")
415
+
416
+ # Validate JSON serialization before returning
417
+ if formatted_tools:
418
+ try:
419
+ import json
420
+ json.dumps(formatted_tools) # Validate serialization
421
+ except (TypeError, ValueError) as e:
422
+ logging.error(f"Tools are not JSON serializable: {e}")
423
+ return None
424
+
425
+ return formatted_tools if formatted_tools else None
426
+
367
427
  def get_response(
368
428
  self,
369
429
  prompt: Union[str, List[Dict]],
@@ -445,33 +505,7 @@ class LLM:
445
505
  litellm.set_verbose = False
446
506
 
447
507
  # Format tools if provided
448
- formatted_tools = None
449
- if tools:
450
- formatted_tools = []
451
- for tool in tools:
452
- # Check if the tool is already in OpenAI format (e.g. from MCP.to_openai_tool())
453
- if isinstance(tool, dict) and 'type' in tool and tool['type'] == 'function':
454
- logging.debug(f"Using pre-formatted OpenAI tool: {tool['function']['name']}")
455
- formatted_tools.append(tool)
456
- # Handle lists of tools (e.g. from MCP.to_openai_tool())
457
- elif isinstance(tool, list):
458
- for subtool in tool:
459
- if isinstance(subtool, dict) and 'type' in subtool and subtool['type'] == 'function':
460
- logging.debug(f"Using pre-formatted OpenAI tool from list: {subtool['function']['name']}")
461
- formatted_tools.append(subtool)
462
- elif callable(tool):
463
- tool_def = self._generate_tool_definition(tool.__name__)
464
- if tool_def:
465
- formatted_tools.append(tool_def)
466
- elif isinstance(tool, str):
467
- tool_def = self._generate_tool_definition(tool)
468
- if tool_def:
469
- formatted_tools.append(tool_def)
470
- else:
471
- logging.debug(f"Skipping tool of unsupported type: {type(tool)}")
472
-
473
- if not formatted_tools:
474
- formatted_tools = None
508
+ formatted_tools = self._format_tools_for_litellm(tools)
475
509
 
476
510
  # Build messages list using shared helper
477
511
  messages, original_prompt = self._build_messages(
@@ -1202,74 +1236,8 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
1202
1236
  start_time = time.time()
1203
1237
  reflection_count = 0
1204
1238
 
1205
- # Format tools for LiteLLM
1206
- formatted_tools = None
1207
- if tools:
1208
- logging.debug(f"Starting tool formatting for {len(tools)} tools")
1209
- formatted_tools = []
1210
- for tool in tools:
1211
- logging.debug(f"Processing tool: {tool.__name__ if hasattr(tool, '__name__') else str(tool)}")
1212
- if hasattr(tool, '__name__'):
1213
- tool_name = tool.__name__
1214
- tool_doc = tool.__doc__ or "No description available"
1215
- # Get function signature
1216
- import inspect
1217
- sig = inspect.signature(tool)
1218
- logging.debug(f"Tool signature: {sig}")
1219
- params = {}
1220
- required = []
1221
- for name, param in sig.parameters.items():
1222
- logging.debug(f"Processing parameter: {name} with annotation: {param.annotation}")
1223
- param_type = "string"
1224
- if param.annotation != inspect.Parameter.empty:
1225
- if param.annotation == int:
1226
- param_type = "integer"
1227
- elif param.annotation == float:
1228
- param_type = "number"
1229
- elif param.annotation == bool:
1230
- param_type = "boolean"
1231
- elif param.annotation == Dict:
1232
- param_type = "object"
1233
- elif param.annotation == List:
1234
- param_type = "array"
1235
- elif hasattr(param.annotation, "__name__"):
1236
- param_type = param.annotation.__name__.lower()
1237
- params[name] = {"type": param_type}
1238
- if param.default == inspect.Parameter.empty:
1239
- required.append(name)
1240
-
1241
- logging.debug(f"Generated parameters: {params}")
1242
- logging.debug(f"Required parameters: {required}")
1243
-
1244
- tool_def = {
1245
- "type": "function",
1246
- "function": {
1247
- "name": tool_name,
1248
- "description": tool_doc,
1249
- "parameters": {
1250
- "type": "object",
1251
- "properties": params,
1252
- "required": required
1253
- }
1254
- }
1255
- }
1256
- # Ensure tool definition is JSON serializable
1257
- try:
1258
- json.dumps(tool_def) # Test serialization
1259
- logging.debug(f"Generated tool definition: {tool_def}")
1260
- formatted_tools.append(tool_def)
1261
- except TypeError as e:
1262
- logging.error(f"Tool definition not JSON serializable: {e}")
1263
- continue
1264
-
1265
- # Validate final tools list
1266
- if formatted_tools:
1267
- try:
1268
- json.dumps(formatted_tools) # Final serialization check
1269
- logging.debug(f"Final formatted tools: {json.dumps(formatted_tools, indent=2)}")
1270
- except TypeError as e:
1271
- logging.error(f"Final tools list not JSON serializable: {e}")
1272
- formatted_tools = None
1239
+ # Format tools for LiteLLM using the shared helper
1240
+ formatted_tools = self._format_tools_for_litellm(tools)
1273
1241
 
1274
1242
  response_text = ""
1275
1243
  if reasoning_steps:
@@ -2070,36 +2038,44 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
2070
2038
  display_error(f"Error in response_async: {str(error)}")
2071
2039
  raise
2072
2040
 
2073
- def _generate_tool_definition(self, function_name: str) -> Optional[Dict]:
2074
- """Generate a tool definition from a function name."""
2075
- logging.debug(f"Attempting to generate tool definition for: {function_name}")
2076
-
2077
- # First try to get the tool definition if it exists
2078
- tool_def_name = f"{function_name}_definition"
2079
- tool_def = globals().get(tool_def_name)
2080
- logging.debug(f"Looking for {tool_def_name} in globals: {tool_def is not None}")
2081
-
2082
- if not tool_def:
2083
- import __main__
2084
- tool_def = getattr(__main__, tool_def_name, None)
2085
- logging.debug(f"Looking for {tool_def_name} in __main__: {tool_def is not None}")
2086
-
2087
- if tool_def:
2088
- logging.debug(f"Found tool definition: {tool_def}")
2089
- return tool_def
2041
+ def _generate_tool_definition(self, function_or_name) -> Optional[Dict]:
2042
+ """Generate a tool definition from a function or function name."""
2043
+ if callable(function_or_name):
2044
+ # Function object passed directly
2045
+ func = function_or_name
2046
+ function_name = func.__name__
2047
+ logging.debug(f"Generating tool definition for callable: {function_name}")
2048
+ else:
2049
+ # Function name string passed
2050
+ function_name = function_or_name
2051
+ logging.debug(f"Attempting to generate tool definition for: {function_name}")
2052
+
2053
+ # First try to get the tool definition if it exists
2054
+ tool_def_name = f"{function_name}_definition"
2055
+ tool_def = globals().get(tool_def_name)
2056
+ logging.debug(f"Looking for {tool_def_name} in globals: {tool_def is not None}")
2057
+
2058
+ if not tool_def:
2059
+ import __main__
2060
+ tool_def = getattr(__main__, tool_def_name, None)
2061
+ logging.debug(f"Looking for {tool_def_name} in __main__: {tool_def is not None}")
2062
+
2063
+ if tool_def:
2064
+ logging.debug(f"Found tool definition: {tool_def}")
2065
+ return tool_def
2090
2066
 
2091
- # Try to find the function
2092
- func = globals().get(function_name)
2093
- logging.debug(f"Looking for {function_name} in globals: {func is not None}")
2094
-
2095
- if not func:
2096
- import __main__
2097
- func = getattr(__main__, function_name, None)
2098
- logging.debug(f"Looking for {function_name} in __main__: {func is not None}")
2099
-
2100
- if not func or not callable(func):
2101
- logging.debug(f"Function {function_name} not found or not callable")
2102
- return None
2067
+ # Try to find the function
2068
+ func = globals().get(function_name)
2069
+ logging.debug(f"Looking for {function_name} in globals: {func is not None}")
2070
+
2071
+ if not func:
2072
+ import __main__
2073
+ func = getattr(__main__, function_name, None)
2074
+ logging.debug(f"Looking for {function_name} in __main__: {func is not None}")
2075
+
2076
+ if not func or not callable(func):
2077
+ logging.debug(f"Function {function_name} not found or not callable")
2078
+ return None
2103
2079
 
2104
2080
  import inspect
2105
2081
  # Handle Langchain and CrewAI tools
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: praisonaiagents
3
- Version: 0.0.112
3
+ Version: 0.0.113
4
4
  Summary: Praison AI agents for completing complex tasks with Self Reflection Agents
5
5
  Author: Mervin Praison
6
6
  Requires-Python: >=3.10
@@ -16,7 +16,7 @@ praisonaiagents/knowledge/__init__.py,sha256=xL1Eh-a3xsHyIcU4foOWF-JdWYIYBALJH9b
16
16
  praisonaiagents/knowledge/chunking.py,sha256=G6wyHa7_8V0_7VpnrrUXbEmUmptlT16ISJYaxmkSgmU,7678
17
17
  praisonaiagents/knowledge/knowledge.py,sha256=OKPar-XGyAp1ndmbOOdCgqFnTCqpOThYVSIZRxZyP58,15683
18
18
  praisonaiagents/llm/__init__.py,sha256=bSywIHBHH0YUf4hSx-FmFXkRv2g1Rlhuk-gjoImE8j8,925
19
- praisonaiagents/llm/llm.py,sha256=Gk4ILbegZtxc3iFCI2H2Zglfc73xNhsEjW1fWu12ltM,113025
19
+ praisonaiagents/llm/llm.py,sha256=z7o4tlKO0NJCqaXlnlwtPT768YjAB6tqNe_lg2KMTkk,111271
20
20
  praisonaiagents/mcp/__init__.py,sha256=ibbqe3_7XB7VrIcUcetkZiUZS1fTVvyMy_AqCSFG8qc,240
21
21
  praisonaiagents/mcp/mcp.py,sha256=-fFx4MHffnN2woLnnV7Pzx3-1SFkn2j8Gp5F5ZIwKJ0,19698
22
22
  praisonaiagents/mcp/mcp_sse.py,sha256=z8TMFhW9xuLQ7QnpOa3n1-nSHt0-Bf27qso0u4qxYSY,8357
@@ -52,7 +52,7 @@ praisonaiagents/tools/xml_tools.py,sha256=iYTMBEk5l3L3ryQ1fkUnNVYK-Nnua2Kx2S0dxN
52
52
  praisonaiagents/tools/yaml_tools.py,sha256=uogAZrhXV9O7xvspAtcTfpKSQYL2nlOTvCQXN94-G9A,14215
53
53
  praisonaiagents/tools/yfinance_tools.py,sha256=s2PBj_1v7oQnOobo2fDbQBACEHl61ftG4beG6Z979ZE,8529
54
54
  praisonaiagents/tools/train/data/generatecot.py,sha256=H6bNh-E2hqL5MW6kX3hqZ05g9ETKN2-kudSjiuU_SD8,19403
55
- praisonaiagents-0.0.112.dist-info/METADATA,sha256=qKFv-nDM6_21eStVCY503Btr0plIKKhVjZQOg6V2jDM,1669
56
- praisonaiagents-0.0.112.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
57
- praisonaiagents-0.0.112.dist-info/top_level.txt,sha256=_HsRddrJ23iDx5TTqVUVvXG2HeHBL5voshncAMDGjtA,16
58
- praisonaiagents-0.0.112.dist-info/RECORD,,
55
+ praisonaiagents-0.0.113.dist-info/METADATA,sha256=er3TyTx5TQPXGEtyNVBXkNnUUKo8jf7nBtv908Y7WlY,1669
56
+ praisonaiagents-0.0.113.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
57
+ praisonaiagents-0.0.113.dist-info/top_level.txt,sha256=_HsRddrJ23iDx5TTqVUVvXG2HeHBL5voshncAMDGjtA,16
58
+ praisonaiagents-0.0.113.dist-info/RECORD,,