How It Works
The user submits a job, the server runs it in the background, and the result comes back on completion.Choose a Result Mode
Pick how the result is delivered once the job finishes.Quick Start
1
Submit via recipe helper
2
Submit via HTTP API
Persistent Store
Point the Jobs API at a SQLite file so job state and idempotency keys survive restarts — no code change, just one env var.SqliteJobStore and create_app are both exported from praisonai.jobs. _build_default_store() in praisonai/jobs/server.py selects the backend from the environment:
Which store should I use?
Atomic Idempotency
JobStore exposes save_if_absent(job) so a duplicate submit resolves to a single job and side effects run exactly once.
- The default
InMemoryJobStoreimplementation is a best-effort check-then-save: it looks up the idempotency key, then inserts if absent. SqliteJobStoreoverridessave_if_absent()to be truly atomic via aUNIQUEindex onidempotency_key. Two concurrent submits with the same key race on theINSERT; the loser catchesIntegrityErrorand returns the winning job, so both callers receive the samejob_id.- NULL idempotency keys are exempt from SQLite’s
UNIQUEconstraint, so keyless jobs are never de-duplicated against each other.
Restart Recovery
OnSqliteJobStore startup, _reconcile_interrupted_jobs() marks any row still in QUEUED or RUNNING as FAILED, with error="Interrupted by service restart" and completed_at=now.
A crashed executor leaves no worker to resume its in-flight jobs, so those rows would otherwise poll forever and pin an idempotency key to a job that can never complete. Terminal reconciliation gives callers — and idempotent retries — a definitive outcome.
Start Server
Submit Job
Submit a Recipe Job
Point the same endpoint at an installed recipe by addingrecipe_name (and, optionally, recipe_config). The prompt field becomes the recipe’s input data.
Idempotency
Since PR #1673, the in-process store is safe to read concurrently with writes. You can safely share a single
InMemoryJobStore instance between the FastAPI app and background tasks that periodically read stats.Polling
SSE Streaming
Webhook Callback
Session Grouping
Cancel Job
List Jobs
Complete Example
CLI Usage
Best Practices
Use idempotency keys for retries
Use idempotency keys for retries
Pass
Idempotency-Key (HTTP) or idempotency_key= (recipe helper) so duplicate submits return the same job instead of duplicating work.Prefer webhooks for long jobs
Prefer webhooks for long jobs
For runs over a few minutes, set
webhook_url and let your service react to completion instead of holding an open poll loop.Start the jobs server before integration tests
Start the jobs server before integration tests
python -m uvicorn praisonai.jobs.server:create_app --port 8005 --factory — the in-process store is safe for concurrent reads after PR #1673.Set PRAISONAI_JOBS_DB_PATH in production
Set PRAISONAI_JOBS_DB_PATH in production
Export
PRAISONAI_JOBS_DB_PATH=/var/lib/praisonai/jobs.db so SqliteJobStore persists jobs and idempotency keys across restarts. With ENVIRONMENT=production and no path set, create_app() raises RuntimeError rather than silently losing state.Related
Background Tasks
Run agent work in-process without a separate jobs server.
Async Jobs CLI
Submit, stream, and cancel jobs from the terminal.
Durable Tool Runs
Persist and resume tool executions across restarts.

