Async Jobs
Submit and manage long-running agent jobs and recipes via HTTP API
The run command manages async job execution for agents and recipes via a jobs server.
Quick Start
# Start the jobs server
praisonai serve jobs --port 8005
# Or, using uvicorn directly (factory mode)
python -m uvicorn praisonai.jobs.server:create_app --port 8005 --factory
# Submit a job
praisonai run submit "Analyze this data"
# Submit a recipe as job
praisonai run submit "Analyze AI trends" --recipe news-analyzerCommands Overview
| Command | Description |
|---|---|
praisonai run submit | Submit a new job |
praisonai run status <id> | Get job status |
praisonai run result <id> | Get job result |
praisonai run stream <id> | Stream job progress |
praisonai run list | List all jobs |
praisonai run cancel <id> | Cancel a job |
How routing works inside run
praisonai run inspects its first positional token to decide between the async-jobs API and the modern agent runner.
graph TB
Start[📋 praisonai run FIRST-TOKEN] --> Flag{🔍 starts<br/>with -?}
Flag -->|Yes| Jobs[🤖 Jobs API<br/>argparse]
Flag -->|No| Verb{🔍 reserved job verb?<br/>submit/status/result/<br/>cancel/list/stream}
Verb -->|Yes| Jobs
Verb -->|No| File{🔍 ends in .yaml/.yml?<br/>(existence not required)<br/>or otherwise an existing file?}
File -->|Yes| Runner[✅ Modern agent runner<br/>run_app]
File -->|No| Jobs
classDef input fill:#6366F1,stroke:#7C90A0,color:#fff
classDef check fill:#F59E0B,stroke:#7C90A0,color:#fff
classDef jobs fill:#189AB4,stroke:#7C90A0,color:#fff
classDef runner fill:#10B981,stroke:#7C90A0,color:#fff
class Start input
class Flag,Verb,File check
class Jobs jobs
class Runner runnersequenceDiagram
participant User
participant CLI as praisonai run
participant Router
participant Runner as Agent Runner
User->>CLI: praisonai run typo.yaml
CLI->>Router: first token = "typo.yaml"
Router->>Router: ends in .yaml? ✅
Router->>Runner: hand off (existence check happens here)
Runner-->>User: Error: file 'typo.yaml' not found
Note over User,Runner: NOT the jobs API "invalid choice" / missing-job_id
classDef user fill:#8B0000,stroke:#7C90A0,color:#fff
classDef tool fill:#189AB4,stroke:#7C90A0,color:#fff
classDef result fill:#10B981,stroke:#7C90A0,color:#fff
class User user
class CLI,Router tool
class Runner result| You type | Route | Result |
|---|---|---|
praisonai run submit "task" | Jobs API | Submit a new job |
praisonai run status run_abc123 | Jobs API | Get job status |
praisonai run agents.yaml | Agent runner | Runs the YAML workflow |
praisonai run ./my_agents.yaml | Agent runner | Same — explicit path |
praisonai run typo.yaml (file absent) | Agent runner | Runner emits a clear file-not-found error — not the async-jobs invalid choice |
praisonai run existing_file.py | Agent runner | Existing-file predicate wins |
praisonai run ./submit (file named submit) | Agent runner | Path separator dodges the reserved-verb guard |
praisonai run submit (file submit also exists in cwd) | Jobs API | Reserved verb wins over same-named file |
praisonai run "some prompt" | Jobs API → exit 2 invalid choice | Free-text prompts belong to praisonai "…", not praisonai run "…" |
Starting the Jobs Server
Before using job commands, start the jobs server:
# Start server on default port (8005)
praisonai serve jobs --port 8005
# With custom host/port
praisonai serve jobs --host 0.0.0.0 --port 8080
# Or, using uvicorn directly (factory mode)
python -m uvicorn praisonai.jobs.server:create_app --port 8005 --factorySubmit a Job
Basic Submission
# Basic submission with prompt
praisonai run submit "Analyze this data"
# Wait for completion
praisonai run submit "Quick task" --wait
# Stream progress after submission
praisonai run submit "Long task" --streamSubmit with Recipe
# Submit with recipe
praisonai run submit "Analyze AI trends" --recipe news-analyzer
# With recipe config
praisonai run submit "Analyze AI trends" --recipe news-analyzer --recipe-config '{"format": "json"}'Submit with Agent File
# With agent file
praisonai run submit "Process task" --agent-file agents.yamlAdvanced Options
# With timeout
praisonai run submit "Complex task" --timeout 7200
# With webhook
praisonai run submit "Task" --webhook-url https://example.com/callback
# With idempotency
praisonai run submit "Task" --idempotency-key order-123 --idempotency-scope session
# With metadata
praisonai run submit "Task" --metadata user=john --metadata priority=high
# JSON output
praisonai run submit "Task" --json
# Custom API URL
praisonai run submit "Task" --api-url http://localhost:8080Submit Options
| Option | Description |
|---|---|
--agent-file | Path to agents.yaml |
--recipe | Recipe name (mutually exclusive with –agent-file) |
--recipe-config | Recipe config as JSON string |
--framework | Framework to use (default: praisonai) |
--timeout | Timeout in seconds (default: 3600) |
--wait | Wait for completion |
--stream | Stream progress after submission |
--idempotency-key | Key to prevent duplicates |
--idempotency-scope | Scope: none, session, global |
--webhook-url | Webhook URL for completion |
--session-id | Session ID for grouping |
--metadata | Custom metadata (KEY=VALUE, repeatable) |
--json | Output JSON for scripting |
--api-url | Jobs API URL (default: http://127.0.0.1:8005) |
Check Job Status
# Get status
praisonai run status run_abc123
# JSON output
praisonai run status run_abc123 --jsonStatus Output
Job: run_abc123
Status: running
Progress: 45%
Step: Processing data
Created: 2024-01-15 10:30:00
Duration: 45.2sGet Job Result
# Get result
praisonai run result run_abc123
# JSON output
praisonai run result run_abc123 --jsonStream Job Progress
Stream real-time progress updates via SSE:
# Stream progress
praisonai run stream run_abc123
# Raw JSON events
praisonai run stream run_abc123 --jsonStream Output
[10%] Initializing agent
[20%] Loading recipe
[50%] Processing data
[90%] Finalizing
[100%] CompletedList Jobs
# List all jobs
praisonai run list
# Filter by status
praisonai run list --status running
praisonai run list --status succeeded
praisonai run list --status failed
# Pagination
praisonai run list --page 1 --page-size 20
# JSON output
praisonai run list --jsonCancel a Job
# Cancel job
praisonai run cancel run_abc123
# JSON output
praisonai run cancel run_abc123 --jsonIdempotency
Prevent duplicate job submissions:
# First submission creates job
praisonai run submit "Process order" --idempotency-key order-123 --json
# Second submission returns same job (no duplicate)
praisonai run submit "Process order" --idempotency-key order-123 --jsonIdempotency Scopes
| Scope | Description |
|---|---|
none | No idempotency (default) |
session | Unique within session |
global | Unique across all sessions |
Webhooks
Configure webhooks to receive notifications when jobs complete:
praisonai run submit "Task" --webhook-url https://example.com/callbackWebhook payload:
{
"job_id": "run_abc123",
"status": "succeeded",
"result": {"output": "..."},
"completed_at": "2024-01-15T10:30:00Z",
"duration_seconds": 45.2
}Session Grouping
Group related jobs by session:
praisonai run submit "Task 1" --session-id project-alpha
praisonai run submit "Task 2" --session-id project-alphaPython API
from praisonai import recipe
# Submit recipe as async job
job = recipe.submit_job(
"my-recipe",
input={"query": "What is AI?"},
config={"max_tokens": 1000},
session_id="session_123",
timeout_sec=3600,
webhook_url="https://example.com/webhook",
idempotency_key="unique-key-123",
api_url="http://127.0.0.1:8005",
)
print(f"Job ID: {job.job_id}")
print(f"Status: {job.status}")
# Wait for completion
result = job.wait(poll_interval=5, timeout=300)
print(f"Result: {result}")Complete Workflow Example
# 1. Start the server (in another terminal)
praisonai serve jobs --port 8005
# 2. Submit a job with recipe
praisonai run submit "Analyze AI news" --recipe news-analyzer --json
# Output: {"job_id": "run_abc123", "status": "queued", ...}
# 3. Check status
praisonai run status run_abc123
# 4. Stream progress
praisonai run stream run_abc123
# 5. Get result when done
praisonai run result run_abc123
# 6. List all jobs
praisonai run listScripting with JSON
#!/bin/bash
API_URL="http://127.0.0.1:8005"
# Submit job
RESULT=$(praisonai run submit "Analyze data" --recipe analyzer --json --api-url $API_URL)
JOB_ID=$(echo $RESULT | jq -r '.job_id')
echo "Submitted job: $JOB_ID"
# Poll for completion
while true; do
STATUS=$(praisonai run status $JOB_ID --json --api-url $API_URL | jq -r '.status')
echo "Status: $STATUS"
if [ "$STATUS" = "succeeded" ] || [ "$STATUS" = "failed" ]; then
break
fi
sleep 5
done
# Get result
praisonai run result $JOB_ID --json --api-url $API_URL