Skip to main content

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

  1. An instrumentor intercepts an LLM call (e.g., OpenAI chat.completions.create)
  2. If no WaxellContext is active, the call data is pushed to a thread-safe buffer
  3. A daemon thread flushes the buffer every 5 seconds
  4. 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:

FieldValue
agent_nameauto:{model} (e.g., auto:gpt-4o)
workflow_nameauto
metadata.auto_collectedtrue
metadata.call_countNumber 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 @observe decorators 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 atexit handler 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