Async Jobs Deployment

Deploy the async jobs server for production agent and recipe execution

Async Jobs Deployment

Deploy the async jobs server for production-grade agent and recipe execution with persistence, webhooks, and streaming.

Overview

The async jobs server provides:

  • HTTP API for job submission and management
  • Persistent job storage
  • Webhook notifications
  • SSE streaming for real-time progress
  • Idempotency support

Quick Start

Start the Server

bash
# One command, done — with a persistent SQLite store
praisonai serve jobs --port 8005 --db /var/lib/praisonai/jobs.db

Advanced / factory mode (direct uvicorn control, multiple workers):

bash
# Development
python -m uvicorn praisonai.jobs.server:create_app --port 8005 --factory

# Production with multiple workers
python -m uvicorn praisonai.jobs.server:create_app \
    --host 0.0.0.0 \
    --port 8005 \
    --workers 4 \
    --factory

Submit a Job

bash
# Submit with CLI
praisonai run submit "Analyze data" --recipe analyzer --api-url http://localhost:8005

# Submit with Python
from praisonai import recipe

job = recipe.submit_job(
    "my-recipe",
    input={"query": "What is AI?"},
    api_url="http://localhost:8005",
)

Docker Deployment

Dockerfile

dockerfile
FROM python:3.11-slim

WORKDIR /app

# Install dependencies
COPY requirements.txt .
RUN pip install -r requirements.txt

# Copy application
COPY . .

# Expose port
EXPOSE 8005

# Set environment variables
ENV OPENAI_API_KEY=${OPENAI_API_KEY}
ENV PRAISONAI_JOBS_PORT=8005
ENV PRAISONAI_JOBS_HOST=0.0.0.0
ENV PRAISONAI_JOBS_BIND_HOST=0.0.0.0
ENV PRAISONAI_JOBS_API_KEY=${PRAISONAI_JOBS_API_KEY}

# Run server
CMD ["praisonai", "serve", "jobs", "--host", "0.0.0.0", "--port", "8005", "--db", "/data/jobs.db"]
# Advanced / factory mode:
# CMD ["python", "-m", "uvicorn", "praisonai.jobs.server:create_app", "--host", "0.0.0.0", "--port", "8005", "--factory"]

Docker Compose

yaml
version: '3.8'

services:
  jobs-server:
    build: .
    ports:
      - "8005:8005"
    environment:
      - OPENAI_API_KEY=${OPENAI_API_KEY}
      - PRAISONAI_JOBS_MAX_CONCURRENT=10
      - PRAISONAI_JOBS_DEFAULT_TIMEOUT=3600
      - PRAISONAI_JOBS_BIND_HOST=0.0.0.0
      - PRAISONAI_JOBS_API_KEY=${PRAISONAI_JOBS_API_KEY}
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8005/health"]
      interval: 30s
      timeout: 10s
      retries: 3
    restart: unless-stopped

Configuration

Environment Variables

VariableDefaultDescription
PRAISONAI_JOBS_PORT8005Server port
PRAISONAI_JOBS_HOST127.0.0.1Server host
PRAISONAI_JOBS_MAX_CONCURRENT10Max concurrent jobs
PRAISONAI_JOBS_DEFAULT_TIMEOUT3600Default timeout (seconds)
PRAISONAI_JOBS_API_KEY(unset)When set, all requests except /health must send the key via Authorization: Bearer <key> or X-API-Key: <key>. Wrong or missing key returns 401.
PRAISONAI_JOBS_BIND_HOST127.0.0.1Bind host used for the auth guard. Any value outside {127.0.0.1, localhost, ::1} requires PRAISONAI_JOBS_API_KEY.
PRAISONAI_JOBS_AGENT_ROOTserver cwdDirectory that submitted agent_file paths must resolve under; anything outside returns 400. See Jobs agent_file Root.

Authentication

The jobs server has two auth modes based on where it binds.

  1. Localhost-only (default)

    No key required, reachable only from the loopback interface. The server logs a startup warning that any local process can submit jobs — set PRAISONAI_JOBS_API_KEY on shared hosts.

    bash
    praisonai serve jobs --port 8005
  2. Public bind

    Set PRAISONAI_JOBS_BIND_HOST to a real interface and PRAISONAI_JOBS_API_KEY together.

    bash
    export PRAISONAI_JOBS_BIND_HOST=0.0.0.0
    export PRAISONAI_JOBS_API_KEY=$JOBS_KEY
    praisonai serve jobs --host 0.0.0.0 --port 8005
    bash
    curl -H "Authorization: Bearer $JOBS_KEY" http://your-host:8005/api/v1/runs

A public bind without a key refuses every request except /health with 401 {"error": "PRAISONAI_JOBS_API_KEY required for non-localhost binding"}.

Diagram

Yes

No

Yes

No

🚀 Server start

🛡️ Bind host loopback?

✅ No key required

🛡️ API key set?

🔑 Key required per request

⛔ 401 on all but /health

TEMPLATE.yaml Runtime Block

Configure job defaults in your recipe:

yaml
runtime:
  job:
    enabled: true
    timeout_sec: 3600
    webhook_url: "${WEBHOOK_URL}"
    idempotency_scope: "session"
    events:
      - completed
      - failed

API Endpoints

EndpointMethodDescription
/api/v1/runsPOSTSubmit a new job
/api/v1/runsGETList all jobs
/api/v1/runs/{id}GETGet job status
/api/v1/runs/{id}/resultGETGet job result
/api/v1/runs/{id}/streamGETStream job progress (SSE)
/api/v1/runs/{id}/cancelPOSTCancel a job
/api/v1/runs/{id}DELETEDelete a job
/healthGETHealth check
/statsGETServer statistics

Production Considerations

Load Balancing

For high availability, deploy multiple instances behind a load balancer:

nginx
upstream jobs_servers {
    server jobs1:8005;
    server jobs2:8005;
    server jobs3:8005;
}

server {
    listen 80;

    location / {
        proxy_pass http://jobs_servers;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

Webhooks

Configure webhooks for job completion notifications:

python
job = recipe.submit_job(
    "my-recipe",
    webhook_url="https://your-server.com/webhook",
)

Webhook payload:

json
{
  "job_id": "run_abc123",
  "status": "succeeded",
  "result": {"output": "..."},
  "completed_at": "2024-01-15T10:30:00Z",
  "duration_seconds": 45.2
}

Idempotency

Prevent duplicate job submissions:

bash
praisonai run submit "Task" --idempotency-key order-123 --idempotency-scope global

Monitoring

bash
# Check server health
curl http://localhost:8005/health

# Get server stats
curl http://localhost:8005/stats

# List running jobs
praisonai run list --status running --json

Kubernetes Deployment

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: praisonai-jobs
spec:
  replicas: 3
  selector:
    matchLabels:
      app: praisonai-jobs
  template:
    metadata:
      labels:
        app: praisonai-jobs
    spec:
      containers:
      - name: jobs-server
        image: praisonai/jobs-server:latest
        ports:
        - containerPort: 8005
        env:
        - name: OPENAI_API_KEY
          valueFrom:
            secretKeyRef:
              name: praisonai-secrets
              key: openai-api-key
        - name: PRAISONAI_JOBS_BIND_HOST
          value: "0.0.0.0"
        - name: PRAISONAI_JOBS_API_KEY
          valueFrom:
            secretKeyRef:
              name: praisonai-secrets
              key: jobs-api-key
        - name: PRAISONAI_JOBS_MAX_CONCURRENT
          value: "10"
        livenessProbe:
          httpGet:
            path: /health
            port: 8005
          initialDelaySeconds: 10
          periodSeconds: 30
        resources:
          requests:
            memory: "512Mi"
            cpu: "500m"
          limits:
            memory: "2Gi"
            cpu: "2000m"
---
apiVersion: v1
kind: Service
metadata:
  name: praisonai-jobs
spec:
  selector:
    app: praisonai-jobs
  ports:
  - port: 8005
    targetPort: 8005
  type: LoadBalancer

See Also