Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "langfuse-observability",
"description": "The Langfuse x Claude Code Observability Plugin",
"version": "1.2.1",
"version": "1.2.2",
"author": {
"name": "Langfuse"
},
Expand Down Expand Up @@ -67,6 +67,12 @@
"description": "Send the conversation so far as each LLM call's input. On long sessions this grows quadratically and span batches can exceed the exporter's 64 MiB limit, which drops them. Set to false to send each call's own new input only.",
"default": true
},
"CC_LANGFUSE_DETERMINISTIC_IDS": {
"type": "boolean",
"title": "Deterministic observation ids",
"description": "Derive each observation id from its trace id and a stable key (message id, tool_use id, agent id). Needed to continue a background agent that resumed after an interim result: its later work attaches to its span id across firings. Set to false for random ids; agents then ship once, as they are on disk at their first result.",
"default": true
},
"CC_LANGFUSE_STATE_DIR": {
"type": "string",
"title": "State directory",
Expand Down
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,43 @@ claude plugin update langfuse-observability@langfuse-observability
Restart Claude Code afterwards. `claude plugin list` shows the installed
version.

## Backfilling a past session

The hook can re-upload a finished session from its transcript, for example
to repair traces a version with a known gap exported incompletely. Run it
only after the session has ended.

```bash
uv run --script hooks/langfuse_hook.py \
--backfill ~/.claude/projects/<project>/<session-id>.jsonl --dry-run
```

`--dry-run` builds every observation, sends nothing, and prints the counts
and token totals that would be exported. Without it, the same Langfuse keys as
the hook are read from the environment.

- The re-uploaded turns go to traces of their own (`--namespace`, default
`backfill-v1`). Langfuse v4 does not merge two uploads of one observation
id, so a namespace that already holds traces of the session is refused;
supersede an earlier backfill with a new namespace.
- After the upload, the backfill reads the session back from Langfuse until
every observation it sent is visible (up to 10 minutes). If any is
missing it exits with code 4 and deletes nothing: a rejected upload is
only logged by the exporter, never raised. `--verify-minutes` changes the
window.
- After exit 4: if Langfuse is only slow to ingest, run again with
`--verify-only` and the same `--namespace`. It uploads nothing (the ids
are deterministic), re-reads, and with `--replace-session-traces`
deletes the old traces once everything is visible. If the upload really
failed, start over with a new `--namespace` and
`--replace-session-traces`; that also removes the partial upload.
- `--replace-session-traces` then deletes the session's traces this hook
named `Claude Code Turn` that are not the backfill's own. Traces under
another name are never touched. With `--dry-run` it lists them instead.
- The session id must be the transcript's own (file name and rows), and a
session with any file under its directory (agents, workflows) modified in
the last 30 minutes is refused unless `--force` is passed.

## Troubleshooting

The hook explains nearly every failure in
Expand Down
Loading
Loading