PraisonAI 2.0.23__cp312-cp312-manylinux_2_39_x86_64.whl → 2.0.25__cp312-cp312-manylinux_2_39_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of PraisonAI might be problematic. Click here for more details.
- praisonai/deploy.py +1 -1
- praisonai/ui/agents.py +232 -85
- praisonai/ui/callbacks.py +57 -0
- praisonai/ui/colab.py +474 -0
- praisonai/ui/colab_chainlit.py +81 -0
- praisonai/ui/config/chainlit.md +1 -1
- {praisonai-2.0.23.dist-info → praisonai-2.0.25.dist-info}/METADATA +42 -17
- {praisonai-2.0.23.dist-info → praisonai-2.0.25.dist-info}/RECORD +11 -8
- {praisonai-2.0.23.dist-info → praisonai-2.0.25.dist-info}/WHEEL +1 -1
- {praisonai-2.0.23.dist-info → praisonai-2.0.25.dist-info}/LICENSE +0 -0
- {praisonai-2.0.23.dist-info → praisonai-2.0.25.dist-info}/entry_points.txt +0 -0
praisonai/deploy.py
CHANGED
|
@@ -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.25 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
|
|
praisonai/ui/agents.py
CHANGED
|
@@ -5,7 +5,7 @@ import chainlit as cl
|
|
|
5
5
|
import os
|
|
6
6
|
from chainlit.types import ThreadDict
|
|
7
7
|
from chainlit.input_widget import Select, TextInput
|
|
8
|
-
from typing import Optional, Dict, Any
|
|
8
|
+
from typing import Optional, Dict, Any, AsyncGenerator, List, Callable
|
|
9
9
|
from dotenv import load_dotenv
|
|
10
10
|
from datetime import datetime
|
|
11
11
|
import json
|
|
@@ -18,6 +18,9 @@ from contextlib import redirect_stdout, asynccontextmanager
|
|
|
18
18
|
from db import DatabaseManager
|
|
19
19
|
import time
|
|
20
20
|
import sqlite3
|
|
21
|
+
from openai import AsyncOpenAI
|
|
22
|
+
from functools import partial
|
|
23
|
+
import yaml
|
|
21
24
|
|
|
22
25
|
# Load environment variables
|
|
23
26
|
load_dotenv()
|
|
@@ -354,37 +357,193 @@ async def on_chat_resume(thread: ThreadDict):
|
|
|
354
357
|
# async def tool(data: Optional[str] = None, language: Optional[str] = None):
|
|
355
358
|
# return cl.Message(content=data, language=language)
|
|
356
359
|
|
|
360
|
+
# Add callback handler class
|
|
361
|
+
class ChainlitCallbackHandler:
|
|
362
|
+
"""Callback handler for streaming agent execution to Chainlit"""
|
|
363
|
+
|
|
364
|
+
def __init__(self, parent_step: Optional[cl.Step] = None):
|
|
365
|
+
self.parent_step = parent_step
|
|
366
|
+
self.current_step = None
|
|
367
|
+
self._steps = {}
|
|
368
|
+
|
|
369
|
+
async def on_agent_start(self, agent_name: str):
|
|
370
|
+
"""Called when an agent starts execution"""
|
|
371
|
+
self.current_step = cl.Step(
|
|
372
|
+
name=f"Agent: {agent_name}",
|
|
373
|
+
type="agent",
|
|
374
|
+
show_input=True,
|
|
375
|
+
parent_id=self.parent_step.id if self.parent_step else None
|
|
376
|
+
)
|
|
377
|
+
await self.current_step.start()
|
|
378
|
+
await self.current_step.stream_token(f"🤖 Agent {agent_name} started\n")
|
|
379
|
+
self._steps[agent_name] = self.current_step
|
|
380
|
+
|
|
381
|
+
async def on_agent_action(self, agent_name: str, action: str):
|
|
382
|
+
"""Called when an agent performs an action"""
|
|
383
|
+
step = self._steps.get(agent_name, self.current_step)
|
|
384
|
+
if step:
|
|
385
|
+
await step.stream_token(f"⚡ {action}\n")
|
|
386
|
+
|
|
387
|
+
async def on_agent_finish(self, agent_name: str, output: Any):
|
|
388
|
+
"""Called when an agent finishes execution"""
|
|
389
|
+
step = self._steps.get(agent_name)
|
|
390
|
+
if step:
|
|
391
|
+
await step.stream_token(f"\n✅ Agent {agent_name} finished\n")
|
|
392
|
+
step.output = str(output)
|
|
393
|
+
await step.end()
|
|
394
|
+
self._steps.pop(agent_name, None)
|
|
395
|
+
|
|
396
|
+
async def on_task_start(self, task_id: str, task_name: str):
|
|
397
|
+
"""Called when a task starts execution"""
|
|
398
|
+
self.current_step = cl.Step(
|
|
399
|
+
name=f"Task: {task_name}",
|
|
400
|
+
type="task",
|
|
401
|
+
show_input=True,
|
|
402
|
+
parent_id=self.parent_step.id if self.parent_step else None
|
|
403
|
+
)
|
|
404
|
+
await self.current_step.start()
|
|
405
|
+
await self.current_step.stream_token(f"📋 Starting task: {task_name}\n")
|
|
406
|
+
self._steps[task_id] = self.current_step
|
|
407
|
+
|
|
408
|
+
async def on_task_finish(self, task_id: str, output: Any):
|
|
409
|
+
"""Called when a task finishes execution"""
|
|
410
|
+
step = self._steps.get(task_id, self.current_step)
|
|
411
|
+
if step:
|
|
412
|
+
await step.stream_token(f"\n✅ Task completed\n")
|
|
413
|
+
step.output = str(output)
|
|
414
|
+
await step.end()
|
|
415
|
+
self._steps.pop(task_id, None)
|
|
416
|
+
|
|
417
|
+
async def on_error(self, error: str):
|
|
418
|
+
"""Called when an error occurs"""
|
|
419
|
+
if self.current_step:
|
|
420
|
+
await self.current_step.stream_token(f"\n❌ Error: {error}\n")
|
|
421
|
+
await self.current_step.end()
|
|
422
|
+
|
|
357
423
|
@cl.step(type="tool", show_input=False)
|
|
358
424
|
async def run_agents(agent_file: str, framework: str):
|
|
359
425
|
"""Runs the agents and returns the result."""
|
|
360
426
|
try:
|
|
361
427
|
logger.debug(f"Running agents with file: {agent_file}, framework: {framework}")
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
stdout_buffer = StringIO()
|
|
367
|
-
with redirect_stdout(stdout_buffer):
|
|
368
|
-
result = agents_generator.generate_crew_and_kickoff()
|
|
428
|
+
|
|
429
|
+
# Create main execution step
|
|
430
|
+
async with cl.Step(name="Agents Execution", type="agents") as agents_step:
|
|
431
|
+
agents_step.input = f"Running agents from {agent_file}"
|
|
369
432
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
async with cl.Step(name="gpt4", type="llm", show_input=True) as step:
|
|
374
|
-
step.input = ""
|
|
433
|
+
# Initialize callback handler
|
|
434
|
+
callback_handler = ChainlitCallbackHandler(parent_step=agents_step)
|
|
375
435
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
436
|
+
try:
|
|
437
|
+
# Load YAML config first
|
|
438
|
+
with open(agent_file, 'r') as f:
|
|
439
|
+
config = yaml.safe_load(f)
|
|
440
|
+
|
|
441
|
+
# Get topic from message content
|
|
442
|
+
topic = cl.user_session.get("message_history", [{}])[-1].get("content", "")
|
|
443
|
+
|
|
444
|
+
# Create agents generator with loaded config
|
|
445
|
+
agents_generator = AgentsGenerator(
|
|
446
|
+
agent_file=agent_file,
|
|
447
|
+
framework=framework,
|
|
448
|
+
config_list=config_list,
|
|
449
|
+
agent_yaml=yaml.dump(config) # Pass the loaded config as YAML string
|
|
450
|
+
)
|
|
451
|
+
|
|
452
|
+
# Execute based on framework
|
|
453
|
+
if framework == "crewai":
|
|
454
|
+
result = agents_generator._run_crewai(config, topic, [])
|
|
455
|
+
elif framework == "autogen":
|
|
456
|
+
result = agents_generator._run_autogen(config, topic, [])
|
|
457
|
+
elif framework == "praisonai":
|
|
458
|
+
result = agents_generator._run_praisonai(config, topic, [])
|
|
459
|
+
else:
|
|
460
|
+
raise ValueError(f"Unsupported framework: {framework}")
|
|
461
|
+
|
|
462
|
+
# Process the result if it has tasks
|
|
463
|
+
if hasattr(result, 'tasks') and result.tasks:
|
|
464
|
+
for task in result.tasks:
|
|
465
|
+
task_id = getattr(task, 'id', str(id(task)))
|
|
466
|
+
task_desc = getattr(task, 'description', 'Executing task...')
|
|
467
|
+
|
|
468
|
+
# Signal task start
|
|
469
|
+
await callback_handler.on_task_start(
|
|
470
|
+
task_id,
|
|
471
|
+
task_desc[:50] + "..." if len(task_desc) > 50 else task_desc
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
try:
|
|
475
|
+
# Handle agent actions if present
|
|
476
|
+
agent = getattr(task, 'agent', None)
|
|
477
|
+
if agent:
|
|
478
|
+
agent_name = getattr(agent, 'name', 'Unknown Agent')
|
|
479
|
+
await callback_handler.on_agent_start(agent_name)
|
|
480
|
+
await callback_handler.on_agent_action(
|
|
481
|
+
agent_name,
|
|
482
|
+
f"Working on task: {task_desc[:50]}..."
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
# Get task output
|
|
486
|
+
task_output = getattr(task, 'output', str(task))
|
|
487
|
+
|
|
488
|
+
# Signal agent completion if exists
|
|
489
|
+
if agent:
|
|
490
|
+
await callback_handler.on_agent_finish(agent_name, task_output)
|
|
491
|
+
|
|
492
|
+
# Signal task completion
|
|
493
|
+
await callback_handler.on_task_finish(task_id, task_output)
|
|
494
|
+
|
|
495
|
+
except Exception as e:
|
|
496
|
+
await callback_handler.on_error(f"Error in task {task_id}: {str(e)}")
|
|
497
|
+
raise
|
|
379
498
|
|
|
380
|
-
|
|
499
|
+
# Return the final result
|
|
500
|
+
agents_step.output = "Agents execution completed"
|
|
501
|
+
return result if isinstance(result, str) else str(result)
|
|
502
|
+
|
|
503
|
+
except Exception as e:
|
|
504
|
+
await callback_handler.on_error(str(e))
|
|
505
|
+
raise
|
|
381
506
|
|
|
382
|
-
return result
|
|
383
507
|
except Exception as e:
|
|
384
508
|
error_msg = f"Error running agents: {str(e)}"
|
|
385
509
|
logger.error(error_msg)
|
|
386
510
|
raise Exception(error_msg)
|
|
387
511
|
|
|
512
|
+
async def stream_agents_execution(agents) -> AsyncGenerator[tuple[str, str, str], None]:
|
|
513
|
+
"""
|
|
514
|
+
Generator to stream agents execution status and messages.
|
|
515
|
+
Yields tuples of (task_id, status, message)
|
|
516
|
+
"""
|
|
517
|
+
try:
|
|
518
|
+
for task_id in agents.tasks:
|
|
519
|
+
task = agents.tasks[task_id]
|
|
520
|
+
|
|
521
|
+
# Signal task start
|
|
522
|
+
yield task_id, "start", ""
|
|
523
|
+
|
|
524
|
+
if task.async_execution:
|
|
525
|
+
# Execute async task
|
|
526
|
+
result = await agents.aexecute_task(task_id)
|
|
527
|
+
else:
|
|
528
|
+
# Execute sync task in thread pool
|
|
529
|
+
result = await cl.make_async(agents.execute_task)(task_id)
|
|
530
|
+
|
|
531
|
+
if result:
|
|
532
|
+
# Stream agent messages
|
|
533
|
+
if isinstance(result, str):
|
|
534
|
+
yield task_id, "agent_message", result
|
|
535
|
+
else:
|
|
536
|
+
yield task_id, "agent_message", result.raw
|
|
537
|
+
|
|
538
|
+
# Signal completion
|
|
539
|
+
yield task_id, "complete", ""
|
|
540
|
+
else:
|
|
541
|
+
yield task_id, "error", "Task execution failed"
|
|
542
|
+
|
|
543
|
+
except Exception as e:
|
|
544
|
+
logger.error(f"Error in stream_agents_execution: {e}")
|
|
545
|
+
yield task_id, "error", str(e)
|
|
546
|
+
|
|
388
547
|
@cl.step(type="tool", show_input=False, language="yaml")
|
|
389
548
|
async def output(output):
|
|
390
549
|
return output
|
|
@@ -405,17 +564,38 @@ def task(output):
|
|
|
405
564
|
{output}
|
|
406
565
|
""")
|
|
407
566
|
|
|
567
|
+
# Add retry decorator for database operations
|
|
568
|
+
def with_retries(max_retries=3, delay=1):
|
|
569
|
+
def decorator(func):
|
|
570
|
+
async def wrapper(*args, **kwargs):
|
|
571
|
+
for attempt in range(max_retries):
|
|
572
|
+
try:
|
|
573
|
+
return await func(*args, **kwargs)
|
|
574
|
+
except sqlite3.OperationalError as e:
|
|
575
|
+
if "database is locked" in str(e) and attempt < max_retries - 1:
|
|
576
|
+
await asyncio.sleep(delay)
|
|
577
|
+
continue
|
|
578
|
+
raise
|
|
579
|
+
return await func(*args, **kwargs)
|
|
580
|
+
return wrapper
|
|
581
|
+
return decorator
|
|
582
|
+
|
|
583
|
+
@with_retries(max_retries=3, delay=1)
|
|
584
|
+
async def update_thread_metadata(thread_id: str, metadata: dict):
|
|
585
|
+
"""Update thread metadata with retry logic"""
|
|
586
|
+
await cl_data.update_thread(thread_id, metadata=metadata)
|
|
587
|
+
|
|
408
588
|
@cl.on_message
|
|
409
589
|
async def main(message: cl.Message):
|
|
410
|
-
"""Run PraisonAI with the provided message as the topic."""
|
|
411
590
|
try:
|
|
412
|
-
# Get
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
message_history = []
|
|
416
|
-
cl.user_session.set("message_history", message_history)
|
|
591
|
+
# Get settings and chat profile
|
|
592
|
+
settings = cl.user_session.get("settings")
|
|
593
|
+
chat_profile = cl.user_session.get("chat_profile")
|
|
417
594
|
|
|
418
|
-
#
|
|
595
|
+
# Get message history or initialize if not exists
|
|
596
|
+
message_history = cl.user_session.get("message_history", [])
|
|
597
|
+
|
|
598
|
+
# Format user message with context
|
|
419
599
|
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
420
600
|
user_message = f"""
|
|
421
601
|
Answer the question and use tools if needed:
|
|
@@ -424,54 +604,27 @@ async def main(message: cl.Message):
|
|
|
424
604
|
|
|
425
605
|
User Question: {message.content}
|
|
426
606
|
"""
|
|
607
|
+
|
|
608
|
+
# Add to message history
|
|
427
609
|
message_history.append({"role": "user", "content": user_message})
|
|
428
610
|
|
|
429
|
-
# Get
|
|
611
|
+
# Get configuration
|
|
612
|
+
framework = settings["Framework"]
|
|
430
613
|
topic = message.content
|
|
431
|
-
chat_profile = cl.user_session.get("chat_profile")
|
|
432
|
-
logger.debug(f"Processing message with chat profile: {chat_profile}")
|
|
433
614
|
|
|
434
615
|
if chat_profile == "Auto":
|
|
435
616
|
agent_file = "agents.yaml"
|
|
436
617
|
logger.info(f"Generating agents for topic: {topic}")
|
|
437
618
|
generator = AutoGenerator(topic=topic, agent_file=agent_file, framework=framework, config_list=config_list)
|
|
619
|
+
|
|
438
620
|
await cl.sleep(2)
|
|
439
621
|
agent_file = generator.generate()
|
|
440
622
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
framework,
|
|
445
|
-
config_list
|
|
446
|
-
)
|
|
623
|
+
# Run agents with streaming while preserving context
|
|
624
|
+
result = await run_agents(agent_file, framework)
|
|
625
|
+
await cl.Message(content=result).send()
|
|
447
626
|
|
|
448
|
-
|
|
449
|
-
stdout_buffer = StringIO()
|
|
450
|
-
with redirect_stdout(stdout_buffer):
|
|
451
|
-
result = agents_generator.generate_crew_and_kickoff()
|
|
452
|
-
|
|
453
|
-
complete_output = stdout_buffer.getvalue()
|
|
454
|
-
logger.debug(f"Agents execution output: {complete_output}")
|
|
455
|
-
|
|
456
|
-
tool_res = await output(complete_output)
|
|
457
|
-
msg = cl.Message(content=result)
|
|
458
|
-
await msg.send()
|
|
459
|
-
|
|
460
|
-
# Save to message history
|
|
461
|
-
message_history.append({"role": "assistant", "content": result})
|
|
462
|
-
cl.user_session.set("message_history", message_history)
|
|
463
|
-
|
|
464
|
-
# Update thread metadata if exists
|
|
465
|
-
thread_id = cl.user_session.get("thread_id")
|
|
466
|
-
if thread_id:
|
|
467
|
-
metadata = {
|
|
468
|
-
"last_response": result,
|
|
469
|
-
"timestamp": now,
|
|
470
|
-
"mode": "auto"
|
|
471
|
-
}
|
|
472
|
-
await cl_data.update_thread(thread_id, metadata=metadata)
|
|
473
|
-
|
|
474
|
-
else: # chat_profile == "Manual"
|
|
627
|
+
else: # Manual mode
|
|
475
628
|
agent_file = "agents.yaml"
|
|
476
629
|
full_agent_file_path = os.path.abspath(agent_file)
|
|
477
630
|
full_tools_file_path = os.path.abspath("tools.py")
|
|
@@ -487,31 +640,25 @@ async def main(message: cl.Message):
|
|
|
487
640
|
tools_content = f.read()
|
|
488
641
|
msg_tools = cl.Message(content=tools_content, language="python")
|
|
489
642
|
await msg_tools.send()
|
|
490
|
-
else:
|
|
491
|
-
logger.info("Generating agents for manual mode")
|
|
492
|
-
generator = AutoGenerator(topic=topic, agent_file=agent_file, framework=framework, config_list=config_list)
|
|
493
|
-
agent_file = generator.generate()
|
|
494
|
-
|
|
495
|
-
logger.debug("Starting agents execution for manual mode")
|
|
496
|
-
agents_generator = AgentsGenerator(agent_file, framework, config_list)
|
|
497
|
-
result = agents_generator.generate_crew_and_kickoff()
|
|
498
|
-
msg = cl.Message(content=result, actions=actions)
|
|
499
|
-
await msg.send()
|
|
500
|
-
|
|
501
|
-
# Save to message history
|
|
502
|
-
message_history.append({"role": "assistant", "content": result})
|
|
503
|
-
cl.user_session.set("message_history", message_history)
|
|
504
643
|
|
|
505
|
-
#
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
644
|
+
# Run agents with streaming while preserving context
|
|
645
|
+
result = await run_agents(agent_file, framework)
|
|
646
|
+
await cl.Message(content=result, actions=actions).send()
|
|
647
|
+
|
|
648
|
+
# Update message history
|
|
649
|
+
message_history.append({"role": "assistant", "content": result})
|
|
650
|
+
cl.user_session.set("message_history", message_history)
|
|
651
|
+
|
|
652
|
+
# Update thread metadata with retry logic
|
|
653
|
+
thread_id = cl.user_session.get("thread_id")
|
|
654
|
+
if thread_id:
|
|
655
|
+
metadata = {
|
|
656
|
+
"last_response": result,
|
|
657
|
+
"timestamp": now,
|
|
658
|
+
"mode": chat_profile.lower()
|
|
659
|
+
}
|
|
660
|
+
await update_thread_metadata(thread_id, metadata)
|
|
661
|
+
|
|
515
662
|
except Exception as e:
|
|
516
663
|
error_msg = f"Error processing message: {str(e)}"
|
|
517
664
|
logger.error(error_msg)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Dict, Any, Callable, Optional, Union
|
|
3
|
+
import asyncio
|
|
4
|
+
|
|
5
|
+
logger = logging.getLogger(__name__)
|
|
6
|
+
|
|
7
|
+
class CallbackManager:
|
|
8
|
+
"""Manages callbacks for the PraisonAI UI"""
|
|
9
|
+
|
|
10
|
+
def __init__(self):
|
|
11
|
+
self._callbacks: Dict[str, Dict[str, Union[Callable, bool]]] = {}
|
|
12
|
+
|
|
13
|
+
def register(self, name: str, callback: Callable, is_async: bool = False) -> None:
|
|
14
|
+
"""Register a callback function"""
|
|
15
|
+
self._callbacks[name] = {
|
|
16
|
+
'func': callback,
|
|
17
|
+
'is_async': is_async
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async def call(self, name: str, **kwargs) -> None:
|
|
21
|
+
"""Call a registered callback"""
|
|
22
|
+
if name not in self._callbacks:
|
|
23
|
+
logger.warning(f"No callback registered for {name}")
|
|
24
|
+
return
|
|
25
|
+
|
|
26
|
+
callback_info = self._callbacks[name]
|
|
27
|
+
func = callback_info['func']
|
|
28
|
+
is_async = callback_info['is_async']
|
|
29
|
+
|
|
30
|
+
try:
|
|
31
|
+
if is_async:
|
|
32
|
+
await func(**kwargs)
|
|
33
|
+
else:
|
|
34
|
+
if asyncio.iscoroutinefunction(func):
|
|
35
|
+
await func(**kwargs)
|
|
36
|
+
else:
|
|
37
|
+
await asyncio.get_event_loop().run_in_executor(None, lambda: func(**kwargs))
|
|
38
|
+
except Exception as e:
|
|
39
|
+
logger.error(f"Error in callback {name}: {str(e)}")
|
|
40
|
+
|
|
41
|
+
# Global callback manager instance
|
|
42
|
+
callback_manager = CallbackManager()
|
|
43
|
+
|
|
44
|
+
def register_callback(name: str, callback: Callable, is_async: bool = False) -> None:
|
|
45
|
+
"""Register a callback with the global callback manager"""
|
|
46
|
+
callback_manager.register(name, callback, is_async)
|
|
47
|
+
|
|
48
|
+
async def trigger_callback(name: str, **kwargs) -> None:
|
|
49
|
+
"""Trigger a callback from the global callback manager"""
|
|
50
|
+
await callback_manager.call(name, **kwargs)
|
|
51
|
+
|
|
52
|
+
# Decorator for registering callbacks
|
|
53
|
+
def callback(name: str, is_async: bool = False):
|
|
54
|
+
def decorator(func):
|
|
55
|
+
register_callback(name, func, is_async)
|
|
56
|
+
return func
|
|
57
|
+
return decorator
|
praisonai/ui/colab.py
ADDED
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
from praisonaiagents import Agent, Task, PraisonAIAgents
|
|
2
|
+
import os
|
|
3
|
+
import importlib
|
|
4
|
+
import inspect
|
|
5
|
+
import yaml
|
|
6
|
+
import logging
|
|
7
|
+
from .callbacks import trigger_callback
|
|
8
|
+
import asyncio
|
|
9
|
+
import chainlit as cl
|
|
10
|
+
from queue import Queue
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
agent_file = "agents.yaml"
|
|
14
|
+
|
|
15
|
+
with open(agent_file, 'r') as f:
|
|
16
|
+
config = yaml.safe_load(f)
|
|
17
|
+
|
|
18
|
+
topic = "get from the message content from the chainlit user message"
|
|
19
|
+
|
|
20
|
+
# Create a message queue
|
|
21
|
+
message_queue = Queue()
|
|
22
|
+
|
|
23
|
+
async def process_message_queue():
|
|
24
|
+
"""Process messages in the queue and send them to Chainlit"""
|
|
25
|
+
while True:
|
|
26
|
+
try:
|
|
27
|
+
if not message_queue.empty():
|
|
28
|
+
msg_data = message_queue.get()
|
|
29
|
+
await cl.Message(**msg_data).send()
|
|
30
|
+
await asyncio.sleep(0.1) # Small delay to prevent busy waiting
|
|
31
|
+
except Exception as e:
|
|
32
|
+
logger.error(f"Error processing message queue: {e}")
|
|
33
|
+
|
|
34
|
+
def load_tools_from_tools_py():
|
|
35
|
+
"""
|
|
36
|
+
Imports and returns all contents from tools.py file.
|
|
37
|
+
Also adds the tools to the global namespace.
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
list: A list of callable functions with proper formatting
|
|
41
|
+
"""
|
|
42
|
+
tools_list = []
|
|
43
|
+
try:
|
|
44
|
+
# Try to import tools.py from current directory
|
|
45
|
+
spec = importlib.util.spec_from_file_location("tools", "tools.py")
|
|
46
|
+
logger.info(f"Spec: {spec}")
|
|
47
|
+
if spec is None:
|
|
48
|
+
logger.info("tools.py not found in current directory")
|
|
49
|
+
return tools_list
|
|
50
|
+
|
|
51
|
+
module = importlib.util.module_from_spec(spec)
|
|
52
|
+
spec.loader.exec_module(module)
|
|
53
|
+
|
|
54
|
+
# Get all module attributes except private ones and classes
|
|
55
|
+
for name, obj in inspect.getmembers(module):
|
|
56
|
+
if (not name.startswith('_') and
|
|
57
|
+
callable(obj) and
|
|
58
|
+
not inspect.isclass(obj)):
|
|
59
|
+
# Add the function to global namespace
|
|
60
|
+
globals()[name] = obj
|
|
61
|
+
# Format the tool as an OpenAI function
|
|
62
|
+
tool = {
|
|
63
|
+
"type": "function",
|
|
64
|
+
"function": {
|
|
65
|
+
"name": name,
|
|
66
|
+
"description": obj.__doc__ or f"Function to {name.replace('_', ' ')}",
|
|
67
|
+
"parameters": {
|
|
68
|
+
"type": "object",
|
|
69
|
+
"properties": {
|
|
70
|
+
"query": {
|
|
71
|
+
"type": "string",
|
|
72
|
+
"description": "The search query to look up information about"
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
"required": ["query"]
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
# Add formatted tool to tools list
|
|
80
|
+
tools_list.append(tool)
|
|
81
|
+
logger.info(f"Loaded and globalized tool function: {name}")
|
|
82
|
+
|
|
83
|
+
logger.info(f"Loaded {len(tools_list)} tool functions from tools.py")
|
|
84
|
+
logger.info(f"Tools list: {tools_list}")
|
|
85
|
+
|
|
86
|
+
except Exception as e:
|
|
87
|
+
logger.warning(f"Error loading tools from tools.py: {e}")
|
|
88
|
+
|
|
89
|
+
return tools_list
|
|
90
|
+
|
|
91
|
+
async def step_callback(step_details):
|
|
92
|
+
"""Callback for agent steps"""
|
|
93
|
+
logger.info(f"[CALLBACK DEBUG] Step callback triggered with details: {step_details}")
|
|
94
|
+
try:
|
|
95
|
+
# Queue message for agent response
|
|
96
|
+
if step_details.get("response"):
|
|
97
|
+
message_queue.put({
|
|
98
|
+
"content": f"Agent Response: {step_details.get('response')}",
|
|
99
|
+
"author": step_details.get("agent_name", "Agent")
|
|
100
|
+
})
|
|
101
|
+
logger.info("[CALLBACK DEBUG] Queued agent response message")
|
|
102
|
+
|
|
103
|
+
# Queue message for tool usage
|
|
104
|
+
if step_details.get("tool_name"):
|
|
105
|
+
message_queue.put({
|
|
106
|
+
"content": f"🛠️ Using tool: {step_details.get('tool_name')}",
|
|
107
|
+
"author": "System"
|
|
108
|
+
})
|
|
109
|
+
logger.info("[CALLBACK DEBUG] Queued tool usage message")
|
|
110
|
+
|
|
111
|
+
except Exception as e:
|
|
112
|
+
logger.error(f"[CALLBACK DEBUG] Error in step callback: {str(e)}", exc_info=True)
|
|
113
|
+
|
|
114
|
+
async def task_callback(task_output):
|
|
115
|
+
"""Callback for task completion"""
|
|
116
|
+
logger.info(f"[CALLBACK DEBUG] Task callback triggered with output: {task_output}")
|
|
117
|
+
try:
|
|
118
|
+
# Create message content
|
|
119
|
+
if hasattr(task_output, 'raw'):
|
|
120
|
+
content = task_output.raw
|
|
121
|
+
elif hasattr(task_output, 'content'):
|
|
122
|
+
content = task_output.content
|
|
123
|
+
else:
|
|
124
|
+
content = str(task_output)
|
|
125
|
+
|
|
126
|
+
# Queue the message
|
|
127
|
+
message_queue.put({
|
|
128
|
+
"content": f"Task Output: {content}",
|
|
129
|
+
"author": "Task"
|
|
130
|
+
})
|
|
131
|
+
logger.info("[CALLBACK DEBUG] Queued task completion message")
|
|
132
|
+
|
|
133
|
+
except Exception as e:
|
|
134
|
+
logger.error(f"[CALLBACK DEBUG] Error in task callback: {str(e)}", exc_info=True)
|
|
135
|
+
|
|
136
|
+
async def step_callback_wrapper(step_details):
|
|
137
|
+
logger.info(f"[CALLBACK DEBUG] Step callback wrapper triggered with details: {step_details}")
|
|
138
|
+
try:
|
|
139
|
+
# Check if we have a Chainlit context
|
|
140
|
+
if not cl.context.context_var.get():
|
|
141
|
+
logger.warning("[CALLBACK DEBUG] No Chainlit context available in wrapper")
|
|
142
|
+
return
|
|
143
|
+
logger.info("[CALLBACK DEBUG] Chainlit context found in wrapper")
|
|
144
|
+
|
|
145
|
+
# Create a message for the agent's response
|
|
146
|
+
if step_details.get("response"):
|
|
147
|
+
logger.info(f"[CALLBACK DEBUG] Sending agent response from wrapper: {step_details.get('response')}")
|
|
148
|
+
try:
|
|
149
|
+
await cl.Message(
|
|
150
|
+
content=f"{role_name}: {step_details.get('response')}",
|
|
151
|
+
author=role_name,
|
|
152
|
+
).send()
|
|
153
|
+
logger.info("[CALLBACK DEBUG] Successfully sent agent response message from wrapper")
|
|
154
|
+
except Exception as e:
|
|
155
|
+
logger.error(f"[CALLBACK DEBUG] Error sending agent response message from wrapper: {str(e)}")
|
|
156
|
+
|
|
157
|
+
# Create a message for any tool usage
|
|
158
|
+
if step_details.get("tool_name"):
|
|
159
|
+
logger.info(f"[CALLBACK DEBUG] Sending tool usage from wrapper: {step_details.get('tool_name')}")
|
|
160
|
+
try:
|
|
161
|
+
await cl.Message(
|
|
162
|
+
content=f"🛠️ {role_name} is using tool: {step_details.get('tool_name')}",
|
|
163
|
+
author="System",
|
|
164
|
+
).send()
|
|
165
|
+
logger.info("[CALLBACK DEBUG] Successfully sent tool usage message from wrapper")
|
|
166
|
+
except Exception as e:
|
|
167
|
+
logger.error(f"[CALLBACK DEBUG] Error sending tool usage message from wrapper: {str(e)}")
|
|
168
|
+
|
|
169
|
+
# Create a message for any thoughts or reasoning
|
|
170
|
+
if step_details.get("thought"):
|
|
171
|
+
logger.info(f"[CALLBACK DEBUG] Sending thought from wrapper: {step_details.get('thought')}")
|
|
172
|
+
try:
|
|
173
|
+
await cl.Message(
|
|
174
|
+
content=f"💭 {role_name}'s thought: {step_details.get('thought')}",
|
|
175
|
+
author=role_name,
|
|
176
|
+
).send()
|
|
177
|
+
logger.info("[CALLBACK DEBUG] Successfully sent thought message from wrapper")
|
|
178
|
+
except Exception as e:
|
|
179
|
+
logger.error(f"[CALLBACK DEBUG] Error sending thought message from wrapper: {str(e)}")
|
|
180
|
+
|
|
181
|
+
except Exception as e:
|
|
182
|
+
logger.error(f"[CALLBACK DEBUG] Error in step callback wrapper: {str(e)}", exc_info=True)
|
|
183
|
+
try:
|
|
184
|
+
await cl.Message(
|
|
185
|
+
content=f"Error in step callback: {str(e)}",
|
|
186
|
+
author="System",
|
|
187
|
+
).send()
|
|
188
|
+
except Exception as send_error:
|
|
189
|
+
logger.error(f"[CALLBACK DEBUG] Error sending error message: {str(send_error)}")
|
|
190
|
+
|
|
191
|
+
async def task_callback_wrapper(task_output):
|
|
192
|
+
logger.info(f"[CALLBACK DEBUG] Task callback wrapper triggered with output type: {type(task_output)}")
|
|
193
|
+
try:
|
|
194
|
+
# Check if we have a Chainlit context
|
|
195
|
+
if not cl.context.context_var.get():
|
|
196
|
+
logger.warning("[CALLBACK DEBUG] No Chainlit context available in task wrapper")
|
|
197
|
+
return
|
|
198
|
+
logger.info("[CALLBACK DEBUG] Chainlit context found in task wrapper")
|
|
199
|
+
|
|
200
|
+
# Create a message for task completion
|
|
201
|
+
if hasattr(task_output, 'raw'):
|
|
202
|
+
content = task_output.raw
|
|
203
|
+
logger.info("[CALLBACK DEBUG] Using raw output")
|
|
204
|
+
elif hasattr(task_output, 'content'):
|
|
205
|
+
content = task_output.content
|
|
206
|
+
logger.info("[CALLBACK DEBUG] Using content output")
|
|
207
|
+
else:
|
|
208
|
+
content = str(task_output)
|
|
209
|
+
logger.info("[CALLBACK DEBUG] Using string representation of output")
|
|
210
|
+
|
|
211
|
+
logger.info(f"[CALLBACK DEBUG] Sending task completion message from wrapper: {content[:100]}...")
|
|
212
|
+
try:
|
|
213
|
+
await cl.Message(
|
|
214
|
+
content=f"✅ {role_name} completed task:\n{content}",
|
|
215
|
+
author=role_name,
|
|
216
|
+
).send()
|
|
217
|
+
logger.info("[CALLBACK DEBUG] Successfully sent task completion message from wrapper")
|
|
218
|
+
except Exception as e:
|
|
219
|
+
logger.error(f"[CALLBACK DEBUG] Error sending task completion message from wrapper: {str(e)}")
|
|
220
|
+
|
|
221
|
+
# If there are any additional task details
|
|
222
|
+
if hasattr(task_output, 'details'):
|
|
223
|
+
logger.info("[CALLBACK DEBUG] Task has additional details")
|
|
224
|
+
try:
|
|
225
|
+
await cl.Message(
|
|
226
|
+
content=f"📝 Additional details:\n{task_output.details}",
|
|
227
|
+
author=role_name,
|
|
228
|
+
).send()
|
|
229
|
+
logger.info("[CALLBACK DEBUG] Successfully sent additional details message")
|
|
230
|
+
except Exception as e:
|
|
231
|
+
logger.error(f"[CALLBACK DEBUG] Error sending additional details message: {str(e)}")
|
|
232
|
+
|
|
233
|
+
except Exception as e:
|
|
234
|
+
logger.error(f"[CALLBACK DEBUG] Error in task callback wrapper: {str(e)}", exc_info=True)
|
|
235
|
+
try:
|
|
236
|
+
await cl.Message(
|
|
237
|
+
content=f"Error in task callback: {str(e)}",
|
|
238
|
+
author="System",
|
|
239
|
+
).send()
|
|
240
|
+
except Exception as send_error:
|
|
241
|
+
logger.error(f"[CALLBACK DEBUG] Error sending error message: {str(send_error)}")
|
|
242
|
+
|
|
243
|
+
def sync_task_callback_wrapper(task_output):
|
|
244
|
+
logger.info("[CALLBACK DEBUG] Sync task callback wrapper triggered")
|
|
245
|
+
try:
|
|
246
|
+
# Create a new event loop for this thread if there isn't one
|
|
247
|
+
try:
|
|
248
|
+
loop = asyncio.get_event_loop()
|
|
249
|
+
logger.info("[CALLBACK DEBUG] Got existing event loop")
|
|
250
|
+
except RuntimeError:
|
|
251
|
+
loop = asyncio.new_event_loop()
|
|
252
|
+
asyncio.set_event_loop(loop)
|
|
253
|
+
logger.info("[CALLBACK DEBUG] Created new event loop")
|
|
254
|
+
|
|
255
|
+
if loop.is_running():
|
|
256
|
+
# If loop is running, schedule the callback
|
|
257
|
+
logger.info("[CALLBACK DEBUG] Loop is running, scheduling callback")
|
|
258
|
+
asyncio.run_coroutine_threadsafe(
|
|
259
|
+
task_callback_wrapper(task_output),
|
|
260
|
+
loop
|
|
261
|
+
)
|
|
262
|
+
else:
|
|
263
|
+
# If loop is not running, run it directly
|
|
264
|
+
logger.info("[CALLBACK DEBUG] Loop is not running, running callback directly")
|
|
265
|
+
loop.run_until_complete(task_callback_wrapper(task_output))
|
|
266
|
+
|
|
267
|
+
except Exception as e:
|
|
268
|
+
logger.error(f"[CALLBACK DEBUG] Error in sync task callback: {str(e)}", exc_info=True)
|
|
269
|
+
|
|
270
|
+
def sync_step_callback_wrapper(step_details):
|
|
271
|
+
logger.info("[CALLBACK DEBUG] Sync step callback wrapper triggered")
|
|
272
|
+
try:
|
|
273
|
+
# Create a new event loop for this thread if there isn't one
|
|
274
|
+
try:
|
|
275
|
+
loop = asyncio.get_event_loop()
|
|
276
|
+
logger.info("[CALLBACK DEBUG] Got existing event loop")
|
|
277
|
+
except RuntimeError:
|
|
278
|
+
loop = asyncio.new_event_loop()
|
|
279
|
+
asyncio.set_event_loop(loop)
|
|
280
|
+
logger.info("[CALLBACK DEBUG] Created new event loop")
|
|
281
|
+
|
|
282
|
+
if loop.is_running():
|
|
283
|
+
# If loop is running, schedule the callback
|
|
284
|
+
logger.info("[CALLBACK DEBUG] Loop is running, scheduling callback")
|
|
285
|
+
asyncio.run_coroutine_threadsafe(
|
|
286
|
+
step_callback_wrapper(step_details),
|
|
287
|
+
loop
|
|
288
|
+
)
|
|
289
|
+
else:
|
|
290
|
+
# If loop is not running, run it directly
|
|
291
|
+
logger.info("[CALLBACK DEBUG] Loop is not running, running callback directly")
|
|
292
|
+
loop.run_until_complete(step_callback_wrapper(step_details))
|
|
293
|
+
|
|
294
|
+
except Exception as e:
|
|
295
|
+
logger.error(f"[CALLBACK DEBUG] Error in sync step callback: {str(e)}", exc_info=True)
|
|
296
|
+
|
|
297
|
+
async def ui_run_praisonai(config, topic, tools_dict):
|
|
298
|
+
"""Run PraisonAI with the given configuration and topic."""
|
|
299
|
+
logger = logging.getLogger(__name__)
|
|
300
|
+
logger.setLevel(logging.DEBUG)
|
|
301
|
+
agents = {}
|
|
302
|
+
tasks = []
|
|
303
|
+
tasks_dict = {}
|
|
304
|
+
|
|
305
|
+
try:
|
|
306
|
+
# Start message queue processor
|
|
307
|
+
queue_processor = asyncio.create_task(process_message_queue())
|
|
308
|
+
|
|
309
|
+
# Create agents for each role
|
|
310
|
+
for role, details in config['roles'].items():
|
|
311
|
+
# Format the role name and other details
|
|
312
|
+
role_name = details.get('name', role).format(topic=topic)
|
|
313
|
+
role_filled = details.get('role', role).format(topic=topic)
|
|
314
|
+
goal_filled = details['goal'].format(topic=topic)
|
|
315
|
+
backstory_filled = details['backstory'].format(topic=topic)
|
|
316
|
+
|
|
317
|
+
# Test message to verify Chainlit is working
|
|
318
|
+
await cl.Message(
|
|
319
|
+
content=f"[DEBUG] Creating agent: {role_name}",
|
|
320
|
+
author="System"
|
|
321
|
+
).send()
|
|
322
|
+
|
|
323
|
+
# Create a sync wrapper for the step callback
|
|
324
|
+
def step_callback_sync(step_details):
|
|
325
|
+
try:
|
|
326
|
+
# Create a new event loop for this thread
|
|
327
|
+
loop = asyncio.new_event_loop()
|
|
328
|
+
asyncio.set_event_loop(loop)
|
|
329
|
+
|
|
330
|
+
# Add agent name to step details
|
|
331
|
+
step_details["agent_name"] = role_name
|
|
332
|
+
|
|
333
|
+
# Run the callback
|
|
334
|
+
loop.run_until_complete(step_callback(step_details))
|
|
335
|
+
loop.close()
|
|
336
|
+
except Exception as e:
|
|
337
|
+
logger.error(f"[CALLBACK DEBUG] Error in step callback: {str(e)}", exc_info=True)
|
|
338
|
+
|
|
339
|
+
agent = Agent(
|
|
340
|
+
name=role_name,
|
|
341
|
+
role=role_filled,
|
|
342
|
+
goal=goal_filled,
|
|
343
|
+
backstory=backstory_filled,
|
|
344
|
+
llm=details.get('llm', 'gpt-4o'),
|
|
345
|
+
verbose=True,
|
|
346
|
+
allow_delegation=details.get('allow_delegation', False),
|
|
347
|
+
max_iter=details.get('max_iter', 15),
|
|
348
|
+
max_rpm=details.get('max_rpm'),
|
|
349
|
+
max_execution_time=details.get('max_execution_time'),
|
|
350
|
+
cache=details.get('cache', True),
|
|
351
|
+
step_callback=step_callback_sync
|
|
352
|
+
)
|
|
353
|
+
agents[role] = agent
|
|
354
|
+
|
|
355
|
+
# Create tasks for each role
|
|
356
|
+
for role, details in config['roles'].items():
|
|
357
|
+
agent = agents[role]
|
|
358
|
+
tools_list = []
|
|
359
|
+
|
|
360
|
+
# Get tools for this role
|
|
361
|
+
for tool_name in details.get('tools', []):
|
|
362
|
+
if tool_name in tools_dict:
|
|
363
|
+
tool_func = tools_dict[tool_name]
|
|
364
|
+
tools_list.append(tool_func)
|
|
365
|
+
|
|
366
|
+
# Create tasks for the agent
|
|
367
|
+
for task_name, task_details in details.get('tasks', {}).items():
|
|
368
|
+
description_filled = task_details['description'].format(topic=topic)
|
|
369
|
+
expected_output_filled = task_details['expected_output'].format(topic=topic)
|
|
370
|
+
|
|
371
|
+
# Test message to verify task creation
|
|
372
|
+
await cl.Message(
|
|
373
|
+
content=f"[DEBUG] Created task: {task_name} for agent {role_name}",
|
|
374
|
+
author="System"
|
|
375
|
+
).send()
|
|
376
|
+
|
|
377
|
+
# Create a sync wrapper for the task callback
|
|
378
|
+
def task_callback_sync(task_output):
|
|
379
|
+
try:
|
|
380
|
+
# Create a new event loop for this thread
|
|
381
|
+
loop = asyncio.new_event_loop()
|
|
382
|
+
asyncio.set_event_loop(loop)
|
|
383
|
+
|
|
384
|
+
# Run the callback
|
|
385
|
+
loop.run_until_complete(task_callback(task_output))
|
|
386
|
+
loop.close()
|
|
387
|
+
except Exception as e:
|
|
388
|
+
logger.error(f"[CALLBACK DEBUG] Error in task callback: {str(e)}", exc_info=True)
|
|
389
|
+
|
|
390
|
+
task = Task(
|
|
391
|
+
description=description_filled,
|
|
392
|
+
expected_output=expected_output_filled,
|
|
393
|
+
agent=agent,
|
|
394
|
+
tools=tools_list,
|
|
395
|
+
async_execution=True,
|
|
396
|
+
context=[],
|
|
397
|
+
config=task_details.get('config', {}),
|
|
398
|
+
output_json=task_details.get('output_json'),
|
|
399
|
+
output_pydantic=task_details.get('output_pydantic'),
|
|
400
|
+
output_file=task_details.get('output_file', ""),
|
|
401
|
+
callback=task_callback_sync,
|
|
402
|
+
create_directory=task_details.get('create_directory', False)
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
tasks.append(task)
|
|
406
|
+
tasks_dict[task_name] = task
|
|
407
|
+
|
|
408
|
+
# Set up task contexts
|
|
409
|
+
for role, details in config['roles'].items():
|
|
410
|
+
for task_name, task_details in details.get('tasks', {}).items():
|
|
411
|
+
task = tasks_dict[task_name]
|
|
412
|
+
context_tasks = [tasks_dict[ctx] for ctx in task_details.get('context', [])
|
|
413
|
+
if ctx in tasks_dict]
|
|
414
|
+
task.context = context_tasks
|
|
415
|
+
|
|
416
|
+
# Send the start message
|
|
417
|
+
await cl.Message(
|
|
418
|
+
content="Starting PraisonAI agents execution...",
|
|
419
|
+
author="System"
|
|
420
|
+
).send()
|
|
421
|
+
|
|
422
|
+
# Create and run the PraisonAI agents
|
|
423
|
+
if config.get('process') == 'hierarchical':
|
|
424
|
+
crew = PraisonAIAgents(
|
|
425
|
+
agents=list(agents.values()),
|
|
426
|
+
tasks=tasks,
|
|
427
|
+
verbose=True,
|
|
428
|
+
process="hierarchical",
|
|
429
|
+
manager_llm=config.get('manager_llm', 'gpt-4o')
|
|
430
|
+
)
|
|
431
|
+
else:
|
|
432
|
+
crew = PraisonAIAgents(
|
|
433
|
+
agents=list(agents.values()),
|
|
434
|
+
tasks=tasks,
|
|
435
|
+
verbose=2
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
# Store the crew in the user session
|
|
439
|
+
cl.user_session.set("crew", crew)
|
|
440
|
+
|
|
441
|
+
# Run the agents in a separate thread
|
|
442
|
+
loop = asyncio.get_event_loop()
|
|
443
|
+
response = await loop.run_in_executor(None, crew.start)
|
|
444
|
+
|
|
445
|
+
logger.debug(f"[CALLBACK DEBUG] Result: {response}")
|
|
446
|
+
|
|
447
|
+
# Convert response to string if it's not already
|
|
448
|
+
if hasattr(response, 'raw'):
|
|
449
|
+
result = response.raw
|
|
450
|
+
elif hasattr(response, 'content'):
|
|
451
|
+
result = response.content
|
|
452
|
+
else:
|
|
453
|
+
result = str(response)
|
|
454
|
+
|
|
455
|
+
# Send the completion message
|
|
456
|
+
await cl.Message(
|
|
457
|
+
content="PraisonAI agents execution completed.",
|
|
458
|
+
author="System"
|
|
459
|
+
).send()
|
|
460
|
+
|
|
461
|
+
# After getting the response, wait a bit for remaining messages
|
|
462
|
+
await asyncio.sleep(1) # Give time for final messages to be processed
|
|
463
|
+
queue_processor.cancel() # Stop the queue processor
|
|
464
|
+
|
|
465
|
+
return result
|
|
466
|
+
|
|
467
|
+
except Exception as e:
|
|
468
|
+
error_msg = f"Error in ui_run_praisonai: {str(e)}"
|
|
469
|
+
logger.error(error_msg, exc_info=True)
|
|
470
|
+
await cl.Message(
|
|
471
|
+
content=error_msg,
|
|
472
|
+
author="System"
|
|
473
|
+
).send()
|
|
474
|
+
raise
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import logging
|
|
3
|
+
from dotenv import load_dotenv
|
|
4
|
+
import chainlit as cl
|
|
5
|
+
from chainlit.types import ThreadDict
|
|
6
|
+
import yaml
|
|
7
|
+
import sys
|
|
8
|
+
import os
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
|
|
11
|
+
# Add the parent directory to sys.path to allow imports
|
|
12
|
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
|
|
13
|
+
|
|
14
|
+
from praisonai.ui.colab import ui_run_praisonai, load_tools_from_tools_py
|
|
15
|
+
from praisonai.ui.callbacks import callback, trigger_callback
|
|
16
|
+
|
|
17
|
+
# Load environment variables
|
|
18
|
+
load_dotenv()
|
|
19
|
+
|
|
20
|
+
# Set up logging
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
log_level = os.getenv("LOGLEVEL", "INFO").upper()
|
|
23
|
+
logger.setLevel(log_level)
|
|
24
|
+
|
|
25
|
+
# Load agent configuration
|
|
26
|
+
agent_file = "agents.yaml"
|
|
27
|
+
with open(agent_file, 'r') as f:
|
|
28
|
+
config = yaml.safe_load(f)
|
|
29
|
+
|
|
30
|
+
# Load tools
|
|
31
|
+
tools_dict = load_tools_from_tools_py()
|
|
32
|
+
|
|
33
|
+
@cl.on_message
|
|
34
|
+
async def main(message: cl.Message):
|
|
35
|
+
"""Main message handler for Chainlit"""
|
|
36
|
+
try:
|
|
37
|
+
logger.info(f"Processing message: {message.content}")
|
|
38
|
+
await cl.Message(
|
|
39
|
+
content=f"🔄 Processing your request about: {message.content}...",
|
|
40
|
+
author="System"
|
|
41
|
+
).send()
|
|
42
|
+
|
|
43
|
+
await cl.Message(
|
|
44
|
+
content="Using Running PraisonAI Agents...",
|
|
45
|
+
author="System"
|
|
46
|
+
).send()
|
|
47
|
+
|
|
48
|
+
# Run PraisonAI with the message content as the topic
|
|
49
|
+
result = await ui_run_praisonai(config, message.content, tools_dict)
|
|
50
|
+
|
|
51
|
+
# Send the final result
|
|
52
|
+
await cl.Message(
|
|
53
|
+
content=result,
|
|
54
|
+
author="System"
|
|
55
|
+
).send()
|
|
56
|
+
|
|
57
|
+
except Exception as e:
|
|
58
|
+
error_msg = f"Error running PraisonAI agents: {str(e)}"
|
|
59
|
+
logger.error(error_msg, exc_info=True)
|
|
60
|
+
await cl.Message(
|
|
61
|
+
content=error_msg,
|
|
62
|
+
author="System"
|
|
63
|
+
).send()
|
|
64
|
+
|
|
65
|
+
@cl.on_chat_start
|
|
66
|
+
async def start():
|
|
67
|
+
"""Handler for chat start"""
|
|
68
|
+
await cl.Message(
|
|
69
|
+
content="👋 Welcome! I'm your AI assistant. What would you like to work on?",
|
|
70
|
+
author="System"
|
|
71
|
+
).send()
|
|
72
|
+
|
|
73
|
+
# Authentication setup (optional)
|
|
74
|
+
if os.getenv("CHAINLIT_AUTH_SECRET"):
|
|
75
|
+
@cl.password_auth_callback
|
|
76
|
+
def auth_callback(username: str, password: str) -> cl.User:
|
|
77
|
+
# Replace with your authentication logic
|
|
78
|
+
if username == os.getenv("CHAINLIT_USERNAME", "admin") and \
|
|
79
|
+
password == os.getenv("CHAINLIT_PASSWORD", "admin"):
|
|
80
|
+
return cl.User(identifier=username, metadata={"role": "user"})
|
|
81
|
+
return None
|
praisonai/ui/config/chainlit.md
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
# Welcome to
|
|
1
|
+
# Welcome to PraisonAI! 🚀
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: PraisonAI
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.25
|
|
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
|
|
@@ -21,43 +21,68 @@ Provides-Extra: google
|
|
|
21
21
|
Provides-Extra: gradio
|
|
22
22
|
Provides-Extra: openai
|
|
23
23
|
Provides-Extra: realtime
|
|
24
|
-
Provides-Extra: train
|
|
25
24
|
Provides-Extra: ui
|
|
26
25
|
Requires-Dist: PyYAML (>=6.0)
|
|
27
26
|
Requires-Dist: agentops (>=0.3.12) ; extra == "agentops"
|
|
28
|
-
Requires-Dist: aiosqlite (>=0.20.0) ; extra == "
|
|
29
|
-
Requires-Dist:
|
|
30
|
-
Requires-Dist:
|
|
31
|
-
Requires-Dist:
|
|
27
|
+
Requires-Dist: aiosqlite (>=0.20.0) ; extra == "chat"
|
|
28
|
+
Requires-Dist: aiosqlite (>=0.20.0) ; extra == "code"
|
|
29
|
+
Requires-Dist: aiosqlite (>=0.20.0) ; extra == "realtime"
|
|
30
|
+
Requires-Dist: aiosqlite (>=0.20.0) ; extra == "ui"
|
|
31
|
+
Requires-Dist: chainlit (==1.3.2) ; extra == "chat"
|
|
32
|
+
Requires-Dist: chainlit (==1.3.2) ; extra == "code"
|
|
33
|
+
Requires-Dist: chainlit (==1.3.2) ; extra == "realtime"
|
|
34
|
+
Requires-Dist: chainlit (==1.3.2) ; extra == "ui"
|
|
35
|
+
Requires-Dist: crawl4ai (==0.3.4) ; extra == "chat"
|
|
36
|
+
Requires-Dist: crawl4ai (==0.3.4) ; extra == "code"
|
|
37
|
+
Requires-Dist: crawl4ai (==0.3.4) ; extra == "realtime"
|
|
38
|
+
Requires-Dist: crewai (>=0.32.0) ; extra == "crewai"
|
|
39
|
+
Requires-Dist: crewai ; extra == "autogen"
|
|
32
40
|
Requires-Dist: duckduckgo_search (>=6.3.0) ; extra == "realtime"
|
|
33
41
|
Requires-Dist: fastapi (>=0.95.0) ; extra == "call"
|
|
34
42
|
Requires-Dist: flaml[automl] (>=2.3.1) ; extra == "call"
|
|
35
43
|
Requires-Dist: flask (>=3.0.0) ; extra == "api"
|
|
36
44
|
Requires-Dist: gradio (>=4.26.0) ; extra == "gradio"
|
|
37
|
-
Requires-Dist: greenlet (>=3.0.3) ; extra == "
|
|
45
|
+
Requires-Dist: greenlet (>=3.0.3) ; extra == "chat"
|
|
46
|
+
Requires-Dist: greenlet (>=3.0.3) ; extra == "code"
|
|
47
|
+
Requires-Dist: greenlet (>=3.0.3) ; extra == "realtime"
|
|
48
|
+
Requires-Dist: greenlet (>=3.0.3) ; extra == "ui"
|
|
38
49
|
Requires-Dist: instructor (>=1.3.3)
|
|
39
50
|
Requires-Dist: langchain-anthropic (>=0.1.13) ; extra == "anthropic"
|
|
40
51
|
Requires-Dist: langchain-cohere (>=0.1.4) ; extra == "cohere"
|
|
41
52
|
Requires-Dist: langchain-google-genai (>=1.0.4) ; extra == "google"
|
|
42
53
|
Requires-Dist: langchain-openai (>=0.1.7) ; extra == "openai"
|
|
43
|
-
Requires-Dist: litellm (>=1.41.8) ; extra == "chat"
|
|
54
|
+
Requires-Dist: litellm (>=1.41.8) ; extra == "chat"
|
|
55
|
+
Requires-Dist: litellm (>=1.41.8) ; extra == "code"
|
|
56
|
+
Requires-Dist: litellm (>=1.41.8) ; extra == "realtime"
|
|
44
57
|
Requires-Dist: markdown (>=3.5)
|
|
45
58
|
Requires-Dist: openai (>=1.54.0) ; extra == "call"
|
|
46
|
-
Requires-Dist: playwright (>=1.47.0) ; extra == "chat"
|
|
59
|
+
Requires-Dist: playwright (>=1.47.0) ; extra == "chat"
|
|
60
|
+
Requires-Dist: playwright (>=1.47.0) ; extra == "code"
|
|
47
61
|
Requires-Dist: plotly (>=5.24.0) ; extra == "realtime"
|
|
48
|
-
Requires-Dist: praisonai-tools (>=0.0.7) ; extra == "
|
|
49
|
-
Requires-Dist:
|
|
62
|
+
Requires-Dist: praisonai-tools (>=0.0.7) ; extra == "autogen"
|
|
63
|
+
Requires-Dist: praisonai-tools (>=0.0.7) ; extra == "crewai"
|
|
64
|
+
Requires-Dist: praisonaiagents (>=0.0.20)
|
|
50
65
|
Requires-Dist: pyautogen (>=0.2.19) ; extra == "autogen"
|
|
51
|
-
Requires-Dist: pydantic (<=2.10.1) ; extra == "
|
|
66
|
+
Requires-Dist: pydantic (<=2.10.1) ; extra == "chat"
|
|
67
|
+
Requires-Dist: pydantic (<=2.10.1) ; extra == "code"
|
|
68
|
+
Requires-Dist: pydantic (<=2.10.1) ; extra == "ui"
|
|
52
69
|
Requires-Dist: pyngrok (>=1.4.0) ; extra == "call"
|
|
53
70
|
Requires-Dist: pyparsing (>=3.0.0)
|
|
54
71
|
Requires-Dist: python-dotenv (>=0.19.0)
|
|
55
|
-
Requires-Dist: rich (>=13.7)
|
|
56
|
-
Requires-Dist:
|
|
57
|
-
Requires-Dist:
|
|
72
|
+
Requires-Dist: rich (>=13.7)
|
|
73
|
+
Requires-Dist: rich ; extra == "call"
|
|
74
|
+
Requires-Dist: rich ; extra == "chat"
|
|
75
|
+
Requires-Dist: sqlalchemy (>=2.0.36) ; extra == "chat"
|
|
76
|
+
Requires-Dist: sqlalchemy (>=2.0.36) ; extra == "code"
|
|
77
|
+
Requires-Dist: sqlalchemy (>=2.0.36) ; extra == "realtime"
|
|
78
|
+
Requires-Dist: sqlalchemy (>=2.0.36) ; extra == "ui"
|
|
79
|
+
Requires-Dist: tavily-python (==0.5.0) ; extra == "chat"
|
|
80
|
+
Requires-Dist: tavily-python (==0.5.0) ; extra == "code"
|
|
81
|
+
Requires-Dist: tavily-python (==0.5.0) ; extra == "realtime"
|
|
58
82
|
Requires-Dist: twilio (>=7.0.0) ; extra == "call"
|
|
59
83
|
Requires-Dist: uvicorn (>=0.20.0) ; extra == "call"
|
|
60
|
-
Requires-Dist: websockets (>=12.0) ; extra == "
|
|
84
|
+
Requires-Dist: websockets (>=12.0) ; extra == "call"
|
|
85
|
+
Requires-Dist: websockets (>=12.0) ; extra == "realtime"
|
|
61
86
|
Requires-Dist: yfinance (>=0.2.44) ; extra == "realtime"
|
|
62
87
|
Project-URL: Homepage, https://docs.praison.ai
|
|
63
88
|
Project-URL: Repository, https://github.com/mervinpraison/PraisonAI
|
|
@@ -5,7 +5,7 @@ praisonai/api/call.py,sha256=iHdAlgIH_oTsEbjaGGu1Jjo6DTfMR-SfFdtSxnOLCeY,11032
|
|
|
5
5
|
praisonai/auto.py,sha256=uLDm8CU3L_3amZsd55yzf9RdBF1uW-BGSx7nl9ctNZ4,8680
|
|
6
6
|
praisonai/chainlit_ui.py,sha256=bNR7s509lp0I9JlJNvwCZRUZosC64qdvlFCt8NmFamQ,12216
|
|
7
7
|
praisonai/cli.py,sha256=M69ji9gQGzWVLewNhTwH5kYZuj63TzlcishxoRx18ug,21210
|
|
8
|
-
praisonai/deploy.py,sha256=
|
|
8
|
+
praisonai/deploy.py,sha256=gBNkHaAdp27OeMcG42wudtqW7BSIH2DbM87bzoB2F2M,6028
|
|
9
9
|
praisonai/inbuilt_tools/__init__.py,sha256=fai4ZJIKz7-iOnGZv5jJX0wmT77PKa4x2jqyaJddKFA,569
|
|
10
10
|
praisonai/inbuilt_tools/autogen_tools.py,sha256=kJdEv61BTYvdHOaURNEpBcWq8Rs-oC03loNFTIjT-ak,4687
|
|
11
11
|
praisonai/inc/__init__.py,sha256=sPDlYBBwdk0VlWzaaM_lG0_LD07lS2HRGvPdxXJFiYg,62
|
|
@@ -35,9 +35,12 @@ praisonai/setup.py,sha256=0jHgKnIPCtBZiGYaYyTz3PzrJI6nBy55VXk2UctXlDo,373
|
|
|
35
35
|
praisonai/test.py,sha256=OL-wesjA5JTohr8rtr6kWoaS4ImkJg2l0GXJ-dUUfRU,4090
|
|
36
36
|
praisonai/train.py,sha256=DvORlrwKOD-2v4r_z84eV3LsfzpNs-WnPKb5cQB3_t4,11071
|
|
37
37
|
praisonai/ui/README.md,sha256=QG9yucvBieVjCjWFzu6hL9xNtYllkoqyJ_q1b0YYAco,1124
|
|
38
|
-
praisonai/ui/agents.py,sha256=
|
|
38
|
+
praisonai/ui/agents.py,sha256=ayrxHw4SCdpye5FewGQyG9cBmzzJhmataX4_6X4VNtU,27530
|
|
39
|
+
praisonai/ui/callbacks.py,sha256=V4_-GjxmjDFmugUZGfQHKtNSysx7rT6i1UblbM_8lIM,1968
|
|
39
40
|
praisonai/ui/chat.py,sha256=rlYwhTd3giBuvtK4Yc9kf6N9jfVT0VrZ-mLIzhANGiQ,13565
|
|
40
41
|
praisonai/ui/code.py,sha256=GD_xQTo7qzpOM98tu4MOPsviJdXU__Ta3JIfsjoRe6U,15797
|
|
42
|
+
praisonai/ui/colab.py,sha256=A2NceDVazMy53mIpp-NIn5w3y8aQKwQu5LmHTepVwlo,19584
|
|
43
|
+
praisonai/ui/colab_chainlit.py,sha256=wrB1O0ttRlmOH8aMxU8QdGpse-X54U87ZcEEA3R1aFg,2432
|
|
41
44
|
praisonai/ui/components/aicoder.py,sha256=Xh95RSEJCel5mEGic4vdtzyNpHNULF3ymft9nbwglXY,11155
|
|
42
45
|
praisonai/ui/config/.chainlit/config.toml,sha256=kxb2APgVauLtfxlcEMlv8GHse6p8AbNTsJhIfZF38bg,3824
|
|
43
46
|
praisonai/ui/config/.chainlit/translations/bn.json,sha256=m2TAaGMS-18_siW5dw4sbosh0Wn8ENWWzdGYkHaBrXw,22679
|
|
@@ -51,7 +54,7 @@ praisonai/ui/config/.chainlit/translations/mr.json,sha256=JaU16y5uW-cH0x7w6RDztE
|
|
|
51
54
|
praisonai/ui/config/.chainlit/translations/ta.json,sha256=8JPW6BwLN2dl9wuq5wSkMvazcY8lM5v1pqbBxwObGUw,24724
|
|
52
55
|
praisonai/ui/config/.chainlit/translations/te.json,sha256=JzW2YXWg1qqvWgIvEgMelQz5s6EzTb_uD_3TEHAHiQw,23526
|
|
53
56
|
praisonai/ui/config/.chainlit/translations/zh-CN.json,sha256=aLBSSSQ0yojlYGuMMlOYvkD_ruG9-d2AgnjJWhPODVw,11737
|
|
54
|
-
praisonai/ui/config/chainlit.md,sha256=
|
|
57
|
+
praisonai/ui/config/chainlit.md,sha256=YCjGjkKOeW0w711tkAdEfC6sPgBRm6G3bxYPFeHx72U,28
|
|
55
58
|
praisonai/ui/config/translations/bn.json,sha256=m2TAaGMS-18_siW5dw4sbosh0Wn8ENWWzdGYkHaBrXw,22679
|
|
56
59
|
praisonai/ui/config/translations/en-US.json,sha256=QoQAg8P5Q5gbGASc-HAHcfhufk71-Uc1u_ewIBfHuLc,9821
|
|
57
60
|
praisonai/ui/config/translations/gu.json,sha256=9wE-NsHf7j5VUFzfE-cCpESTyHtzVHRcZXAwC3ACMl0,21660
|
|
@@ -78,8 +81,8 @@ praisonai/ui/realtimeclient/realtimedocs.txt,sha256=hmgd8Uwy2SkjSndyyF_-ZOaNxiyH
|
|
|
78
81
|
praisonai/ui/realtimeclient/tools.py,sha256=IJOYwVOBW5Ocn5_iV9pFkmSKR3WU3YpX3kwF0I3jikQ,7855
|
|
79
82
|
praisonai/ui/sql_alchemy.py,sha256=cfyL9uFfuizKFvW0aZfUBlJWPQYI-YBi1v4vxlkb1BQ,29615
|
|
80
83
|
praisonai/version.py,sha256=ugyuFliEqtAwQmH4sTlc16YXKYbFWDmfyk87fErB8-8,21
|
|
81
|
-
praisonai-2.0.
|
|
82
|
-
praisonai-2.0.
|
|
83
|
-
praisonai-2.0.
|
|
84
|
-
praisonai-2.0.
|
|
85
|
-
praisonai-2.0.
|
|
84
|
+
praisonai-2.0.25.dist-info/LICENSE,sha256=kqvFysVlnFxYOu0HxCe2HlmZmJtdmNGOxWRRkT9TsWc,1035
|
|
85
|
+
praisonai-2.0.25.dist-info/METADATA,sha256=dPOcVl-6dVUSktySry3AaxyKJkb0ItVOtOLM5-5MVJo,21774
|
|
86
|
+
praisonai-2.0.25.dist-info/WHEEL,sha256=rqU-pzVJ6on7cnU-SP20blBtY1yM7u0ejCbKCZ5K36I,110
|
|
87
|
+
praisonai-2.0.25.dist-info/entry_points.txt,sha256=I_xc6a6MNTTfLxYmAxe0rgey0G-_hbY07oFW-ZDnkw4,135
|
|
88
|
+
praisonai-2.0.25.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|