Langfuse Bridge
Forward OpenClaw model usage diagnostics to Langfuse
Install
openclaw plugins install clawhub:openclaw-x-langfuse-pluginopenclaw-x-langfuse-plugin
Forward OpenClaw model-usage diagnostics to Langfuse.
The plugin registers a background service that subscribes to OpenClaw's internal
diagnostics bus and translates model.usage events into Langfuse generations
(token usage + cost), grouped into a trace per OpenClaw session. model.call.error
events are forwarded as Langfuse error events.
It subscribes via the public onInternalDiagnosticEvent SDK export. (The
ctx.internalDiagnostics.onEvent capability is privileged — the runtime injects
it only for the bundled diagnostics-otel/diagnostics-prometheus services, so
third-party plugins never receive it.) The public listener delivers the same
model.usage event bodies this bridge maps.
Install
openclaw plugins install openclaw-x-langfuse-plugin
OpenClaw resolves through ClawHub first and falls back to npm. For local
development, install from a path with --link:
openclaw plugins install ./openclaw-x-langfuse-plugin --link
Configure
Enable the plugin and provide Langfuse credentials in openclaw.json:
{
"plugins": {
"allow": ["langfuse-bridge"],
"entries": {
"langfuse-bridge": {
"enabled": true,
"config": {
"publicKey": "pk-lf-...",
"secretKey": "sk-lf-...",
"baseUrl": "https://cloud.langfuse.com"
}
}
}
}
}
Credentials may also be supplied via environment variables, which take effect when the corresponding config field is absent:
| Config field | Environment fallback | Default |
|---|---|---|
publicKey | LANGFUSE_PUBLIC_KEY | — |
secretKey | LANGFUSE_SECRET_KEY | — |
baseUrl | LANGFUSE_BASE_URL | https://cloud.langfuse.com |
Then restart the gateway:
openclaw gateway restart
If publicKey/secretKey are missing, the service logs a warning and does not
start — it never blocks the gateway.
What gets sent
For each model.usage diagnostic event:
- Trace — id and
sessionIdset to the OpenClaw session id (so a session's calls coalesce into one trace), named after the channel. - Generation —
model,usageDetails(input,output,cache_read,cache_write,total),costDetails.total(USD), start/end time from the event timestamp and duration, plus provider/channel/agent metadata. - Generation input/output — the turn's prompt and response text. OpenClaw
does not deliver message content to third-party plugins, so this is recovered
best-effort from the per-session trajectory transcript
(
<stateDir>/agents/<agentId>/sessions/<sessionId>.trajectory.jsonl). If the transcript is unavailable, usage/cost are still forwarded with empty input/output.
How it works
import { onInternalDiagnosticEvent } from "openclaw/plugin-sdk/diagnostic-runtime";
api.registerService({
id: "langfuse-bridge",
start(ctx) {
const unsubscribe = onInternalDiagnosticEvent((evt) => {
if (evt.type === "model.usage") {
// -> langfuse trace.generation({ model, usageDetails, costDetails })
}
});
},
});
Note:
model.usageis emitted on OpenClaw's reply/delivery path (channel messages, webchat/TUI turns) — not on directopenclaw agentCLI runs, which use the embedded runner and don't emit it.
License
MIT
