PraisonAI 2.0.26__tar.gz → 2.0.28__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-2.0.26 → praisonai-2.0.28}/PKG-INFO +1 -1
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/deploy.py +1 -1
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/agents.py +110 -63
- praisonai-2.0.28/praisonai/ui/tools.md +133 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/pyproject.toml +2 -2
- praisonai-2.0.26/praisonai/ui/together.py +0 -775
- {praisonai-2.0.26 → praisonai-2.0.28}/LICENSE +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/README.md +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/__init__.py +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/__main__.py +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/agents_generator.py +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/api/call.py +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/auto.py +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/chainlit_ui.py +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/cli.py +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/inbuilt_tools/__init__.py +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/inbuilt_tools/autogen_tools.py +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/inc/__init__.py +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/inc/config.py +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/inc/models.py +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/public/android-chrome-192x192.png +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/public/android-chrome-512x512.png +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/public/apple-touch-icon.png +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/public/fantasy.svg +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/public/favicon-16x16.png +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/public/favicon-32x32.png +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/public/favicon.ico +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/public/game.svg +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/public/logo_dark.png +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/public/logo_light.png +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/public/movie.svg +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/public/praison-ai-agents-architecture-dark.png +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/public/praison-ai-agents-architecture.png +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/public/thriller.svg +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/setup/__init__.py +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/setup/build.py +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/setup/config.yaml +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/setup/post_install.py +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/setup/setup_conda_env.py +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/setup/setup_conda_env.sh +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/setup.py +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/test.py +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/train.py +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/README.md +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/callbacks.py +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/chat.py +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/code.py +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/colab.py +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/colab_chainlit.py +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/components/aicoder.py +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/config/.chainlit/config.toml +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/config/.chainlit/translations/bn.json +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/config/.chainlit/translations/en-US.json +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/config/.chainlit/translations/gu.json +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/config/.chainlit/translations/he-IL.json +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/config/.chainlit/translations/hi.json +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/config/.chainlit/translations/kn.json +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/config/.chainlit/translations/ml.json +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/config/.chainlit/translations/mr.json +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/config/.chainlit/translations/ta.json +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/config/.chainlit/translations/te.json +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/config/.chainlit/translations/zh-CN.json +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/config/chainlit.md +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/config/translations/bn.json +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/config/translations/en-US.json +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/config/translations/gu.json +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/config/translations/he-IL.json +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/config/translations/hi.json +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/config/translations/kn.json +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/config/translations/ml.json +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/config/translations/mr.json +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/config/translations/ta.json +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/config/translations/te.json +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/config/translations/zh-CN.json +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/context.py +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/db.py +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/public/fantasy.svg +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/public/game.svg +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/public/logo_dark.png +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/public/logo_light.png +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/public/movie.svg +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/public/praison.css +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/public/thriller.svg +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/realtime.py +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/realtimeclient/__init__.py +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/realtimeclient/realtimedocs.txt +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/realtimeclient/tools.py +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/ui/sql_alchemy.py +0 -0
- {praisonai-2.0.26 → praisonai-2.0.28}/praisonai/version.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: PraisonAI
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.28
|
|
4
4
|
Summary: PraisonAI is an AI Agents Framework with Self Reflection. PraisonAI application combines PraisonAI Agents, AutoGen, and CrewAI into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customisation, and efficient human–agent collaboration.
|
|
5
5
|
Author: Mervin Praison
|
|
6
6
|
Requires-Python: >=3.10,<3.13
|
|
@@ -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==2.0.
|
|
59
|
+
file.write("RUN pip install flask praisonai==2.0.28 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
|
|
|
@@ -5,6 +5,8 @@ import yaml
|
|
|
5
5
|
import logging
|
|
6
6
|
import inspect
|
|
7
7
|
import chainlit as cl
|
|
8
|
+
from praisonaiagents import Agent, Task, PraisonAIAgents, register_display_callback
|
|
9
|
+
|
|
8
10
|
framework = "praisonai"
|
|
9
11
|
config_list = [
|
|
10
12
|
{
|
|
@@ -14,12 +16,19 @@ config_list = [
|
|
|
14
16
|
}
|
|
15
17
|
]
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
actions=[
|
|
19
|
+
actions = [
|
|
20
20
|
cl.Action(name="run", value="run", label="✅ Run"),
|
|
21
21
|
cl.Action(name="modify", value="modify", label="🔧 Modify"),
|
|
22
22
|
]
|
|
23
|
+
|
|
24
|
+
@cl.action_callback("run")
|
|
25
|
+
async def on_run(action):
|
|
26
|
+
await main(cl.Message(content=""))
|
|
27
|
+
|
|
28
|
+
@cl.action_callback("modify")
|
|
29
|
+
async def on_modify(action):
|
|
30
|
+
await cl.Message(content="Modify the agents and tools from below settings").send()
|
|
31
|
+
|
|
23
32
|
import os
|
|
24
33
|
import sys
|
|
25
34
|
import yaml
|
|
@@ -37,9 +46,6 @@ import chainlit as cl
|
|
|
37
46
|
from chainlit.types import ThreadDict
|
|
38
47
|
import chainlit.data as cl_data
|
|
39
48
|
|
|
40
|
-
# External imports from your local packages (adjust if needed)
|
|
41
|
-
from praisonaiagents import Agent, Task, PraisonAIAgents
|
|
42
|
-
|
|
43
49
|
# -----------------------------------------------------------------------------
|
|
44
50
|
# Global Setup
|
|
45
51
|
# -----------------------------------------------------------------------------
|
|
@@ -53,14 +59,12 @@ message_queue = Queue() # Queue to handle messages sent to Chainlit UI
|
|
|
53
59
|
agent_file = "agents.yaml"
|
|
54
60
|
|
|
55
61
|
# -----------------------------------------------------------------------------
|
|
56
|
-
# Database and Settings Logic
|
|
62
|
+
# Database and Settings Logic
|
|
57
63
|
# -----------------------------------------------------------------------------
|
|
58
64
|
|
|
59
65
|
MAX_RETRIES = 3
|
|
60
66
|
RETRY_DELAY = 1 # seconds
|
|
61
67
|
|
|
62
|
-
# Original DatabaseManager from chainlit_ui.py, unmodified logic, plus minimal
|
|
63
|
-
# placeholder methods for Chainlit's data layer calls (create_user, get_user, etc.)
|
|
64
68
|
from db import DatabaseManager
|
|
65
69
|
|
|
66
70
|
async def init_database_with_retry():
|
|
@@ -118,7 +122,7 @@ async def update_thread_metadata(thread_id: str, metadata: dict):
|
|
|
118
122
|
raise
|
|
119
123
|
|
|
120
124
|
# -----------------------------------------------------------------------------
|
|
121
|
-
# Callback Manager
|
|
125
|
+
# Callback Manager
|
|
122
126
|
# -----------------------------------------------------------------------------
|
|
123
127
|
|
|
124
128
|
class CallbackManager:
|
|
@@ -161,7 +165,50 @@ def callback(name: str, is_async: bool = False):
|
|
|
161
165
|
return decorator
|
|
162
166
|
|
|
163
167
|
# -----------------------------------------------------------------------------
|
|
164
|
-
#
|
|
168
|
+
# ADDITIONAL CALLBACKS
|
|
169
|
+
# -----------------------------------------------------------------------------
|
|
170
|
+
def interaction_callback(message=None, response=None, **kwargs):
|
|
171
|
+
logger.debug(f"[CALLBACK: interaction] Message: {message} | Response: {response}")
|
|
172
|
+
message_queue.put({
|
|
173
|
+
"content": f"[CALLBACK: interaction] Message: {message} | Response: {response}",
|
|
174
|
+
"author": "Callback"
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
def error_callback(message=None, **kwargs):
|
|
178
|
+
logger.error(f"[CALLBACK: error] Message: {message}")
|
|
179
|
+
message_queue.put({
|
|
180
|
+
"content": f"[CALLBACK: error] Message: {message}",
|
|
181
|
+
"author": "Callback"
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
def tool_call_callback(message=None, **kwargs):
|
|
185
|
+
logger.debug(f"[CALLBACK: tool_call] Tool used: {message}")
|
|
186
|
+
message_queue.put({
|
|
187
|
+
"content": f"[CALLBACK: tool_call] Tool used: {message}",
|
|
188
|
+
"author": "Callback"
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
def instruction_callback(message=None, **kwargs):
|
|
192
|
+
logger.debug(f"[CALLBACK: instruction] Instruction: {message}")
|
|
193
|
+
message_queue.put({
|
|
194
|
+
"content": f"[CALLBACK: instruction] Instruction: {message}",
|
|
195
|
+
"author": "Callback"
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
def self_reflection_callback(message=None, **kwargs):
|
|
199
|
+
logger.debug(f"[CALLBACK: self_reflection] Reflection: {message}")
|
|
200
|
+
message_queue.put({
|
|
201
|
+
"content": f"[CALLBACK: self_reflection] Reflection: {message}",
|
|
202
|
+
"author": "Callback"
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
register_display_callback('error', error_callback)
|
|
206
|
+
register_display_callback('tool_call', tool_call_callback)
|
|
207
|
+
register_display_callback('instruction', instruction_callback)
|
|
208
|
+
register_display_callback('self_reflection', self_reflection_callback)
|
|
209
|
+
|
|
210
|
+
# -----------------------------------------------------------------------------
|
|
211
|
+
# Tools Loader
|
|
165
212
|
# -----------------------------------------------------------------------------
|
|
166
213
|
|
|
167
214
|
def load_tools_from_tools_py():
|
|
@@ -172,7 +219,6 @@ def load_tools_from_tools_py():
|
|
|
172
219
|
tools_dict = {}
|
|
173
220
|
try:
|
|
174
221
|
spec = importlib.util.spec_from_file_location("tools", "tools.py")
|
|
175
|
-
logger.info(f"Spec: {spec}")
|
|
176
222
|
if spec is None:
|
|
177
223
|
logger.info("tools.py not found in current directory")
|
|
178
224
|
return tools_dict
|
|
@@ -182,7 +228,10 @@ def load_tools_from_tools_py():
|
|
|
182
228
|
|
|
183
229
|
for name, obj in inspect.getmembers(module):
|
|
184
230
|
if not name.startswith('_') and callable(obj) and not inspect.isclass(obj):
|
|
231
|
+
# Store the function in globals
|
|
185
232
|
globals()[name] = obj
|
|
233
|
+
|
|
234
|
+
# Build the function definition
|
|
186
235
|
tool_def = {
|
|
187
236
|
"type": "function",
|
|
188
237
|
"function": {
|
|
@@ -190,16 +239,14 @@ def load_tools_from_tools_py():
|
|
|
190
239
|
"description": obj.__doc__ or f"Function to {name.replace('_', ' ')}",
|
|
191
240
|
"parameters": {
|
|
192
241
|
"type": "object",
|
|
193
|
-
"properties": {
|
|
194
|
-
|
|
195
|
-
"type": "string",
|
|
196
|
-
"description": "The search query to look up information about"
|
|
197
|
-
}
|
|
198
|
-
},
|
|
199
|
-
"required": ["query"]
|
|
242
|
+
"properties": {},
|
|
243
|
+
"required": []
|
|
200
244
|
}
|
|
201
|
-
}
|
|
245
|
+
},
|
|
246
|
+
# Keep the actual callable as well
|
|
247
|
+
"callable": obj,
|
|
202
248
|
}
|
|
249
|
+
|
|
203
250
|
tools_dict[name] = tool_def
|
|
204
251
|
logger.info(f"Loaded and globalized tool function: {name}")
|
|
205
252
|
|
|
@@ -209,7 +256,7 @@ def load_tools_from_tools_py():
|
|
|
209
256
|
return tools_dict
|
|
210
257
|
|
|
211
258
|
# -----------------------------------------------------------------------------
|
|
212
|
-
# Async Queue Processor
|
|
259
|
+
# Async Queue Processor
|
|
213
260
|
# -----------------------------------------------------------------------------
|
|
214
261
|
|
|
215
262
|
async def process_message_queue():
|
|
@@ -223,7 +270,7 @@ async def process_message_queue():
|
|
|
223
270
|
logger.error(f"Error processing message queue: {e}")
|
|
224
271
|
|
|
225
272
|
# -----------------------------------------------------------------------------
|
|
226
|
-
# Step & Task Callbacks
|
|
273
|
+
# Step & Task Callbacks
|
|
227
274
|
# -----------------------------------------------------------------------------
|
|
228
275
|
|
|
229
276
|
async def step_callback(step_details):
|
|
@@ -351,9 +398,8 @@ def sync_step_callback_wrapper(step_details):
|
|
|
351
398
|
logger.error(f"Error in sync_step_callback_wrapper: {e}", exc_info=True)
|
|
352
399
|
|
|
353
400
|
# -----------------------------------------------------------------------------
|
|
354
|
-
# Main PraisonAI Runner
|
|
401
|
+
# Main PraisonAI Runner
|
|
355
402
|
# -----------------------------------------------------------------------------
|
|
356
|
-
|
|
357
403
|
async def ui_run_praisonai(config, topic, tools_dict):
|
|
358
404
|
logger.info("Starting ui_run_praisonai")
|
|
359
405
|
agents_map = {}
|
|
@@ -370,8 +416,6 @@ async def ui_run_praisonai(config, topic, tools_dict):
|
|
|
370
416
|
goal_filled = details['goal'].format(topic=topic)
|
|
371
417
|
backstory_filled = details['backstory'].format(topic=topic)
|
|
372
418
|
|
|
373
|
-
await cl.Message(content=f"[DEBUG] Creating agent: {role_name}", author="System").send()
|
|
374
|
-
|
|
375
419
|
def step_callback_sync(step_details):
|
|
376
420
|
step_details["agent_name"] = role_name
|
|
377
421
|
try:
|
|
@@ -394,7 +438,8 @@ async def ui_run_praisonai(config, topic, tools_dict):
|
|
|
394
438
|
max_rpm=details.get('max_rpm'),
|
|
395
439
|
max_execution_time=details.get('max_execution_time'),
|
|
396
440
|
cache=details.get('cache', True),
|
|
397
|
-
step_callback=step_callback_sync
|
|
441
|
+
step_callback=step_callback_sync,
|
|
442
|
+
self_reflect=details.get('self_reflect', False)
|
|
398
443
|
)
|
|
399
444
|
agents_map[role] = agent
|
|
400
445
|
|
|
@@ -402,17 +447,35 @@ async def ui_run_praisonai(config, topic, tools_dict):
|
|
|
402
447
|
for role, details in config['roles'].items():
|
|
403
448
|
agent = agents_map[role]
|
|
404
449
|
role_name = agent.name
|
|
450
|
+
|
|
451
|
+
# -------------------------------------------------------------
|
|
452
|
+
# FIX: Skip empty or invalid tool names to avoid null tool objects
|
|
453
|
+
# -------------------------------------------------------------
|
|
405
454
|
role_tools = []
|
|
455
|
+
task_tools = [] # Initialize task_tools outside the loop
|
|
456
|
+
|
|
406
457
|
for tool_name in details.get('tools', []):
|
|
458
|
+
if not tool_name or not tool_name.strip():
|
|
459
|
+
logger.warning("Skipping empty tool name.")
|
|
460
|
+
continue
|
|
407
461
|
if tool_name in tools_dict:
|
|
408
|
-
|
|
462
|
+
# Create a copy of the tool definition
|
|
463
|
+
tool_def = tools_dict[tool_name].copy()
|
|
464
|
+
# Store the callable separately and remove from definition
|
|
465
|
+
callable_func = tool_def.pop("callable")
|
|
466
|
+
# Add callable to role_tools for task execution
|
|
467
|
+
role_tools.append(callable_func)
|
|
468
|
+
# Add API tool definition to task's tools
|
|
469
|
+
task_tools.append(tool_def)
|
|
470
|
+
# Also set the agent's tools to include both
|
|
471
|
+
agent.tools = role_tools
|
|
472
|
+
else:
|
|
473
|
+
logger.warning(f"Tool '{tool_name}' not found. Skipping.")
|
|
409
474
|
|
|
410
475
|
for tname, tdetails in details.get('tasks', {}).items():
|
|
411
476
|
description_filled = tdetails['description'].format(topic=topic)
|
|
412
477
|
expected_output_filled = tdetails['expected_output'].format(topic=topic)
|
|
413
478
|
|
|
414
|
-
await cl.Message(content=f"[DEBUG] Created task: {tname} for {role_name}", author="System").send()
|
|
415
|
-
|
|
416
479
|
def task_callback_sync(task_output):
|
|
417
480
|
try:
|
|
418
481
|
loop_ = asyncio.new_event_loop()
|
|
@@ -426,7 +489,7 @@ async def ui_run_praisonai(config, topic, tools_dict):
|
|
|
426
489
|
description=description_filled,
|
|
427
490
|
expected_output=expected_output_filled,
|
|
428
491
|
agent=agent,
|
|
429
|
-
tools=
|
|
492
|
+
tools=task_tools, # Pass API tool definitions
|
|
430
493
|
async_execution=True,
|
|
431
494
|
context=[],
|
|
432
495
|
config=tdetails.get('config', {}),
|
|
@@ -454,6 +517,7 @@ async def ui_run_praisonai(config, topic, tools_dict):
|
|
|
454
517
|
|
|
455
518
|
await cl.Message(content="Starting PraisonAI agents execution...", author="System").send()
|
|
456
519
|
|
|
520
|
+
# Decide how to process tasks
|
|
457
521
|
if config.get('process') == 'hierarchical':
|
|
458
522
|
prai_agents = PraisonAIAgents(
|
|
459
523
|
agents=list(agents_map.values()),
|
|
@@ -493,10 +557,11 @@ async def ui_run_praisonai(config, topic, tools_dict):
|
|
|
493
557
|
raise
|
|
494
558
|
|
|
495
559
|
# -----------------------------------------------------------------------------
|
|
496
|
-
# Chainlit Handlers + logic
|
|
560
|
+
# Chainlit Handlers + logic
|
|
497
561
|
# -----------------------------------------------------------------------------
|
|
498
562
|
|
|
499
563
|
tools_dict = load_tools_from_tools_py()
|
|
564
|
+
print(f"[DEBUG] tools_dict: {tools_dict}")
|
|
500
565
|
|
|
501
566
|
# Load agent config (default) from 'agents.yaml'
|
|
502
567
|
with open(agent_file, 'r') as f:
|
|
@@ -520,9 +585,6 @@ if AUTH_PASSWORD_ENABLED:
|
|
|
520
585
|
|
|
521
586
|
@cl.set_chat_profiles
|
|
522
587
|
async def set_profiles(current_user: cl.User):
|
|
523
|
-
"""
|
|
524
|
-
Keep all the same starter logic from chainlit_ui.py.
|
|
525
|
-
"""
|
|
526
588
|
return [
|
|
527
589
|
cl.ChatProfile(
|
|
528
590
|
name="Auto",
|
|
@@ -577,7 +639,6 @@ async def set_profiles(current_user: cl.User):
|
|
|
577
639
|
@cl.on_chat_start
|
|
578
640
|
async def start_chat():
|
|
579
641
|
try:
|
|
580
|
-
# Load model name from database
|
|
581
642
|
model_name = load_setting("model_name") or os.getenv("MODEL_NAME", "gpt-4o-mini")
|
|
582
643
|
cl.user_session.set("model_name", model_name)
|
|
583
644
|
logger.debug(f"Model name: {model_name}")
|
|
@@ -587,16 +648,19 @@ async def start_chat():
|
|
|
587
648
|
[{"role": "system", "content": "You are a helpful assistant."}],
|
|
588
649
|
)
|
|
589
650
|
|
|
590
|
-
# Create tools.py if it doesn't exist
|
|
591
651
|
if not os.path.exists("tools.py"):
|
|
592
652
|
with open("tools.py", "w") as f:
|
|
593
653
|
f.write("# Add your custom tools here\n")
|
|
594
654
|
|
|
655
|
+
if not os.path.exists("agents.yaml"):
|
|
656
|
+
with open("agents.yaml", "w") as f:
|
|
657
|
+
f.write("# Add your custom agents here\n")
|
|
658
|
+
|
|
595
659
|
settings = await cl.ChatSettings(
|
|
596
660
|
[
|
|
597
661
|
TextInput(id="Model", label="OpenAI - Model", initial=model_name),
|
|
598
662
|
TextInput(id="BaseUrl", label="OpenAI - Base URL", initial=config_list[0]['base_url']),
|
|
599
|
-
TextInput(id="ApiKey", label="OpenAI - API Key", initial=config_list[0]['api_key']),
|
|
663
|
+
TextInput(id="ApiKey", label="OpenAI - API Key", initial=config_list[0]['api_key']),
|
|
600
664
|
Select(
|
|
601
665
|
id="Framework",
|
|
602
666
|
label="Framework",
|
|
@@ -608,7 +672,7 @@ async def start_chat():
|
|
|
608
672
|
cl.user_session.set("settings", settings)
|
|
609
673
|
chat_profile = cl.user_session.get("chat_profile")
|
|
610
674
|
|
|
611
|
-
if chat_profile=="Manual":
|
|
675
|
+
if chat_profile == "Manual":
|
|
612
676
|
agent_file = "agents.yaml"
|
|
613
677
|
full_agent_file_path = os.path.abspath(agent_file)
|
|
614
678
|
if os.path.exists(full_agent_file_path):
|
|
@@ -628,7 +692,7 @@ async def start_chat():
|
|
|
628
692
|
[
|
|
629
693
|
TextInput(id="Model", label="OpenAI - Model", initial=model_name),
|
|
630
694
|
TextInput(id="BaseUrl", label="OpenAI - Base URL", initial=config_list[0]['base_url']),
|
|
631
|
-
TextInput(id="ApiKey", label="OpenAI - API Key", initial=config_list[0]['api_key']),
|
|
695
|
+
TextInput(id="ApiKey", label="OpenAI - API Key", initial=config_list[0]['api_key']),
|
|
632
696
|
Select(
|
|
633
697
|
id="Framework",
|
|
634
698
|
label="Framework",
|
|
@@ -655,13 +719,8 @@ async def start_chat():
|
|
|
655
719
|
logger.error(f"Error in start_chat: {str(e)}")
|
|
656
720
|
await cl.Message(content=f"An error occurred while starting the chat: {str(e)}").send()
|
|
657
721
|
|
|
658
|
-
|
|
659
722
|
@cl.on_chat_resume
|
|
660
723
|
async def on_chat_resume(thread: ThreadDict):
|
|
661
|
-
"""
|
|
662
|
-
Logic from chainlit_ui.py for chat resume:
|
|
663
|
-
- Restore message history from thread steps
|
|
664
|
-
"""
|
|
665
724
|
try:
|
|
666
725
|
message_history = cl.user_session.get("message_history", [])
|
|
667
726
|
root_messages = [m for m in thread["steps"] if m["parentId"] is None]
|
|
@@ -676,27 +735,19 @@ async def on_chat_resume(thread: ThreadDict):
|
|
|
676
735
|
|
|
677
736
|
@cl.on_message
|
|
678
737
|
async def main(message: cl.Message):
|
|
679
|
-
"""
|
|
680
|
-
Merged logic from colab_combined.py and chainlit_ui.py for main message:
|
|
681
|
-
- Use existing ui_run_praisonai to process user message with PraisonAI
|
|
682
|
-
- Keep message history, no changes to logic
|
|
683
|
-
"""
|
|
684
738
|
try:
|
|
685
739
|
logger.info(f"User message: {message.content}")
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
).send()
|
|
690
|
-
|
|
740
|
+
msg = cl.Message(content="")
|
|
741
|
+
await msg.stream_token(f"🔄 Processing your request: {message.content}...")
|
|
742
|
+
|
|
691
743
|
# Run PraisonAI
|
|
692
744
|
result = await ui_run_praisonai(config, message.content, tools_dict)
|
|
693
745
|
|
|
694
|
-
# Update message history
|
|
695
746
|
message_history = cl.user_session.get("message_history", [])
|
|
696
747
|
message_history.append({"role": "user", "content": message.content})
|
|
697
748
|
message_history.append({"role": "assistant", "content": str(result)})
|
|
698
749
|
cl.user_session.set("message_history", message_history)
|
|
699
|
-
|
|
750
|
+
await msg.send()
|
|
700
751
|
except Exception as e:
|
|
701
752
|
error_msg = f"Error running PraisonAI agents: {str(e)}"
|
|
702
753
|
logger.error(error_msg, exc_info=True)
|
|
@@ -704,14 +755,12 @@ async def main(message: cl.Message):
|
|
|
704
755
|
|
|
705
756
|
@cl.on_settings_update
|
|
706
757
|
async def on_settings_update(settings):
|
|
707
|
-
"""Handle updates to the ChatSettings form."""
|
|
708
758
|
try:
|
|
709
759
|
global config_list, framework
|
|
710
760
|
config_list[0]['model'] = settings["Model"]
|
|
711
761
|
config_list[0]['base_url'] = settings["BaseUrl"]
|
|
712
762
|
config_list[0]['api_key'] = settings["ApiKey"]
|
|
713
763
|
|
|
714
|
-
# Save settings to database with retry
|
|
715
764
|
for attempt in range(MAX_RETRIES):
|
|
716
765
|
try:
|
|
717
766
|
await save_setting_with_retry("model_name", config_list[0]['model'])
|
|
@@ -724,7 +773,6 @@ async def on_settings_update(settings):
|
|
|
724
773
|
continue
|
|
725
774
|
raise
|
|
726
775
|
|
|
727
|
-
# Save to environment variables for compatibility
|
|
728
776
|
os.environ["OPENAI_API_KEY"] = config_list[0]['api_key']
|
|
729
777
|
os.environ["OPENAI_MODEL_NAME"] = config_list[0]['model']
|
|
730
778
|
os.environ["OPENAI_API_BASE"] = config_list[0]['base_url']
|
|
@@ -739,7 +787,6 @@ async def on_settings_update(settings):
|
|
|
739
787
|
with open("tools.py", "w") as f:
|
|
740
788
|
f.write(settings["tools"])
|
|
741
789
|
|
|
742
|
-
# Update thread metadata if exists with retry
|
|
743
790
|
thread_id = cl.user_session.get("thread_id")
|
|
744
791
|
if thread_id:
|
|
745
792
|
for attempt in range(MAX_RETRIES):
|
|
@@ -749,6 +796,7 @@ async def on_settings_update(settings):
|
|
|
749
796
|
metadata = thread.get("metadata", {})
|
|
750
797
|
if isinstance(metadata, str):
|
|
751
798
|
try:
|
|
799
|
+
import json
|
|
752
800
|
metadata = json.loads(metadata)
|
|
753
801
|
except json.JSONDecodeError:
|
|
754
802
|
metadata = {}
|
|
@@ -766,10 +814,9 @@ async def on_settings_update(settings):
|
|
|
766
814
|
except Exception as e:
|
|
767
815
|
logger.error(f"Error updating settings: {str(e)}")
|
|
768
816
|
await cl.Message(content=f"An error occurred while updating settings: {str(e)}. Retrying...").send()
|
|
769
|
-
# One final retry after a longer delay
|
|
770
817
|
try:
|
|
771
818
|
await asyncio.sleep(RETRY_DELAY * 2)
|
|
772
819
|
await on_settings_update(settings)
|
|
773
820
|
except Exception as e:
|
|
774
821
|
logger.error(f"Final retry failed: {str(e)}")
|
|
775
|
-
await cl.Message(content=f"Failed to update settings after retries: {str(e)}").send()
|
|
822
|
+
await cl.Message(content=f"Failed to update settings after retries: {str(e)}").send()
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Understanding Tool Integration in AI Agents - A Beginner's Guide
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
This guide explains how to properly integrate tools (functions) that an AI agent can use, making them both understandable to the OpenAI API and executable by your code.
|
|
5
|
+
|
|
6
|
+
## Key Components
|
|
7
|
+
|
|
8
|
+
### 1. Tool Definition Structure
|
|
9
|
+
```python
|
|
10
|
+
# Example tool definition in tools.py
|
|
11
|
+
def search_tool(query: str) -> list:
|
|
12
|
+
"""
|
|
13
|
+
Perform a web search using DuckDuckGo.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
query (str): The search query string.
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
list: Search results with title, url, and snippet.
|
|
20
|
+
"""
|
|
21
|
+
# Function implementation...
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### 2. Tool Dictionary Format
|
|
25
|
+
```python
|
|
26
|
+
tools_dict = {
|
|
27
|
+
'search_tool': {
|
|
28
|
+
'type': 'function',
|
|
29
|
+
'function': {
|
|
30
|
+
'name': 'search_tool',
|
|
31
|
+
'description': '...',
|
|
32
|
+
'parameters': {
|
|
33
|
+
'type': 'object',
|
|
34
|
+
'properties': {
|
|
35
|
+
'query': {'type': 'string'}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
'callable': search_tool # The actual Python function
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## The Two-Part System
|
|
45
|
+
|
|
46
|
+
### Part 1: OpenAI API Communication
|
|
47
|
+
```python
|
|
48
|
+
# task_tools: What OpenAI understands
|
|
49
|
+
task_tools = []
|
|
50
|
+
tool_def = tools_dict[tool_name].copy()
|
|
51
|
+
callable_func = tool_def.pop("callable") # Remove the Python function
|
|
52
|
+
task_tools.append(tool_def) # Add clean JSON-serializable definition
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Part 2: Function Execution
|
|
56
|
+
```python
|
|
57
|
+
# role_tools: What your code executes
|
|
58
|
+
role_tools = []
|
|
59
|
+
role_tools.append(callable_func) # Store the actual function
|
|
60
|
+
agent.tools = role_tools # Give agent access to executable functions
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Putting It All Together
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
# Initialize empty lists
|
|
67
|
+
role_tools = [] # For executable functions
|
|
68
|
+
task_tools = [] # For OpenAI API definitions
|
|
69
|
+
|
|
70
|
+
# Process each tool
|
|
71
|
+
for tool_name in tools_list:
|
|
72
|
+
if tool_name in tools_dict:
|
|
73
|
+
# 1. Get the tool definition
|
|
74
|
+
tool_def = tools_dict[tool_name].copy()
|
|
75
|
+
|
|
76
|
+
# 2. Separate the callable function
|
|
77
|
+
callable_func = tool_def.pop("callable")
|
|
78
|
+
|
|
79
|
+
# 3. Store the function for execution
|
|
80
|
+
role_tools.append(callable_func)
|
|
81
|
+
|
|
82
|
+
# 4. Store the API definition
|
|
83
|
+
task_tools.append(tool_def)
|
|
84
|
+
|
|
85
|
+
# 5. Give agent access to functions
|
|
86
|
+
agent.tools = role_tools
|
|
87
|
+
|
|
88
|
+
# Create task with API definitions
|
|
89
|
+
task = Task(
|
|
90
|
+
description="...",
|
|
91
|
+
tools=task_tools, # OpenAI API will use these
|
|
92
|
+
agent=agent, # Agent has access to callable functions
|
|
93
|
+
# ... other parameters ...
|
|
94
|
+
)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Why This Works
|
|
98
|
+
|
|
99
|
+
1. **API Communication**
|
|
100
|
+
- OpenAI API receives clean JSON tool definitions
|
|
101
|
+
- No Python functions that would cause serialization errors
|
|
102
|
+
|
|
103
|
+
2. **Function Execution**
|
|
104
|
+
- Agent has access to actual Python functions
|
|
105
|
+
- Can execute tools when OpenAI decides to use them
|
|
106
|
+
|
|
107
|
+
3. **Separation of Concerns**
|
|
108
|
+
- `task_tools`: Describes what tools can do (for OpenAI)
|
|
109
|
+
- `role_tools`: Actually does the work (for Python)
|
|
110
|
+
|
|
111
|
+
## Common Errors and Solutions
|
|
112
|
+
|
|
113
|
+
1. **"Invalid type for 'tools[0]'"**
|
|
114
|
+
- Cause: Sending null or invalid tool definition to OpenAI
|
|
115
|
+
- Solution: Use proper tool definition format in `task_tools`
|
|
116
|
+
|
|
117
|
+
2. **"Object of type function is not JSON serializable"**
|
|
118
|
+
- Cause: Trying to send Python function to OpenAI API
|
|
119
|
+
- Solution: Remove callable function from API definition
|
|
120
|
+
|
|
121
|
+
3. **"Tool is not callable"**
|
|
122
|
+
- Cause: Agent doesn't have access to executable functions
|
|
123
|
+
- Solution: Set `agent.tools = role_tools`
|
|
124
|
+
|
|
125
|
+
## Best Practices
|
|
126
|
+
|
|
127
|
+
1. Always initialize both `task_tools` and `role_tools` lists
|
|
128
|
+
2. Make clean copies of tool definitions to avoid modifying originals
|
|
129
|
+
3. Keep tool definitions JSON-serializable for API communication
|
|
130
|
+
4. Ensure agents have access to callable functions
|
|
131
|
+
5. Document tool parameters and return values clearly
|
|
132
|
+
|
|
133
|
+
This structure maintains clean separation between API communication and actual function execution, making your AI agent system both reliable and maintainable.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "PraisonAI"
|
|
3
|
-
version = "2.0.
|
|
3
|
+
version = "2.0.28"
|
|
4
4
|
description = "PraisonAI is an AI Agents Framework with Self Reflection. PraisonAI application combines PraisonAI Agents, AutoGen, and CrewAI into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customisation, and efficient human–agent collaboration."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = ""
|
|
@@ -84,7 +84,7 @@ autogen = ["pyautogen>=0.2.19", "praisonai-tools>=0.0.7", "crewai"]
|
|
|
84
84
|
|
|
85
85
|
[tool.poetry]
|
|
86
86
|
name = "PraisonAI"
|
|
87
|
-
version = "2.0.
|
|
87
|
+
version = "2.0.28"
|
|
88
88
|
description = "PraisonAI is an AI Agents Framework with Self Reflection. PraisonAI application combines PraisonAI Agents, AutoGen, and CrewAI into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customisation, and efficient human–agent collaboration."
|
|
89
89
|
authors = ["Mervin Praison"]
|
|
90
90
|
license = ""
|