Skip to content

Read Langfuse observations with Basic auth and the v2 endpoint - #787

Open
volodymyrshk wants to merge 1 commit into
Metabuilder-Labs:mainfrom
volodymyrshk:fix/langfuse-live-backfill-v2-basic-auth
Open

volodymyrshk wants to merge 1 commit into
Metabuilder-Labs:mainfrom
volodymyrshk:fix/langfuse-live-backfill-v2-basic-auth

Conversation

@volodymyrshk

Copy link
Copy Markdown

Summary

tj backfill langfuse --source-url reads observations with a Bearer public key from the old /api/public/observations endpoint. This PR makes the live mode use the project key pair with Basic auth and the Observations API v2.

Two things change:

  • Auth. In Langfuse's apiAuth.ts, a Bearer public key gets accessLevel: "scores", a limited scope. Reading observations needs Basic auth with the public and secret key. --api-key now takes PUBLIC_KEY:SECRET_KEY. A value without a colon is still sent as a Bearer token, so nothing that worked before stops working.
  • Endpoint. Langfuse Cloud removes the legacy APIs on 16 November 2026 and keeps Observations API v2 (cursor pagination, field groups). The adapter reads /api/public/v2/observations, follows meta.cursor until it is absent (the spec says a page can be empty while a cursor is present, so the old "stop on an empty page" rule would drop data), and falls back to the v1 endpoint with page numbers when the server answers 404 for v2, for example an older self-hosted instance.

The field mapping needed no change: _observation_to_span already reads usageDetails and totalCost, which are the v2 names.

Please read the testing note below. I could not run this against a live Langfuse server.

Related issue

Closes #785

Testing

  • 8 new unit tests in tests/unit/test_ingest_langfuse.py, using httpx.MockTransport (no network): Basic auth for a key pair, Bearer kept for a plain key, v2 with cursors including an empty page that has a cursor, the since bound, a source URL that already names the old endpoint, the v1 fallback on 404, an auth error is not hidden, and a v2 observation maps to a span. Before the change, four of them fail (it sends Bearer pk:sk and calls the v1 path).
  • ruff check tokenjam/ and mypy tokenjam/ are clean.
  • pytest tests/unit/ tests/synthetic/ tests/agents/ tests/integration/: in a parallel run (-n 4) three tests failed (test_summarize_relocate.py::test_the_cli_says_why_when_nothing_is_offered and two in test_xdist_isolation.py). The same parallel run on a clean main also fails (five tests, including the same two xdist ones), and all three pass when run serially on this branch.
  • Not tested against a live server. The v2 parameters and response shape come from Langfuse's OpenAPI spec (web/public/generated/api/openapi.yml) and the auth behaviour from apiAuth.ts, not from a run. If you have a Langfuse project key pair you can try tj backfill langfuse --source-url https://cloud.langfuse.com --api-key pk-lf-...:sk-lf-... --since 1d once, that would settle it. I am happy to adjust anything it shows.

Checklist

  • Tests pass (pytest tests/unit/ tests/synthetic/ tests/agents/ tests/integration/), apart from the parallel-run failures described above that also fail on main
  • Lint clean (ruff check tokenjam/)
  • Type check clean (mypy tokenjam/)
  • CLAUDE.md updated (not needed, no architecture change)
  • Test spans use tests/factories.py (not applicable, the new tests use observation payloads)
  • Requested @anilmurty as reviewer (@-mentioned here: @anilmurty, this is ready for a look)

`tj backfill langfuse --source-url` sent the key as a Bearer token and read
the v1 observations endpoint.

- Langfuse gives a Bearer public key a scores-only scope, so reading
  observations needs Basic auth with the public and secret key. `--api-key`
  now takes PUBLIC_KEY:SECRET_KEY. A value without a colon is still sent as a
  Bearer token.
- Langfuse Cloud removes the v1 observations endpoint on 16 November 2026 and
  keeps Observations API v2. The adapter reads v2 with cursor pagination and
  keeps following the cursor through empty pages, and falls back to v1 on a
  server that answers 404 for v2.

Closes Metabuilder-Labs#785
@greptile-apps

greptile-apps Bot commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no actionable issues were found.

Summary

Updates live Langfuse imports to use project key pairs and Observations API v2.

  • Live Langfuse backfills use project keys and follow v2 cursors.

No actionable issues were found. Review checks were static; no live Langfuse request was made.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Live Langfuse import] --> B[Build auth header]
  B --> C[Request v2 observations]
  C --> D{Initial request returns 404?}
  D -->|Yes| E[Read v1 with page numbers]
  D -->|No| F{Request succeeded?}
  F -->|No| G[Raise error]
  F -->|Yes| H[Collect observations]
  H --> I{Cursor present?}
  I -->|Yes, including empty pages| C
  I -->|No| J[Map and write spans]
  E --> J
Loading

Reviews (1) · Last reviewed commit: "Read Langfuse observations with Basic au..." · Reviewed by Greptile

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Langfuse v4 cutover (Nov 16): langfuse.py uses removed endpoints

1 participant