PraisonAI 2.0.26__tar.gz → 2.0.27__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.

Files changed (89) hide show
  1. {praisonai-2.0.26 → praisonai-2.0.27}/PKG-INFO +1 -1
  2. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/deploy.py +1 -1
  3. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/agents.py +106 -56
  4. praisonai-2.0.27/praisonai/ui/tools.md +133 -0
  5. {praisonai-2.0.26 → praisonai-2.0.27}/pyproject.toml +2 -2
  6. praisonai-2.0.26/praisonai/ui/together.py +0 -775
  7. {praisonai-2.0.26 → praisonai-2.0.27}/LICENSE +0 -0
  8. {praisonai-2.0.26 → praisonai-2.0.27}/README.md +0 -0
  9. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/__init__.py +0 -0
  10. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/__main__.py +0 -0
  11. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/agents_generator.py +0 -0
  12. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/api/call.py +0 -0
  13. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/auto.py +0 -0
  14. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/chainlit_ui.py +0 -0
  15. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/cli.py +0 -0
  16. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/inbuilt_tools/__init__.py +0 -0
  17. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/inbuilt_tools/autogen_tools.py +0 -0
  18. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/inc/__init__.py +0 -0
  19. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/inc/config.py +0 -0
  20. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/inc/models.py +0 -0
  21. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/public/android-chrome-192x192.png +0 -0
  22. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/public/android-chrome-512x512.png +0 -0
  23. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/public/apple-touch-icon.png +0 -0
  24. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/public/fantasy.svg +0 -0
  25. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/public/favicon-16x16.png +0 -0
  26. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/public/favicon-32x32.png +0 -0
  27. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/public/favicon.ico +0 -0
  28. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/public/game.svg +0 -0
  29. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/public/logo_dark.png +0 -0
  30. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/public/logo_light.png +0 -0
  31. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/public/movie.svg +0 -0
  32. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/public/praison-ai-agents-architecture-dark.png +0 -0
  33. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/public/praison-ai-agents-architecture.png +0 -0
  34. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/public/thriller.svg +0 -0
  35. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/setup/__init__.py +0 -0
  36. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/setup/build.py +0 -0
  37. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/setup/config.yaml +0 -0
  38. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/setup/post_install.py +0 -0
  39. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/setup/setup_conda_env.py +0 -0
  40. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/setup/setup_conda_env.sh +0 -0
  41. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/setup.py +0 -0
  42. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/test.py +0 -0
  43. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/train.py +0 -0
  44. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/README.md +0 -0
  45. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/callbacks.py +0 -0
  46. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/chat.py +0 -0
  47. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/code.py +0 -0
  48. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/colab.py +0 -0
  49. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/colab_chainlit.py +0 -0
  50. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/components/aicoder.py +0 -0
  51. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/config/.chainlit/config.toml +0 -0
  52. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/config/.chainlit/translations/bn.json +0 -0
  53. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/config/.chainlit/translations/en-US.json +0 -0
  54. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/config/.chainlit/translations/gu.json +0 -0
  55. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/config/.chainlit/translations/he-IL.json +0 -0
  56. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/config/.chainlit/translations/hi.json +0 -0
  57. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/config/.chainlit/translations/kn.json +0 -0
  58. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/config/.chainlit/translations/ml.json +0 -0
  59. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/config/.chainlit/translations/mr.json +0 -0
  60. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/config/.chainlit/translations/ta.json +0 -0
  61. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/config/.chainlit/translations/te.json +0 -0
  62. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/config/.chainlit/translations/zh-CN.json +0 -0
  63. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/config/chainlit.md +0 -0
  64. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/config/translations/bn.json +0 -0
  65. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/config/translations/en-US.json +0 -0
  66. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/config/translations/gu.json +0 -0
  67. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/config/translations/he-IL.json +0 -0
  68. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/config/translations/hi.json +0 -0
  69. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/config/translations/kn.json +0 -0
  70. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/config/translations/ml.json +0 -0
  71. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/config/translations/mr.json +0 -0
  72. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/config/translations/ta.json +0 -0
  73. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/config/translations/te.json +0 -0
  74. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/config/translations/zh-CN.json +0 -0
  75. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/context.py +0 -0
  76. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/db.py +0 -0
  77. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/public/fantasy.svg +0 -0
  78. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/public/game.svg +0 -0
  79. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/public/logo_dark.png +0 -0
  80. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/public/logo_light.png +0 -0
  81. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/public/movie.svg +0 -0
  82. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/public/praison.css +0 -0
  83. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/public/thriller.svg +0 -0
  84. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/realtime.py +0 -0
  85. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/realtimeclient/__init__.py +0 -0
  86. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/realtimeclient/realtimedocs.txt +0 -0
  87. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/realtimeclient/tools.py +0 -0
  88. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/ui/sql_alchemy.py +0 -0
  89. {praisonai-2.0.26 → praisonai-2.0.27}/praisonai/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: PraisonAI
3
- Version: 2.0.26
3
+ Version: 2.0.27
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.26 gunicorn markdown\n")
59
+ file.write("RUN pip install flask praisonai==2.0.27 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
- agent_file = "test.yaml"
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 (untouched logic with minimal additions for Chainlit)
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 (untouched from colab_combined.py)
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
- # Tools Loader (untouched logic but returning dict for tools)
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():
@@ -182,7 +229,10 @@ def load_tools_from_tools_py():
182
229
 
183
230
  for name, obj in inspect.getmembers(module):
184
231
  if not name.startswith('_') and callable(obj) and not inspect.isclass(obj):
232
+ # Store the function in globals
185
233
  globals()[name] = obj
234
+
235
+ # Build the function definition
186
236
  tool_def = {
187
237
  "type": "function",
188
238
  "function": {
@@ -190,16 +240,14 @@ def load_tools_from_tools_py():
190
240
  "description": obj.__doc__ or f"Function to {name.replace('_', ' ')}",
191
241
  "parameters": {
192
242
  "type": "object",
193
- "properties": {
194
- "query": {
195
- "type": "string",
196
- "description": "The search query to look up information about"
197
- }
198
- },
199
- "required": ["query"]
243
+ "properties": {},
244
+ "required": []
200
245
  }
201
- }
246
+ },
247
+ # Keep the actual callable as well
248
+ "callable": obj,
202
249
  }
250
+
203
251
  tools_dict[name] = tool_def
204
252
  logger.info(f"Loaded and globalized tool function: {name}")
205
253
 
@@ -209,7 +257,7 @@ def load_tools_from_tools_py():
209
257
  return tools_dict
210
258
 
211
259
  # -----------------------------------------------------------------------------
212
- # Async Queue Processor (untouched logic)
260
+ # Async Queue Processor
213
261
  # -----------------------------------------------------------------------------
214
262
 
215
263
  async def process_message_queue():
@@ -223,7 +271,7 @@ async def process_message_queue():
223
271
  logger.error(f"Error processing message queue: {e}")
224
272
 
225
273
  # -----------------------------------------------------------------------------
226
- # Step & Task Callbacks (Async & Sync) (untouched logic)
274
+ # Step & Task Callbacks
227
275
  # -----------------------------------------------------------------------------
228
276
 
229
277
  async def step_callback(step_details):
@@ -351,9 +399,8 @@ def sync_step_callback_wrapper(step_details):
351
399
  logger.error(f"Error in sync_step_callback_wrapper: {e}", exc_info=True)
352
400
 
353
401
  # -----------------------------------------------------------------------------
354
- # Main PraisonAI Runner (untouched logic)
402
+ # Main PraisonAI Runner
355
403
  # -----------------------------------------------------------------------------
356
-
357
404
  async def ui_run_praisonai(config, topic, tools_dict):
358
405
  logger.info("Starting ui_run_praisonai")
359
406
  agents_map = {}
@@ -370,8 +417,6 @@ async def ui_run_praisonai(config, topic, tools_dict):
370
417
  goal_filled = details['goal'].format(topic=topic)
371
418
  backstory_filled = details['backstory'].format(topic=topic)
372
419
 
373
- await cl.Message(content=f"[DEBUG] Creating agent: {role_name}", author="System").send()
374
-
375
420
  def step_callback_sync(step_details):
376
421
  step_details["agent_name"] = role_name
377
422
  try:
@@ -394,7 +439,8 @@ async def ui_run_praisonai(config, topic, tools_dict):
394
439
  max_rpm=details.get('max_rpm'),
395
440
  max_execution_time=details.get('max_execution_time'),
396
441
  cache=details.get('cache', True),
397
- step_callback=step_callback_sync
442
+ step_callback=step_callback_sync,
443
+ self_reflect=details.get('self_reflect', False)
398
444
  )
399
445
  agents_map[role] = agent
400
446
 
@@ -402,17 +448,35 @@ async def ui_run_praisonai(config, topic, tools_dict):
402
448
  for role, details in config['roles'].items():
403
449
  agent = agents_map[role]
404
450
  role_name = agent.name
451
+
452
+ # -------------------------------------------------------------
453
+ # FIX: Skip empty or invalid tool names to avoid null tool objects
454
+ # -------------------------------------------------------------
405
455
  role_tools = []
456
+ task_tools = [] # Initialize task_tools outside the loop
457
+
406
458
  for tool_name in details.get('tools', []):
459
+ if not tool_name or not tool_name.strip():
460
+ logger.warning("Skipping empty tool name.")
461
+ continue
407
462
  if tool_name in tools_dict:
408
- role_tools.append(tools_dict[tool_name])
463
+ # Create a copy of the tool definition
464
+ tool_def = tools_dict[tool_name].copy()
465
+ # Store the callable separately and remove from definition
466
+ callable_func = tool_def.pop("callable")
467
+ # Add callable to role_tools for task execution
468
+ role_tools.append(callable_func)
469
+ # Add API tool definition to task's tools
470
+ task_tools.append(tool_def)
471
+ # Also set the agent's tools to include both
472
+ agent.tools = role_tools
473
+ else:
474
+ logger.warning(f"Tool '{tool_name}' not found. Skipping.")
409
475
 
410
476
  for tname, tdetails in details.get('tasks', {}).items():
411
477
  description_filled = tdetails['description'].format(topic=topic)
412
478
  expected_output_filled = tdetails['expected_output'].format(topic=topic)
413
479
 
414
- await cl.Message(content=f"[DEBUG] Created task: {tname} for {role_name}", author="System").send()
415
-
416
480
  def task_callback_sync(task_output):
417
481
  try:
418
482
  loop_ = asyncio.new_event_loop()
@@ -426,7 +490,7 @@ async def ui_run_praisonai(config, topic, tools_dict):
426
490
  description=description_filled,
427
491
  expected_output=expected_output_filled,
428
492
  agent=agent,
429
- tools=role_tools,
493
+ tools=task_tools, # Pass API tool definitions
430
494
  async_execution=True,
431
495
  context=[],
432
496
  config=tdetails.get('config', {}),
@@ -454,6 +518,7 @@ async def ui_run_praisonai(config, topic, tools_dict):
454
518
 
455
519
  await cl.Message(content="Starting PraisonAI agents execution...", author="System").send()
456
520
 
521
+ # Decide how to process tasks
457
522
  if config.get('process') == 'hierarchical':
458
523
  prai_agents = PraisonAIAgents(
459
524
  agents=list(agents_map.values()),
@@ -493,10 +558,11 @@ async def ui_run_praisonai(config, topic, tools_dict):
493
558
  raise
494
559
 
495
560
  # -----------------------------------------------------------------------------
496
- # Chainlit Handlers + logic from chainlit_ui.py (no changes in logic)
561
+ # Chainlit Handlers + logic
497
562
  # -----------------------------------------------------------------------------
498
563
 
499
564
  tools_dict = load_tools_from_tools_py()
565
+ print(f"[DEBUG] tools_dict: {tools_dict}")
500
566
 
501
567
  # Load agent config (default) from 'agents.yaml'
502
568
  with open(agent_file, 'r') as f:
@@ -520,9 +586,6 @@ if AUTH_PASSWORD_ENABLED:
520
586
 
521
587
  @cl.set_chat_profiles
522
588
  async def set_profiles(current_user: cl.User):
523
- """
524
- Keep all the same starter logic from chainlit_ui.py.
525
- """
526
589
  return [
527
590
  cl.ChatProfile(
528
591
  name="Auto",
@@ -577,7 +640,6 @@ async def set_profiles(current_user: cl.User):
577
640
  @cl.on_chat_start
578
641
  async def start_chat():
579
642
  try:
580
- # Load model name from database
581
643
  model_name = load_setting("model_name") or os.getenv("MODEL_NAME", "gpt-4o-mini")
582
644
  cl.user_session.set("model_name", model_name)
583
645
  logger.debug(f"Model name: {model_name}")
@@ -587,16 +649,19 @@ async def start_chat():
587
649
  [{"role": "system", "content": "You are a helpful assistant."}],
588
650
  )
589
651
 
590
- # Create tools.py if it doesn't exist
591
652
  if not os.path.exists("tools.py"):
592
653
  with open("tools.py", "w") as f:
593
654
  f.write("# Add your custom tools here\n")
594
655
 
656
+ if not os.path.exists("agents.yaml"):
657
+ with open("agents.yaml", "w") as f:
658
+ f.write("# Add your custom agents here\n")
659
+
595
660
  settings = await cl.ChatSettings(
596
661
  [
597
662
  TextInput(id="Model", label="OpenAI - Model", initial=model_name),
598
663
  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']),
664
+ TextInput(id="ApiKey", label="OpenAI - API Key", initial=config_list[0]['api_key']),
600
665
  Select(
601
666
  id="Framework",
602
667
  label="Framework",
@@ -608,7 +673,7 @@ async def start_chat():
608
673
  cl.user_session.set("settings", settings)
609
674
  chat_profile = cl.user_session.get("chat_profile")
610
675
 
611
- if chat_profile=="Manual":
676
+ if chat_profile == "Manual":
612
677
  agent_file = "agents.yaml"
613
678
  full_agent_file_path = os.path.abspath(agent_file)
614
679
  if os.path.exists(full_agent_file_path):
@@ -628,7 +693,7 @@ async def start_chat():
628
693
  [
629
694
  TextInput(id="Model", label="OpenAI - Model", initial=model_name),
630
695
  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']),
696
+ TextInput(id="ApiKey", label="OpenAI - API Key", initial=config_list[0]['api_key']),
632
697
  Select(
633
698
  id="Framework",
634
699
  label="Framework",
@@ -655,13 +720,8 @@ async def start_chat():
655
720
  logger.error(f"Error in start_chat: {str(e)}")
656
721
  await cl.Message(content=f"An error occurred while starting the chat: {str(e)}").send()
657
722
 
658
-
659
723
  @cl.on_chat_resume
660
724
  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
725
  try:
666
726
  message_history = cl.user_session.get("message_history", [])
667
727
  root_messages = [m for m in thread["steps"] if m["parentId"] is None]
@@ -676,11 +736,6 @@ async def on_chat_resume(thread: ThreadDict):
676
736
 
677
737
  @cl.on_message
678
738
  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
739
  try:
685
740
  logger.info(f"User message: {message.content}")
686
741
  await cl.Message(
@@ -691,7 +746,6 @@ async def main(message: cl.Message):
691
746
  # Run PraisonAI
692
747
  result = await ui_run_praisonai(config, message.content, tools_dict)
693
748
 
694
- # Update message history
695
749
  message_history = cl.user_session.get("message_history", [])
696
750
  message_history.append({"role": "user", "content": message.content})
697
751
  message_history.append({"role": "assistant", "content": str(result)})
@@ -704,14 +758,12 @@ async def main(message: cl.Message):
704
758
 
705
759
  @cl.on_settings_update
706
760
  async def on_settings_update(settings):
707
- """Handle updates to the ChatSettings form."""
708
761
  try:
709
762
  global config_list, framework
710
763
  config_list[0]['model'] = settings["Model"]
711
764
  config_list[0]['base_url'] = settings["BaseUrl"]
712
765
  config_list[0]['api_key'] = settings["ApiKey"]
713
766
 
714
- # Save settings to database with retry
715
767
  for attempt in range(MAX_RETRIES):
716
768
  try:
717
769
  await save_setting_with_retry("model_name", config_list[0]['model'])
@@ -724,7 +776,6 @@ async def on_settings_update(settings):
724
776
  continue
725
777
  raise
726
778
 
727
- # Save to environment variables for compatibility
728
779
  os.environ["OPENAI_API_KEY"] = config_list[0]['api_key']
729
780
  os.environ["OPENAI_MODEL_NAME"] = config_list[0]['model']
730
781
  os.environ["OPENAI_API_BASE"] = config_list[0]['base_url']
@@ -739,7 +790,6 @@ async def on_settings_update(settings):
739
790
  with open("tools.py", "w") as f:
740
791
  f.write(settings["tools"])
741
792
 
742
- # Update thread metadata if exists with retry
743
793
  thread_id = cl.user_session.get("thread_id")
744
794
  if thread_id:
745
795
  for attempt in range(MAX_RETRIES):
@@ -749,6 +799,7 @@ async def on_settings_update(settings):
749
799
  metadata = thread.get("metadata", {})
750
800
  if isinstance(metadata, str):
751
801
  try:
802
+ import json
752
803
  metadata = json.loads(metadata)
753
804
  except json.JSONDecodeError:
754
805
  metadata = {}
@@ -766,10 +817,9 @@ async def on_settings_update(settings):
766
817
  except Exception as e:
767
818
  logger.error(f"Error updating settings: {str(e)}")
768
819
  await cl.Message(content=f"An error occurred while updating settings: {str(e)}. Retrying...").send()
769
- # One final retry after a longer delay
770
820
  try:
771
821
  await asyncio.sleep(RETRY_DELAY * 2)
772
822
  await on_settings_update(settings)
773
823
  except Exception as e:
774
824
  logger.error(f"Final retry failed: {str(e)}")
775
- await cl.Message(content=f"Failed to update settings after retries: {str(e)}").send()
825
+ 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.26"
3
+ version = "2.0.27"
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.26"
87
+ version = "2.0.27"
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 = ""