LangChain integration
AuditTrail traces LangChain apps through per-event callback handlers: every LLM call, tool invocation and chain becomes a span in the decision DAG, with token/cost accounting and constitutional rule evaluation.
There are three honest paths, depending on your language and where your agent runs relative to the AuditTrail server. Pick the row that matches:
| Your setup | Use |
|---|---|
| LangChain-JS / LangGraph-JS, any deployment | the TypeScript callback handler (below) — streams spans over HTTP |
| Python + LangGraph | run_langgraph — one call emits the whole graph over HTTP |
| Python LangChain, agent on a different machine than AuditTrail | the OpenTelemetry path via OpenLLMetry — zero AuditTrail SDK |
| Python LangChain, agent co-hosted with a self-hosted AuditTrail | the embedded Python handler (below) |
TypeScript / LangChain-JS
The TS SDK ships a duck-typed BaseCallbackHandler that carries
LangChain's runId/parentRunId chain into the AuditTrail span tree and
batch-flushes over HTTP:
import { ChatOpenAI } from "@langchain/openai";
import { AuditTrailClient, AuditTrailCallbackHandler } from "@audittrail/sdk";
const audit = new AuditTrailClient({
baseUrl: "https://your-audittrail-host",
apiKey: process.env.AUDITTRAIL_API_KEY, // sk-at-... from /profile → API Keys
});
const model = new ChatOpenAI({
callbacks: [new AuditTrailCallbackHandler(audit)],
});That's the whole integration — chains, agents, tools and retrievers all
emit spans through the handler. The SDK has no runtime dependency on
@langchain/core; it's shape-compatible with whatever version you run.
(The SDK builds from packages/sdk-typescript in the repo — registry
publishing is tracked for launch.)
Python, embedded (self-host)
The Python AuditTrailCallbackHandler is part of the in-process
tracer: it emits into the same buffer as the @traceable decorator,
which flushes directly to the AuditTrail database. That makes it the
right tool when your agent runs inside your self-hosted AuditTrail
environment (same process or same deployment with DB access) — and the
wrong tool for a remote agent, where nothing would carry the spans across
the network.
cd apps/api && pip install -e ".[agent]" # installs langchain-core alongsidefrom audittrail.callback_handler import AuditTrailCallbackHandler
from audittrail.traceable import traceable
@traceable(span_type="agent", name="handle_ticket")
async def handle_ticket(text: str) -> str:
return await chain.ainvoke(
{"input": text},
config={"callbacks": [AuditTrailCallbackHandler()]},
)The handler reuses the ambient trace started by @traceable, so all
LangChain activity lands under one root span; pass
AuditTrailCallbackHandler(trace_id=...) to pin it explicitly.
Python, remote
For a Python LangChain agent talking to a remote AuditTrail server:
- On LangGraph? Use the HTTP client's
run_langgraphhelper — it emits the full span tree with one call. - Plain chains? Instrument with OpenLLMetry → OTLP ingest (no AuditTrail code at all), or emit spans yourself against the REST ingest API — the Quick Start's Agent Integration Guide walks the span contract.
Gotchas
- The Python handler is async (
AsyncCallbackHandler) — useainvoke/astream; syncinvokepaths won't fire it. - Importing
audittrail.callback_handlerwithoutlangchain-coreinstalled raises a clearImportErrorpointing at the[agent]extra — it never fails silently at runtime. - Cost estimates come from the model-pricing catalog (~3k known models,
operator-overridable via
rules/pricing.yaml); unknown models fall back to the default rate.