Skip to content

Commit 3fbaa6d

Browse files
committed
Increment version to 0.0.78 in pyproject.toml, add API dependencies for FastAPI and Uvicorn, and implement HTTP API launch functionality in agent.py for enhanced agent interaction.
1 parent ab7f3ec commit 3fbaa6d

File tree

7 files changed

+230
-4
lines changed

7 files changed

+230
-4
lines changed

examples/mcp/mcp-sse-weather.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from praisonaiagents import Agent, MCP
2+
3+
search_agent = Agent(
4+
instructions="""You are a weather agent that can provide weather information for a given city.""",
5+
llm="openai/gpt-4o-mini",
6+
tools=MCP("http://localhost:8080/sse")
7+
)
8+
9+
search_agent.start("What is the weather in London?")
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from praisonaiagents import Agent
2+
3+
# Create a weather agent
4+
weather_agent = Agent(
5+
instructions="""You are a weather agent that can provide weather information for a given city.""",
6+
llm="gpt-4o-mini"
7+
)
8+
9+
# Create a stock market agent
10+
stock_agent = Agent(
11+
instructions="""You are a stock market agent that can provide information about stock prices and market trends.""",
12+
llm="gpt-4o-mini"
13+
)
14+
15+
# Create a travel agent
16+
travel_agent = Agent(
17+
instructions="""You are a travel agent that can provide recommendations for destinations, hotels, and activities.""",
18+
llm="gpt-4o-mini"
19+
)
20+
21+
# Register the first two agents with blocking=False so they don't block execution
22+
weather_agent.launch(path="/weather", port=3030, blocking=False)
23+
stock_agent.launch(path="/stock", port=3030, blocking=False)
24+
25+
# Register the last agent with blocking=True to keep the server running
26+
# This must be the last launch() call
27+
travel_agent.launch(path="/travel", port=3030, blocking=True)
28+
29+
# The script will block at the last launch() call until the user presses Ctrl+C

src/praisonai-agents/praisonaiagents/agent/agent.py

Lines changed: 161 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,16 @@
2222
import uuid
2323
from dataclasses import dataclass
2424

25+
# Don't import FastAPI dependencies here - use lazy loading instead
26+
2527
if TYPE_CHECKING:
2628
from ..task.task import Task
2729

30+
# Shared variables for API server
31+
_shared_app = None
32+
_server_started = False
33+
_registered_agents = {}
34+
2835
@dataclass
2936
class ChatCompletionMessage:
3037
content: str
@@ -1399,4 +1406,157 @@ async def execute_tool_async(self, function_name: str, arguments: Dict[str, Any]
13991406

14001407
except Exception as e:
14011408
logging.error(f"Error in execute_tool_async: {str(e)}", exc_info=True)
1402-
return {"error": f"Error in execute_tool_async: {str(e)}"}
1409+
return {"error": f"Error in execute_tool_async: {str(e)}"}
1410+
1411+
def launch(self, path: str = '/', port: int = 8000, host: str = '0.0.0.0', autostart: bool = True, debug: bool = False, blocking: bool = True):
1412+
"""
1413+
Launch the agent as an HTTP API endpoint.
1414+
1415+
Args:
1416+
path: API endpoint path (default: '/')
1417+
port: Server port (default: 8000)
1418+
host: Server host (default: '0.0.0.0')
1419+
autostart: Whether to start the server automatically (default: True)
1420+
debug: Enable debug mode for uvicorn (default: False)
1421+
blocking: If True, blocks the main thread to keep the server running (default: True)
1422+
1423+
Returns:
1424+
None
1425+
"""
1426+
global _server_started, _registered_agents, _shared_app
1427+
1428+
# Try to import FastAPI dependencies - lazy loading
1429+
try:
1430+
import uvicorn
1431+
from fastapi import FastAPI, HTTPException, Request
1432+
from fastapi.responses import JSONResponse
1433+
from pydantic import BaseModel
1434+
1435+
# Define the request model here since we need pydantic
1436+
class AgentQuery(BaseModel):
1437+
query: str
1438+
1439+
except ImportError as e:
1440+
# Check which specific module is missing
1441+
missing_module = str(e).split("No module named '")[-1].rstrip("'")
1442+
display_error(f"Missing dependency: {missing_module}. Required for launch() method.")
1443+
logging.error(f"Missing dependency: {missing_module}. Required for launch() method.")
1444+
print(f"\nTo add API capabilities, install the required dependencies:")
1445+
print(f"pip install {missing_module}")
1446+
print("\nOr install all API dependencies with:")
1447+
print("pip install 'praisonaiagents[api]'")
1448+
return None
1449+
1450+
# Initialize shared FastAPI app if not already created
1451+
if _shared_app is None:
1452+
_shared_app = FastAPI(
1453+
title="PraisonAI Agents API",
1454+
description="API for interacting with PraisonAI Agents"
1455+
)
1456+
1457+
# Add a root endpoint with a welcome message
1458+
@_shared_app.get("/")
1459+
async def root():
1460+
return {"message": "Welcome to PraisonAI Agents API. See /docs for usage."}
1461+
1462+
# Normalize path to ensure it starts with /
1463+
if not path.startswith('/'):
1464+
path = f'/{path}'
1465+
1466+
# Check if path is already registered by another agent
1467+
if path in _registered_agents and _registered_agents[path] != self.agent_id:
1468+
existing_agent = _registered_agents[path]
1469+
logging.warning(f"Path '{path}' is already registered by another agent. Please use a different path.")
1470+
print(f"⚠️ Warning: Path '{path}' is already registered by another agent.")
1471+
# Use a modified path to avoid conflicts
1472+
original_path = path
1473+
path = f"{path}_{self.agent_id[:6]}"
1474+
logging.warning(f"Using '{path}' instead of '{original_path}'")
1475+
print(f"🔄 Using '{path}' instead")
1476+
1477+
# Register the agent to this path
1478+
_registered_agents[path] = self.agent_id
1479+
1480+
# Define the endpoint handler
1481+
@_shared_app.post(path)
1482+
async def handle_agent_query(request: Request, query_data: Optional[AgentQuery] = None):
1483+
# Handle both direct JSON with query field and form data
1484+
if query_data is None:
1485+
try:
1486+
request_data = await request.json()
1487+
if "query" not in request_data:
1488+
raise HTTPException(status_code=400, detail="Missing 'query' field in request")
1489+
query = request_data["query"]
1490+
except:
1491+
# Fallback to form data or query params
1492+
form_data = await request.form()
1493+
if "query" in form_data:
1494+
query = form_data["query"]
1495+
else:
1496+
raise HTTPException(status_code=400, detail="Missing 'query' field in request")
1497+
else:
1498+
query = query_data.query
1499+
1500+
try:
1501+
# Use async version if available, otherwise use sync version
1502+
if asyncio.iscoroutinefunction(self.chat):
1503+
response = await self.achat(query)
1504+
else:
1505+
# Run sync function in a thread to avoid blocking
1506+
loop = asyncio.get_event_loop()
1507+
response = await loop.run_in_executor(None, lambda: self.chat(query))
1508+
1509+
return {"response": response}
1510+
except Exception as e:
1511+
logging.error(f"Error processing query: {str(e)}", exc_info=True)
1512+
return JSONResponse(
1513+
status_code=500,
1514+
content={"error": f"Error processing query: {str(e)}"}
1515+
)
1516+
1517+
print(f"🚀 Agent '{self.name}' available at http://{host}:{port}{path}")
1518+
1519+
# Start the server if this is the first launch call and autostart is True
1520+
if autostart and not _server_started:
1521+
_server_started = True
1522+
1523+
# Add healthcheck endpoint
1524+
@_shared_app.get("/health")
1525+
async def healthcheck():
1526+
return {"status": "ok", "agents": list(_registered_agents.keys())}
1527+
1528+
# Start the server in a separate thread to not block execution
1529+
import threading
1530+
def run_server():
1531+
try:
1532+
uvicorn.run(_shared_app, host=host, port=port, log_level="debug" if debug else "info")
1533+
except Exception as e:
1534+
logging.error(f"Error starting server: {str(e)}", exc_info=True)
1535+
print(f"❌ Error starting server: {str(e)}")
1536+
1537+
server_thread = threading.Thread(target=run_server, daemon=True)
1538+
server_thread.start()
1539+
1540+
# Give the server a moment to start up
1541+
import time
1542+
time.sleep(0.5)
1543+
1544+
print(f"✅ FastAPI server started at http://{host}:{port}")
1545+
print(f"📚 API documentation available at http://{host}:{port}/docs")
1546+
1547+
# If blocking is True, keep the main thread alive
1548+
if blocking:
1549+
print("\nServer is running in blocking mode. Press Ctrl+C to stop...")
1550+
try:
1551+
while True:
1552+
time.sleep(1)
1553+
except KeyboardInterrupt:
1554+
print("\nServer stopped")
1555+
else:
1556+
# Note for non-blocking mode
1557+
print("\nNote: Server is running in a background thread. To keep it alive, either:")
1558+
print("1. Set blocking=True when calling launch()")
1559+
print("2. Keep your main application running")
1560+
print("3. Use a loop in your code to prevent the program from exiting")
1561+
1562+
return None

src/praisonai-agents/pyproject.toml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "praisonaiagents"
7-
version = "0.0.77"
7+
version = "0.0.78"
88
description = "Praison AI agents for completing complex tasks with Self Reflection Agents"
99
authors = [
1010
{ name="Mervin Praison" }
@@ -38,10 +38,17 @@ llm = [
3838
"pydantic>=2.4.2"
3939
]
4040

41+
# Add API dependencies
42+
api = [
43+
"fastapi>=0.115.0",
44+
"uvicorn>=0.34.0"
45+
]
46+
4147
# Combined features
4248
all = [
4349
"praisonaiagents[memory]",
4450
"praisonaiagents[knowledge]",
4551
"praisonaiagents[llm]",
46-
"praisonaiagents[mcp]"
52+
"praisonaiagents[mcp]",
53+
"praisonaiagents[api]"
4754
]
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from praisonaiagents import Agent, MCP
2+
3+
search_agent = Agent(
4+
instructions="""You are a weather agent that can provide weather information for a given city.""",
5+
llm="openai/gpt-4o-mini",
6+
tools=MCP("http://localhost:8080/sse")
7+
)
8+
search_agent.launch(path="/weather", port=3030)

src/praisonai-agents/simple-api.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from praisonaiagents import Agent
2+
3+
agent = Agent(instructions="""You are a helpful assistant.""", llm="gpt-4o-mini")
4+
agent.launch(path="/ask", port=3030)

src/praisonai-agents/uv.lock

Lines changed: 10 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)