Module: Langfuse::ReadApi

Included in:
ApiClient
Defined in:
lib/langfuse/read_api.rb

Overview

Note:

The v2 observations and metrics endpoints require Langfuse v4. There is no fallback to legacy endpoints because their response and pagination semantics differ.

Read endpoints for the current Langfuse query surface.

Implements v2 observation and metrics reads plus the v3 scores read. Mixed into ApiClient, whose private request helper provides HTTP transport and error handling.

Instance Method Summary collapse

Instance Method Details

#list_observations(from_start_time: nil, to_start_time: nil, trace_id: nil, fields: nil, cursor: nil, limit: nil, filter: nil, name: nil, user_id: nil, type: nil, level: nil, parent_observation_id: nil, is_root_observation: nil, environment: nil, session_id: nil, version: nil, expand_metadata: nil) ⇒ Hash

List observations with cursor-based pagination and field selection

Delegates to GET /api/public/v2/observations on Langfuse v4. Returns observation rows, not reconstructed trace objects. The full response envelope is preserved: "data" holds the observation rows and "meta" holds the pagination cursor for the next page.

Broad reads must be bounded: unless trace_id narrows the query, both from_start_time and to_start_time are required.

rubocop:disable Metrics/ParameterLists

Examples:

Bounded read of recent generations

page = api_client.list_observations(
  from_start_time: Time.now - 3600,
  to_start_time: Time.now,
  type: "GENERATION",
  fields: "core,basic,usage"
)
page["data"].each { |obs| puts obs["id"] }
next_cursor = page.dig("meta", "cursor")

Parameters:

  • from_start_time (Time, String, nil) (defaults to: nil)

    Inclusive lower bound on observation start time

  • to_start_time (Time, String, nil) (defaults to: nil)

    Exclusive upper bound on observation start time

  • trace_id (String, nil) (defaults to: nil)

    Filter by trace ID

  • fields (String, nil) (defaults to: nil)

    Comma-separated field groups to include (core, basic, time, io, metadata, model, usage, prompt, metrics, trace_context)

  • cursor (String, nil) (defaults to: nil)

    Cursor from the previous response's meta for the next page

  • limit (Integer, nil) (defaults to: nil)

    Items per page (max 1000, default 50)

  • filter (String, nil) (defaults to: nil)

    JSON string with structured filter conditions; takes precedence over individual query parameter filters

  • name (String, nil) (defaults to: nil)

    Filter by observation name

  • user_id (String, nil) (defaults to: nil)

    Filter by user ID

  • type (String, nil) (defaults to: nil)

    Filter by observation type (e.g. "GENERATION", "SPAN")

  • level (String, nil) (defaults to: nil)

    Filter by level (e.g. "DEFAULT", "ERROR")

  • parent_observation_id (String, nil) (defaults to: nil)

    Filter by parent observation ID

  • is_root_observation (Boolean, nil) (defaults to: nil)

    Filter by logical root status

  • environment (Array<String>, nil) (defaults to: nil)

    Filter by one or more environments

  • session_id (String, nil) (defaults to: nil)

    Filter by session ID

  • version (String, nil) (defaults to: nil)

    Filter by observation version

  • expand_metadata (String, nil) (defaults to: nil)

    Comma-separated metadata keys to return non-truncated

Returns:

  • (Hash)

    Full response hash with "data" rows and "meta" cursor info

Raises:

  • (ArgumentError)

    if the read is unbounded (no trace_id and missing start-time bounds)

  • (UnauthorizedError)

    if authentication fails

  • (ApiError)

    for other API errors (including deployments without Langfuse v4)



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/langfuse/read_api.rb', line 83

def list_observations(from_start_time: nil, to_start_time: nil, trace_id: nil,
                      fields: nil, cursor: nil, limit: nil, filter: nil,
                      name: nil, user_id: nil, type: nil, level: nil,
                      parent_observation_id: nil, is_root_observation: nil,
                      environment: nil, session_id: nil, version: nil,
                      expand_metadata: nil)
  validate_bounded_observation_read!(trace_id, from_start_time, to_start_time)
  params = build_observations_params(
    from_start_time: from_start_time, to_start_time: to_start_time,
    trace_id: trace_id, fields: fields, cursor: cursor, limit: limit,
    filter: filter, name: name, user_id: user_id, type: type, level: level,
    parent_observation_id: parent_observation_id, environment: environment,
    is_root_observation: is_root_observation, session_id: session_id,
    version: version, expand_metadata: 
  )
  request(
    :get,
    "/api/public/v2/observations",
    params: params,
    params_encoder: Faraday::FlatParamsEncoder
  )
end

#list_scores(limit: nil, cursor: nil, fields: nil, id: nil, name: nil, source: nil, data_type: nil, environment: nil, config_id: nil, queue_id: nil, author_user_id: nil, value: nil, value_min: nil, value_max: nil, trace_id: nil, session_id: nil, observation_id: nil, experiment_id: nil, from_timestamp: nil, to_timestamp: nil) ⇒ Hash

List scores with polymorphic values (v3)

Delegates to GET /api/public/v3/scores. The full response envelope is preserved: "data" holds score rows and "meta" holds the pagination cursor. Score values are polymorphic by dataType: NUMERIC scores return numbers, BOOLEAN scores return booleans, and CATEGORICAL, TEXT, and CORRECTION scores return strings.

rubocop:disable Metrics/ParameterLists

Examples:

Read corrections for a trace

page = api_client.list_scores(trace_id: trace_id, data_type: "CORRECTION", fields: "subject,details")
page["data"].each { |score| puts score["value"] }

Parameters:

  • limit (Integer, nil) (defaults to: nil)

    Items per page (max 100, default 50)

  • cursor (String, nil) (defaults to: nil)

    Cursor from the previous response's meta for the next page

  • fields (String, nil) (defaults to: nil)

    Comma-separated field groups in addition to core (details, subject, annotation)

  • id (String, nil) (defaults to: nil)

    Comma-separated score IDs to filter by

  • name (String, nil) (defaults to: nil)

    Comma-separated score names to filter by

  • source (String, nil) (defaults to: nil)

    Comma-separated score sources (e.g. API, ANNOTATION, EVAL)

  • data_type (String, nil) (defaults to: nil)

    Comma-separated data types (NUMERIC, BOOLEAN, CATEGORICAL, TEXT, CORRECTION)

  • environment (String, nil) (defaults to: nil)

    Comma-separated environments to filter by

  • config_id (String, nil) (defaults to: nil)

    Comma-separated score config IDs

  • queue_id (String, nil) (defaults to: nil)

    Comma-separated annotation queue IDs

  • author_user_id (String, nil) (defaults to: nil)

    Comma-separated author user IDs

  • value (String, nil) (defaults to: nil)

    Comma-separated exact values (requires a single NUMERIC, BOOLEAN, or CATEGORICAL data_type)

  • value_min (Numeric, nil) (defaults to: nil)

    Inclusive lower bound (requires data_type: "NUMERIC")

  • value_max (Numeric, nil) (defaults to: nil)

    Inclusive upper bound (requires data_type: "NUMERIC")

  • trace_id (String, nil) (defaults to: nil)

    Comma-separated trace IDs (mutually exclusive with session_id and experiment_id)

  • session_id (String, nil) (defaults to: nil)

    Comma-separated session IDs

  • observation_id (String, nil) (defaults to: nil)

    Comma-separated observation IDs (requires trace_id)

  • experiment_id (String, nil) (defaults to: nil)

    Comma-separated dataset run (experiment) IDs

  • from_timestamp (Time, String, nil) (defaults to: nil)

    Inclusive lower bound on score timestamp

  • to_timestamp (Time, String, nil) (defaults to: nil)

    Exclusive upper bound on score timestamp

Returns:

  • (Hash)

    Full response hash with "data" rows and "meta" cursor info

Raises:



172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/langfuse/read_api.rb', line 172

def list_scores(limit: nil, cursor: nil, fields: nil, id: nil, name: nil,
                source: nil, data_type: nil, environment: nil, config_id: nil,
                queue_id: nil, author_user_id: nil, value: nil, value_min: nil,
                value_max: nil, trace_id: nil, session_id: nil,
                observation_id: nil, experiment_id: nil,
                from_timestamp: nil, to_timestamp: nil)
  params = build_scores_params(
    limit: limit, cursor: cursor, fields: fields, id: id, name: name,
    source: source, data_type: data_type, environment: environment,
    config_id: config_id, queue_id: queue_id, author_user_id: author_user_id,
    value: value, value_min: value_min, value_max: value_max,
    trace_id: trace_id, session_id: session_id, observation_id: observation_id,
    experiment_id: experiment_id, from_timestamp: from_timestamp, to_timestamp: to_timestamp
  )
  request(:get, "/api/public/v3/scores", params: params)
end

#query_metrics(query:) ⇒ Hash

Query aggregate metrics on Langfuse v4

Delegates to GET /api/public/v2/metrics. Supports the observations, scores-numeric, scores-categorical, and scores-boolean views.

Examples:

Count observations by name

api_client.query_metrics(query: {
  view: "observations",
  metrics: [{ measure: "count", aggregation: "count" }],
  dimensions: [{ field: "name" }],
  fromTimestamp: "2026-07-01T00:00:00Z",
  toTimestamp: "2026-07-02T00:00:00Z"
})

Parameters:

  • query (Hash, String)

    Metrics query. A Hash is JSON-encoded into the endpoint's query parameter; a pre-encoded JSON String is passed through unchanged.

Returns:

  • (Hash)

    The parsed metrics response

Raises:

  • (ArgumentError)

    if query is neither a Hash nor a String

  • (UnauthorizedError)

    if authentication fails

  • (ApiError)

    for other API errors (including deployments without Langfuse v4)



128
129
130
# File 'lib/langfuse/read_api.rb', line 128

def query_metrics(query:)
  request(:get, "/api/public/v2/metrics", params: { query: encode_metrics_query(query) })
end