mirror of
https://github.com/MervinPraison/PraisonAI.git
synced 2026-11-13 18:48:28 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
43a15e6049 | ||
|
|
8bf5a08246 | ||
|
|
aa81094ac3 |
No files matched your search
@@ -1419,6 +1419,36 @@ praisonai commit
|
||||
praisonai commit --push
|
||||
```
|
||||
|
||||
### Serve CLI (API Server):
|
||||
```bash
|
||||
# Start API server for agents defined in YAML
|
||||
praisonai serve agents.yaml
|
||||
|
||||
# With custom port and host
|
||||
praisonai serve agents.yaml --port 8005 --host 0.0.0.0
|
||||
|
||||
# Alternative flag style
|
||||
praisonai agents.yaml --serve
|
||||
|
||||
# The server provides:
|
||||
# POST /agents - Run all agents sequentially
|
||||
# POST /agents/{name} - Run specific agent (e.g., /agents/researcher)
|
||||
# GET /agents/list - List available agents
|
||||
```
|
||||
|
||||
### n8n Integration CLI:
|
||||
```bash
|
||||
# Export workflow to n8n and open in browser
|
||||
praisonai agents.yaml --n8n
|
||||
|
||||
# With custom n8n URL
|
||||
praisonai agents.yaml --n8n --n8n-url http://localhost:5678
|
||||
|
||||
# Set N8N_API_KEY for auto-import
|
||||
export N8N_API_KEY="your-api-key"
|
||||
praisonai agents.yaml --n8n
|
||||
```
|
||||
|
||||
### @Mentions in Prompts:
|
||||
```bash
|
||||
# Include file content in prompt
|
||||
|
||||
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai_tools \
|
||||
"praisonai>=2.3.33" \
|
||||
"praisonai>=2.3.36" \
|
||||
"praisonai[chat]" \
|
||||
"embedchain[github,youtube]"
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai_tools \
|
||||
"praisonai>=2.3.33" \
|
||||
"praisonai>=2.3.36" \
|
||||
"praisonai[ui]" \
|
||||
"praisonai[chat]" \
|
||||
"praisonai[realtime]" \
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# Dockerfile for PraisonAI Serve API
|
||||
# Deploy to Railway, Render, Fly.io, or any container platform
|
||||
#
|
||||
# Build: docker build -f Dockerfile.serve -t praisonai-serve .
|
||||
# Run: docker run -p 8005:8005 -e OPENAI_API_KEY=sk-xxx praisonai-serve
|
||||
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install dependencies
|
||||
RUN pip install --no-cache-dir praisonai praisonaiagents
|
||||
|
||||
# Copy agents configuration
|
||||
COPY agents.yaml .
|
||||
|
||||
# Expose the API port
|
||||
EXPOSE 8005
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
||||
CMD curl -f http://localhost:8005/health || exit 1
|
||||
|
||||
# Start the API server
|
||||
CMD ["praisonai", "serve", "agents.yaml", "--host", "0.0.0.0", "--port", "8005"]
|
||||
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai_tools \
|
||||
"praisonai>=2.3.33" \
|
||||
"praisonai>=2.3.36" \
|
||||
"praisonai[ui]" \
|
||||
"praisonai[crewai]"
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
# PraisonAI Agents - Real Execution Benchmark
|
||||
|
||||
**Generated:** 2025-12-18 14:43:52
|
||||
**Model:** gpt-4o-mini
|
||||
**Iterations:** 3
|
||||
**Prompt:** "What is 2+2? Reply with just the number."
|
||||
|
||||
## Results
|
||||
|
||||
| Framework | Method | Avg Time | Relative |
|
||||
|-----------|--------|----------|----------|
|
||||
| **PraisonAI** | `agent.start()` | **0.45s** | **1.00x (fastest)** |
|
||||
| PraisonAI (LiteLLM) | `agent.start()` | 0.55s | 1.22x |
|
||||
| CrewAI | `crew.kickoff()` | 0.58s | 1.28x |
|
||||
| Agno | `agent.run()` | 0.92s | 2.05x |
|
||||
|
||||
## Package Versions
|
||||
|
||||
| Package | Version |
|
||||
|---------|--------|
|
||||
| PraisonAI | 0.1.5 |
|
||||
| Agno | 2.3.14 |
|
||||
| CrewAI | 1.6.1 |
|
||||
|
||||
## How to Reproduce
|
||||
|
||||
```bash
|
||||
export OPENAI_API_KEY=your_key
|
||||
cd praisonai-agents
|
||||
python benchmarks/execution_benchmark.py
|
||||
```
|
||||
@@ -0,0 +1,286 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
PraisonAI Agents - Real Execution Benchmark
|
||||
|
||||
Benchmarks ACTUAL agent execution with LLM API calls.
|
||||
Tests single agent running a simple task across frameworks.
|
||||
|
||||
Usage:
|
||||
export OPENAI_API_KEY=your_key
|
||||
python benchmarks/execution_benchmark.py
|
||||
python benchmarks/execution_benchmark.py --no-save
|
||||
"""
|
||||
|
||||
import time
|
||||
import os
|
||||
import argparse
|
||||
from importlib.metadata import version as get_version
|
||||
|
||||
# Check API key
|
||||
if not os.environ.get('OPENAI_API_KEY'):
|
||||
print("ERROR: Set OPENAI_API_KEY environment variable")
|
||||
exit(1)
|
||||
|
||||
MODEL = "gpt-4o-mini"
|
||||
ITERATIONS = 3
|
||||
PROMPT = "What is 2+2? Reply with just the number."
|
||||
|
||||
|
||||
def get_package_versions():
|
||||
"""Get version numbers for benchmarked packages."""
|
||||
packages = {
|
||||
'PraisonAI': 'praisonaiagents',
|
||||
'Agno': 'agno',
|
||||
'CrewAI': 'crewai',
|
||||
}
|
||||
versions = {}
|
||||
for name, pkg in packages.items():
|
||||
try:
|
||||
versions[name] = get_version(pkg)
|
||||
except:
|
||||
versions[name] = 'not installed'
|
||||
return versions
|
||||
|
||||
|
||||
def benchmark_praisonai():
|
||||
"""Benchmark PraisonAI agent execution using agent.start()"""
|
||||
from praisonaiagents import Agent
|
||||
|
||||
print("\n--- PraisonAI ---")
|
||||
print(f"Method: agent.start()")
|
||||
|
||||
times = []
|
||||
for i in range(ITERATIONS):
|
||||
agent = Agent(
|
||||
name="Calculator",
|
||||
instructions="You are a helpful assistant. Be very brief.",
|
||||
llm=MODEL,
|
||||
verbose=False
|
||||
)
|
||||
|
||||
start = time.perf_counter()
|
||||
response = agent.start(PROMPT)
|
||||
elapsed = time.perf_counter() - start
|
||||
|
||||
times.append(elapsed)
|
||||
print(f" Run {i+1}: {elapsed:.2f}s - Response: {str(response)[:50]}")
|
||||
|
||||
avg = sum(times) / len(times)
|
||||
print(f" Average: {avg:.2f}s")
|
||||
return avg
|
||||
|
||||
|
||||
def benchmark_praisonai_litellm():
|
||||
"""Benchmark PraisonAI with LiteLLM backend using agent.start()"""
|
||||
from praisonaiagents import Agent
|
||||
|
||||
print("\n--- PraisonAI (LiteLLM) ---")
|
||||
print(f"Method: agent.start()")
|
||||
|
||||
times = []
|
||||
for i in range(ITERATIONS):
|
||||
agent = Agent(
|
||||
name="Calculator",
|
||||
instructions="You are a helpful assistant. Be very brief.",
|
||||
llm=f"openai/{MODEL}",
|
||||
verbose=False
|
||||
)
|
||||
|
||||
start = time.perf_counter()
|
||||
response = agent.start(PROMPT)
|
||||
elapsed = time.perf_counter() - start
|
||||
|
||||
times.append(elapsed)
|
||||
print(f" Run {i+1}: {elapsed:.2f}s - Response: {str(response)[:50]}")
|
||||
|
||||
avg = sum(times) / len(times)
|
||||
print(f" Average: {avg:.2f}s")
|
||||
return avg
|
||||
|
||||
|
||||
def benchmark_agno():
|
||||
"""Benchmark Agno agent execution using agent.run()"""
|
||||
from agno.agent import Agent
|
||||
from agno.models.openai import OpenAIChat
|
||||
|
||||
print("\n--- Agno ---")
|
||||
print(f"Method: agent.run()")
|
||||
|
||||
times = []
|
||||
for i in range(ITERATIONS):
|
||||
agent = Agent(
|
||||
model=OpenAIChat(id=MODEL),
|
||||
instructions=["You are a helpful assistant. Be very brief."],
|
||||
)
|
||||
|
||||
start = time.perf_counter()
|
||||
response = agent.run(PROMPT)
|
||||
elapsed = time.perf_counter() - start
|
||||
|
||||
times.append(elapsed)
|
||||
# Extract content from response
|
||||
content = str(response.content)[:50] if hasattr(response, 'content') else str(response)[:50]
|
||||
print(f" Run {i+1}: {elapsed:.2f}s - Response: {content}")
|
||||
|
||||
avg = sum(times) / len(times)
|
||||
print(f" Average: {avg:.2f}s")
|
||||
return avg
|
||||
|
||||
|
||||
def benchmark_crewai():
|
||||
"""Benchmark CrewAI agent execution using crew.kickoff()"""
|
||||
from crewai import Agent, Task, Crew
|
||||
|
||||
print("\n--- CrewAI ---")
|
||||
print(f"Method: crew.kickoff()")
|
||||
|
||||
times = []
|
||||
for i in range(ITERATIONS):
|
||||
agent = Agent(
|
||||
role="Calculator",
|
||||
goal="Answer math questions",
|
||||
backstory="You are a helpful assistant.",
|
||||
llm=MODEL,
|
||||
verbose=False
|
||||
)
|
||||
|
||||
task = Task(
|
||||
description=PROMPT,
|
||||
expected_output="A number",
|
||||
agent=agent
|
||||
)
|
||||
|
||||
crew = Crew(
|
||||
agents=[agent],
|
||||
tasks=[task],
|
||||
verbose=False
|
||||
)
|
||||
|
||||
start = time.perf_counter()
|
||||
response = crew.kickoff()
|
||||
elapsed = time.perf_counter() - start
|
||||
|
||||
times.append(elapsed)
|
||||
print(f" Run {i+1}: {elapsed:.2f}s - Response: {str(response)[:50]}")
|
||||
|
||||
avg = sum(times) / len(times)
|
||||
print(f" Average: {avg:.2f}s")
|
||||
return avg
|
||||
|
||||
|
||||
def save_results(results: dict):
|
||||
"""Save results to markdown file."""
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
filepath = os.path.join(os.path.dirname(__file__), 'EXECUTION_BENCHMARK_RESULTS.md')
|
||||
versions = get_package_versions()
|
||||
|
||||
# Sort by time
|
||||
sorted_results = sorted(results.items(), key=lambda x: x[1])
|
||||
fastest = sorted_results[0][1]
|
||||
|
||||
with open(filepath, 'w') as f:
|
||||
f.write('# PraisonAI Agents - Real Execution Benchmark\n\n')
|
||||
f.write(f'**Generated:** {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}\n')
|
||||
f.write(f'**Model:** {MODEL}\n')
|
||||
f.write(f'**Iterations:** {ITERATIONS}\n')
|
||||
f.write(f'**Prompt:** "{PROMPT}"\n\n')
|
||||
f.write('## Results\n\n')
|
||||
f.write('| Framework | Method | Avg Time | Relative |\n')
|
||||
f.write('|-----------|--------|----------|----------|\n')
|
||||
|
||||
methods = {
|
||||
'PraisonAI': 'agent.start()',
|
||||
'PraisonAI (LiteLLM)': 'agent.start()',
|
||||
'Agno': 'agent.run()',
|
||||
'CrewAI': 'crew.kickoff()',
|
||||
}
|
||||
|
||||
for name, avg in sorted_results:
|
||||
ratio = avg / fastest
|
||||
method = methods.get(name, 'unknown')
|
||||
if ratio == 1.0:
|
||||
f.write(f'| **{name}** | `{method}` | **{avg:.2f}s** | **1.00x (fastest)** |\n')
|
||||
else:
|
||||
f.write(f'| {name} | `{method}` | {avg:.2f}s | {ratio:.2f}x |\n')
|
||||
|
||||
f.write('\n## Package Versions\n\n')
|
||||
f.write('| Package | Version |\n')
|
||||
f.write('|---------|--------|\n')
|
||||
for pkg, ver in versions.items():
|
||||
f.write(f'| {pkg} | {ver} |\n')
|
||||
|
||||
f.write('\n## How to Reproduce\n\n')
|
||||
f.write('```bash\n')
|
||||
f.write('export OPENAI_API_KEY=your_key\n')
|
||||
f.write('cd praisonai-agents\n')
|
||||
f.write('python benchmarks/execution_benchmark.py\n')
|
||||
f.write('```\n')
|
||||
|
||||
print(f'\nResults saved to: {filepath}')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description='PraisonAI Agents - Real Execution Benchmark')
|
||||
parser.add_argument('--no-save', action='store_true', help='Do not save results to file')
|
||||
args = parser.parse_args()
|
||||
|
||||
print('=' * 60)
|
||||
print('PraisonAI Agents - Real Execution Benchmark')
|
||||
print('=' * 60)
|
||||
print(f'\nModel: {MODEL}')
|
||||
print(f'Iterations: {ITERATIONS}')
|
||||
print(f'Prompt: "{PROMPT}"')
|
||||
|
||||
versions = get_package_versions()
|
||||
print('\nPackage versions:')
|
||||
for pkg, ver in versions.items():
|
||||
print(f' {pkg}: {ver}')
|
||||
|
||||
results = {}
|
||||
|
||||
# Run benchmarks
|
||||
try:
|
||||
results['PraisonAI'] = benchmark_praisonai()
|
||||
except Exception as e:
|
||||
print(f"PraisonAI error: {e}")
|
||||
|
||||
try:
|
||||
results['PraisonAI (LiteLLM)'] = benchmark_praisonai_litellm()
|
||||
except Exception as e:
|
||||
print(f"PraisonAI (LiteLLM) error: {e}")
|
||||
|
||||
try:
|
||||
results['Agno'] = benchmark_agno()
|
||||
except Exception as e:
|
||||
print(f"Agno error: {e}")
|
||||
|
||||
try:
|
||||
results['CrewAI'] = benchmark_crewai()
|
||||
except Exception as e:
|
||||
print(f"CrewAI error: {e}")
|
||||
|
||||
# Summary
|
||||
print('\n' + '=' * 60)
|
||||
print('SUMMARY')
|
||||
print('=' * 60)
|
||||
|
||||
if results:
|
||||
sorted_results = sorted(results.items(), key=lambda x: x[1])
|
||||
fastest = sorted_results[0][1]
|
||||
|
||||
print(f"\n{'Framework':<25} {'Avg Time':<12} {'Relative':<10}")
|
||||
print('-' * 47)
|
||||
|
||||
for name, avg in sorted_results:
|
||||
ratio = avg / fastest
|
||||
print(f'{name:<25} {avg:<12.2f}s {ratio:.2f}x')
|
||||
|
||||
print('\n' + '=' * 60)
|
||||
|
||||
# Save results (unless --no-save)
|
||||
if results and not args.no_save:
|
||||
save_results(results)
|
||||
elif args.no_save:
|
||||
print('\nResults not saved (--no-save flag used)')
|
||||
@@ -1419,6 +1419,36 @@ praisonai commit
|
||||
praisonai commit --push
|
||||
```
|
||||
|
||||
### Serve CLI (API Server):
|
||||
```bash
|
||||
# Start API server for agents defined in YAML
|
||||
praisonai serve agents.yaml
|
||||
|
||||
# With custom port and host
|
||||
praisonai serve agents.yaml --port 8005 --host 0.0.0.0
|
||||
|
||||
# Alternative flag style
|
||||
praisonai agents.yaml --serve
|
||||
|
||||
# The server provides:
|
||||
# POST /agents - Run all agents sequentially
|
||||
# POST /agents/{name} - Run specific agent (e.g., /agents/researcher)
|
||||
# GET /agents/list - List available agents
|
||||
```
|
||||
|
||||
### n8n Integration CLI:
|
||||
```bash
|
||||
# Export workflow to n8n and open in browser
|
||||
praisonai agents.yaml --n8n
|
||||
|
||||
# With custom n8n URL
|
||||
praisonai agents.yaml --n8n --n8n-url http://localhost:5678
|
||||
|
||||
# Set N8N_API_KEY for auto-import
|
||||
export N8N_API_KEY="your-api-key"
|
||||
praisonai agents.yaml --n8n
|
||||
```
|
||||
|
||||
### @Mentions in Prompts:
|
||||
```bash
|
||||
# Include file content in prompt
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
"url": "http://127.0.0.1:8005/agents/researcher",
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={{ JSON.stringify({ query: $json.body?.query || $json.query || 'Research about Mars environment and cat behavior for the movie concept' } }) }}",
|
||||
"jsonBody": "={{ JSON.stringify({ query: $json.body?.query || $json.query || 'Research about Mars environment and cat behavior for the movie concept' }) }}",
|
||||
"options": {
|
||||
"timeout": 300000
|
||||
}
|
||||
@@ -54,7 +54,7 @@
|
||||
"url": "http://127.0.0.1:8005/agents/narrative_designer",
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={{ JSON.stringify({ query: $json.response || 'Create story concept based on research: {{previous_output}}' } }) }}",
|
||||
"jsonBody": "={{ JSON.stringify({ query: $json.response || 'Create story concept based on research: previous_output' }) }}",
|
||||
"options": {
|
||||
"timeout": 300000
|
||||
}
|
||||
@@ -75,7 +75,7 @@
|
||||
"url": "http://127.0.0.1:8005/agents/scriptwriter",
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={{ JSON.stringify({ query: $json.response || 'Write the final movie script based on the story concept: {{previous_output}}' } }) }}",
|
||||
"jsonBody": "={{ JSON.stringify({ query: $json.response || 'Write the final movie script based on the story concept: previous_output' }) }}",
|
||||
"options": {
|
||||
"timeout": 300000
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ class Praisonai < Formula
|
||||
|
||||
desc "AI tools for various AI applications"
|
||||
homepage "https://github.com/MervinPraison/PraisonAI"
|
||||
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.3.33.tar.gz"
|
||||
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.3.33.tar.gz | shasum -a 256`.split.first
|
||||
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.3.36.tar.gz"
|
||||
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.3.36.tar.gz | shasum -a 256`.split.first
|
||||
license "MIT"
|
||||
|
||||
depends_on "[email protected]"
|
||||
|
||||
@@ -35,7 +35,7 @@ class N8nHandler(FlagHandler):
|
||||
"""
|
||||
|
||||
def __init__(self, verbose: bool = False, n8n_url: str = "http://localhost:5678",
|
||||
use_execute_command: bool = False):
|
||||
use_execute_command: bool = False, api_url: str = "http://127.0.0.1:8005"):
|
||||
"""
|
||||
Initialize the n8n handler.
|
||||
|
||||
@@ -44,10 +44,11 @@ class N8nHandler(FlagHandler):
|
||||
n8n_url: Base URL of the n8n instance
|
||||
use_execute_command: Use Execute Command nodes (runs praisonai directly)
|
||||
instead of HTTP Request nodes
|
||||
api_url: PraisonAI API URL that n8n will call (for tunnel/cloud deployments)
|
||||
"""
|
||||
super().__init__(verbose=verbose)
|
||||
self.n8n_url = n8n_url
|
||||
self.praisonai_api_url = "http://127.0.0.1:8005"
|
||||
self.praisonai_api_url = api_url
|
||||
self.use_execute_command = use_execute_command
|
||||
|
||||
@property
|
||||
@@ -412,12 +413,14 @@ class N8nHandler(FlagHandler):
|
||||
agent_url_id = agent_id.lower().replace(' ', '_')
|
||||
|
||||
# Build the query - first agent uses webhook input, others use previous response
|
||||
# Escape single quotes and double braces (n8n expression syntax)
|
||||
escaped_action = action.replace("'", "\\'").replace("{{", "").replace("}}", "")
|
||||
if is_first:
|
||||
# First agent: use query from webhook body
|
||||
query_expr = f"={{ $json.body?.query || $json.query || '{action}' }}"
|
||||
query_expr = "$json.body?.query || $json.query || '" + escaped_action + "'"
|
||||
else:
|
||||
# Subsequent agents: use previous agent's response + original action context
|
||||
query_expr = f"={{ $json.response || '{action}' }}"
|
||||
query_expr = "$json.response || '" + escaped_action + "'"
|
||||
|
||||
return {
|
||||
"id": f"agent_{index}",
|
||||
@@ -430,7 +433,7 @@ class N8nHandler(FlagHandler):
|
||||
"url": f"{self.praisonai_api_url}/agents/{agent_url_id}",
|
||||
"sendBody": True,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": f"={{{{ JSON.stringify({{ query: {query_expr[2:]} }}) }}}}",
|
||||
"jsonBody": f"={{{{ JSON.stringify({{ query: {query_expr} }}) }}}}",
|
||||
"options": {
|
||||
"timeout": 300000 # 5 minute timeout per agent
|
||||
}
|
||||
|
||||
@@ -504,7 +504,8 @@ class PraisonAI:
|
||||
from .features.n8n import N8nHandler
|
||||
n8n_handler = N8nHandler(
|
||||
verbose=getattr(args, 'verbose', False),
|
||||
n8n_url=getattr(args, 'n8n_url', 'http://localhost:5678')
|
||||
n8n_url=getattr(args, 'n8n_url', 'http://localhost:5678'),
|
||||
api_url=getattr(args, 'api_url', 'http://127.0.0.1:8005')
|
||||
)
|
||||
result = n8n_handler.execute(self.agent_file)
|
||||
return result
|
||||
@@ -682,6 +683,7 @@ class PraisonAI:
|
||||
# n8n Integration - export workflow to n8n
|
||||
parser.add_argument("--n8n", action="store_true", help="Export workflow to n8n and open in browser")
|
||||
parser.add_argument("--n8n-url", type=str, default="http://localhost:5678", help="n8n instance URL (default: http://localhost:5678)")
|
||||
parser.add_argument("--api-url", type=str, default="http://127.0.0.1:8005", help="PraisonAI API URL for n8n to call (default: http://127.0.0.1:8005)")
|
||||
|
||||
# Serve - start API server for agents
|
||||
parser.add_argument("--serve", action="store_true", help="Start API server for agents (use with agents.yaml)")
|
||||
@@ -3060,6 +3062,11 @@ Now, {final_instruction.lower()}:"""
|
||||
"""
|
||||
Handle the serve command - start API server for agents.
|
||||
|
||||
Supports:
|
||||
- Sequential workflows with agents and steps
|
||||
- Workflow patterns: route, parallel, loop, repeat
|
||||
- All process types: sequential, workflow, hierarchical
|
||||
|
||||
Usage:
|
||||
praisonai serve agents.yaml
|
||||
praisonai serve agents.yaml --port 8005 --host 0.0.0.0
|
||||
@@ -3090,7 +3097,7 @@ Now, {final_instruction.lower()}:"""
|
||||
print(" praisonai agents.yaml --serve")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"📄 Loading agents from: {yaml_file}")
|
||||
print(f"📄 Loading workflow from: {yaml_file}")
|
||||
|
||||
# Load YAML file
|
||||
with open(yaml_file, 'r') as f:
|
||||
@@ -3098,6 +3105,7 @@ Now, {final_instruction.lower()}:"""
|
||||
|
||||
# Extract agents
|
||||
agents_config = config.get('agents', config.get('roles', {}))
|
||||
agents_dict = {} # Map agent_id to Agent instance
|
||||
agents_list = []
|
||||
|
||||
for agent_id, agent_config in agents_config.items():
|
||||
@@ -3111,24 +3119,157 @@ Now, {final_instruction.lower()}:"""
|
||||
llm=agent_config.get('llm', 'gpt-4o-mini'),
|
||||
verbose=False
|
||||
)
|
||||
agents_dict[agent_id] = agent
|
||||
agents_list.append(agent)
|
||||
print(f" ✓ Loaded: {agent.name}")
|
||||
print(f" ✓ Loaded agent: {agent.name}")
|
||||
|
||||
if not agents_list:
|
||||
print("[red]ERROR: No agents found in YAML file[/red]")
|
||||
sys.exit(1)
|
||||
|
||||
# Extract tasks from steps (if defined)
|
||||
steps = config.get('steps', [])
|
||||
tasks_list = []
|
||||
|
||||
# Detect advanced workflow patterns
|
||||
has_route = any(isinstance(s, dict) and 'route' in s for s in steps)
|
||||
has_parallel = any(isinstance(s, dict) and 'parallel' in s for s in steps)
|
||||
has_loop = any(isinstance(s, dict) and 'loop' in s for s in steps)
|
||||
has_repeat = any(isinstance(s, dict) and 'repeat' in s for s in steps)
|
||||
|
||||
if has_route or has_parallel or has_loop or has_repeat:
|
||||
patterns = []
|
||||
if has_route: patterns.append("route")
|
||||
if has_parallel: patterns.append("parallel")
|
||||
if has_loop: patterns.append("loop")
|
||||
if has_repeat: patterns.append("repeat")
|
||||
print(f"\n🔀 Detected workflow patterns: {', '.join(patterns)}")
|
||||
|
||||
if steps:
|
||||
from praisonaiagents import Task
|
||||
print(f"\n📋 Loading workflow steps...")
|
||||
|
||||
# First pass: create all tasks
|
||||
task_name_map = {} # Map step names to Task objects for context/next_tasks
|
||||
for i, step in enumerate(steps):
|
||||
if isinstance(step, dict):
|
||||
# Handle advanced patterns
|
||||
if 'route' in step:
|
||||
print(f" ✓ Loaded routing step: {step.get('name', f'route_{i+1}')}")
|
||||
continue # Route steps are handled by workflow process
|
||||
if 'parallel' in step:
|
||||
parallel_tasks = step.get('parallel', [])
|
||||
print(f" ✓ Loaded parallel step: {len(parallel_tasks)} concurrent tasks")
|
||||
for j, pt in enumerate(parallel_tasks):
|
||||
if isinstance(pt, dict):
|
||||
agent_id = pt.get('agent', '')
|
||||
action = pt.get('action', pt.get('description', ''))
|
||||
if agent_id in agents_dict:
|
||||
task = Task(
|
||||
name=f"parallel_{i}_{j}",
|
||||
description=action,
|
||||
agent=agents_dict[agent_id],
|
||||
expected_output=pt.get('expected_output', 'Completed task output'),
|
||||
async_execution=True, # Parallel tasks run async
|
||||
)
|
||||
tasks_list.append(task)
|
||||
task_name_map[f"parallel_{i}_{j}"] = task
|
||||
continue
|
||||
if 'loop' in step:
|
||||
loop_config = step.get('loop', {})
|
||||
print(f" ✓ Loaded loop step: over '{loop_config.get('over', 'items')}'")
|
||||
continue # Loop steps are handled by workflow process
|
||||
if 'repeat' in step:
|
||||
repeat_config = step.get('repeat', {})
|
||||
print(f" ✓ Loaded repeat step: until '{repeat_config.get('until', 'done')}'")
|
||||
continue # Repeat steps are handled by workflow process
|
||||
|
||||
# Standard step
|
||||
agent_id = step.get('agent', '')
|
||||
action = step.get('action', step.get('description', ''))
|
||||
task_name = step.get('name', f"step_{i+1}")
|
||||
|
||||
if agent_id in agents_dict:
|
||||
task = Task(
|
||||
name=task_name,
|
||||
description=action,
|
||||
agent=agents_dict[agent_id],
|
||||
expected_output=step.get('expected_output', 'Completed task output'),
|
||||
task_type=step.get('task_type', 'task'),
|
||||
is_start=step.get('is_start', i == 0),
|
||||
async_execution=step.get('async_execution', False),
|
||||
output_file=step.get('output_file'),
|
||||
)
|
||||
tasks_list.append(task)
|
||||
task_name_map[task_name] = task
|
||||
print(f" ✓ Loaded step {i+1}: {agent_id} → {action[:50]}...")
|
||||
|
||||
# Second pass: set up next_tasks and context relationships
|
||||
for i, step in enumerate(steps):
|
||||
if isinstance(step, dict) and 'agent' in step:
|
||||
task_name = step.get('name', f"step_{i+1}")
|
||||
if task_name in task_name_map:
|
||||
task = task_name_map[task_name]
|
||||
|
||||
# Set next_tasks if specified
|
||||
next_tasks = step.get('next_tasks', [])
|
||||
if next_tasks:
|
||||
task.next_tasks = next_tasks
|
||||
|
||||
# Set context if specified
|
||||
context_refs = step.get('context', [])
|
||||
if context_refs:
|
||||
task.context = [task_name_map[ref] for ref in context_refs if ref in task_name_map]
|
||||
|
||||
# Set condition for workflow branching
|
||||
condition = step.get('condition', {})
|
||||
if condition:
|
||||
task.condition = condition
|
||||
|
||||
port = args.port
|
||||
host = args.host
|
||||
|
||||
# Extract process type from workflow config (default: sequential)
|
||||
workflow_config = config.get('workflow', {})
|
||||
process_type = workflow_config.get('process', 'sequential')
|
||||
verbose = workflow_config.get('verbose', False)
|
||||
|
||||
# Also check top-level process key
|
||||
if 'process' in config:
|
||||
process_type = config['process']
|
||||
|
||||
print(f"\n🚀 Starting PraisonAI API server...")
|
||||
print(f" Host: {host}")
|
||||
print(f" Port: {port}")
|
||||
print(f" Agents: {len(agents_list)}")
|
||||
if tasks_list:
|
||||
print(f" Tasks: {len(tasks_list)}")
|
||||
print(f" Process: {process_type}")
|
||||
|
||||
# Create and launch
|
||||
praison = PraisonAIAgents(agents=agents_list)
|
||||
# Create and launch - with tasks if defined
|
||||
if tasks_list:
|
||||
praison = PraisonAIAgents(
|
||||
agents=agents_list,
|
||||
tasks=tasks_list,
|
||||
process=process_type,
|
||||
verbose=1 if verbose else 0
|
||||
)
|
||||
else:
|
||||
praison = PraisonAIAgents(
|
||||
agents=agents_list,
|
||||
process=process_type,
|
||||
verbose=1 if verbose else 0
|
||||
)
|
||||
praison.launch(port=port, host=host)
|
||||
|
||||
# Keep the main thread alive to prevent exit
|
||||
print("\n✅ Server running. Press Ctrl+C to stop.")
|
||||
try:
|
||||
import time
|
||||
while True:
|
||||
time.sleep(1)
|
||||
except KeyboardInterrupt:
|
||||
print("\n👋 Server stopped.")
|
||||
|
||||
def create_chainlit_chat_interface(self):
|
||||
"""
|
||||
|
||||
@@ -57,7 +57,7 @@ class CloudDeployer:
|
||||
file.write("FROM python:3.11-slim\n")
|
||||
file.write("WORKDIR /app\n")
|
||||
file.write("COPY . .\n")
|
||||
file.write("RUN pip install flask praisonai==2.3.33 gunicorn markdown\n")
|
||||
file.write("RUN pip install flask praisonai==2.3.36 gunicorn markdown\n")
|
||||
file.write("EXPOSE 8080\n")
|
||||
file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n')
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "2.3.33"
|
||||
__version__ = "2.3.36"
|
||||
Reference in new issue
Block a user