Background Collector
When auto-instrumentation is enabled via init(), LLM calls made outside any WaxellContext or @observe decorator are automatically buffered and flushed to auto-generated runs. This means you get visibility into every LLM call without any code changes beyond init().
How It Works
- An instrumentor intercepts an LLM call (e.g., OpenAI
chat.completions.create) - If no
WaxellContextis active, the call data is pushed to a thread-safe buffer - A daemon thread flushes the buffer every 5 seconds
- On flush, an auto-generated run is created (named
auto:{model}) and the buffered calls are recorded against it
import waxell_observe
waxell_observe.init(api_key="wax_sk_...", api_url="https://acme.waxell.dev")
from openai import OpenAI
client = OpenAI()
# No decorator, no context manager — still captured!
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Hello!"}],
)
# This call is buffered and will appear in the dashboard
# under an auto-generated run named "auto:gpt-4o"
Auto-Generated Runs
Buffered calls are grouped into runs with these properties:
| Field | Value |
|---|---|
agent_name | auto:{model} (e.g., auto:gpt-4o) |
workflow_name | auto |
metadata.auto_collected | true |
metadata.call_count | Number of calls in the batch |
When It's Useful
- Simple scripts — Add
init()and get full observability without decorators or context managers - Gradual adoption — Start with auto-capture, then add
@observedecorators as you identify important agents - Library code — LLM calls deep in third-party libraries are captured automatically
- Background tasks — Cron jobs or workers that make LLM calls without explicit instrumentation
Flush Behavior
- The background thread flushes every 5 seconds
- On
waxell_observe.shutdown(), all remaining calls are flushed immediately - An
atexithandler ensures clean shutdown on process exit - If the Waxell client is not configured, buffered calls are silently dropped
Combining with Explicit Instrumentation
When a WaxellContext is active, LLM calls are attributed to that context's run instead of being sent to the background collector. The background collector only catches calls that would otherwise be untracked.
import waxell_observe
from waxell_observe import WaxellContext
from openai import OpenAI
waxell_observe.init(api_key="wax_sk_...")
client = OpenAI()
# This call goes to the background collector (no active context)
client.chat.completions.create(model="gpt-4o", messages=[...])
# This call is attributed to the explicit context
async with WaxellContext(agent_name="my-agent") as ctx:
client.chat.completions.create(model="gpt-4o", messages=[...])
# This call goes back to the background collector
client.chat.completions.create(model="gpt-4o", messages=[...])
Next Steps
- Auto-Instrumentation -- How
init()sets up auto-capture - Decorator Pattern -- Add structure with
@observe - Context Manager -- Maximum control with
WaxellContext