mirror of
https://github.com/MervinPraison/PraisonAI.git
synced 2026-11-13 05:01:35 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c1a8338a24 | ||
|
|
dba5a49569 | ||
|
|
6dab69e9d7 | ||
|
|
90563207f7 |
No files matched your search
+1
-1
@@ -1,6 +1,6 @@
|
||||
FROM python:3.11-slim
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
RUN pip install flask praisonai==2.1.4 gunicorn markdown
|
||||
RUN pip install flask praisonai==2.1.5 gunicorn markdown
|
||||
EXPOSE 8080
|
||||
CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]
|
||||
@@ -13,7 +13,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonaiagents>=0.0.4 \
|
||||
praisonai_tools \
|
||||
"praisonai==2.1.4" \
|
||||
"praisonai==2.1.5" \
|
||||
"praisonai[chat]" \
|
||||
"embedchain[github,youtube]"
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonaiagents>=0.0.4 \
|
||||
praisonai_tools \
|
||||
"praisonai==2.1.4" \
|
||||
"praisonai==2.1.5" \
|
||||
"praisonai[ui]" \
|
||||
"praisonai[chat]" \
|
||||
"praisonai[realtime]" \
|
||||
|
||||
@@ -13,7 +13,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonaiagents>=0.0.4 \
|
||||
praisonai_tools \
|
||||
"praisonai==2.1.4" \
|
||||
"praisonai==2.1.5" \
|
||||
"praisonai[ui]" \
|
||||
"praisonai[crewai]"
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ Loads environment variables from .env file or system and sets them.</p>
|
||||
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.1.4 gunicorn markdown\n")
|
||||
file.write("RUN pip install flask praisonai==2.1.5 gunicorn markdown\n")
|
||||
file.write("EXPOSE 8080\n")
|
||||
file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n')
|
||||
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
---
|
||||
title: "Deploying Multi-Agent Systems as APIs"
|
||||
sidebarTitle: "Multi-Agent Deployment"
|
||||
description: "Learn how to deploy PraisonAI multi-agent systems as RESTful APIs for production environments"
|
||||
icon: "network-wired"
|
||||
---
|
||||
|
||||
# Deploying Multi-Agent Systems as APIs
|
||||
|
||||
PraisonAI allows you to deploy sophisticated multi-agent systems as RESTful APIs, enabling seamless integration with various applications and services. This guide covers different approaches to deploying multi-agent systems.
|
||||
|
||||
## Quick Start
|
||||
|
||||
<Steps>
|
||||
<Step title="Install Dependencies">
|
||||
Make sure you have the required packages installed:
|
||||
```bash
|
||||
pip install "praisonaiagents[api]>=0.0.81"
|
||||
```
|
||||
</Step>
|
||||
<Step title="Set API Key">
|
||||
```bash
|
||||
export OPENAI_API_KEY="your_api_key"
|
||||
```
|
||||
</Step>
|
||||
<Step title="Deploy a Multi-Agent System">
|
||||
Create a file named `multi-agents-api.py` with the following code:
|
||||
```python
|
||||
from praisonaiagents import Agent, Agents, Tools
|
||||
|
||||
research_agent = Agent(name="Research", instructions="You are a research agent to search internet about AI 2024", tools=[Tools.internet_search])
|
||||
summarise_agent = Agent(name="Summarise", instructions="You are a summarize agent to summarise in points")
|
||||
agents = Agents(agents=[research_agent, summarise_agent])
|
||||
agents.launch(path="/agents", port=3030)
|
||||
```
|
||||
</Step>
|
||||
<Step title="Run the API Server">
|
||||
```bash
|
||||
python multi-agents-api.py
|
||||
```
|
||||
|
||||
Your multi-agent API will be available at `http://localhost:3030/agents`
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Making API Requests
|
||||
|
||||
Once your multi-agent system is deployed, you can make POST requests to interact with it:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3030/agents \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"message": "What are the latest developments in AI in 2024?"}'
|
||||
```
|
||||
|
||||
The response will include the collaborative output from both the research and summarization agents:
|
||||
|
||||
```json
|
||||
{
|
||||
"response": "# Latest AI Developments in 2024\n\n- Multimodal AI models have become mainstream, combining text, image, audio, and video understanding\n- Significant advancements in AI reasoning capabilities with models like GPT-4o and Claude 3\n- Increased focus on AI alignment and safety research\n- Emergence of specialized AI agents for specific domains\n- Growth in open-source AI models and frameworks\n- Regulatory frameworks for AI being established globally"
|
||||
}
|
||||
```
|
||||
|
||||
## Multiple Agent Groups
|
||||
|
||||
You can deploy multiple agent groups on the same server, each with its own endpoint:
|
||||
|
||||
```python
|
||||
from praisonaiagents import Agent, Agents, Tools
|
||||
|
||||
research_agent = Agent(name="Research", instructions="You are a research agent to search internet about AI 2024", tools=[Tools.internet_search])
|
||||
summarise_agent = Agent(name="Summarise", instructions="You are a summarize agent to summarise in points")
|
||||
agents = Agents(agents=[research_agent, summarise_agent])
|
||||
agents2 = Agents(agents=[research_agent])
|
||||
agents.launch(path="/agents", port=3030)
|
||||
agents2.launch(path="/agents2", port=3030)
|
||||
```
|
||||
|
||||
With this setup, you can access:
|
||||
- The full agent group at `http://localhost:3030/agents`
|
||||
- Just the research agent at `http://localhost:3030/agents2`
|
||||
|
||||
## Independent Multi-Agent Deployment
|
||||
|
||||
You can also deploy multiple independent agents on the same server:
|
||||
|
||||
```python
|
||||
from praisonaiagents import Agent
|
||||
|
||||
weather_agent = Agent(
|
||||
instructions="""You are a weather agent that can provide weather information for a given city.""",
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
stock_agent = Agent(
|
||||
instructions="""You are a stock market agent that can provide information about stock prices and market trends.""",
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
travel_agent = Agent(
|
||||
instructions="""You are a travel agent that can provide recommendations for destinations, hotels, and activities.""",
|
||||
llm="gpt-4o-mini"
|
||||
)
|
||||
|
||||
weather_agent.launch(path="/weather", port=3030)
|
||||
stock_agent.launch(path="/stock", port=3030)
|
||||
travel_agent.launch(path="/travel", port=3030)
|
||||
```
|
||||
|
||||
## Production Deployment Options
|
||||
|
||||
For production environments, consider the following deployment options:
|
||||
|
||||
### Docker Deployment
|
||||
|
||||
<Steps>
|
||||
<Step title="Create a Dockerfile">
|
||||
```dockerfile
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY . .
|
||||
|
||||
EXPOSE 3030
|
||||
|
||||
CMD ["python", "multi-agents-api.py"]
|
||||
```
|
||||
</Step>
|
||||
<Step title="Create requirements.txt">
|
||||
```
|
||||
praisonaiagents[api]>=0.0.81
|
||||
```
|
||||
</Step>
|
||||
<Step title="Build and Run Docker Container">
|
||||
```bash
|
||||
docker build -t praisonai-multi-agent .
|
||||
docker run -p 3030:3030 -e OPENAI_API_KEY=your_api_key praisonai-multi-agent
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
### Cloud Deployment
|
||||
|
||||
For detailed cloud deployment instructions, refer to:
|
||||
- [AWS Deployment Guide](/deploy/aws)
|
||||
- [Google Cloud Deployment Guide](/deploy/googlecloud)
|
||||
|
||||
## Scaling Multi-Agent Systems
|
||||
|
||||
When deploying multi-agent systems to production, consider these scaling strategies:
|
||||
|
||||
1. **Horizontal Scaling**: Deploy multiple instances behind a load balancer
|
||||
2. **Vertical Scaling**: Allocate more CPU and memory resources for complex agent interactions
|
||||
3. **Caching**: Implement response caching for frequently asked questions
|
||||
4. **Asynchronous Processing**: Use message queues for handling long-running agent tasks
|
||||
|
||||
## API Configuration Options
|
||||
|
||||
When launching your multi-agent system as an API, you can customize various parameters:
|
||||
|
||||
```python
|
||||
agents.launch(
|
||||
path="/custom-endpoint", # API endpoint path
|
||||
port=8080, # Port number
|
||||
host="0.0.0.0", # Host address (0.0.0.0 for external access)
|
||||
debug=True, # Enable debug mode
|
||||
cors_origins=["*"], # CORS configuration
|
||||
api_key="your-api-key" # Optional API key for authentication
|
||||
)
|
||||
```
|
||||
|
||||
## Securing Your Multi-Agent API
|
||||
|
||||
For production deployments, consider implementing:
|
||||
|
||||
1. **API Key Authentication**: Require API keys for all requests
|
||||
2. **Rate Limiting**: Limit the number of requests per client
|
||||
3. **HTTPS**: Use SSL/TLS certificates for encrypted communication
|
||||
4. **Input Validation**: Validate all input data before processing
|
||||
5. **Output Filtering**: Implement content filtering for agent responses
|
||||
|
||||
## Monitoring and Logging
|
||||
|
||||
For production environments, consider:
|
||||
|
||||
1. **Centralized Logging**: Collect logs from all agents in a central location
|
||||
2. **Performance Metrics**: Track response times and resource usage
|
||||
3. **Error Tracking**: Monitor and alert on agent failures
|
||||
4. **Usage Analytics**: Track which agents are used most frequently
|
||||
|
||||
## Features
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Collaborative Agents" icon="users-gear">
|
||||
Deploy agent systems that collaborate to solve complex problems.
|
||||
</Card>
|
||||
<Card title="Specialized Endpoints" icon="sitemap">
|
||||
Create dedicated endpoints for different agent groups or individual agents.
|
||||
</Card>
|
||||
<Card title="Tool Integration" icon="screwdriver-wrench">
|
||||
Deploy agents with specialized tools like web search capabilities.
|
||||
</Card>
|
||||
<Card title="Scalable Architecture" icon="server">
|
||||
Scale your multi-agent systems to handle production workloads.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -27,7 +27,7 @@ WORKDIR /app
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN pip install flask praisonai==2.1.4 watchdog
|
||||
RUN pip install flask praisonai==2.1.5 watchdog
|
||||
|
||||
EXPOSE 5555
|
||||
|
||||
|
||||
@@ -376,6 +376,7 @@
|
||||
"group": "Deploy",
|
||||
"pages": [
|
||||
"deploy/deploy",
|
||||
"deploy/multi-agents-deploy",
|
||||
"deploy/aws",
|
||||
"deploy/googlecloud"
|
||||
]
|
||||
|
||||
+1
-1
@@ -155,7 +155,7 @@ To facilitate local development with live reload, you can use Docker. Follow the
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN pip install flask praisonai==2.1.4 watchdog
|
||||
RUN pip install flask praisonai==2.1.5 watchdog
|
||||
|
||||
EXPOSE 5555
|
||||
|
||||
|
||||
+1
-1
@@ -208,7 +208,7 @@ To facilitate local development with live reload, you can use Docker. Follow the
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN pip install flask praisonai==2.1.4 watchdog
|
||||
RUN pip install flask praisonai==2.1.5 watchdog
|
||||
|
||||
EXPOSE 5555
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
from praisonaiagents import Agent, Agents, Tools
|
||||
|
||||
research_agent = Agent(name="Research", instructions="You are a research agent to search internet about AI 2024", tools=[Tools.internet_search])
|
||||
summarise_agent = Agent(name="Summarise", instructions="You are a summarize agent to summarise in points")
|
||||
agents = Agents(agents=[research_agent, summarise_agent])
|
||||
agents.launch(path="/agents", port=3030)
|
||||
@@ -0,0 +1,8 @@
|
||||
from praisonaiagents import Agent, Agents, Tools
|
||||
|
||||
research_agent = Agent(name="Research", instructions="You are a research agent to search internet about AI 2024", tools=[Tools.internet_search])
|
||||
summarise_agent = Agent(name="Summarise", instructions="You are a summarize agent to summarise in points")
|
||||
agents = Agents(agents=[research_agent, summarise_agent])
|
||||
agents2 = Agents(agents=[research_agent])
|
||||
agents.launch(path="/agents", port=3030)
|
||||
agents2.launch(path="/agents2", port=3030)
|
||||
+1
-1
@@ -56,7 +56,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.1.4 gunicorn markdown\n")
|
||||
file.write("RUN pip install flask praisonai==2.1.5 gunicorn markdown\n")
|
||||
file.write("EXPOSE 8080\n")
|
||||
file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n')
|
||||
|
||||
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "PraisonAI"
|
||||
version = "2.1.4"
|
||||
version = "2.1.5"
|
||||
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."
|
||||
readme = "README.md"
|
||||
license = ""
|
||||
@@ -12,7 +12,7 @@ dependencies = [
|
||||
"rich>=13.7",
|
||||
"markdown>=3.5",
|
||||
"pyparsing>=3.0.0",
|
||||
"praisonaiagents>=0.0.79",
|
||||
"praisonaiagents>=0.0.81",
|
||||
"python-dotenv>=0.19.0",
|
||||
"instructor>=1.3.3",
|
||||
"PyYAML>=6.0",
|
||||
@@ -89,7 +89,7 @@ autogen = ["pyautogen>=0.2.19", "praisonai-tools>=0.0.15", "crewai"]
|
||||
|
||||
[tool.poetry]
|
||||
name = "PraisonAI"
|
||||
version = "2.1.4"
|
||||
version = "2.1.5"
|
||||
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."
|
||||
authors = ["Mervin Praison"]
|
||||
license = ""
|
||||
@@ -107,7 +107,7 @@ python = ">=3.10,<3.13"
|
||||
rich = ">=13.7"
|
||||
markdown = ">=3.5"
|
||||
pyparsing = ">=3.0.0"
|
||||
praisonaiagents = ">=0.0.79"
|
||||
praisonaiagents = ">=0.0.81"
|
||||
python-dotenv = ">=0.19.0"
|
||||
instructor = ">=1.3.3"
|
||||
PyYAML = ">=6.0"
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
from praisonaiagents import Agent, Agents, Tools
|
||||
|
||||
research_agent = Agent(name="Research", instructions="You are a research agent to search internet about AI 2024", tools=[Tools.internet_search])
|
||||
summarise_agent = Agent(name="Summarise", instructions="You are a summarize agent to summarise in points")
|
||||
agents = Agents(agents=[research_agent, summarise_agent])
|
||||
agents.launch(path="/agents", port=3030)
|
||||
@@ -0,0 +1,8 @@
|
||||
from praisonaiagents import Agent, Agents, Tools
|
||||
|
||||
research_agent = Agent(name="Research", instructions="You are a research agent to search internet about AI 2024", tools=[Tools.internet_search])
|
||||
summarise_agent = Agent(name="Summarise", instructions="You are a summarize agent to summarise in points")
|
||||
agents = Agents(agents=[research_agent, summarise_agent])
|
||||
agents2 = Agents(agents=[research_agent])
|
||||
agents.launch(path="/agents", port=3030)
|
||||
agents2.launch(path="/agents2", port=3030)
|
||||
@@ -23,9 +23,9 @@ import uuid
|
||||
from dataclasses import dataclass
|
||||
|
||||
# Global variables for API server
|
||||
_server_started = False
|
||||
_registered_agents = {}
|
||||
_shared_app = None
|
||||
_server_started = {} # Dict of port -> started boolean
|
||||
_registered_agents = {} # Dict of port -> Dict of path -> agent_id
|
||||
_shared_apps = {} # Dict of port -> FastAPI app
|
||||
|
||||
# Don't import FastAPI dependencies here - use lazy loading instead
|
||||
|
||||
@@ -1421,7 +1421,7 @@ Your Goal: {self.goal}
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
global _server_started, _registered_agents, _shared_app
|
||||
global _server_started, _registered_agents, _shared_apps
|
||||
|
||||
# Try to import FastAPI dependencies - lazy loading
|
||||
try:
|
||||
@@ -1447,32 +1447,41 @@ Your Goal: {self.goal}
|
||||
print("pip install 'praisonaiagents[api]'")
|
||||
return None
|
||||
|
||||
# Initialize shared FastAPI app if not already created
|
||||
if _shared_app is None:
|
||||
_shared_app = FastAPI(
|
||||
title="PraisonAI Agents API",
|
||||
# Initialize port-specific collections if needed
|
||||
if port not in _registered_agents:
|
||||
_registered_agents[port] = {}
|
||||
|
||||
# Initialize shared FastAPI app if not already created for this port
|
||||
if _shared_apps.get(port) is None:
|
||||
_shared_apps[port] = FastAPI(
|
||||
title=f"PraisonAI Agents API (Port {port})",
|
||||
description="API for interacting with PraisonAI Agents"
|
||||
)
|
||||
|
||||
# Add a root endpoint with a welcome message
|
||||
@_shared_app.get("/")
|
||||
@_shared_apps[port].get("/")
|
||||
async def root():
|
||||
return {"message": "Welcome to PraisonAI Agents API. See /docs for usage."}
|
||||
return {
|
||||
"message": f"Welcome to PraisonAI Agents API on port {port}. See /docs for usage.",
|
||||
"endpoints": list(_registered_agents[port].keys())
|
||||
}
|
||||
|
||||
# Add healthcheck endpoint
|
||||
@_shared_app.get("/health")
|
||||
@_shared_apps[port].get("/health")
|
||||
async def healthcheck():
|
||||
return {"status": "ok", "agents": list(_registered_agents.keys())}
|
||||
return {
|
||||
"status": "ok",
|
||||
"endpoints": list(_registered_agents[port].keys())
|
||||
}
|
||||
|
||||
# Normalize path to ensure it starts with /
|
||||
if not path.startswith('/'):
|
||||
path = f'/{path}'
|
||||
|
||||
# Check if path is already registered by another agent
|
||||
if path in _registered_agents and _registered_agents[path] != self.agent_id:
|
||||
existing_agent = _registered_agents[path]
|
||||
logging.warning(f"Path '{path}' is already registered by another agent. Please use a different path.")
|
||||
print(f"⚠️ Warning: Path '{path}' is already registered by another agent.")
|
||||
# Check if path is already registered for this port
|
||||
if path in _registered_agents[port]:
|
||||
logging.warning(f"Path '{path}' is already registered on port {port}. Please use a different path.")
|
||||
print(f"⚠️ Warning: Path '{path}' is already registered on port {port}.")
|
||||
# Use a modified path to avoid conflicts
|
||||
original_path = path
|
||||
path = f"{path}_{self.agent_id[:6]}"
|
||||
@@ -1480,10 +1489,10 @@ Your Goal: {self.goal}
|
||||
print(f"🔄 Using '{path}' instead")
|
||||
|
||||
# Register the agent to this path
|
||||
_registered_agents[path] = self.agent_id
|
||||
_registered_agents[port][path] = self.agent_id
|
||||
|
||||
# Define the endpoint handler
|
||||
@_shared_app.post(path)
|
||||
@_shared_apps[port].post(path)
|
||||
async def handle_agent_query(request: Request, query_data: Optional[AgentQuery] = None):
|
||||
# Handle both direct JSON with query field and form data
|
||||
if query_data is None:
|
||||
@@ -1521,17 +1530,18 @@ Your Goal: {self.goal}
|
||||
|
||||
print(f"🚀 Agent '{self.name}' available at http://{host}:{port}{path}")
|
||||
|
||||
# Start the server if it's not already running
|
||||
if not _server_started:
|
||||
_server_started = True
|
||||
# Start the server if it's not already running for this port
|
||||
if not _server_started.get(port, False):
|
||||
# Mark the server as started first to prevent duplicate starts
|
||||
_server_started[port] = True
|
||||
|
||||
# Start the server in a separate thread
|
||||
def run_server():
|
||||
try:
|
||||
print(f"✅ FastAPI server started at http://{host}:{port}")
|
||||
print(f"📚 API documentation available at http://{host}:{port}/docs")
|
||||
print(f"🔌 Available endpoints: {', '.join(list(_registered_agents.keys()))}")
|
||||
uvicorn.run(_shared_app, host=host, port=port, log_level="debug" if debug else "info")
|
||||
print(f"🔌 Available endpoints: {', '.join(list(_registered_agents[port].keys()))}")
|
||||
uvicorn.run(_shared_apps[port], host=host, port=port, log_level="debug" if debug else "info")
|
||||
except Exception as e:
|
||||
logging.error(f"Error starting server: {str(e)}", exc_info=True)
|
||||
print(f"❌ Error starting server: {str(e)}")
|
||||
@@ -1545,7 +1555,7 @@ Your Goal: {self.goal}
|
||||
else:
|
||||
# If server is already running, wait a moment to make sure the endpoint is registered
|
||||
time.sleep(0.1)
|
||||
print(f"🔌 Available endpoints: {', '.join(list(_registered_agents.keys()))}")
|
||||
print(f"🔌 Available endpoints on port {port}: {', '.join(list(_registered_agents[port].keys()))}")
|
||||
|
||||
# Get the stack frame to check if this is the last launch() call in the script
|
||||
import inspect
|
||||
@@ -1571,18 +1581,19 @@ Your Goal: {self.goal}
|
||||
# If this is the last launch call, block the main thread
|
||||
if not has_more_launches:
|
||||
try:
|
||||
print("\nAll agents registered. Press Ctrl+C to stop the server.")
|
||||
print("\nAll agents registered. Press Ctrl+C to stop the servers.")
|
||||
while True:
|
||||
time.sleep(1)
|
||||
except KeyboardInterrupt:
|
||||
print("\nServer stopped")
|
||||
print("\nServers stopped")
|
||||
except Exception as e:
|
||||
# If something goes wrong with detection, block anyway to be safe
|
||||
logging.error(f"Error in launch detection: {e}")
|
||||
try:
|
||||
print("\nKeeping servers alive. Press Ctrl+C to stop.")
|
||||
while True:
|
||||
time.sleep(1)
|
||||
except KeyboardInterrupt:
|
||||
print("\nServer stopped")
|
||||
print("\nServers stopped")
|
||||
|
||||
return None
|
||||
@@ -17,6 +17,11 @@ import uuid
|
||||
# Set up logger
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Global variables for managing the shared servers
|
||||
_agents_server_started = {} # Dict of port -> started boolean
|
||||
_agents_registered_endpoints = {} # Dict of port -> Dict of path -> endpoint_id
|
||||
_agents_shared_apps = {} # Dict of port -> FastAPI app
|
||||
|
||||
def encode_file_to_base64(file_path: str) -> str:
|
||||
"""Base64-encode a file."""
|
||||
import base64
|
||||
@@ -878,4 +883,230 @@ Context:
|
||||
|
||||
def clear_state(self) -> None:
|
||||
"""Clear all state values"""
|
||||
self._state.clear()
|
||||
self._state.clear()
|
||||
|
||||
def launch(self, path: str = '/agents', port: int = 8000, host: str = '0.0.0.0', debug: bool = False):
|
||||
"""
|
||||
Launch all agents as a single API endpoint. The endpoint accepts a query and processes it through
|
||||
all agents in sequence, with the output of each agent feeding into the next.
|
||||
|
||||
Args:
|
||||
path: API endpoint path (default: '/agents')
|
||||
port: Server port (default: 8000)
|
||||
host: Server host (default: '0.0.0.0')
|
||||
debug: Enable debug mode for uvicorn (default: False)
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
global _agents_server_started, _agents_registered_endpoints, _agents_shared_apps
|
||||
|
||||
if not self.agents:
|
||||
logging.warning("No agents to launch. Add agents to the Agents instance first.")
|
||||
return
|
||||
|
||||
# Try to import FastAPI dependencies - lazy loading
|
||||
try:
|
||||
import uvicorn
|
||||
from fastapi import FastAPI, HTTPException, Request
|
||||
from fastapi.responses import JSONResponse
|
||||
from pydantic import BaseModel
|
||||
import threading
|
||||
import time
|
||||
|
||||
# Define the request model here since we need pydantic
|
||||
class AgentQuery(BaseModel):
|
||||
query: str
|
||||
|
||||
except ImportError as e:
|
||||
# Check which specific module is missing
|
||||
missing_module = str(e).split("No module named '")[-1].rstrip("'")
|
||||
display_error(f"Missing dependency: {missing_module}. Required for launch() method.")
|
||||
logging.error(f"Missing dependency: {missing_module}. Required for launch() method.")
|
||||
print(f"\nTo add API capabilities, install the required dependencies:")
|
||||
print(f"pip install {missing_module}")
|
||||
print("\nOr install all API dependencies with:")
|
||||
print("pip install 'praisonaiagents[api]'")
|
||||
return None
|
||||
|
||||
# Initialize port-specific collections if needed
|
||||
if port not in _agents_registered_endpoints:
|
||||
_agents_registered_endpoints[port] = {}
|
||||
|
||||
# Initialize shared FastAPI app if not already created for this port
|
||||
if _agents_shared_apps.get(port) is None:
|
||||
_agents_shared_apps[port] = FastAPI(
|
||||
title=f"PraisonAI Agents API (Port {port})",
|
||||
description="API for interacting with multiple PraisonAI Agents"
|
||||
)
|
||||
|
||||
# Add a root endpoint with a welcome message
|
||||
@_agents_shared_apps[port].get("/")
|
||||
async def root():
|
||||
return {
|
||||
"message": f"Welcome to PraisonAI Agents API on port {port}. See /docs for usage.",
|
||||
"endpoints": list(_agents_registered_endpoints[port].keys())
|
||||
}
|
||||
|
||||
# Add healthcheck endpoint
|
||||
@_agents_shared_apps[port].get("/health")
|
||||
async def healthcheck():
|
||||
return {
|
||||
"status": "ok",
|
||||
"endpoints": list(_agents_registered_endpoints[port].keys())
|
||||
}
|
||||
|
||||
# Normalize path to ensure it starts with /
|
||||
if not path.startswith('/'):
|
||||
path = f'/{path}'
|
||||
|
||||
# Check if path is already registered for this port
|
||||
if path in _agents_registered_endpoints[port]:
|
||||
logging.warning(f"Path '{path}' is already registered on port {port}. Please use a different path.")
|
||||
print(f"⚠️ Warning: Path '{path}' is already registered on port {port}.")
|
||||
# Use a modified path to avoid conflicts
|
||||
original_path = path
|
||||
instance_id = str(uuid.uuid4())[:6]
|
||||
path = f"{path}_{instance_id}"
|
||||
logging.warning(f"Using '{path}' instead of '{original_path}'")
|
||||
print(f"🔄 Using '{path}' instead")
|
||||
|
||||
# Generate a unique ID for this agent group's endpoint
|
||||
endpoint_id = str(uuid.uuid4())
|
||||
_agents_registered_endpoints[port][path] = endpoint_id
|
||||
|
||||
# Define the endpoint handler
|
||||
@_agents_shared_apps[port].post(path)
|
||||
async def handle_query(request: Request, query_data: Optional[AgentQuery] = None):
|
||||
# Handle both direct JSON with query field and form data
|
||||
if query_data is None:
|
||||
try:
|
||||
request_data = await request.json()
|
||||
if "query" not in request_data:
|
||||
raise HTTPException(status_code=400, detail="Missing 'query' field in request")
|
||||
query = request_data["query"]
|
||||
except:
|
||||
# Fallback to form data or query params
|
||||
form_data = await request.form()
|
||||
if "query" in form_data:
|
||||
query = form_data["query"]
|
||||
else:
|
||||
raise HTTPException(status_code=400, detail="Missing 'query' field in request")
|
||||
else:
|
||||
query = query_data.query
|
||||
|
||||
try:
|
||||
# Process the query sequentially through all agents
|
||||
current_input = query
|
||||
results = []
|
||||
|
||||
for agent in self.agents:
|
||||
try:
|
||||
# Use async version if available, otherwise use sync version
|
||||
if asyncio.iscoroutinefunction(agent.chat):
|
||||
response = await agent.achat(current_input)
|
||||
else:
|
||||
# Run sync function in a thread to avoid blocking
|
||||
loop = asyncio.get_event_loop()
|
||||
response = await loop.run_in_executor(None, lambda: agent.chat(current_input))
|
||||
|
||||
# Store this agent's result
|
||||
results.append({
|
||||
"agent": agent.name,
|
||||
"response": response
|
||||
})
|
||||
|
||||
# Use this response as input to the next agent
|
||||
current_input = response
|
||||
except Exception as e:
|
||||
logging.error(f"Error with agent {agent.name}: {str(e)}", exc_info=True)
|
||||
results.append({
|
||||
"agent": agent.name,
|
||||
"error": str(e)
|
||||
})
|
||||
# Continue with original input if there's an error
|
||||
|
||||
# Return all results and the final output
|
||||
return {
|
||||
"query": query,
|
||||
"results": results,
|
||||
"final_response": current_input
|
||||
}
|
||||
except Exception as e:
|
||||
logging.error(f"Error processing query: {str(e)}", exc_info=True)
|
||||
return JSONResponse(
|
||||
status_code=500,
|
||||
content={"error": f"Error processing query: {str(e)}"}
|
||||
)
|
||||
|
||||
print(f"🚀 Multi-Agent API available at http://{host}:{port}{path}")
|
||||
agent_names = ", ".join([agent.name for agent in self.agents])
|
||||
print(f"📊 Available agents ({len(self.agents)}): {agent_names}")
|
||||
|
||||
# Start the server if it's not already running for this port
|
||||
if not _agents_server_started.get(port, False):
|
||||
# Mark the server as started first to prevent duplicate starts
|
||||
_agents_server_started[port] = True
|
||||
|
||||
# Start the server in a separate thread
|
||||
def run_server():
|
||||
try:
|
||||
print(f"✅ FastAPI server started at http://{host}:{port}")
|
||||
print(f"📚 API documentation available at http://{host}:{port}/docs")
|
||||
print(f"🔌 Available endpoints: {', '.join(list(_agents_registered_endpoints[port].keys()))}")
|
||||
uvicorn.run(_agents_shared_apps[port], host=host, port=port, log_level="debug" if debug else "info")
|
||||
except Exception as e:
|
||||
logging.error(f"Error starting server: {str(e)}", exc_info=True)
|
||||
print(f"❌ Error starting server: {str(e)}")
|
||||
|
||||
# Run server in a background thread
|
||||
server_thread = threading.Thread(target=run_server, daemon=True)
|
||||
server_thread.start()
|
||||
|
||||
# Wait for a moment to allow the server to start and register endpoints
|
||||
time.sleep(0.5)
|
||||
else:
|
||||
# If server is already running, wait a moment to make sure the endpoint is registered
|
||||
time.sleep(0.1)
|
||||
print(f"🔌 Available endpoints on port {port}: {', '.join(list(_agents_registered_endpoints[port].keys()))}")
|
||||
|
||||
# Get the stack frame to check if this is the last launch() call in the script
|
||||
import inspect
|
||||
stack = inspect.stack()
|
||||
|
||||
# If this is called from a Python script (not interactive), try to detect if it's the last launch call
|
||||
if len(stack) > 1 and stack[1].filename.endswith('.py'):
|
||||
caller_frame = stack[1]
|
||||
caller_line = caller_frame.lineno
|
||||
|
||||
try:
|
||||
# Read the file to check if there are more launch calls after this one
|
||||
with open(caller_frame.filename, 'r') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
# Check if there are more launch() calls after the current line
|
||||
has_more_launches = False
|
||||
for line in lines[caller_line:]:
|
||||
if '.launch(' in line and not line.strip().startswith('#'):
|
||||
has_more_launches = True
|
||||
break
|
||||
|
||||
# If this is the last launch call, block the main thread
|
||||
if not has_more_launches:
|
||||
try:
|
||||
print("\nAll agents registered. Press Ctrl+C to stop the servers.")
|
||||
while True:
|
||||
time.sleep(1)
|
||||
except KeyboardInterrupt:
|
||||
print("\nServers stopped")
|
||||
except Exception as e:
|
||||
# If something goes wrong with detection, block anyway to be safe
|
||||
logging.error(f"Error in launch detection: {e}")
|
||||
try:
|
||||
print("\nKeeping servers alive. Press Ctrl+C to stop.")
|
||||
while True:
|
||||
time.sleep(1)
|
||||
except KeyboardInterrupt:
|
||||
print("\nServers stopped")
|
||||
|
||||
return None
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "praisonaiagents"
|
||||
version = "0.0.79"
|
||||
version = "0.0.81"
|
||||
description = "Praison AI agents for completing complex tasks with Self Reflection Agents"
|
||||
authors = [
|
||||
{ name="Mervin Praison" }
|
||||
|
||||
Generated
+1
-1
@@ -1883,7 +1883,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "praisonaiagents"
|
||||
version = "0.0.79"
|
||||
version = "0.0.81"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "mcp" },
|
||||
|
||||
@@ -3614,7 +3614,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "praisonai"
|
||||
version = "2.1.4"
|
||||
version = "2.1.5"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "instructor" },
|
||||
@@ -3756,7 +3756,7 @@ requires-dist = [
|
||||
{ name = "plotly", marker = "extra == 'realtime'", specifier = ">=5.24.0" },
|
||||
{ name = "praisonai-tools", marker = "extra == 'autogen'", specifier = ">=0.0.15" },
|
||||
{ name = "praisonai-tools", marker = "extra == 'crewai'", specifier = ">=0.0.15" },
|
||||
{ name = "praisonaiagents", specifier = ">=0.0.79" },
|
||||
{ name = "praisonaiagents", specifier = ">=0.0.81" },
|
||||
{ name = "pyautogen", marker = "extra == 'autogen'", specifier = ">=0.2.19" },
|
||||
{ name = "pydantic", marker = "extra == 'chat'", specifier = "<=2.10.1" },
|
||||
{ name = "pydantic", marker = "extra == 'code'", specifier = "<=2.10.1" },
|
||||
@@ -3813,7 +3813,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "praisonaiagents"
|
||||
version = "0.0.79"
|
||||
version = "0.0.81"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "mcp" },
|
||||
@@ -3821,9 +3821,9 @@ dependencies = [
|
||||
{ name = "pydantic" },
|
||||
{ name = "rich" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b2/c3/172035c72e559b5fb918eb98faedaeccdaad2c96eb8116c277406259541f/praisonaiagents-0.0.79.tar.gz", hash = "sha256:5ab55b2c1130961ffd7091193a819be3b7fe6675429ee19eb56d94dc073ed125", size = 118276 }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/aa/e8/5c254f6e097db086f6c72652a81edfbdd4426daa91d80f9eb020fc1d8e2d/praisonaiagents-0.0.81.tar.gz", hash = "sha256:daec5dc448bd9221f5b2ec1ea9af0a942eb0d0f19fb0e0e588842e3d0259bddd", size = 121033 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/65/e1e40a2c1219246c0fc775be02f2a414fe453f4f9bac5ab074a6e292c39f/praisonaiagents-0.0.79-py3-none-any.whl", hash = "sha256:24b2c2288cb7231b6a2a658141e3c97bb1a73389bb4a4ff1939cab106bd81099", size = 137869 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a5/4d/c49811749f579d11b84e683cc0c2685f81bcbda6ed7edec907863087d1cb/praisonaiagents-0.0.81-py3-none-any.whl", hash = "sha256:41252168220aa2326c77dbaff53918e09fcc62f059ff4ab9633fc95b6b8bc4f7", size = 140816 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
Reference in new issue
Block a user