Skip to main content

Recipe Serve - Python Usage

This guide covers how to configure and manage the recipe server programmatically.

Starting the Server Programmatically

Basic Server Start

from praisonai.recipe.serve import serve, create_app

# Start server (blocking)
serve(host="127.0.0.1", port=8765)

With Configuration

from praisonai.recipe.serve import serve, load_config

# Load config from file
config = load_config("serve.yaml")

# Override with custom settings
config["auth"] = "api-key"
config["api_key"] = "my-secret-key"

# Start server
serve(
    host=config.get("host", "127.0.0.1"),
    port=config.get("port", 8765),
    reload=False,
    config=config
)

Creating ASGI App for Custom Deployment

from praisonai.recipe.serve import create_app

# Create ASGI app
app = create_app(config={
    "auth": "api-key",
    "api_key": "my-secret-key",
    "cors_origins": "*"
})

# Use with uvicorn programmatically
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8765)

# Or with other ASGI servers (hypercorn, daphne, etc.)

Configuration File Format

serve.yaml

# Server binding
host: 127.0.0.1
port: 8765

# Authentication
auth: api-key  # none, api-key, jwt
api_key: your-secret-key  # or use PRAISONAI_API_KEY env var

# Recipe filtering
recipes:
  - support-reply-drafter
  - meeting-action-items
  - code-review

# Performance
preload: true  # Preload recipes on startup

# CORS (for web clients)
cors_origins: "https://app.example.com,https://admin.example.com"

# Logging
log_level: info

Using agents.yaml (Unified Config)

You can include serve configuration in your existing agents.yaml:
# agents.yaml
framework: praisonai
topic: My Application

roles:
  assistant:
    role: AI Assistant
    goal: Help users
    tasks:
      respond:
        description: Respond to {query}

# Server configuration section
serve:
  host: 127.0.0.1
  port: 8765
  auth: api-key
  preload: true
Then load it:
from praisonai.recipe.serve import load_config, serve

# Load unified config
config = load_config("agents.yaml")

# Extract serve section
serve_config = config.get("serve", {})

# Start server
serve(
    host=serve_config.get("host", "127.0.0.1"),
    port=serve_config.get("port", 8765),
    config=serve_config
)

Authentication Middleware

API Key Authentication

from praisonai.recipe.serve import create_auth_middleware, create_app

# Create auth middleware
auth_middleware = create_auth_middleware(
    auth_type="api-key",
    api_key="my-secret-key"
)

# Create app with auth
app = create_app(config={
    "auth": "api-key",
    "api_key": "my-secret-key"
})

Custom Authentication

from starlette.middleware.base import BaseHTTPMiddleware
from starlette.responses import JSONResponse

class CustomAuthMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next):
        # Skip auth for health endpoint
        if request.url.path == "/health":
            return await call_next(request)
        
        # Custom auth logic
        token = request.headers.get("Authorization")
        if not self.validate_token(token):
            return JSONResponse(
                {"error": "Unauthorized"},
                status_code=401
            )
        
        return await call_next(request)
    
    def validate_token(self, token):
        # Your validation logic
        return token == "Bearer valid-token"

Route Handlers

Available Routes

RouteMethodDescription
/healthGETHealth check
/v1/recipesGETList recipes
/v1/recipes/{name}GETDescribe recipe
/v1/recipes/{name}/schemaGETGet recipe schema
/v1/recipes/runPOSTRun recipe (sync)
/v1/recipes/streamPOSTRun recipe (SSE)
/v1/recipes/validatePOSTValidate recipe

Custom Route Example

from starlette.applications import Starlette
from starlette.routing import Route
from starlette.responses import JSONResponse
from praisonai.recipe.serve import create_app

# Get base app
base_app = create_app()

# Add custom route
async def custom_endpoint(request):
    return JSONResponse({"custom": "response"})

# Extend routes
custom_routes = [
    Route("/custom", custom_endpoint, methods=["GET"]),
]

# Create new app with extended routes
from starlette.routing import Mount
extended_app = Starlette(
    routes=list(base_app.routes) + custom_routes
)

Docker Deployment

Dockerfile

FROM python:3.11-slim

# Install dependencies
RUN pip install praisonai[serve]

# Copy configuration
COPY serve.yaml /app/
WORKDIR /app

# Set environment variables
ENV PRAISONAI_API_KEY=${PRAISONAI_API_KEY}
ENV OPENAI_API_KEY=${OPENAI_API_KEY}

# Expose port
EXPOSE 8765

# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:8765/health || exit 1

# Start server
CMD ["praisonai", "recipe", "serve", "--config", "serve.yaml"]

docker-compose.yaml

version: '3.8'

services:
  recipe-server:
    build: .
    ports:
      - "8765:8765"
    environment:
      - PRAISONAI_API_KEY=${PRAISONAI_API_KEY}
      - OPENAI_API_KEY=${OPENAI_API_KEY}
    volumes:
      - ./recipes:/app/recipes
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8765/health"]
      interval: 30s
      timeout: 10s
      retries: 3
    restart: unless-stopped

Kubernetes Deployment

deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: recipe-server
spec:
  replicas: 3
  selector:
    matchLabels:
      app: recipe-server
  template:
    metadata:
      labels:
        app: recipe-server
    spec:
      containers:
      - name: recipe-server
        image: your-registry/recipe-server:latest
        ports:
        - containerPort: 8765
        env:
        - name: PRAISONAI_API_KEY
          valueFrom:
            secretKeyRef:
              name: recipe-secrets
              key: api-key
        - name: OPENAI_API_KEY
          valueFrom:
            secretKeyRef:
              name: recipe-secrets
              key: openai-key
        livenessProbe:
          httpGet:
            path: /health
            port: 8765
          initialDelaySeconds: 10
          periodSeconds: 30
        readinessProbe:
          httpGet:
            path: /health
            port: 8765
          initialDelaySeconds: 5
          periodSeconds: 10
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"
---
apiVersion: v1
kind: Service
metadata:
  name: recipe-server
spec:
  selector:
    app: recipe-server
  ports:
  - port: 80
    targetPort: 8765
  type: ClusterIP

Testing the Server

Unit Test Example

import pytest
from starlette.testclient import TestClient
from praisonai.recipe.serve import create_app

@pytest.fixture
def client():
    app = create_app(config={"auth": "none"})
    return TestClient(app)

def test_health(client):
    response = client.get("/health")
    assert response.status_code == 200
    assert response.json()["status"] == "healthy"

def test_list_recipes(client):
    response = client.get("/v1/recipes")
    assert response.status_code == 200
    assert "recipes" in response.json()

def test_auth_required():
    app = create_app(config={
        "auth": "api-key",
        "api_key": "test-key"
    })
    client = TestClient(app)
    
    # Without key - should fail
    response = client.get("/v1/recipes")
    assert response.status_code == 401
    
    # With key - should succeed
    response = client.get(
        "/v1/recipes",
        headers={"X-API-Key": "test-key"}
    )
    assert response.status_code == 200

Best Practices

1. Always Use Auth in Production

# Development
serve(host="127.0.0.1", port=8765)

# Production
serve(
    host="0.0.0.0",
    port=8765,
    config={
        "auth": "api-key",
        "api_key": os.environ["PRAISONAI_API_KEY"]
    }
)

2. Preload Recipes for Faster First Request

# serve.yaml
preload: true
recipes:
  - frequently-used-recipe

3. Use Health Checks

# In your monitoring system
import requests

def check_server_health():
    try:
        response = requests.get("http://localhost:8765/health", timeout=5)
        return response.json()["status"] == "healthy"
    except:
        return False

4. Configure CORS for Web Clients

# serve.yaml
cors_origins: "https://app.example.com"

Next Steps