Langfuse Bridge

DiagnosticsLangfuseObservabilityTracing

Forward OpenClaw model usage diagnostics to Langfuse

Install

openclaw plugins install clawhub:openclaw-x-langfuse-plugin

openclaw-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 fieldEnvironment fallbackDefault
publicKeyLANGFUSE_PUBLIC_KEY
secretKeyLANGFUSE_SECRET_KEY
baseUrlLANGFUSE_BASE_URLhttps://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 sessionId set to the OpenClaw session id (so a session's calls coalesce into one trace), named after the channel.
  • Generationmodel, 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.usage is emitted on OpenClaw's reply/delivery path (channel messages, webchat/TUI turns) — not on direct openclaw agent CLI runs, which use the embedded runner and don't emit it.

License

MIT