Production is silent until it screams. At 2am you get the page:Documentation Index
Fetch the complete documentation index at: https://docs.getbindu.com/llms.txt
Use this file to discover all available pages before exploring further.
agents/research is slow. You log in. Which call is slow? Which downstream API? Was it the LLM, the retriever, or the tool? You scroll through unstructured logs hoping to spot the timestamp that explains everything.
You shouldn’t have to do that. Bindu wires up two complementary signals on startup:
- OpenInference — semantic spans for agent frameworks (Agno, CrewAI, LangChain, LlamaIndex, DSPy, Haystack, AutoGen, etc.) and LLM providers (OpenAI, Anthropic, Mistral, Groq, Bedrock, VertexAI, Google GenAI, LiteLLM). Auto-detected at boot. Shipped over OTLP/HTTP to any compatible backend — Phoenix, Arize, Langfuse — or printed to your console if no endpoint is set.
- Sentry — exceptions, 5xx requests, performance transactions, release tagging. Auto-instruments Starlette HTTP endpoints, SQLAlchemy queries, Redis calls, and asyncio tasks. Strips secrets before they leave the process.
Why Agent Observability Is Different
A typical web app trace ends with a SQL query. An agent trace doesn’t.| Concern | Web app | Agent |
|---|---|---|
| Slowest hop is usually… | DB query | LLM call or tool loop |
| Cost per request | ~free | 1+ per turn |
| Determinism | High | None |
| Retries | Idempotent | Side-effects on every call |
| ”What did it do?” | Stack trace | Prompt + response + tool sequence |
Traceable
OpenInference auto-instruments your agent framework and LLM client, attaching prompts,
completions, tool calls, and token usage to each span.
Actionable
Sentry captures unhandled exceptions and 5xx responses with stack trace, release tag,
environment, and request context. Secrets are scrubbed before send.
Portable
OTLP/HTTP spans flow to Phoenix, Arize, Langfuse, or anything that speaks
OpenTelemetry — no vendor lock-in.
How It Works
Instrument
On startup, Bindu calls
observability.setup() (OpenInference + OTel) and
init_sentry(). The tracer provider is always created — even with no endpoint,
spans print to console so local development works without a backend.Detect
setup() walks installed Python distributions, picks the first supported framework
(agent frameworks before raw LLM SDKs to avoid double-instrumentation), and calls
its OpenInference instrumentor.If no agent framework is detected (or the installed version is below the OpenInference
minimum), Bindu logs the missing packages and the suggested install command, then
continues without LLM-level tracing. The tracer provider stays active so any other
OTel-emitting library still works.
OpenInference Setup
Auto-Detected Frameworks
Bindu picks the first match it finds, in this priority order. Agent frameworks come before raw LLM SDKs so you don’t get duplicate spans (e.g. Agno calling OpenAI shouldn’t emit one span from each).| Tier | Frameworks |
|---|---|
| Agent frameworks | agno, crewai, langchain, llama-index, dspy, haystack, instructor, pydantic-ai, autogen, smolagents |
| LLM providers | litellm, openai, anthropic, mistralai, groq, bedrock, vertexai, google-genai |
pyproject.toml is what gets traced.
Supported Backends
Phoenix
Local LLM observability UI. Default Bindu dev target. Run with
docker run -p 6006:6006 arizephoenix/phoenix.Langfuse
Self-hosted or cloud. LLM analytics, evals, and prompt management.
Arize
Production AI observability with drift detection.
Configuration
Bindu’s setup function takes its arguments either programmatically (viabindufy config)
or from environment variables that the config enricher reads. Env vars use the OLTP_
prefix (yes, with the L — it’s the canonical spelling in the Bindu codebase).
OLTP_HEADERS must be valid JSON. The enricher calls json.loads() on it and raises
if it isn’t parseable.setup() function also accepts these — pass them through bindufy config or rely
on the defaults in bindu/observability/openinference.py:
Per-Backend Setup
Phoenix (local dev)
Phoenix (local dev)
Start Phoenix locally:Point Bindu at it:No headers required. Open
http://localhost:6006 to see traces stream in.Langfuse
Langfuse
- Sign up at cloud.langfuse.com (or self-host).
- Settings → API Keys → create a key pair.
-
Base64-encode
<public_key>:<secret_key>: -
Configure env:
/api/public/otel/v1/traces. Bindu’s exporter
will log a hint if the endpoint looks wrong.Arize
Arize
- Sign up at arize.com.
- Settings → API Keys → copy Space ID and API Key.
-
Configure env:
Any OTLP/HTTP backend
Any OTLP/HTTP backend
Bindu uses
OTLPSpanExporter from opentelemetry.exporter.otlp.proto.http. Anything
that accepts OTLP over HTTP (Jaeger, Honeycomb, Tempo, New Relic, Datadog OTLP) will
work — set the endpoint and required headers.Multiple endpoints
Multiple endpoints
setup() accepts a list — useful when you want Phoenix locally and Langfuse in
parallel. Each endpoint gets its own BatchSpanProcessor:What Ends Up In a Trace
A typical agent turn looks roughly like this in Phoenix or Langfuse:Sentry Setup
Sentry handles the operational side — exceptions, 5xx responses, slow transactions. Bindu wires four integrations automatically:- Starlette — every HTTP endpoint under
bindu/server/endpoints/. Failed-request status codes default to 500–511. - SQLAlchemy — query spans when using PostgreSQL storage.
- Redis — Redis scheduler commands.
- Asyncio — task and gather instrumentation so async errors don’t disappear.
Configuration
Sentry uses two env-var shapes: flat (
SENTRY_ENABLED, SENTRY_DSN) for the master
switches read by the config enricher, and nested (SENTRY__*) for fields on
SentrySettings — the double underscore maps to Pydantic’s nested env delimiter.SENTRY_ENABLED=true but SENTRY_DSN is missing, the enricher raises at startup.
Built-in Safety Rails
These are wired inbindu/observability/sentry.py — you get them for free.
- PII scrubbing on every event.
_before_sendstripsauthorization,x-api-key,cookie,x-auth-tokenheaders andpassword,token,secret,api_key,private_keybody keys before the event leaves the process. - Health-check noise filtering.
_before_send_transactiondrops transactions whose name matches/healthz,/health,/metrics,/favicon.ico. - Auto release tagging. If
SENTRY__RELEASEis unset, Bindu falls back tobindu@<version>frombindu._version. - Hostname as
server_name— set viasocket.gethostname()if you don’t override it. - Ignored exceptions.
KeyboardInterruptandSystemExitare never reported.
Enabling
Create a Sentry project
sentry.io → New Project → Python → copy the DSN.
Adding Custom Spans and Context
OpenInference auto-instruments the framework. Anything outside the framework — your own preprocessing, post-processing, business logic — needs explicit spans:Agent Configuration
No code changes are required — observability reads env vars on startup.Production Tips
Sample traces, not errors
Separate environments
No backend? You still get traces
IfOLTP_ENDPOINT is unset, Bindu falls back to ConsoleSpanExporter — every span
pretty-prints to stdout. Great for local debugging, terrible for production. Set an
endpoint before you ship.
When Bindu can’t reach the configured OTLP endpoint, the wrapped exporter logs a
one-time hint specific to the URL pattern (e.g. “Langfuse requires endpoint:
<base-url>/api/public/otel/v1/traces”). Check logs first when traces don’t show up.
Related
- Health Check & Metrics
- OpenInference (GitHub)
- OpenInference Semantic Conventions
- Phoenix Documentation
- Langfuse Documentation
- Sentry Python SDK
- OpenTelemetry Python