Python SDK
ML training loops, Rake-style scripts, AI agents.
The Python SDK is the primary integration path for long-running Python work - model training loops, scraping pipelines, batch jobs, AI agents. The `@track` decorator is one line above any function and gives you a Live Activity that starts when the function is entered, updates as you call `update()`, and ends green or red depending on whether the function returned or threw.
Two flavors are exported. `@track` is the simple decorator; `ChirpAgent` is a class for finer control - manual lifecycle, multiple updates, custom dismissal policy. Both rely on the same lightweight HTTP client (stdlib `urllib`, no third-party deps).
Prerequisites
- Python ≥ 3.9.
- Chirp installed on your phone, signed in.
Setup
- 1
Install + authenticate
chirp loginis a one-time pairing - opens a browser, drops a token at~/.chirprc. The SDK auto-discovers it. CI environments useCHIRP_API_KEYinstead.shellpip install chirp-sdk chirp login - 2
Wrap a function with `@track`
The simplest pattern. Decorate any function and Chirp owns its lifecycle: starts the activity on call, updates every time you log progress, ends green on return or red on exception. Works for sync and async functions.
train.pyfrom chirp import track, update @track("model training", theme="#818cf8") def train(): for epoch in range(10): train_one_epoch() # Update fields as you go - only the keys you pass are touched. update(progress=(epoch + 1) / 10, status=f"Epoch {epoch + 1}/10") train() # phone shows the Live Activity for the entire call - 3
Use `ChirpAgent` for fine-grained control
When the function shape doesn't fit a decorator (e.g. you want to keep an activity alive across multiple top-level entry points, or branch the lifecycle yourself), open a context manager.
pythonfrom chirp import ChirpAgent with ChirpAgent("data backfill", schema="@progress") as a: a.update(progress=0.1, metaLeft="reading source") rows = read_source() a.update(progress=0.5, metaLeft=f"{len(rows)} rows fetched") write_destination(rows) a.update(progress=1.0, metaLeft="done") # Auto-ends green on context exit, red if an exception escaped. - 4
Pick a schema that matches your workload
The default schema is
@progress(a generic 0-100% bar). If your job has a dollar cost (LLM inference), use@openrouter-spend. If it's a long GPU run, use@gpu-job. Browse the catalog at/templatesfor the full list. Passschema="@<id>"to switch.
Async / asyncio
from chirp import track
@track("scrape", schema="@scrape-deep")
async def crawl(start_url):
async for page in crawler(start_url):
await update(pages=page.index, queue=page.queue_size)
import asyncio
asyncio.run(crawl("https://docs.example.com"))What you’ll see
When `train()` is called, an @progress activity starts on your phone with the title "model training" and progress 0%. Each `update()` call refreshes the bar. When the function returns, the activity ends with a green checkmark; if it raises, the activity ends red with the exception type as the final status. Multiple concurrent decorated functions get their own activities.
Troubleshooting
- I get `chirp.errors.NoCredentialsError: ~/.chirprc not found`.
- Run
chirp loginonce on the machine. In CI environments where you can't run that, setCHIRP_API_KEYinstead - the SDK reads it as a fallback. - My activity ends red even though the function completed successfully.
- Check the function isn't catching an exception silently. The decorator inspects what the wrapped function actually returned vs. threw - if you
try/exceptinside, the SDK sees a clean return. - The lock screen card shows generic placeholders instead of my data.
- Make sure the keys you pass to
update()match the schema's bindings.@progressacceptsprogress(0-1),metaLeft,metaRight,trailing. Other schemas have different keys - see/templates.