Skip to content

[AAI-235] Add OTLP v4 header and v2 reads#95

Open
kxzk wants to merge 4 commits into
mainfrom
feature/aai-235-add-otlp-v4-header-and-v2-reads
Open

[AAI-235] Add OTLP v4 header and v2 reads#95
kxzk wants to merge 4 commits into
mainfrom
feature/aai-235-add-otlp-v4-header-and-v2-reads

Conversation

@kxzk

@kxzk kxzk commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator

TL;DR

Send the Langfuse Fast Preview OTLP headers (x-langfuse-ingestion-version: 4 plus SDK identification) on every span export, and add flat list_observations, query_metrics, and list_scores read methods backed by the current v2/v3 endpoints.

Why

Langfuse requires direct OpenTelemetry exporters to send x-langfuse-ingestion-version: 4 for real-time Fast Preview processing; without it data can be delayed on the new UI and v2 read surfaces. The SDK also had no way to read observations, metrics, or scores through the current API generation — only legacy /api/public/traces reads, which remain unchanged.

  • OtelSetup now sends Basic auth, ingestion version, x-langfuse-sdk-name, x-langfuse-sdk-version, and x-langfuse-public-key from one header builder.
  • New ReadApi module (mixed into ApiClient, delegated from Client):
    • list_observationsGET /api/public/v2/observations (Cloud-only, cursor pagination, full data/meta envelope preserved; unbounded reads without trace_id raise ArgumentError)
    • query_metricsGET /api/public/v2/metrics (Hash queries JSON-encoded; pre-encoded strings pass through)
    • list_scoresGET /api/public/v3/scores (polymorphic values preserved)
  • No fallback to legacy endpoints: v2 availability errors surface verbatim.

Live-validated against Langfuse Cloud: OTLP export with the new headers round-trips, list_scores returns one page with a cursor, and v2 endpoints surface the API's v4-write-mode guidance verbatim on a project not yet in v4 write mode.

Checklist

  • Has label
  • Has linked issue
  • Tests added for new behavior
  • Docs updated (if user-facing)

Closes AAI-235


Note

Medium Risk
Changes affect all OTLP trace ingestion (behavior/timing on Cloud) and introduce new HTTP read surfaces; observation listing enforces bounds client-side to avoid unbounded scans.

Overview
OTLP exports now send Langfuse Fast Preview headers on every span export: x-langfuse-ingestion-version: 4 plus SDK name, version, and public key (in addition to Basic auth), so ingested traces show up promptly on the new UI and v2 read surfaces.

Read APIs are added via a new ReadApi module on ApiClient and delegated from Client: list_observations (Cloud v2, cursor pagination, bounded reads unless trace_id is set), query_metrics (Cloud v2), and list_scores (v3, polymorphic values). There is no fallback to legacy trace endpoints—errors from missing v2 routes surface as-is. API_REFERENCE.md documents the new methods.

Specs cover header building, query param mapping, validation, and client delegation.

Reviewed by Cursor Bugbot for commit 91ba75d. Bugbot is set up for automated code reviews on this repo. Configure here.

Copilot AI review requested due to automatic review settings July 13, 2026 13:29
@kxzk kxzk added the enhancement New feature or request label Jul 13, 2026
@linear-code

linear-code Bot commented Jul 13, 2026

Copy link
Copy Markdown

AAI-235

Comment thread lib/langfuse/read_api.rb

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds Langfuse Cloud “Fast Preview” OTLP export headers (ingestion v4 + SDK identification) and introduces new flat read APIs on the Ruby SDK client for observations, metrics, and scores, aligning the SDK with the current v2/v3 query surfaces.

Changes:

  • OTLP exporter now sends x-langfuse-ingestion-version: 4 plus SDK identification headers on every span export.
  • New Langfuse::ReadApi mixed into ApiClient and delegated from Client: list_observations, query_metrics, list_scores.
  • Adds specs and API reference documentation for the new read methods and OTLP header behavior.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
spec/langfuse/read_api_spec.rb Adds request/param-mapping and error-handling coverage for the new read endpoints and client delegation.
spec/langfuse/otel_setup_spec.rb Verifies OTLP header construction now includes ingestion version and SDK identification.
lib/langfuse/read_api.rb Implements v2/v3 read methods and query param mapping helpers.
lib/langfuse/otel_setup.rb Updates OTLP exporter to use a unified header builder emitting v4 ingestion + SDK headers.
lib/langfuse/client.rb Delegates new read methods on the flat Client API surface.
lib/langfuse/api_client.rb Mixes in ReadApi so reads share existing HTTP transport and error handling.
lib/langfuse.rb Ensures ReadApi is required as part of the gem load.
docs/API_REFERENCE.md Documents list_observations, query_metrics, and list_scores.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/langfuse/read_api.rb
Comment on lines +183 to +189
def validate_bounded_observation_read!(trace_id, from_start_time, to_start_time)
return if trace_id
return if from_start_time && to_start_time

raise ArgumentError,
"from_start_time and to_start_time are required unless trace_id is provided"
end

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c4a2d4d with a present? check covering trace_id and both time bounds; specs added.

Comment thread lib/langfuse/read_api.rb Outdated
# @param type [String, nil] Filter by observation type (e.g. "GENERATION", "SPAN")
# @param level [String, nil] Filter by level (e.g. "DEFAULT", "ERROR")
# @param parent_observation_id [String, nil] Filter by parent observation ID
# @param environment [String, Array<String>, nil] Filter by environment(s)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c4a2d4denvironment is now documented as String only, consistent with list_traces. Multi-environment filtering can go through the structured filter param.

Comment thread docs/API_REFERENCE.md Outdated
| `type` | String | No | Filter by observation type (e.g. `"GENERATION"`, `"SPAN"`) |
| `level` | String | No | Filter by level (e.g. `"DEFAULT"`, `"ERROR"`) |
| `parent_observation_id` | String | No | Filter by parent observation ID |
| `environment` | String, Array<String> | No | Filter by environment(s) |

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c4a2d4d — docs now list environment as String.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit c4a2d4d. Configure here.

Comment thread lib/langfuse/read_api.rb
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants