Class: Langfuse::ApiClient

Inherits:
Object
  • Object
show all
Defined in:
lib/langfuse/api_client.rb

Overview

HTTP client for Langfuse API

Handles authentication, connection management, and HTTP requests to the Langfuse REST API.

Examples:

api_client = Langfuse::ApiClient.new(
  public_key: "pk_...",
  secret_key: "sk_...",
  base_url: "https://cloud.langfuse.com",
  timeout: 5,
  logger: Logger.new($stdout)
)

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(public_key:, secret_key:, base_url:, timeout: 5, logger: nil, cache: nil) ⇒ ApiClient

Initialize a new API client

Parameters:

  • public_key (String)

    Langfuse public API key

  • secret_key (String)

    Langfuse secret API key

  • base_url (String)

    Base URL for Langfuse API

  • timeout (Integer) (defaults to: 5)

    HTTP request timeout in seconds

  • logger (Logger) (defaults to: nil)

    Logger instance for debugging

  • cache (PromptCache, nil) (defaults to: nil)

    Optional cache for prompt responses



35
36
37
38
39
40
41
42
# File 'lib/langfuse/api_client.rb', line 35

def initialize(public_key:, secret_key:, base_url:, timeout: 5, logger: nil, cache: nil)
  @public_key = public_key
  @secret_key = secret_key
  @base_url = base_url
  @timeout = timeout
  @logger = logger || Logger.new($stdout, level: Logger::WARN)
  @cache = cache
end

Instance Attribute Details

#base_urlObject (readonly)

rubocop:disable Metrics/ClassLength



25
26
27
# File 'lib/langfuse/api_client.rb', line 25

def base_url
  @base_url
end

#cacheObject (readonly)

rubocop:disable Metrics/ClassLength



25
26
27
# File 'lib/langfuse/api_client.rb', line 25

def cache
  @cache
end

#loggerObject (readonly)

rubocop:disable Metrics/ClassLength



25
26
27
# File 'lib/langfuse/api_client.rb', line 25

def logger
  @logger
end

#public_keyObject (readonly)

rubocop:disable Metrics/ClassLength



25
26
27
# File 'lib/langfuse/api_client.rb', line 25

def public_key
  @public_key
end

#secret_keyObject (readonly)

rubocop:disable Metrics/ClassLength



25
26
27
# File 'lib/langfuse/api_client.rb', line 25

def secret_key
  @secret_key
end

#timeoutObject (readonly)

rubocop:disable Metrics/ClassLength



25
26
27
# File 'lib/langfuse/api_client.rb', line 25

def timeout
  @timeout
end

Instance Method Details

#connection(timeout: nil) ⇒ Faraday::Connection

Get a Faraday connection

Parameters:

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

    Optional custom timeout for this connection

Returns:

  • (Faraday::Connection)


48
49
50
51
52
53
54
55
56
# File 'lib/langfuse/api_client.rb', line 48

def connection(timeout: nil)
  if timeout
    # Create dedicated connection for custom timeout
    # to avoid mutating shared connection
    build_connection(timeout: timeout)
  else
    @connection ||= build_connection
  end
end

#create_prompt(name:, prompt:, type:, config: {}, labels: [], tags: [], commit_message: nil) ⇒ Hash

Create a new prompt (or new version if prompt with same name exists)

rubocop:disable Metrics/ParameterLists

Examples:

Create a text prompt

api_client.create_prompt(
  name: "greeting",
  prompt: "Hello {{name}}!",
  type: "text",
  labels: ["production"]
)

Parameters:

  • name (String)

    The prompt name

  • prompt (String, Array<Hash>)

    The prompt content

  • type (String)

    Prompt type (“text” or “chat”)

  • config (Hash) (defaults to: {})

    Optional configuration (model params, etc.)

  • labels (Array<String>) (defaults to: [])

    Optional labels (e.g., [“production”])

  • tags (Array<String>) (defaults to: [])

    Optional tags

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

    Optional commit message

Returns:

  • (Hash)

    The created prompt data

Raises:



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/langfuse/api_client.rb', line 137

def create_prompt(name:, prompt:, type:, config: {}, labels: [], tags: [], commit_message: nil)
  path = "/api/public/v2/prompts"
  payload = {
    name: name,
    prompt: prompt,
    type: type,
    config: config,
    labels: labels,
    tags: tags
  }
  payload[:commitMessage] = commit_message if commit_message

  response = connection.post(path, payload)
  handle_response(response)
rescue Faraday::RetriableResponse => e
  logger.error("Faraday error: Retries exhausted - #{e.response.status}")
  handle_response(e.response)
rescue Faraday::Error => e
  logger.error("Faraday error: #{e.message}")
  raise ApiError, "HTTP request failed: #{e.message}"
end

#get_prompt(name, version: nil, label: nil) ⇒ Hash

Fetch a prompt from the Langfuse API

Checks cache first if caching is enabled. On cache miss, fetches from API and stores in cache. When using Rails.cache backend, uses distributed lock to prevent cache stampedes.

Parameters:

  • name (String)

    The name of the prompt

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

    Optional specific version number

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

    Optional label (e.g., “production”, “latest”)

Returns:

  • (Hash)

    The prompt data

Raises:

  • (ArgumentError)

    if both version and label are provided

  • (NotFoundError)

    if the prompt is not found

  • (UnauthorizedError)

    if authentication fails

  • (ApiError)

    for other API errors



107
108
109
110
111
112
113
# File 'lib/langfuse/api_client.rb', line 107

def get_prompt(name, version: nil, label: nil)
  raise ArgumentError, "Cannot specify both version and label" if version && label
  return fetch_prompt_from_api(name, version: version, label: label) if cache.nil?

  cache_key = PromptCache.build_key(name, version: version, label: label)
  fetch_with_appropriate_caching_strategy(cache_key, name, version, label)
end

#list_prompts(page: nil, limit: nil) ⇒ Array<Hash>

List all prompts in the Langfuse project

Fetches a list of all prompt names available in your project. Note: This returns metadata only, not full prompt content.

Examples:

prompts = api_client.list_prompts
prompts.each do |prompt|
  puts "#{prompt['name']} (v#{prompt['version']})"
end

Parameters:

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

    Optional page number for pagination

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

    Optional limit per page (default: API default)

Returns:

  • (Array<Hash>)

    Array of prompt metadata hashes

Raises:



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/langfuse/api_client.rb', line 74

def list_prompts(page: nil, limit: nil)
  params = {}
  params[:page] = page if page
  params[:limit] = limit if limit

  path = "/api/public/v2/prompts"
  response = connection.get(path, params)
  result = handle_response(response)

  # API returns { data: [...], meta: {...} }
  result["data"] || []
rescue Faraday::RetriableResponse => e
  logger.error("Faraday error: Retries exhausted - #{e.response.status}")
  handle_response(e.response)
rescue Faraday::Error => e
  logger.error("Faraday error: #{e.message}")
  raise ApiError, "HTTP request failed: #{e.message}"
end

#send_batch(events) ⇒ void

This method returns an undefined value.

Send a batch of events to the Langfuse ingestion API

Sends events (scores, traces, observations) to the ingestion endpoint. Retries transient errors (429, 503, 504, network errors) with exponential backoff. Batch operations are idempotent (events have unique IDs), so retries are safe.

Examples:

events = [
  {
    id: SecureRandom.uuid,
    type: "score-create",
    timestamp: Time.now.iso8601,
    body: { name: "quality", value: 0.85, trace_id: "abc123..." }
  }
]
api_client.send_batch(events)

Parameters:

  • events (Array<Hash>)

    Array of event hashes to send

Raises:



214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/langfuse/api_client.rb', line 214

def send_batch(events)
  raise ArgumentError, "events must be an array" unless events.is_a?(Array)
  raise ArgumentError, "events array cannot be empty" if events.empty?

  path = "/api/public/ingestion"
  payload = { batch: events }

  response = connection.post(path, payload)
  handle_batch_response(response)
rescue Faraday::RetriableResponse => e
  # Retry middleware exhausted all retries - handle the final response
  logger.error("Langfuse batch send failed: Retries exhausted - #{e.response.status}")
  handle_batch_response(e.response)
rescue Faraday::Error => e
  logger.error("Langfuse batch send failed: #{e.message}")
  raise ApiError, "Batch send failed: #{e.message}"
end

#shutdownObject



232
233
234
# File 'lib/langfuse/api_client.rb', line 232

def shutdown
  cache.shutdown if cache.respond_to?(:shutdown)
end

#update_prompt(name:, version:, labels:) ⇒ Hash

Update labels for an existing prompt version

Examples:

Promote a prompt to production

api_client.update_prompt(
  name: "greeting",
  version: 2,
  labels: ["production"]
)

Parameters:

  • name (String)

    The prompt name

  • version (Integer)

    The version number to update

  • labels (Array<String>)

    New labels (replaces existing). Required.

Returns:

  • (Hash)

    The updated prompt data

Raises:



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/langfuse/api_client.rb', line 177

def update_prompt(name:, version:, labels:)
  raise ArgumentError, "labels must be an array" unless labels.is_a?(Array)

  path = "/api/public/v2/prompts/#{URI.encode_uri_component(name)}/versions/#{version}"
  payload = { newLabels: labels }

  response = connection.patch(path, payload)
  handle_response(response)
rescue Faraday::RetriableResponse => e
  logger.error("Faraday error: Retries exhausted - #{e.response.status}")
  handle_response(e.response)
rescue Faraday::Error => e
  logger.error("Faraday error: #{e.message}")
  raise ApiError, "HTTP request failed: #{e.message}"
end