By Markus van Kempen |
Series: WXO Labs & Tutorial Guide (Part 5 of 15)
Interactive Lab Portal: https://markusvankempen.github.io/wxo-labs/
GitHub Repository: https://github.com/markusvankempen/wxo-labs
HTTP requests to watsonx Orchestrate tools will timeout if an operation takes longer than 30–60 seconds (such as training an ML model or aggregating 100,000 DB rows). In this lab, we build a non-blocking asynchronous pattern: start the background job, return an immediate Job ID to the user, and allow conversational polling for status.
Ensure your environment has the required dependencies:
pip install ibm-watsonx-orchestrate
Verify active environment:
orchestrate env list
async_tools.py)from ibm_watsonx_orchestrate import tool
import threading
import uuid
import time
from datetime import datetime
# In-memory job registry (use Redis/Postgres in production clusters)
JOBS = {}
@tool
def start_background_job(job_name: str, duration_seconds: int = 15) -> str:
"""
Start a long-running background task asynchronously without blocking the chat.
"""
job_id = str(uuid.uuid4())[:8]
JOBS[job_id] = {
'id': job_id,
'name': job_name,
'status': 'RUNNING',
'started_at': datetime.now().strftime("%H:%M:%S"),
'progress': '0%'
}
def worker():
for pct in [25, 50, 75, 100]:
time.sleep(duration_seconds / 4)
JOBS[job_id]['progress'] = f"{pct}%"
JOBS[job_id]['status'] = 'COMPLETED'
JOBS[job_id]['completed_at'] = datetime.now().strftime("%H:%M:%S")
JOBS[job_id]['result'] = f"Success: Dataset '{job_name}' processed (14,200 rows indexed)."
thread = threading.Thread(target=worker, daemon=True)
thread.start()
return f"🚀 Background job '{job_name}' dispatched! Job ID: {job_id}. You can continue chatting while it runs."
@tool
def check_job_status(job_id: str) -> str:
"""Check the status and output of a running background job."""
job = JOBS.get(job_id)
if not job:
return f"❌ Job ID '{job_id}' not found."
if job['status'] == 'COMPLETED':
return f"✅ Job {job_id} [{job['name']}] COMPLETED at {job['completed_at']}: {job['result']}"
else:
return f"⏳ Job {job_id} [{job['name']}] is STILL RUNNING (Progress: {job['progress']}). Started at {job['started_at']}."
requirements.txt)ibm-watsonx-orchestrate>=2.9.0
agent.yaml)spec_version: v1
kind: native
name: async_job_manager
title: "Async Job Dispatcher"
description: "Dispatches and monitors asynchronous long-running batch operations."
model: ibm/granite-3-8b-instruct
instructions: |
You are a background operations assistant. When a user asks to run an extensive task,
dispatch it using `start_background_job` and give them their Job ID.
When they query status, use `check_job_status`.
tools:
- start_background_job
- check_job_status
Import tool and test conversationally:
orchestrate chat ask -n async_job_manager "Start data aggregation for Q3 Sales"
Live Response:
╭─ 🤖 async_job_manager ─────────────────────────────────────────────────╮
│ │
│ 🚀 Background job 'Q3 Sales' dispatched! Job ID: 4f8a19de. │
│ Feel free to ask other questions while the pipeline executes! │
╰────────────────────────────────────────────────────────────────────────╯
When publishing this blog post, select or enter the following tags in the community editor:
watsonx Orchestrate, Async Execution, Background Jobs, Non-Blocking, Worker Threads, Polling Pattern
This tutorial is Part 5 of 15 in the complete enterprise hands-on series:
Explore the interactive curriculum and code repository: