praisonaiagents 0.0.66__py3-none-any.whl → 0.0.68__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.
- praisonaiagents/agent/agent.py +89 -19
- praisonaiagents/llm/llm.py +10 -4
- praisonaiagents/mcp/__init__.py +6 -0
- praisonaiagents/mcp/mcp.py +84 -4
- {praisonaiagents-0.0.66.dist-info → praisonaiagents-0.0.68.dist-info}/METADATA +3 -2
- {praisonaiagents-0.0.66.dist-info → praisonaiagents-0.0.68.dist-info}/RECORD +8 -7
- {praisonaiagents-0.0.66.dist-info → praisonaiagents-0.0.68.dist-info}/WHEEL +1 -1
- {praisonaiagents-0.0.66.dist-info → praisonaiagents-0.0.68.dist-info}/top_level.txt +0 -0
praisonaiagents/agent/agent.py
CHANGED
@@ -421,6 +421,12 @@ class Agent:
|
|
421
421
|
# Pass the entire string so LiteLLM can parse provider/model
|
422
422
|
self.llm_instance = LLM(model=llm)
|
423
423
|
self._using_custom_llm = True
|
424
|
+
|
425
|
+
# Ensure tools are properly accessible when using custom LLM
|
426
|
+
if tools:
|
427
|
+
logging.debug(f"Tools passed to Agent with custom LLM: {tools}")
|
428
|
+
# Store the tools for later use
|
429
|
+
self.tools = tools
|
424
430
|
except ImportError as e:
|
425
431
|
raise ImportError(
|
426
432
|
"LLM features requested but dependencies not installed. "
|
@@ -519,9 +525,20 @@ Your Goal: {self.goal}
|
|
519
525
|
"""
|
520
526
|
logging.debug(f"{self.name} executing tool {function_name} with arguments: {arguments}")
|
521
527
|
|
528
|
+
# Special handling for MCP tools
|
529
|
+
# Check if tools is an MCP instance with the requested function name
|
530
|
+
from ..mcp.mcp import MCP
|
531
|
+
if isinstance(self.tools, MCP):
|
532
|
+
logging.debug(f"Looking for MCP tool {function_name}")
|
533
|
+
# Check if any of the MCP tools match the function name
|
534
|
+
for mcp_tool in self.tools.runner.tools:
|
535
|
+
if hasattr(mcp_tool, 'name') and mcp_tool.name == function_name:
|
536
|
+
logging.debug(f"Found matching MCP tool: {function_name}")
|
537
|
+
return self.tools.runner.call_tool(function_name, arguments)
|
538
|
+
|
522
539
|
# Try to find the function in the agent's tools list first
|
523
540
|
func = None
|
524
|
-
for tool in self.tools:
|
541
|
+
for tool in self.tools if isinstance(self.tools, (list, tuple)) else []:
|
525
542
|
if (callable(tool) and getattr(tool, '__name__', '') == function_name) or \
|
526
543
|
(inspect.isclass(tool) and tool.__name__ == function_name):
|
527
544
|
func = tool
|
@@ -643,24 +660,64 @@ Your Goal: {self.goal}
|
|
643
660
|
logging.warning(f"Tool {tool} not recognized")
|
644
661
|
|
645
662
|
try:
|
646
|
-
if
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
663
|
+
# Use the custom LLM instance if available
|
664
|
+
if self._using_custom_llm and hasattr(self, 'llm_instance'):
|
665
|
+
if stream:
|
666
|
+
# Debug logs for tool info
|
667
|
+
if formatted_tools:
|
668
|
+
logging.debug(f"Passing {len(formatted_tools)} formatted tools to LLM instance: {formatted_tools}")
|
669
|
+
|
670
|
+
# Use the LLM instance for streaming responses
|
671
|
+
final_response = self.llm_instance.get_response(
|
672
|
+
prompt=messages[1:], # Skip system message as LLM handles it separately
|
673
|
+
system_prompt=messages[0]['content'] if messages and messages[0]['role'] == 'system' else None,
|
674
|
+
temperature=temperature,
|
675
|
+
tools=formatted_tools if formatted_tools else None,
|
676
|
+
verbose=self.verbose,
|
677
|
+
markdown=self.markdown,
|
678
|
+
stream=True,
|
679
|
+
console=self.console,
|
680
|
+
execute_tool_fn=self.execute_tool,
|
681
|
+
agent_name=self.name,
|
682
|
+
agent_role=self.role,
|
683
|
+
reasoning_steps=reasoning_steps
|
684
|
+
)
|
685
|
+
else:
|
686
|
+
# Non-streaming with custom LLM
|
687
|
+
final_response = self.llm_instance.get_response(
|
688
|
+
prompt=messages[1:],
|
689
|
+
system_prompt=messages[0]['content'] if messages and messages[0]['role'] == 'system' else None,
|
690
|
+
temperature=temperature,
|
691
|
+
tools=formatted_tools if formatted_tools else None,
|
692
|
+
verbose=self.verbose,
|
693
|
+
markdown=self.markdown,
|
694
|
+
stream=False,
|
695
|
+
console=self.console,
|
696
|
+
execute_tool_fn=self.execute_tool,
|
697
|
+
agent_name=self.name,
|
698
|
+
agent_role=self.role,
|
699
|
+
reasoning_steps=reasoning_steps
|
700
|
+
)
|
655
701
|
else:
|
656
|
-
#
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
702
|
+
# Use the standard OpenAI client approach
|
703
|
+
if stream:
|
704
|
+
# Process as streaming response with formatted tools
|
705
|
+
final_response = self._process_stream_response(
|
706
|
+
messages,
|
707
|
+
temperature,
|
708
|
+
start_time,
|
709
|
+
formatted_tools=formatted_tools if formatted_tools else None,
|
710
|
+
reasoning_steps=reasoning_steps
|
711
|
+
)
|
712
|
+
else:
|
713
|
+
# Process as regular non-streaming response
|
714
|
+
final_response = client.chat.completions.create(
|
715
|
+
model=self.llm,
|
716
|
+
messages=messages,
|
717
|
+
temperature=temperature,
|
718
|
+
tools=formatted_tools if formatted_tools else None,
|
719
|
+
stream=False
|
720
|
+
)
|
664
721
|
|
665
722
|
tool_calls = getattr(final_response.choices[0].message, 'tool_calls', None)
|
666
723
|
|
@@ -748,13 +805,26 @@ Your Goal: {self.goal}
|
|
748
805
|
|
749
806
|
if self._using_custom_llm:
|
750
807
|
try:
|
808
|
+
# Special handling for MCP tools when using provider/model format
|
809
|
+
tool_param = self.tools if tools is None else tools
|
810
|
+
|
811
|
+
# Convert MCP tool objects to OpenAI format if needed
|
812
|
+
if tool_param is not None:
|
813
|
+
from ..mcp.mcp import MCP
|
814
|
+
if isinstance(tool_param, MCP) and hasattr(tool_param, 'to_openai_tool'):
|
815
|
+
logging.debug("Converting MCP tool to OpenAI format")
|
816
|
+
openai_tool = tool_param.to_openai_tool()
|
817
|
+
if openai_tool:
|
818
|
+
tool_param = [openai_tool]
|
819
|
+
logging.debug(f"Converted MCP tool: {tool_param}")
|
820
|
+
|
751
821
|
# Pass everything to LLM class
|
752
822
|
response_text = self.llm_instance.get_response(
|
753
823
|
prompt=prompt,
|
754
824
|
system_prompt=f"{self.backstory}\n\nYour Role: {self.role}\n\nYour Goal: {self.goal}" if self.use_system_prompt else None,
|
755
825
|
chat_history=self.chat_history,
|
756
826
|
temperature=temperature,
|
757
|
-
tools=
|
827
|
+
tools=tool_param,
|
758
828
|
output_json=output_json,
|
759
829
|
output_pydantic=output_pydantic,
|
760
830
|
verbose=self.verbose,
|
praisonaiagents/llm/llm.py
CHANGED
@@ -289,15 +289,21 @@ class LLM:
|
|
289
289
|
if tools:
|
290
290
|
formatted_tools = []
|
291
291
|
for tool in tools:
|
292
|
-
if
|
292
|
+
# Check if the tool is already in OpenAI format (e.g. from MCP.to_openai_tool())
|
293
|
+
if isinstance(tool, dict) and 'type' in tool and tool['type'] == 'function':
|
294
|
+
logging.debug(f"Using pre-formatted OpenAI tool: {tool['function']['name']}")
|
295
|
+
formatted_tools.append(tool)
|
296
|
+
elif callable(tool):
|
293
297
|
tool_def = self._generate_tool_definition(tool.__name__)
|
298
|
+
if tool_def:
|
299
|
+
formatted_tools.append(tool_def)
|
294
300
|
elif isinstance(tool, str):
|
295
301
|
tool_def = self._generate_tool_definition(tool)
|
302
|
+
if tool_def:
|
303
|
+
formatted_tools.append(tool_def)
|
296
304
|
else:
|
297
|
-
|
305
|
+
logging.debug(f"Skipping tool of unsupported type: {type(tool)}")
|
298
306
|
|
299
|
-
if tool_def:
|
300
|
-
formatted_tools.append(tool_def)
|
301
307
|
if not formatted_tools:
|
302
308
|
formatted_tools = None
|
303
309
|
|
praisonaiagents/mcp/mcp.py
CHANGED
@@ -4,7 +4,9 @@ import queue
|
|
4
4
|
import time
|
5
5
|
import inspect
|
6
6
|
import shlex
|
7
|
-
|
7
|
+
import logging
|
8
|
+
import os
|
9
|
+
from typing import Any, List, Optional, Callable, Iterable, Union
|
8
10
|
from functools import wraps, partial
|
9
11
|
|
10
12
|
from mcp import ClientSession, StdioServerParameters
|
@@ -128,7 +130,7 @@ class MCP:
|
|
128
130
|
```
|
129
131
|
"""
|
130
132
|
|
131
|
-
def __init__(self, command_or_string=None, args=None, *, command=None, **kwargs):
|
133
|
+
def __init__(self, command_or_string=None, args=None, *, command=None, timeout=60, debug=False, **kwargs):
|
132
134
|
"""
|
133
135
|
Initialize the MCP connection and get tools.
|
134
136
|
|
@@ -136,8 +138,11 @@ class MCP:
|
|
136
138
|
command_or_string: Either:
|
137
139
|
- The command to run the MCP server (e.g., Python path)
|
138
140
|
- A complete command string (e.g., "/path/to/python /path/to/app.py")
|
141
|
+
- For NPX: 'npx' command with args for smithery tools
|
139
142
|
args: Arguments to pass to the command (when command_or_string is the command)
|
140
143
|
command: Alternative parameter name for backward compatibility
|
144
|
+
timeout: Timeout in seconds for MCP server initialization and tool calls (default: 60)
|
145
|
+
debug: Enable debug logging for MCP operations (default: False)
|
141
146
|
**kwargs: Additional parameters for StdioServerParameters
|
142
147
|
"""
|
143
148
|
# Handle backward compatibility with named parameter 'command'
|
@@ -168,9 +173,24 @@ class MCP:
|
|
168
173
|
# Wait for initialization
|
169
174
|
if not self.runner.initialized.wait(timeout=30):
|
170
175
|
print("Warning: MCP initialization timed out")
|
176
|
+
|
177
|
+
# Store additional parameters
|
178
|
+
self.timeout = timeout
|
179
|
+
self.debug = debug
|
180
|
+
|
181
|
+
if debug:
|
182
|
+
logging.getLogger("mcp-wrapper").setLevel(logging.DEBUG)
|
183
|
+
|
184
|
+
# Automatically detect if this is an NPX command
|
185
|
+
self.is_npx = cmd == 'npx' or (isinstance(cmd, str) and os.path.basename(cmd) == 'npx')
|
171
186
|
|
172
|
-
#
|
173
|
-
|
187
|
+
# For NPX-based MCP servers, use a different approach
|
188
|
+
if self.is_npx:
|
189
|
+
self._function_declarations = []
|
190
|
+
self._initialize_npx_mcp_tools(cmd, arguments)
|
191
|
+
else:
|
192
|
+
# Generate tool functions immediately and store them
|
193
|
+
self._tools = self._generate_tool_functions()
|
174
194
|
|
175
195
|
def _generate_tool_functions(self) -> List[Callable]:
|
176
196
|
"""
|
@@ -264,6 +284,27 @@ class MCP:
|
|
264
284
|
|
265
285
|
return wrapper
|
266
286
|
|
287
|
+
def _initialize_npx_mcp_tools(self, cmd, arguments):
|
288
|
+
"""Initialize the NPX MCP tools by extracting tool definitions."""
|
289
|
+
try:
|
290
|
+
# For NPX tools, we'll use the same approach as regular MCP tools
|
291
|
+
# but we need to handle the initialization differently
|
292
|
+
if self.debug:
|
293
|
+
logging.debug(f"Initializing NPX MCP tools with command: {cmd} {' '.join(arguments)}")
|
294
|
+
|
295
|
+
# Generate tool functions using the regular MCP approach
|
296
|
+
self._tools = self._generate_tool_functions()
|
297
|
+
|
298
|
+
if self.debug:
|
299
|
+
logging.debug(f"Generated {len(self._tools)} NPX MCP tools")
|
300
|
+
|
301
|
+
except Exception as e:
|
302
|
+
if self.debug:
|
303
|
+
logging.error(f"Failed to initialize NPX MCP tools: {e}")
|
304
|
+
raise RuntimeError(f"Failed to initialize NPX MCP tools: {e}")
|
305
|
+
|
306
|
+
|
307
|
+
|
267
308
|
def __iter__(self) -> Iterable[Callable]:
|
268
309
|
"""
|
269
310
|
Allow the MCP instance to be used directly as an iterable of tools.
|
@@ -272,6 +313,45 @@ class MCP:
|
|
272
313
|
"""
|
273
314
|
return iter(self._tools)
|
274
315
|
|
316
|
+
def to_openai_tool(self):
|
317
|
+
"""Convert the MCP tool to an OpenAI-compatible tool definition.
|
318
|
+
|
319
|
+
This method is specifically invoked by the Agent class when using
|
320
|
+
provider/model format (e.g., "openai/gpt-4o-mini").
|
321
|
+
|
322
|
+
Returns:
|
323
|
+
dict: OpenAI-compatible tool definition
|
324
|
+
"""
|
325
|
+
# For simplicity, we'll convert the first tool only if multiple exist
|
326
|
+
# More complex implementations could handle multiple tools
|
327
|
+
if not self.runner.tools:
|
328
|
+
logging.warning("No MCP tools available to convert to OpenAI format")
|
329
|
+
return None
|
330
|
+
|
331
|
+
# Get the first tool's schema
|
332
|
+
tool = self.runner.tools[0]
|
333
|
+
|
334
|
+
# Create OpenAI tool definition
|
335
|
+
parameters = {}
|
336
|
+
if hasattr(tool, 'inputSchema') and tool.inputSchema:
|
337
|
+
parameters = tool.inputSchema
|
338
|
+
else:
|
339
|
+
# Create a minimal schema if none exists
|
340
|
+
parameters = {
|
341
|
+
"type": "object",
|
342
|
+
"properties": {},
|
343
|
+
"required": []
|
344
|
+
}
|
345
|
+
|
346
|
+
return {
|
347
|
+
"type": "function",
|
348
|
+
"function": {
|
349
|
+
"name": tool.name,
|
350
|
+
"description": tool.description if hasattr(tool, 'description') else f"Call the {tool.name} tool",
|
351
|
+
"parameters": parameters
|
352
|
+
}
|
353
|
+
}
|
354
|
+
|
275
355
|
def __del__(self):
|
276
356
|
"""Clean up resources when the object is garbage collected."""
|
277
357
|
if hasattr(self, 'runner'):
|
@@ -1,11 +1,12 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: praisonaiagents
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.68
|
4
4
|
Summary: Praison AI agents for completing complex tasks with Self Reflection Agents
|
5
5
|
Author: Mervin Praison
|
6
6
|
Requires-Dist: pydantic
|
7
7
|
Requires-Dist: rich
|
8
8
|
Requires-Dist: openai
|
9
|
+
Requires-Dist: mcp==1.6.0
|
9
10
|
Provides-Extra: memory
|
10
11
|
Requires-Dist: chromadb>=0.5.23; extra == "memory"
|
11
12
|
Provides-Extra: knowledge
|
@@ -1,7 +1,7 @@
|
|
1
1
|
praisonaiagents/__init__.py,sha256=Z2_rSA6mYozz0r3ioUgKzl3QV8uWRDS_QaqPg2oGjqg,1324
|
2
2
|
praisonaiagents/main.py,sha256=l29nGEbV2ReBi4szURbnH0Fk0w2F_QZTmECysyZjYcA,15066
|
3
3
|
praisonaiagents/agent/__init__.py,sha256=j0T19TVNbfZcClvpbZDDinQxZ0oORgsMrMqx16jZ-bA,128
|
4
|
-
praisonaiagents/agent/agent.py,sha256=
|
4
|
+
praisonaiagents/agent/agent.py,sha256=NiTUqkibCKW6Rx0HbR1peY3TtOcQl2O8XdrEV__gPlM,65805
|
5
5
|
praisonaiagents/agent/image_agent.py,sha256=-5MXG594HVwSpFMcidt16YBp7udtik-Cp7eXlzLE1fY,8696
|
6
6
|
praisonaiagents/agents/__init__.py,sha256=_1d6Pqyk9EoBSo7E68sKyd1jDRlN1vxvVIRpoMc0Jcw,168
|
7
7
|
praisonaiagents/agents/agents.py,sha256=KLfSuz6nGEJM-n4xCXdtIkES6cUI-LwvMxI3R-MjrD0,37862
|
@@ -10,8 +10,9 @@ praisonaiagents/knowledge/__init__.py,sha256=xL1Eh-a3xsHyIcU4foOWF-JdWYIYBALJH9b
|
|
10
10
|
praisonaiagents/knowledge/chunking.py,sha256=FzoNY0q8MkvG4gADqk4JcRhmH3lcEHbRdonDgitQa30,6624
|
11
11
|
praisonaiagents/knowledge/knowledge.py,sha256=fQNREDiwdoisfIxJBLVkteXgq_8Gbypfc3UaZbxf5QY,13210
|
12
12
|
praisonaiagents/llm/__init__.py,sha256=ttPQQJQq6Tah-0updoEXDZFKWtJAM93rBWRoIgxRWO8,689
|
13
|
-
praisonaiagents/llm/llm.py,sha256=
|
14
|
-
praisonaiagents/mcp/
|
13
|
+
praisonaiagents/llm/llm.py,sha256=l7Z2QjD9eFy0Zq5bwTVK7VOUHxeTyx866YWt3fS3vz8,74606
|
14
|
+
praisonaiagents/mcp/__init__.py,sha256=IkYdrAK1bDQDm_0t3Wjt63Zwv3_IJgqz84Wqz9GH2iQ,111
|
15
|
+
praisonaiagents/mcp/mcp.py,sha256=BPPf5AIPXx28PaJJqOg6T3NRyymQH9YAD-Km7Ma9-KA,13681
|
15
16
|
praisonaiagents/memory/memory.py,sha256=I8dOTkrl1i-GgQbDcrFOsSruzJ7MiI6Ys37DK27wrUs,35537
|
16
17
|
praisonaiagents/process/__init__.py,sha256=lkYbL7Hn5a0ldvJtkdH23vfIIZLIcanK-65C0MwaorY,52
|
17
18
|
praisonaiagents/process/process.py,sha256=HPw84OhnKQW3EyrDkpoQu0DcpxThbrzR2hWUgwQh9Pw,59955
|
@@ -38,7 +39,7 @@ praisonaiagents/tools/xml_tools.py,sha256=iYTMBEk5l3L3ryQ1fkUnNVYK-Nnua2Kx2S0dxN
|
|
38
39
|
praisonaiagents/tools/yaml_tools.py,sha256=uogAZrhXV9O7xvspAtcTfpKSQYL2nlOTvCQXN94-G9A,14215
|
39
40
|
praisonaiagents/tools/yfinance_tools.py,sha256=s2PBj_1v7oQnOobo2fDbQBACEHl61ftG4beG6Z979ZE,8529
|
40
41
|
praisonaiagents/tools/train/data/generatecot.py,sha256=H6bNh-E2hqL5MW6kX3hqZ05g9ETKN2-kudSjiuU_SD8,19403
|
41
|
-
praisonaiagents-0.0.
|
42
|
-
praisonaiagents-0.0.
|
43
|
-
praisonaiagents-0.0.
|
44
|
-
praisonaiagents-0.0.
|
42
|
+
praisonaiagents-0.0.68.dist-info/METADATA,sha256=wMy9WDu6aGcQWrQIFT1UMOfB-wcvrGqpeDwXqbQXIvM,856
|
43
|
+
praisonaiagents-0.0.68.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
44
|
+
praisonaiagents-0.0.68.dist-info/top_level.txt,sha256=_HsRddrJ23iDx5TTqVUVvXG2HeHBL5voshncAMDGjtA,16
|
45
|
+
praisonaiagents-0.0.68.dist-info/RECORD,,
|
File without changes
|