# @posthog/openclaw [![npm version](https://img.shields.io/npm/v/@posthog/openclaw)](https://www.npmjs.com/package/@posthog/openclaw) [![CI](https://github.com/PostHog/posthog-openclaw/actions/workflows/ci.yml/badge.svg)](https://github.com/PostHog/posthog-openclaw/actions/workflows/ci.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) PostHog LLM Analytics plugin for [OpenClaw](https://github.com/openclaw/openclaw). Captures LLM generations, tool executions, and conversation traces, sending them to PostHog as structured `$ai_*` events for the [LLM Analytics dashboard](https://posthog.com/docs/llm-analytics). https://github.com/user-attachments/assets/bf7ce150-becb-4df9-b78d-5660d0a3e489 ## Install ```bash openclaw plugins install @posthog/openclaw ``` ## Configuration Add to your `openclaw.json` (or `openclaw.yaml`): ```jsonc { "plugins": { "entries": { "posthog": { "enabled": true, "config": { "apiKey": "phc_your_project_key", "host": "https://us.i.posthog.com", "privacyMode": false, "traceGrouping": "message", "sessionWindowMinutes": 60, }, }, }, }, "diagnostics": { "enabled": true, }, } ``` ### Options | Option | Type | Default | Description | | ---------------------- | -------------------------- | -------------------------- | --------------------------------------------------------------------------------------------------------------------- | | `apiKey` | `string` | _(required\*)_ | Your PostHog project API key. Can also be provided via the `POSTHOG_API_KEY` environment variable | | `host` | `string` | `https://us.i.posthog.com` | PostHog instance URL. Can also be provided via the `POSTHOG_HOST` environment variable | | `privacyMode` | `boolean` | `false` | When enabled, LLM input/output content is not sent to PostHog | | `traceGrouping` | `"message"` \| `"session"` | `"message"` | Trace grouping mode. `"message"`: one trace per runId. `"session"`: group all generations in a session into one trace | | `sessionWindowMinutes` | `number` | `60` | Minutes of inactivity before starting a new session window. Applies to both trace grouping modes | | `enabled` | `boolean` | `true` | Enable or disable the plugin | > \* Required unless provided via the `POSTHOG_API_KEY` environment variable. The config value takes precedence when both are set. > **Note:** `diagnostics.enabled` must be `true` in your OpenClaw config for trace-level events (`$ai_trace`) to be captured. ## What gets captured ### `$ai_generation` Captured on every LLM call (correlated `llm_input` + `llm_output` hooks). | Property | Description | | ----------------------------- | ---------------------------------------------------------------- | | `$ai_model` | Model name (e.g. `gpt-4o`, `claude-3`) | | `$ai_provider` | Provider name (e.g. `openai`, `anthropic`) | | `$ai_latency` | Request duration in seconds | | `$ai_input_tokens` | Input token count | | `$ai_output_tokens` | Output token count | | `$ai_total_cost_usd` | Total cost in USD | | `$ai_input_cost_usd` | Input cost in USD | | `$ai_output_cost_usd` | Output cost in USD | | `$ai_stop_reason` | Why generation stopped (`stop`, `length`, `tool_calls`, `error`) | | `$ai_is_error` | Whether the generation errored | | `$ai_error` | Error message (if any) | | `$ai_input` | Input messages in OpenAI format (redacted in privacy mode) | | `$ai_output_choices` | Output choices (redacted in privacy mode) | | `$ai_trace_id` | Trace ID for hierarchical grouping | | `$ai_span_id` | Span ID for this generation | | `$ai_session_id` | Session identifier | | `$ai_channel` | Message channel (e.g. `telegram`, `slack`) | | `$ai_agent_id` | Agent identifier | | `cache_read_input_tokens` | Cache read token count | | `cache_creation_input_tokens` | Cache creation token count | ### `$ai_span` Captured for each tool call (`after_tool_call` hook). | Property | Description | | ------------------ | ------------------------------------------------ | | `$ai_span_name` | Tool name | | `$ai_latency` | Tool execution duration in seconds | | `$ai_is_error` | Whether the tool call errored | | `$ai_error` | Error message (if any) | | `$ai_input_state` | Tool input parameters (redacted in privacy mode) | | `$ai_output_state` | Tool output result (redacted in privacy mode) | | `$ai_trace_id` | Trace ID | | `$ai_span_id` | Span ID for this tool call | | `$ai_parent_id` | Parent generation span ID | ### `$ai_trace` Captured when a message cycle completes (`message.processed` diagnostic event). | Property | Description | | ------------------------- | ------------------------------------------------------------- | | `$ai_trace_id` | Trace ID | | `$ai_session_id` | Session identifier | | `$ai_latency` | Total message cycle duration in seconds | | `$ai_total_input_tokens` | Accumulated input tokens across all generations in the trace | | `$ai_total_output_tokens` | Accumulated output tokens across all generations in the trace | | `$ai_is_error` | Whether the message cycle errored | | `$ai_error` | Error message (if any) | | `$ai_channel` | Message channel | ## Privacy With `privacyMode: true`, no message content, prompts, or tool parameters are sent to PostHog. Token counts, latency, model info, and error status are always captured. ## Development ```bash pnpm install pnpm test pnpm typecheck pnpm lint ``` ### Local testing with OpenClaw To test the plugin locally against a running OpenClaw gateway, use `plugins.load.paths` to point OpenClaw at your local checkout: ```jsonc // ~/.openclaw/openclaw.json { "plugins": { "load": { "paths": ["/path/to/posthog-openclaw"], }, "entries": { "posthog": { "enabled": true, "config": { "apiKey": "phc_your_project_key", "host": "https://us.i.posthog.com", "privacyMode": false, }, }, }, }, "diagnostics": { "enabled": true }, } ``` Then start the gateway: ```bash cd /path/to/openclaw pnpm install && pnpm build node openclaw.mjs gateway --force --allow-unconfigured ``` Open WebChat at http://127.0.0.1:18789/__openclaw__/canvas/ and send a message. Events will appear in your PostHog project under [LLM Analytics](https://us.posthog.com/llm-analytics/traces). > **Note:** The config entry key must be `"posthog"` (matching the `id` field in `openclaw.plugin.json`), not the npm package name. ## License MIT