Langfuse integration
Already instrumented for Langfuse? You don't have to choose. Langfuse's modern SDKs are built on OpenTelemetry, and AuditTrail ingests OTLP natively — so the same instrumentation can ship spans to both: Langfuse for its scores and prompt management, AuditTrail for the causal WHY (ablation → SHAP → counterfactuals), constitutional governance and the operations control plane.
There is no bespoke Langfuse plugin, and that's deliberate: the honest contract is OTel. Anything Langfuse-instrumented that can emit OTLP/JSON works.
Dual-shipping via OTel
Add AuditTrail as a second OTLP destination alongside your existing Langfuse setup. With the Python OTel SDK that's one extra span processor — pointed at a local OpenTelemetry Collector, because Python's OTLP exporters emit protobuf and AuditTrail ingests JSON only (the Collector re-encodes; its 8-line config is on the OpenTelemetry page):
from opentelemetry import trace
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import (
OTLPSpanExporter,
)
# Protobuf to the local Collector; the Collector forwards OTLP/JSON to
# AuditTrail (otlphttp exporter with `encoding: json` + your API key).
audittrail_exporter = OTLPSpanExporter(
endpoint="http://localhost:4318/v1/traces",
)
provider = trace.get_tracer_provider()
provider.add_span_processor(BatchSpanProcessor(audittrail_exporter))AuditTrail's ingest endpoint returns a clear
415for protobuf bodies. JS/Node SDKs can skip the Collector entirely — they implementOTEL_EXPORTER_OTLP_PROTOCOL=http/jsonnatively and can point straight at/api/v1/ingest/otlp. The Python SDK does not implementhttp/json(grpc + http/protobuf only), which is why the Python recipe above bridges through a Collector.
Migrating instead of dual-shipping
Remove the Langfuse exporter and ship only to AuditTrail — your instrumentation code doesn't change.
JS / Node (direct):
export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="https://your-audittrail-host/api/v1/ingest/otlp"
export OTEL_EXPORTER_OTLP_PROTOCOL="http/json"
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer sk-at-..."Python (via the Collector from the OpenTelemetry page):
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318"What maps to what
Langfuse's generations/spans arrive as OTel GenAI spans; AuditTrail's
mapper reads gen_ai.* attributes for span type, model, token usage and
prompt/completion content — the full table is on the
OpenTelemetry page. Once ingested they are
first-class spans: YAML rules evaluate at ingest, the live DAG streams,
and the XAI pipeline can run over the tool calls.
Gotchas
- Sampling: if your Langfuse setup samples traces, AuditTrail sees the same sample — attach the AuditTrail processor before samplers you don't want applied to it.
- Model names must arrive in
gen_ai.request.model/gen_ai.response.modelfor cost accounting; Langfuse-native attributes that aren't GenAI-convention are preserved underspan.attributesbut don't drive pricing.