Async Jobs provide a server-based approach to running recipes and agents. Jobs are submitted to an API server, persisted, and can be monitored, streamed, and cancelled. Ideal for production deployments with multiple clients.
JobHandle is a long-lived handle: one wait() may poll the jobs API dozens of times. It holds a lazily-constructed shared httpx.Client, so those polls share one keep-alive connection instead of doing a fresh TLS handshake per call. Use the handle as a context manager — or call close() — to release the connection pool deterministically:
from praisonai import recipewith recipe.submit_job("my-recipe", input="test") as job: result = job.wait(poll_interval=5, timeout=300) print(result)# `job.client` is closed here — pooled sockets are released.
Not using with? Call close() explicitly:
from praisonai import recipejob = recipe.submit_job("my-recipe", input="test")try: result = job.wait(poll_interval=5, timeout=300)finally: job.close()
A 10-minute wait(poll_interval=5) uses one keep-alive HTTP connection for all ~120 polls, not one fresh TCP+TLS handshake per poll. This applies to get_status(), get_result(), cancel(), and every iteration of wait().
recipe_name and recipe_config are honoured by the router as of PR #4085; earlier versions accepted the fields but did not forward them to the executor.If your code creates its own client to hit the Jobs API directly (rather than using submit_job), keep the client alive across calls — the wrapper’s own JobHandle does exactly this.
from praisonai.jobs import Job, JobSubmitRequest, JobExecutor, create_app# Create job requestrequest = JobSubmitRequest( prompt="Analyze this data", recipe_name="data-analyzer", recipe_config={"format": "json"}, timeout=3600, webhook_url="https://example.com/callback", session_id="session_123",)# Submit via API clientimport httpxwith httpx.Client() as client: response = client.post( "http://127.0.0.1:8005/api/v1/runs", json=request.model_dump(), ) job_data = response.json() print(f"Job ID: {job_data['job_id']}")
Prevent duplicate job submissions with idempotency keys:
# Same key = same job (no duplicate)job1 = recipe.submit_job("my-recipe", idempotency_key="order-123")job2 = recipe.submit_job("my-recipe", idempotency_key="order-123")assert job1.job_id == job2.job_id # Same job returned