PraisonAI 1.0.8__tar.gz → 2.0.1__tar.gz
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.
Potentially problematic release.
This version of PraisonAI might be problematic. Click here for more details.
- {praisonai-1.0.8 → praisonai-2.0.1}/PKG-INFO +18 -16
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/agents_generator.py +145 -4
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/auto.py +13 -1
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/cli.py +19 -13
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/deploy.py +1 -1
- praisonai-2.0.1/praisonai/ui/components/aicoder.py +269 -0
- praisonai-2.0.1/praisonai/ui/config/.chainlit/config.toml +120 -0
- praisonai-2.0.1/praisonai/ui/config/.chainlit/translations/bn.json +231 -0
- praisonai-2.0.1/praisonai/ui/config/.chainlit/translations/en-US.json +229 -0
- praisonai-2.0.1/praisonai/ui/config/.chainlit/translations/gu.json +231 -0
- praisonai-2.0.1/praisonai/ui/config/.chainlit/translations/he-IL.json +231 -0
- praisonai-2.0.1/praisonai/ui/config/.chainlit/translations/hi.json +231 -0
- praisonai-2.0.1/praisonai/ui/config/.chainlit/translations/kn.json +231 -0
- praisonai-2.0.1/praisonai/ui/config/.chainlit/translations/ml.json +231 -0
- praisonai-2.0.1/praisonai/ui/config/.chainlit/translations/mr.json +231 -0
- praisonai-2.0.1/praisonai/ui/config/.chainlit/translations/ta.json +231 -0
- praisonai-2.0.1/praisonai/ui/config/.chainlit/translations/te.json +231 -0
- praisonai-2.0.1/praisonai/ui/config/.chainlit/translations/zh-CN.json +229 -0
- praisonai-2.0.1/praisonai/ui/config/chainlit.md +1 -0
- praisonai-2.0.1/praisonai/ui/config/translations/bn.json +231 -0
- praisonai-2.0.1/praisonai/ui/config/translations/en-US.json +229 -0
- praisonai-2.0.1/praisonai/ui/config/translations/gu.json +231 -0
- praisonai-2.0.1/praisonai/ui/config/translations/he-IL.json +231 -0
- praisonai-2.0.1/praisonai/ui/config/translations/hi.json +231 -0
- praisonai-2.0.1/praisonai/ui/config/translations/kn.json +231 -0
- praisonai-2.0.1/praisonai/ui/config/translations/ml.json +231 -0
- praisonai-2.0.1/praisonai/ui/config/translations/mr.json +231 -0
- praisonai-2.0.1/praisonai/ui/config/translations/ta.json +231 -0
- praisonai-2.0.1/praisonai/ui/config/translations/te.json +231 -0
- praisonai-2.0.1/praisonai/ui/config/translations/zh-CN.json +229 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/ui/db.py +51 -2
- praisonai-2.0.1/praisonai/ui/public/praison.css +3 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/pyproject.toml +86 -5
- {praisonai-1.0.8 → praisonai-2.0.1}/LICENSE +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/README.md +13 -13
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/__init__.py +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/__main__.py +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/api/call.py +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/chainlit_ui.py +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/inbuilt_tools/__init__.py +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/inbuilt_tools/autogen_tools.py +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/inc/__init__.py +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/inc/config.py +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/inc/models.py +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/public/android-chrome-192x192.png +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/public/android-chrome-512x512.png +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/public/apple-touch-icon.png +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/public/fantasy.svg +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/public/favicon-16x16.png +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/public/favicon-32x32.png +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/public/favicon.ico +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/public/game.svg +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/public/logo_dark.png +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/public/logo_light.png +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/public/movie.svg +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/public/thriller.svg +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/setup/__init__.py +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/setup/build.py +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/setup/config.yaml +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/setup/post_install.py +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/setup/setup_conda_env.py +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/setup/setup_conda_env.sh +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/setup.py +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/test.py +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/train.py +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/ui/README.md +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/ui/chat.py +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/ui/code.py +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/ui/context.py +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/ui/public/fantasy.svg +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/ui/public/game.svg +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/ui/public/logo_dark.png +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/ui/public/logo_light.png +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/ui/public/movie.svg +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/ui/public/thriller.svg +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/ui/realtime.py +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/ui/realtimeclient/__init__.py +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/ui/realtimeclient/realtimedocs.txt +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/ui/realtimeclient/tools.py +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/ui/sql_alchemy.py +0 -0
- {praisonai-1.0.8 → praisonai-2.0.1}/praisonai/version.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: PraisonAI
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.1
|
|
4
4
|
Summary: PraisonAI application combines AutoGen and CrewAI or similar frameworks into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customization, and efficient human-agent collaboration.
|
|
5
5
|
Author: Mervin Praison
|
|
6
6
|
Requires-Python: >=3.10,<3.13
|
|
@@ -23,9 +23,10 @@ Provides-Extra: openai
|
|
|
23
23
|
Provides-Extra: realtime
|
|
24
24
|
Provides-Extra: train
|
|
25
25
|
Provides-Extra: ui
|
|
26
|
+
Requires-Dist: PyYAML (>=6.0)
|
|
26
27
|
Requires-Dist: agentops (>=0.3.12) ; extra == "agentops"
|
|
27
28
|
Requires-Dist: aiosqlite (>=0.20.0) ; extra == "chat" or extra == "code" or extra == "realtime"
|
|
28
|
-
Requires-Dist: chainlit (==
|
|
29
|
+
Requires-Dist: chainlit (==2.0rc1) ; extra == "ui" or extra == "chat" or extra == "code" or extra == "realtime"
|
|
29
30
|
Requires-Dist: crawl4ai (==0.3.4) ; extra == "chat" or extra == "code" or extra == "realtime"
|
|
30
31
|
Requires-Dist: crewai (>=0.32.0) ; extra == "crewai" or extra == "autogen"
|
|
31
32
|
Requires-Dist: duckduckgo_search (>=6.3.0) ; extra == "realtime"
|
|
@@ -45,11 +46,12 @@ Requires-Dist: openai (>=1.54.0) ; extra == "call"
|
|
|
45
46
|
Requires-Dist: playwright (>=1.47.0) ; extra == "chat" or extra == "code"
|
|
46
47
|
Requires-Dist: plotly (>=5.24.0) ; extra == "realtime"
|
|
47
48
|
Requires-Dist: praisonai-tools (>=0.0.7) ; extra == "crewai" or extra == "autogen"
|
|
49
|
+
Requires-Dist: praisonaiagents (>=0.0.4)
|
|
48
50
|
Requires-Dist: pyautogen (>=0.2.19) ; extra == "autogen"
|
|
49
51
|
Requires-Dist: pydantic (<=2.10.1) ; extra == "chat" or extra == "code"
|
|
50
52
|
Requires-Dist: pyngrok (>=1.4.0) ; extra == "call"
|
|
51
53
|
Requires-Dist: pyparsing (>=3.0.0)
|
|
52
|
-
Requires-Dist: python-dotenv (>=0.
|
|
54
|
+
Requires-Dist: python-dotenv (>=1.0.1) ; extra == "call"
|
|
53
55
|
Requires-Dist: rich (>=13.7) ; extra == "chat" or extra == "call"
|
|
54
56
|
Requires-Dist: sqlalchemy (>=2.0.36) ; extra == "chat" or extra == "code" or extra == "realtime"
|
|
55
57
|
Requires-Dist: tavily-python (==0.5.0) ; extra == "chat" or extra == "code" or extra == "realtime"
|
|
@@ -121,19 +123,6 @@ Praison AI, leveraging both AutoGen and CrewAI or any other agent framework, rep
|
|
|
121
123
|
pip install praisonai
|
|
122
124
|
```
|
|
123
125
|
|
|
124
|
-
### Using uv (Fast Python Package Installer)
|
|
125
|
-
```bash
|
|
126
|
-
# Install uv if you haven't already
|
|
127
|
-
pip install uv
|
|
128
|
-
|
|
129
|
-
# Install from requirements
|
|
130
|
-
uv pip install -r pyproject.toml
|
|
131
|
-
|
|
132
|
-
# Install with extras
|
|
133
|
-
uv pip install -r pyproject.toml --extra code
|
|
134
|
-
uv pip install -r pyproject.toml --extra "crewai,autogen"
|
|
135
|
-
```
|
|
136
|
-
|
|
137
126
|
### Framework-specific Installation
|
|
138
127
|
```bash
|
|
139
128
|
# Install with CrewAI support
|
|
@@ -455,6 +444,19 @@ if __name__ == "__main__":
|
|
|
455
444
|
|
|
456
445
|
This configuration ensures that your development dependencies are correctly categorized and installed as needed.
|
|
457
446
|
|
|
447
|
+
### Using uv (Fast Python Package Installer)
|
|
448
|
+
```bash
|
|
449
|
+
# Install uv if you haven't already
|
|
450
|
+
pip install uv
|
|
451
|
+
|
|
452
|
+
# Install from requirements
|
|
453
|
+
uv pip install -r pyproject.toml
|
|
454
|
+
|
|
455
|
+
# Install with extras
|
|
456
|
+
uv pip install -r pyproject.toml --extra code
|
|
457
|
+
uv pip install -r pyproject.toml --extra "crewai,autogen"
|
|
458
|
+
```
|
|
459
|
+
|
|
458
460
|
## Contributing
|
|
459
461
|
|
|
460
462
|
- Fork on GitHub: Use the "Fork" button on the repository page.
|
|
@@ -20,6 +20,13 @@ CREWAI_AVAILABLE = False
|
|
|
20
20
|
AUTOGEN_AVAILABLE = False
|
|
21
21
|
PRAISONAI_TOOLS_AVAILABLE = False
|
|
22
22
|
AGENTOPS_AVAILABLE = False
|
|
23
|
+
PRAISONAI_AVAILABLE = False
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
from praisonaiagents import Agent as PraisonAgent, Task as PraisonTask, PraisonAIAgents
|
|
27
|
+
PRAISONAI_AVAILABLE = True
|
|
28
|
+
except ImportError:
|
|
29
|
+
pass
|
|
23
30
|
|
|
24
31
|
try:
|
|
25
32
|
from crewai import Agent, Task, Crew
|
|
@@ -37,11 +44,14 @@ except ImportError:
|
|
|
37
44
|
try:
|
|
38
45
|
import agentops
|
|
39
46
|
AGENTOPS_AVAILABLE = True
|
|
47
|
+
AGENTOPS_API_KEY = os.getenv("AGENTOPS_API_KEY")
|
|
48
|
+
if not AGENTOPS_API_KEY:
|
|
49
|
+
AGENTOPS_AVAILABLE = False
|
|
40
50
|
except ImportError:
|
|
41
51
|
pass
|
|
42
52
|
|
|
43
53
|
# Only try to import praisonai_tools if either CrewAI or AutoGen is available
|
|
44
|
-
if CREWAI_AVAILABLE or AUTOGEN_AVAILABLE:
|
|
54
|
+
if CREWAI_AVAILABLE or AUTOGEN_AVAILABLE or PRAISONAI_AVAILABLE:
|
|
45
55
|
try:
|
|
46
56
|
from praisonai_tools import (
|
|
47
57
|
CodeDocsSearchTool, CSVSearchTool, DirectorySearchTool, DOCXSearchTool, DirectoryReadTool,
|
|
@@ -115,6 +125,8 @@ class AgentsGenerator:
|
|
|
115
125
|
raise ImportError("CrewAI is not installed. Please install it with 'pip install praisonai[crewai]'")
|
|
116
126
|
elif framework == "autogen" and not AUTOGEN_AVAILABLE:
|
|
117
127
|
raise ImportError("AutoGen is not installed. Please install it with 'pip install praisonai[autogen]'")
|
|
128
|
+
elif framework == "praisonai" and not PRAISONAI_AVAILABLE:
|
|
129
|
+
raise ImportError("PraisonAI is not installed. Please install it with 'pip install praisonaiagents'")
|
|
118
130
|
|
|
119
131
|
def is_function_or_decorated(self, obj):
|
|
120
132
|
"""
|
|
@@ -221,7 +233,7 @@ class AgentsGenerator:
|
|
|
221
233
|
tools_dict = {}
|
|
222
234
|
|
|
223
235
|
# Only try to use praisonai_tools if it's available and needed
|
|
224
|
-
if PRAISONAI_TOOLS_AVAILABLE and (CREWAI_AVAILABLE or AUTOGEN_AVAILABLE):
|
|
236
|
+
if PRAISONAI_TOOLS_AVAILABLE and (CREWAI_AVAILABLE or AUTOGEN_AVAILABLE or PRAISONAI_AVAILABLE):
|
|
225
237
|
tools_dict = {
|
|
226
238
|
'CodeDocsSearchTool': CodeDocsSearchTool(),
|
|
227
239
|
'CSVSearchTool': CSVSearchTool(),
|
|
@@ -266,13 +278,19 @@ class AgentsGenerator:
|
|
|
266
278
|
if not AUTOGEN_AVAILABLE:
|
|
267
279
|
raise ImportError("AutoGen is not installed. Please install it with 'pip install praisonai[autogen]'")
|
|
268
280
|
if AGENTOPS_AVAILABLE:
|
|
269
|
-
agentops.init(os.environ.get("AGENTOPS_API_KEY"),
|
|
281
|
+
agentops.init(os.environ.get("AGENTOPS_API_KEY"), default_tags=["autogen"])
|
|
270
282
|
return self._run_autogen(config, topic, tools_dict)
|
|
283
|
+
elif framework == "praisonai":
|
|
284
|
+
if not PRAISONAI_AVAILABLE:
|
|
285
|
+
raise ImportError("PraisonAI is not installed. Please install it with 'pip install praisonaiagents'")
|
|
286
|
+
if AGENTOPS_AVAILABLE:
|
|
287
|
+
agentops.init(os.environ.get("AGENTOPS_API_KEY"), default_tags=["praisonai"])
|
|
288
|
+
return self._run_praisonai(config, topic, tools_dict)
|
|
271
289
|
else: # framework=crewai
|
|
272
290
|
if not CREWAI_AVAILABLE:
|
|
273
291
|
raise ImportError("CrewAI is not installed. Please install it with 'pip install praisonai[crewai]'")
|
|
274
292
|
if AGENTOPS_AVAILABLE:
|
|
275
|
-
agentops.init(os.environ.get("AGENTOPS_API_KEY"),
|
|
293
|
+
agentops.init(os.environ.get("AGENTOPS_API_KEY"), default_tags=["crewai"])
|
|
276
294
|
return self._run_crewai(config, topic, tools_dict)
|
|
277
295
|
|
|
278
296
|
def _run_autogen(self, config, topic, tools_dict):
|
|
@@ -472,3 +490,126 @@ class AgentsGenerator:
|
|
|
472
490
|
|
|
473
491
|
return result
|
|
474
492
|
|
|
493
|
+
def _run_praisonai(self, config, topic, tools_dict):
|
|
494
|
+
"""
|
|
495
|
+
Run agents using the PraisonAI framework.
|
|
496
|
+
"""
|
|
497
|
+
agents = {}
|
|
498
|
+
tasks = []
|
|
499
|
+
tasks_dict = {}
|
|
500
|
+
|
|
501
|
+
# Create agents from config
|
|
502
|
+
for role, details in config['roles'].items():
|
|
503
|
+
role_filled = details['role'].format(topic=topic)
|
|
504
|
+
goal_filled = details['goal'].format(topic=topic)
|
|
505
|
+
backstory_filled = details['backstory'].format(topic=topic)
|
|
506
|
+
|
|
507
|
+
# Get agent tools
|
|
508
|
+
agent_tools = [tools_dict[tool] for tool in details.get('tools', [])
|
|
509
|
+
if tool in tools_dict]
|
|
510
|
+
|
|
511
|
+
# Configure LLM
|
|
512
|
+
llm_model = details.get('llm')
|
|
513
|
+
if llm_model:
|
|
514
|
+
llm = llm_model.get("model", os.environ.get("MODEL_NAME", "gpt-4o"))
|
|
515
|
+
else:
|
|
516
|
+
llm = os.environ.get("MODEL_NAME", "gpt-4o")
|
|
517
|
+
|
|
518
|
+
# Configure function calling LLM
|
|
519
|
+
function_calling_llm_model = details.get('function_calling_llm')
|
|
520
|
+
if function_calling_llm_model:
|
|
521
|
+
function_calling_llm = function_calling_llm_model.get("model", os.environ.get("MODEL_NAME", "openai/gpt-4o"))
|
|
522
|
+
else:
|
|
523
|
+
function_calling_llm = os.environ.get("MODEL_NAME", "gpt-4o")
|
|
524
|
+
|
|
525
|
+
# Create PraisonAI agent
|
|
526
|
+
agent = PraisonAgent(
|
|
527
|
+
name=role_filled,
|
|
528
|
+
role=role_filled,
|
|
529
|
+
goal=goal_filled,
|
|
530
|
+
backstory=backstory_filled,
|
|
531
|
+
tools=agent_tools,
|
|
532
|
+
allow_delegation=details.get('allow_delegation', False),
|
|
533
|
+
llm=llm,
|
|
534
|
+
function_calling_llm=function_calling_llm,
|
|
535
|
+
max_iter=details.get('max_iter', 15),
|
|
536
|
+
max_rpm=details.get('max_rpm'),
|
|
537
|
+
max_execution_time=details.get('max_execution_time'),
|
|
538
|
+
verbose=details.get('verbose', True),
|
|
539
|
+
cache=details.get('cache', True),
|
|
540
|
+
system_template=details.get('system_template'),
|
|
541
|
+
prompt_template=details.get('prompt_template'),
|
|
542
|
+
response_template=details.get('response_template'),
|
|
543
|
+
)
|
|
544
|
+
|
|
545
|
+
# Set agent callback if provided
|
|
546
|
+
if self.agent_callback:
|
|
547
|
+
agent.step_callback = self.agent_callback
|
|
548
|
+
|
|
549
|
+
agents[role] = agent
|
|
550
|
+
|
|
551
|
+
# Create tasks for the agent
|
|
552
|
+
for task_name, task_details in details.get('tasks', {}).items():
|
|
553
|
+
description_filled = task_details['description'].format(topic=topic)
|
|
554
|
+
expected_output_filled = task_details['expected_output'].format(topic=topic)
|
|
555
|
+
|
|
556
|
+
# Create task using PraisonAI Task class
|
|
557
|
+
task = PraisonTask(
|
|
558
|
+
description=description_filled,
|
|
559
|
+
expected_output=expected_output_filled,
|
|
560
|
+
agent=agent,
|
|
561
|
+
tools=task_details.get('tools', []),
|
|
562
|
+
async_execution=task_details.get('async_execution', False),
|
|
563
|
+
context=[],
|
|
564
|
+
config=task_details.get('config', {}),
|
|
565
|
+
output_json=task_details.get('output_json'),
|
|
566
|
+
output_pydantic=task_details.get('output_pydantic'),
|
|
567
|
+
output_file=task_details.get('output_file', ""),
|
|
568
|
+
callback=task_details.get('callback'),
|
|
569
|
+
create_directory=task_details.get('create_directory', False)
|
|
570
|
+
)
|
|
571
|
+
|
|
572
|
+
# Set task callback if provided
|
|
573
|
+
if self.task_callback:
|
|
574
|
+
task.callback = self.task_callback
|
|
575
|
+
|
|
576
|
+
tasks.append(task)
|
|
577
|
+
tasks_dict[task_name] = task
|
|
578
|
+
|
|
579
|
+
# Set up task contexts
|
|
580
|
+
for role, details in config['roles'].items():
|
|
581
|
+
for task_name, task_details in details.get('tasks', {}).items():
|
|
582
|
+
task = tasks_dict[task_name]
|
|
583
|
+
context_tasks = [tasks_dict[ctx] for ctx in task_details.get('context', [])
|
|
584
|
+
if ctx in tasks_dict]
|
|
585
|
+
task.context = context_tasks
|
|
586
|
+
|
|
587
|
+
# Create and run the PraisonAI agents
|
|
588
|
+
if config.get('process') == 'hierarchical':
|
|
589
|
+
agents = PraisonAIAgents(
|
|
590
|
+
agents=list(agents.values()),
|
|
591
|
+
tasks=tasks,
|
|
592
|
+
verbose=True,
|
|
593
|
+
process="hierarchical",
|
|
594
|
+
manager_llm=config.get('manager_llm', 'gpt-4o'),
|
|
595
|
+
)
|
|
596
|
+
else:
|
|
597
|
+
agents = PraisonAIAgents(
|
|
598
|
+
agents=list(agents.values()),
|
|
599
|
+
tasks=tasks,
|
|
600
|
+
verbose=2
|
|
601
|
+
)
|
|
602
|
+
|
|
603
|
+
self.logger.debug("Final Configuration:")
|
|
604
|
+
self.logger.debug(f"Agents: {agents.agents}")
|
|
605
|
+
self.logger.debug(f"Tasks: {agents.tasks}")
|
|
606
|
+
|
|
607
|
+
response = agents.start()
|
|
608
|
+
# result = f"### Task Output ###\n{response}"
|
|
609
|
+
self.logger.debug(f"Result: {response}")
|
|
610
|
+
result = ""
|
|
611
|
+
|
|
612
|
+
if AGENTOPS_AVAILABLE:
|
|
613
|
+
agentops.end_session("Success")
|
|
614
|
+
|
|
615
|
+
return result
|
|
@@ -12,6 +12,13 @@ import logging
|
|
|
12
12
|
CREWAI_AVAILABLE = False
|
|
13
13
|
AUTOGEN_AVAILABLE = False
|
|
14
14
|
PRAISONAI_TOOLS_AVAILABLE = False
|
|
15
|
+
PRAISONAI_AVAILABLE = False
|
|
16
|
+
|
|
17
|
+
try:
|
|
18
|
+
from praisonaiagents import Agent as PraisonAgent, Task as PraisonTask, PraisonAIAgents
|
|
19
|
+
PRAISONAI_AVAILABLE = True
|
|
20
|
+
except ImportError:
|
|
21
|
+
pass
|
|
15
22
|
|
|
16
23
|
try:
|
|
17
24
|
from crewai import Agent, Task, Crew
|
|
@@ -71,6 +78,11 @@ CrewAI is not installed. Please install with:
|
|
|
71
78
|
AutoGen is not installed. Please install with:
|
|
72
79
|
pip install "praisonai[autogen]"
|
|
73
80
|
""")
|
|
81
|
+
elif framework == "praisonai" and not PRAISONAI_AVAILABLE:
|
|
82
|
+
raise ImportError("""
|
|
83
|
+
Praisonai is not installed. Please install with:
|
|
84
|
+
pip install praisonaiagents
|
|
85
|
+
""")
|
|
74
86
|
|
|
75
87
|
# Only show tools message if using a framework and tools are needed
|
|
76
88
|
if (framework in ["crewai", "autogen"]) and not PRAISONAI_TOOLS_AVAILABLE:
|
|
@@ -88,7 +100,7 @@ Tools are not available for {framework}. To use tools, install:
|
|
|
88
100
|
]
|
|
89
101
|
self.topic = topic
|
|
90
102
|
self.agent_file = agent_file
|
|
91
|
-
self.framework = framework or "
|
|
103
|
+
self.framework = framework or "praisonai"
|
|
92
104
|
self.client = instructor.patch(
|
|
93
105
|
OpenAI(
|
|
94
106
|
base_url=self.config_list[0]['base_url'],
|
|
@@ -24,11 +24,19 @@ GRADIO_AVAILABLE = False
|
|
|
24
24
|
CALL_MODULE_AVAILABLE = False
|
|
25
25
|
CREWAI_AVAILABLE = False
|
|
26
26
|
AUTOGEN_AVAILABLE = False
|
|
27
|
+
PRAISONAI_AVAILABLE = False
|
|
27
28
|
|
|
28
29
|
try:
|
|
29
|
-
#
|
|
30
|
+
# Create necessary directories and set CHAINLIT_APP_ROOT
|
|
30
31
|
if "CHAINLIT_APP_ROOT" not in os.environ:
|
|
31
|
-
|
|
32
|
+
chainlit_root = os.path.join(os.path.expanduser("~"), ".praison")
|
|
33
|
+
os.environ["CHAINLIT_APP_ROOT"] = chainlit_root
|
|
34
|
+
else:
|
|
35
|
+
chainlit_root = os.environ["CHAINLIT_APP_ROOT"]
|
|
36
|
+
|
|
37
|
+
os.makedirs(chainlit_root, exist_ok=True)
|
|
38
|
+
os.makedirs(os.path.join(chainlit_root, ".files"), exist_ok=True)
|
|
39
|
+
|
|
32
40
|
from chainlit.cli import chainlit_run
|
|
33
41
|
CHAINLIT_AVAILABLE = True
|
|
34
42
|
except ImportError:
|
|
@@ -58,6 +66,12 @@ try:
|
|
|
58
66
|
except ImportError:
|
|
59
67
|
pass
|
|
60
68
|
|
|
69
|
+
try:
|
|
70
|
+
from praisonaiagents import Agent as PraisonAgent, Task as PraisonTask, PraisonAIAgents
|
|
71
|
+
PRAISONAI_AVAILABLE = True
|
|
72
|
+
except ImportError:
|
|
73
|
+
pass
|
|
74
|
+
|
|
61
75
|
logging.basicConfig(level=os.environ.get('LOGLEVEL', 'INFO'), format='%(asctime)s - %(levelname)s - %(message)s')
|
|
62
76
|
|
|
63
77
|
def stream_subprocess(command, env=None):
|
|
@@ -266,7 +280,7 @@ class PraisonAI:
|
|
|
266
280
|
Parse the command-line arguments for the PraisonAI CLI.
|
|
267
281
|
"""
|
|
268
282
|
parser = argparse.ArgumentParser(prog="praisonai", description="praisonAI command-line interface")
|
|
269
|
-
parser.add_argument("--framework", choices=["crewai", "autogen"], help="Specify the framework")
|
|
283
|
+
parser.add_argument("--framework", choices=["crewai", "autogen", "praisonai"], help="Specify the framework")
|
|
270
284
|
parser.add_argument("--ui", choices=["chainlit", "gradio"], help="Specify the UI framework (gradio or chainlit).")
|
|
271
285
|
parser.add_argument("--auto", nargs=argparse.REMAINDER, help="Enable auto mode and pass arguments for it")
|
|
272
286
|
parser.add_argument("--init", nargs=argparse.REMAINDER, help="Initialize agents with optional topic")
|
|
@@ -374,11 +388,12 @@ class PraisonAI:
|
|
|
374
388
|
|
|
375
389
|
# Only check framework availability for agent-related operations
|
|
376
390
|
if not args.command and (args.init or args.auto or args.framework):
|
|
377
|
-
if not CREWAI_AVAILABLE and not AUTOGEN_AVAILABLE:
|
|
391
|
+
if not CREWAI_AVAILABLE and not AUTOGEN_AVAILABLE and not PRAISONAI_AVAILABLE:
|
|
378
392
|
print("[red]ERROR: No framework is installed. Please install at least one framework:[/red]")
|
|
379
393
|
print("\npip install \"praisonai\\[crewai]\" # For CrewAI")
|
|
380
394
|
print("pip install \"praisonai\\[autogen]\" # For AutoGen")
|
|
381
395
|
print("pip install \"praisonai\\[crewai,autogen]\" # For both frameworks\n")
|
|
396
|
+
print("pip install praisonaiagents # For PraisonAIAgents\n")
|
|
382
397
|
sys.exit(1)
|
|
383
398
|
|
|
384
399
|
return args
|
|
@@ -393,15 +408,6 @@ class PraisonAI:
|
|
|
393
408
|
root_path = os.path.join(os.path.expanduser("~"), ".praison")
|
|
394
409
|
if "CHAINLIT_APP_ROOT" not in os.environ:
|
|
395
410
|
os.environ["CHAINLIT_APP_ROOT"] = root_path
|
|
396
|
-
public_folder = os.path.join(os.path.dirname(praisonai.__file__), 'public')
|
|
397
|
-
if not os.path.exists(os.path.join(root_path, "public")):
|
|
398
|
-
if os.path.exists(public_folder):
|
|
399
|
-
shutil.copytree(public_folder, os.path.join(root_path, "public"), dirs_exist_ok=True)
|
|
400
|
-
logging.info("Public folder copied successfully!")
|
|
401
|
-
else:
|
|
402
|
-
logging.info("Public folder not found in the package.")
|
|
403
|
-
else:
|
|
404
|
-
logging.info("Public folder already exists.")
|
|
405
411
|
chat_ui_path = os.path.join(os.path.dirname(praisonai.__file__), 'ui', 'chat.py')
|
|
406
412
|
chainlit_run([chat_ui_path])
|
|
407
413
|
else:
|
|
@@ -56,7 +56,7 @@ class CloudDeployer:
|
|
|
56
56
|
file.write("FROM python:3.11-slim\n")
|
|
57
57
|
file.write("WORKDIR /app\n")
|
|
58
58
|
file.write("COPY . .\n")
|
|
59
|
-
file.write("RUN pip install flask praisonai==
|
|
59
|
+
file.write("RUN pip install flask praisonai==2.0.1 gunicorn markdown\n")
|
|
60
60
|
file.write("EXPOSE 8080\n")
|
|
61
61
|
file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n')
|
|
62
62
|
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import asyncio
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
import difflib
|
|
5
|
+
from typing import Dict, Any
|
|
6
|
+
from litellm import acompletion
|
|
7
|
+
import json
|
|
8
|
+
import dotenv
|
|
9
|
+
from tavily import TavilyClient
|
|
10
|
+
from crawl4ai import AsyncWebCrawler
|
|
11
|
+
|
|
12
|
+
dotenv.load_dotenv()
|
|
13
|
+
|
|
14
|
+
class AICoder:
|
|
15
|
+
def __init__(self, cwd: str = None, tavily_api_key: str = None):
|
|
16
|
+
self.cwd = cwd or os.getcwd()
|
|
17
|
+
self.tools = [
|
|
18
|
+
{
|
|
19
|
+
"type": "function",
|
|
20
|
+
"function": {
|
|
21
|
+
"name": "write_to_file",
|
|
22
|
+
"description": "Write content to a file at the specified path. If the file exists, it will be overwritten.",
|
|
23
|
+
"parameters": {
|
|
24
|
+
"type": "object",
|
|
25
|
+
"properties": {
|
|
26
|
+
"path": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"description": "The path of the file to write to."
|
|
29
|
+
},
|
|
30
|
+
"content": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"description": "The content to write to the file."
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"required": ["path", "content"]
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"type": "function",
|
|
41
|
+
"function": {
|
|
42
|
+
"name": "execute_command",
|
|
43
|
+
"description": "Execute a CLI command on the system.",
|
|
44
|
+
"parameters": {
|
|
45
|
+
"type": "object",
|
|
46
|
+
"properties": {
|
|
47
|
+
"command": {
|
|
48
|
+
"type": "string",
|
|
49
|
+
"description": "The CLI command to execute."
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"required": ["command"]
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"type": "function",
|
|
58
|
+
"function": {
|
|
59
|
+
"name": "read_file",
|
|
60
|
+
"description": "Read the contents of a file at the specified path.",
|
|
61
|
+
"parameters": {
|
|
62
|
+
"type": "object",
|
|
63
|
+
"properties": {
|
|
64
|
+
"path": {
|
|
65
|
+
"type": "string",
|
|
66
|
+
"description": "The path of the file to read."
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"required": ["path"]
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
self.tavily_api_key = tavily_api_key
|
|
76
|
+
if self.tavily_api_key:
|
|
77
|
+
self.tavily_client = TavilyClient(api_key=self.tavily_api_key)
|
|
78
|
+
self.tools.append({
|
|
79
|
+
"type": "function",
|
|
80
|
+
"function": {
|
|
81
|
+
"name": "tavily_web_search",
|
|
82
|
+
"description": "Search the web using Tavily API and crawl the resulting URLs",
|
|
83
|
+
"parameters": {
|
|
84
|
+
"type": "object",
|
|
85
|
+
"properties": {
|
|
86
|
+
"query": {"type": "string", "description": "Search query"}
|
|
87
|
+
},
|
|
88
|
+
"required": ["query"]
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
else:
|
|
93
|
+
self.tavily_client = None
|
|
94
|
+
|
|
95
|
+
async def create_directories(self, file_path):
|
|
96
|
+
file_path_obj = Path(file_path)
|
|
97
|
+
dir_path = file_path_obj.parent
|
|
98
|
+
if not dir_path.exists():
|
|
99
|
+
os.makedirs(dir_path, exist_ok=True)
|
|
100
|
+
return dir_path
|
|
101
|
+
|
|
102
|
+
async def file_exists(self, file_path):
|
|
103
|
+
return Path(file_path).exists()
|
|
104
|
+
|
|
105
|
+
async def write_to_file(self, file_path, content, existing=False):
|
|
106
|
+
if not existing:
|
|
107
|
+
await self.create_directories(file_path)
|
|
108
|
+
try:
|
|
109
|
+
with open(file_path, 'w') as file:
|
|
110
|
+
file.write(content)
|
|
111
|
+
return True
|
|
112
|
+
except Exception as e:
|
|
113
|
+
return False
|
|
114
|
+
|
|
115
|
+
async def read_file(self, file_path):
|
|
116
|
+
try:
|
|
117
|
+
with open(file_path, 'r') as file:
|
|
118
|
+
return file.read()
|
|
119
|
+
except:
|
|
120
|
+
return None
|
|
121
|
+
|
|
122
|
+
async def execute_command(self, command: str):
|
|
123
|
+
try:
|
|
124
|
+
process = await asyncio.create_subprocess_shell(
|
|
125
|
+
command,
|
|
126
|
+
stdout=asyncio.subprocess.PIPE,
|
|
127
|
+
stderr=asyncio.subprocess.PIPE,
|
|
128
|
+
cwd=self.cwd
|
|
129
|
+
)
|
|
130
|
+
stdout, stderr = await process.communicate()
|
|
131
|
+
if stdout:
|
|
132
|
+
return f"Command output:\n{stdout.decode()}"
|
|
133
|
+
if stderr:
|
|
134
|
+
return f"Command error:\n{stderr.decode()}"
|
|
135
|
+
return process.returncode == 0
|
|
136
|
+
except Exception as e:
|
|
137
|
+
return f"Error executing command: {str(e)}"
|
|
138
|
+
|
|
139
|
+
async def tavily_web_search(self, query):
|
|
140
|
+
if not self.tavily_client:
|
|
141
|
+
return json.dumps({
|
|
142
|
+
"query": query,
|
|
143
|
+
"error": "Tavily API key is not set. Web search is unavailable."
|
|
144
|
+
})
|
|
145
|
+
response = self.tavily_client.search(query)
|
|
146
|
+
results = []
|
|
147
|
+
async with AsyncWebCrawler() as crawler:
|
|
148
|
+
for result in response.get('results', []):
|
|
149
|
+
url = result.get('url')
|
|
150
|
+
if url:
|
|
151
|
+
try:
|
|
152
|
+
crawl_result = await crawler.arun(url=url)
|
|
153
|
+
results.append({
|
|
154
|
+
"content": result.get('content'),
|
|
155
|
+
"url": url,
|
|
156
|
+
"full_content": crawl_result.markdown
|
|
157
|
+
})
|
|
158
|
+
except Exception:
|
|
159
|
+
results.append({
|
|
160
|
+
"content": result.get('content'),
|
|
161
|
+
"url": url,
|
|
162
|
+
"full_content": "Error: Unable to crawl this URL"
|
|
163
|
+
})
|
|
164
|
+
return json.dumps({
|
|
165
|
+
"query": query,
|
|
166
|
+
"results": results
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
def generate_diff(self, original_content: str, new_content: str, filename="file.txt"):
|
|
170
|
+
diff_lines = difflib.unified_diff(
|
|
171
|
+
original_content.splitlines(keepends=True),
|
|
172
|
+
new_content.splitlines(keepends=True),
|
|
173
|
+
fromfile=f"original_{filename}",
|
|
174
|
+
tofile=f"modified_{filename}"
|
|
175
|
+
)
|
|
176
|
+
return "".join(diff_lines)
|
|
177
|
+
|
|
178
|
+
def parse_json_response(self, json_object: Dict) -> Dict[str, Any]:
|
|
179
|
+
if 'choices' in json_object and json_object['choices'][0]['message']:
|
|
180
|
+
message = json_object['choices'][0]['message']
|
|
181
|
+
if 'tool_calls' in message and message['tool_calls']:
|
|
182
|
+
return {"type": "tool_calls", "data": message['tool_calls']}
|
|
183
|
+
return {"type": "content", "data": message.get('content', "")}
|
|
184
|
+
return {"type": "content", "data": json.dumps(json_object)}
|
|
185
|
+
|
|
186
|
+
def parse_llm_response(self, response: Any) -> Dict[str, Any]:
|
|
187
|
+
if response is None:
|
|
188
|
+
return {"type": "content", "data": ""}
|
|
189
|
+
if isinstance(response, str):
|
|
190
|
+
try:
|
|
191
|
+
json_object = json.loads(response)
|
|
192
|
+
if isinstance(json_object, dict):
|
|
193
|
+
return self.parse_json_response(json_object)
|
|
194
|
+
except json.JSONDecodeError:
|
|
195
|
+
return {"type": "content", "data": response}
|
|
196
|
+
if hasattr(response, 'choices') and response.choices:
|
|
197
|
+
message = response.choices[0].message
|
|
198
|
+
if hasattr(message, 'tool_calls') and message.tool_calls:
|
|
199
|
+
tool_calls_data = []
|
|
200
|
+
for tool_call in message.tool_calls:
|
|
201
|
+
tool_calls_data.append({
|
|
202
|
+
'id': tool_call.id,
|
|
203
|
+
'type': tool_call.type,
|
|
204
|
+
'function': {
|
|
205
|
+
'name': tool_call.function.name,
|
|
206
|
+
'arguments': tool_call.function.arguments
|
|
207
|
+
}
|
|
208
|
+
})
|
|
209
|
+
return {"type": "tool_calls", "data": tool_calls_data}
|
|
210
|
+
return {"type": "content", "data": message.content or ""}
|
|
211
|
+
return {"type": "content", "data": str(response)}
|
|
212
|
+
|
|
213
|
+
async def apply_llm_response(self, task, llm_response):
|
|
214
|
+
parsed_response = self.parse_llm_response(llm_response)
|
|
215
|
+
if parsed_response["type"] == "tool_calls":
|
|
216
|
+
for tool_call in parsed_response["data"]:
|
|
217
|
+
if tool_call["function"]["name"] == "write_to_file":
|
|
218
|
+
args = json.loads(tool_call["function"]["arguments"])
|
|
219
|
+
file_path = os.path.join(self.cwd, args["path"].strip())
|
|
220
|
+
content = args["content"]
|
|
221
|
+
if await self.file_exists(file_path):
|
|
222
|
+
original_content = await self.read_file(file_path)
|
|
223
|
+
file_diff = self.generate_diff(original_content, content, os.path.basename(file_path))
|
|
224
|
+
# Interaction with user removed for automation context
|
|
225
|
+
return await self.write_to_file(file_path, content, True)
|
|
226
|
+
else:
|
|
227
|
+
return await self.write_to_file(file_path, content)
|
|
228
|
+
elif tool_call["function"]["name"] == "execute_command":
|
|
229
|
+
args = json.loads(tool_call["function"]["arguments"])
|
|
230
|
+
command = args.get("command", "").strip()
|
|
231
|
+
if command:
|
|
232
|
+
return await self.execute_command(command)
|
|
233
|
+
else:
|
|
234
|
+
return False
|
|
235
|
+
elif tool_call["function"]["name"] == "read_file":
|
|
236
|
+
args = json.loads(tool_call["function"]["arguments"])
|
|
237
|
+
file_path = args.get("path", "").strip()
|
|
238
|
+
if file_path:
|
|
239
|
+
content = await self.read_file(os.path.join(self.cwd, file_path))
|
|
240
|
+
return True if content is not None else False
|
|
241
|
+
else:
|
|
242
|
+
return False
|
|
243
|
+
elif tool_call["function"]["name"] == "tavily_web_search":
|
|
244
|
+
args = json.loads(tool_call["function"]["arguments"])
|
|
245
|
+
return await self.tavily_web_search(args.get("query"))
|
|
246
|
+
else:
|
|
247
|
+
return False
|
|
248
|
+
return True
|
|
249
|
+
|
|
250
|
+
async def process_task(self, task: str):
|
|
251
|
+
llm_response = await acompletion(
|
|
252
|
+
model="gpt-4",
|
|
253
|
+
messages=[
|
|
254
|
+
{"role": "user", "content": task}
|
|
255
|
+
],
|
|
256
|
+
tools=self.tools,
|
|
257
|
+
tool_choice="auto"
|
|
258
|
+
)
|
|
259
|
+
return await self.apply_llm_response(task, llm_response)
|
|
260
|
+
|
|
261
|
+
async def main():
|
|
262
|
+
ai_coder = AICoder()
|
|
263
|
+
await ai_coder.process_task("Create a file called `hello.txt` with the content 'Hello, world!'")
|
|
264
|
+
await ai_coder.process_task("Edit the file called `hello.txt` and change the content to 'Hello again, world!'")
|
|
265
|
+
await ai_coder.process_task("Show me the current working directory")
|
|
266
|
+
await ai_coder.process_task("Read the contents of hello.txt")
|
|
267
|
+
|
|
268
|
+
if __name__ == "__main__":
|
|
269
|
+
asyncio.run(main())
|