Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.bentolabs.ai/llms.txt

Use this file to discover all available pages before exploring further.

Nothing shows up in the dashboard

Walk this list top to bottom. The fix is almost always one of these five.
The exporter batches spans. On os._exit(), a Lambda timeout, or a script ending right after track_ai, the daemon worker is killed before its next 5-second tick and the queue is lost.
import bentolabs_sdk.analytics as bento
bento.track_ai(...)
bento.flush()  # or bento.shutdown() which flushes too
Long-running services don’t need this. atexit runs the shutdown automatically.
Setting it in ~/.zshrc doesn’t help if your IDE or CI launched the process from a different env. Verify inside the process:
import os
print(os.environ.get("BENTOLABS_API_KEY"))
The key must start with bl_pk_. The SDK raises BentoAuthError("invalid_api_key_format") on construction otherwise.
Defaults to https://api.bentolabs.ai. If you set this for local dev and forgot to unset it in production, your spans go nowhere.
from bentolabs_sdk import resolve_options
print(resolve_options().base_url)
import threading, bentolabs_sdk.analytics as bento
bento.init()
print([t.name for t in threading.enumerate()])
# Should include: 'OtelBatchSpanRecordProcessor'
If you don’t see the worker thread, init() failed silently (suppressed exception in user code) or you’re calling SDK functions before init resolved.
The queue holds 2048 spans. Past that, drops are logged at WARNING level:
Queue full, dropping span.
Enable Python logging to see it:
import logging
logging.basicConfig(level=logging.WARNING)
If you’re hitting this, you’re emitting faster than network egress. Reduce volume or talk to us about higher-throughput tiers.

Fields look wrong in the dashboard

There is no auto-inference from model name. Pass provider="anthropic" (or openai, google, aws_bedrock, etc.) on every track_ai call. See Attributes.
You’re not passing convo_id. Add the same convo_id to every turn in a conversation, including assistant messages and tool calls.
Either user_id is missing, or you’re passing it as a property (properties={"user_id": ...}). It must be the top-level user_id= kwarg so it lands on gen_ai.user.id.
Bedrock model IDs (anthropic.claude-3-sonnet-20240229-v1:0) look like Anthropic to us but route through AWS. Pass provider="aws_bedrock" explicitly.
Properties that are int, float, bool, or str keep their type. Dicts and mixed lists fall back to JSON-string. See Properties → Type fidelity.

Spans nest weirdly

Check task context. The trajectory’s parent context lives in a ContextVar, which is per-thread for sync code and per-task for asyncio. Calling track_ai from a new thread or a concurrent.futures worker that didn’t inherit your context will produce a root span.See threading model for the full rules.
It doesn’t. track_ai and begin both detach from the caller’s OTel context on purpose, so your customer’s FastAPI/Django instrumentation isn’t pulled into our trace. If you want the Bento span to be a child of a parent OTel span, set its traceparent explicitly via the lower-level OTel transport.
A nested bento.begin() is still open. Trajectories must be finished LIFO. Use with bento.begin(...) as i: to guarantee correct nesting; the context manager handles cleanup on exception too.

Local development

# Point at your local backend
import bentolabs_sdk.analytics as bento

bento.init(api_key="bl_pk_dev_...", base_url="http://localhost:8080")
bento.track_ai(event="local_test", input="hi", output="ok", user_id="me")
bento.flush()
Or via env vars:
export BENTOLABS_API_KEY="bl_pk_dev_..."
export BENTOLABS_BASE_URL="http://localhost:8080"
Only http:// and https:// schemes are accepted. A typo like BENTOLABS_BASE_URL=localhost:8080 (no scheme) raises ValueError on resolution.

Still stuck

Email support@bentolabs.ai with:
  1. SDK version: python -c "import bentolabs_sdk; print(bentolabs_sdk.SDK_VERSION)"
  2. The track_ai call you’re making (redact secrets)
  3. The output of print([t.name for t in threading.enumerate()]) after init
  4. Anything you see at WARNING-level Python logs