Skip to main content
Every supported framework follows the same pattern: pass a flag to init() (or call the framework subpackage’s instrument() directly for more control), and DecimalAI auto-captures traces, tool calls, and the underlying manifest.
instrument() requires 0.10.2 or newer. On 0.10.0 and earlier the function was called install() — same arguments, same behaviour. It still works after the rename, with a DeprecationWarning, so code you have already written keeps running. The name moved because install had come to mean something else entirely: adding a skill to your workspace. See Vocabulary.decimalai.init(langchain=True) is unaffected on every version.
Integrations come in three tiers. The tier tells you how much DecimalAI captures without extra work — every tier still gets tracing; the deeper tiers add schema-level manifests and the skills rail.

First-class

Native adapters with the deepest capture: full manifests (tool schemas or full config introspection), multi-agent structure, and — where the framework’s architecture allows it — live skill delivery.

LangChain & LangGraph

Callback handler. Chains, agents, LangGraph nodes all auto-traced, with skill injection via instrument(enable_skill_loader=True).

OpenAI Agents SDK

Deepest integration. Full manifest from agent introspection, plus the live load_skill tool.

Claude Agent SDK / Claude Code

Stream-wrapped tracing for query(), disk-installed skills for Claude Code.

Supported

Native adapters with a narrower surface — either tracing-only or skills-only, documented honestly on each page.

Pydantic AI

Skill loader + live load_skill tool via instrument(). Tracing flows through the underlying provider SDK.

Google ADK

Native ADK plugin. One trace per invocation with model, tools, and sub-agent activity.

LlamaIndex

Query engines, retrievers, synthesizers via the LlamaIndex instrumentation dispatcher.

Community / OTel-generic

These route through the generic OpenTelemetry exporter. They keep working, but they aren’t native adapters — manifest capture is limited to what OTel gen_ai.* conventions express.

CrewAI

Crews, tasks, agent conversations via OpenTelemetry.

Haystack & anything else

Any framework emitting OTel gen_ai.* semantic-convention spans, Haystack included.

Capability comparison

“Names only” means DecimalAI knows which tool was called but not its full input schema. If schema-aware manifests matter for your use case (e.g. you want the regression check to flag schema changes), use the explicit register_manifest() form for those frameworks. “Skills rail” is how registry skills reach the model on that adapter — see Skills for the offered → delivered → activated ladder.

Module-level reference

Install each framework’s dependencies with its extra — e.g. pip install "decimalai[langchain]" — so the adapter has the package it hooks into. CrewAI and Haystack ship through the OTel pathway rather than a subpackage of their own: there’s no decimalai.crewai module, and init(crewai=True) is a convenience alias for the OTel exporter.
AutoGen / AG2 is no longer an integration. decimalai.init(autogen=True) and decimalai.autogen.instrument() still run — they install the generic OTel exporter and warn — but nothing instruments AutoGen agents for you. The classic autogen distribution is frozen at 0.14.1 (AG2 moved to ag2 1.x, which no longer provides the autogen API), and Microsoft’s autogen-core/autogen-agentchat is a different framework that was never advertised here. Both land on generic OpenTelemetry, which is where to go next.

No framework at all

Two more rails cover raw provider-SDK usage:
  • Direct provider tracinginit(openai=True), init(anthropic=True), or init(google=True) auto-trace raw openai / anthropic / google.genai SDK calls via OpenInference instrumentors (install the matching openinference-instrumentation-<provider> package, or the flag warns and skips). Don’t combine with a framework flag that already traces the same provider, or calls are captured twice.
  • decimalai.anthropic — not a tracing adapter: it’s the SkillRouter prompt-injection adapter for the raw Anthropic Messages API (instrument(enable_skill_loader=True) patches client.messages.create() to inject skills into system). There is no tool loop in a single messages.create() call, so enable_load_skill_tool is accepted but dormant there.

Mark where a run begins and ends

On the raw rails there is one thing you have to say yourself. A provider instrumentor sees a single SDK call at a time — it has no idea which calls belong to the same run, because there is no run object for it to hook. Left alone, a tool-use loop of two messages.create() calls arrives as two unrelated single-span traces, and a second agent in the same process can’t be told apart from the first. Wrap the run and both problems go away:
One trace, both calls nested under it, filed under support-bot. Concurrent runs stay separate — the boundary is per-context, not global — so agent_run is what makes a threaded or asyncio service traceable at all. Nest it inside your own request handler and every call the run makes lands in the right place. It is also what lets the skills rail land on the trace. The trace it opens is what a routing decision gets attributed to, so without it a raw-provider run still ships — it just carries no routing_id and no offered skill names. The SDK records nothing rather than guess which run a decision belonged to. Two things it deliberately does not do: it adds no input/output of its own (previews still come from the real LLM calls), and it invents no steps — a tool your code ran in-process still emits nothing, because a waterfall should only show what actually happened.
Grouping is only for the raw provider rails. Every framework adapter — LangChain, OpenAI Agents, CrewAI, LlamaIndex, Pydantic AI, ADK, Claude Agent SDK — already knows where its runs start, and wraps them for you. Pydantic AI is the one worth calling out: it does no tracing of its own and rides entirely on the provider instrumentor, so decimalai.pydantic_ai.instrument() opens the run scope on your behalf. You don’t need agent_run there.agent_run does one more thing, and that half is not provider-only: it says whose run this is. LlamaIndex installs a single span handler for the whole process, so if you serve more than one agent from one process, wrap each run in agent_run("...") to give it its own name and its own manifest. Adapters that take an agent name per run — LangChain’s CallbackHandler(agent_name=...), for instance — don’t need it.

What’s next

Tracing

Manual decorators if you need custom span boundaries inside an instrumented framework.

Manifests

Override the auto-detected manifest when needed.