0.01
The project is in a healthy, maintained state
Ruby client library for Langfuse, providing tracing, prompt management, and evaluation capabilities for LLM applications
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Development

~> 2.0
~> 13.0
~> 3.0
~> 1.0
~> 6.0
~> 3.0
~> 0.9
>= 0.1.0

Runtime

~> 2.0
>= 1.8, < 3.0
>= 1.0, < 4.0
>= 0.1.0
 Project Readme

Langfuse Ruby SDK

Gem Version CI Ruby License

Ruby SDK for Langfuse — the open-source LLM engineering platform. Tracing, prompt management, and evaluation for Ruby LLM apps, with first-class support for the Langfuse v4 observations-first data model.

New projects should use ingestion_mode: :otel (Langfuse v4). That path sends traces over OTLP/HTTP (/api/public/otel/v1/traces) with x-langfuse-ingestion-version: 4, so data shows up in real time and observation-level evaluators, cost, and the Observations API v2 work as designed. The tracing API (Langfuse.trace, #generation, #span, #agent, …) is unchanged; only the transport and ID format differ.

On Langfuse Cloud, POST /api/public/ingestion stops accepting everything except scores on 16 November 2026. Self-hosted v4 should use OTEL as well. See the Langfuse v4 guide and the official custom-ingestion migration.

Features

  • ⚡ Langfuse v4 / OpenTelemetry: OTLP ingestion, W3C hex IDs, usage_details / cost_details, observation types (agent, tool, retriever, …)
  • 🔍 Tracing: Traces, spans, generations, events, and typed observations
  • 📝 Prompt Management: Versioned prompts with a bounded cache and stale-on-outage reads
  • 📊 Evaluation: Built-in evaluators and scores (trace, observation, session, dataset run)
  • 🚀 Async Processing: Background batching, queue bounds, fork-safe flush, at_exit shutdown
  • 🔒 Resilience: Typed errors, Retry-After + jittered backoff, null-object degradation for Langfuse.trace

Installation

This gem requires Ruby >= 3.1 and is tested against Ruby 3.1–4.0. For development, Ruby version is managed with mise (defaults to the latest stable Ruby via .mise.toml).

Add this line to your application's Gemfile:

gem 'langfuse-ruby'

And then execute:

$ bundle install

Or install it yourself as:

$ gem install langfuse-ruby

Development setup with mise

# Install mise (if not already installed), then trust the project config and
# install the pinned Ruby version.
brew install mise                  # macOS; see mise docs for other OSes
mise install                       # installs Ruby from .mise.toml
bundle install                     # install gem dependencies
bundle exec rake spec              # run the test suite

Quick Start (Langfuse v4)

1. Configure the client for v4

The SDK default host is US Cloud (https://us.cloud.langfuse.com). Override host / LANGFUSE_HOST / LANGFUSE_BASE_URL for EU, Japan, HIPAA, or self-hosted. ingestion_mode still defaults to :legacy for compatibility; set it to :otel for v4.

require "langfuse"

Langfuse.configure do |config|
  config.public_key = ENV.fetch("LANGFUSE_PUBLIC_KEY")
  config.secret_key = ENV.fetch("LANGFUSE_SECRET_KEY")
  config.host = ENV["LANGFUSE_HOST"] || ENV["LANGFUSE_BASE_URL"] || "https://us.cloud.langfuse.com"
  config.ingestion_mode = :otel   # Langfuse v4 — required for real-time OTEL ingestion
end

# Equivalent:
#   Langfuse.new(..., ingestion_mode: :otel)
#   LANGFUSE_INGESTION_MODE=otel
Region host
US (SDK default) https://us.cloud.langfuse.com
EU https://cloud.langfuse.com
Japan https://jp.cloud.langfuse.com
HIPAA https://hipaa.cloud.langfuse.com
Self-hosted your Langfuse origin (no trailing path)

No extra gems are required. The SDK maps traces/spans/generations to OTLP JSON and sets x-langfuse-ingestion-version: 4 on the OTEL connection.

2. Trace an LLM call (recommended API)

v4 is observations-first: a trace is the set of observations that share a trace_id. Put the overall request/response on the trace (root span) and on the generation/span that actually produced them. Prefer usage_details / cost_details over the legacy usage hash — v4 uses usage_details for cost.

Langfuse.trace("chat-completion", user_id: "user-123", session_id: "sess-456",
               input: { message: "Hello, world!" }) do |trace|
  generation = trace.generation(
    name: "openai-completion",
    model: "gpt-4o",
    input: [{ role: "user", content: "Hello, world!" }],
    model_parameters: { temperature: 0.7, max_tokens: 100 }
  )

  response = call_llm(...)  # your code

  generation.end(
    output: response.content,
    usage_details: { input: 12, output: 18, total: 30 },   # v4 cost model
    cost_details: { input: 0.0001, output: 0.0006, total: 0.0007 }
  )
  generation.score(name: "faithfulness", value: 0.9)

  trace.update(output: response.content)
end  # flush is automatic in the block form

Scores always go through /api/public/ingestion (score-create), even in :otel mode. Trace/observation IDs are normalized to W3C hex so they attach to the OTEL-ingested spans. If an OTEL export fails mid-batch, both the OTEL events and that batch's scores are re-queued.

3. Nested work with v4 observation types

Typed observations (agent, tool, chain, retriever, embedding, evaluator, guardrail) are spans with langfuse.observation.type set. They filter and evaluate correctly in the v4 UI.

Langfuse.trace("document-qa", user_id: "user-456", input: { query: "What is Ruby?" }) do |trace|
  agent = trace.agent(name: "qa-agent", input: { query: "What is Ruby?" })

  retrieval = agent.retriever(name: "vector-search", input: { query: "What is Ruby?", top_k: 5 })
  retrieval.end(output: { documents: ["Ruby is a programming language..."] })

  gen = agent.generation(
    name: "openai-completion",
    model: "gpt-4o",
    input: [{ role: "user", content: "What is Ruby?" }],
    prompt: Langfuse.get_prompt("qa-system")  # optional prompt link
  )
  gen.end(
    output: "Ruby is a dynamic programming language.",
    usage_details: { input: 50, output: 20, total: 70 }
  )

  agent.end(output: { answer: "Ruby is a dynamic programming language." })
  trace.update(output: { answer: "Ruby is a dynamic programming language." })
end

What :otel changes (and what it does not)

:otel (v4, recommended) :legacy
Transport OTLP/HTTP JSON /api/public/otel/v1/traces batched POST /api/public/ingestion
Header x-langfuse-ingestion-version: 4 none
IDs W3C 32-char trace / 16-char span hex UUIDs
Create + update collapsed into one span (v4 is append-only) sent as separate events
Usage usage normalized into langfuse.observation.usage_details + gen_ai.usage.* legacy usage object
Scores still the ingestion API, IDs coerced to hex ingestion API
Tracing API identical identical

Do not dual-send the same IDs through both modes into one project. Switch with ingestion_mode: / LANGFUSE_INGESTION_MODE (values are downcased; a typo falls back to :legacy with a warning). Full attribute mapping, evaluator notes, and a cutover checklist: docs/V4.md.

Simplified Usage (Recommended)

For most use cases, you can use the simplified class-level API with automatic flush:

Block-based Tracing

require 'langfuse'

# Configure once — v4 / OTEL is the recommended ingestion path
Langfuse.configure do |config|
  config.public_key = ENV["LANGFUSE_PUBLIC_KEY"]
  config.secret_key = ENV["LANGFUSE_SECRET_KEY"]
  config.ingestion_mode = :otel
end

# Use block-based tracing - flush happens automatically!
Langfuse.trace("my-trace", user_id: "user-1", input: { message: "Hello" }) do |trace|
  generation = trace.generation(
    name: "openai-chat",
    model: "gpt-4",
    input: [{ role: "user", content: "Hello" }],
    model_parameters: { temperature: 0.7 }
  )

  # Call your LLM
  response = call_openai(...)

  # Record the response
  generation.end(
    output: response.content,
    usage_details: { input: 10, output: 15, total: 25 }  # or usage: response.usage
  )
  trace.update(output: response.content)
end  # Automatic flush here!

Get Prompts with Variables

# Get and compile a prompt in one call
prompt = Langfuse.get_prompt("greeting-prompt", variables: { name: "Alice" })
# => "Hello Alice! How can I help you today?"

# Get prompt without compilation
prompt_obj = Langfuse.get_prompt("greeting-prompt")
compiled = prompt_obj.compile(name: "Bob")

Graceful Degradation

The simplified API includes null objects that ensure your code continues working even if Langfuse is unavailable:

# If Langfuse fails, a NullTrace is used - your code still runs
Langfuse.trace("my-trace") do |trace|
  # This works even if Langfuse is down
  gen = trace.generation(name: "test", model: "gpt-4", input: "hello")
  gen.end(output: "world")
end

Other Class Methods

# Get the process-wide, thread-safe singleton client
client = Langfuse.client

# Manual flush (when not using block-based tracing)
Langfuse.flush

# Shutdown the client (idempotent; also runs via at_exit when shutdown_on_exit is true)
Langfuse.shutdown

# Reset the singleton (useful for testing; prefer Langfuse.new for isolated clients)
Langfuse.reset!

Nested Spans

trace = client.trace(name: "document-qa")

# Create a span for document retrieval
retrieval_span = trace.span(
  name: "document-retrieval",
  input: { query: "What is machine learning?" }
)

# Add a generation for embedding
embedding_gen = retrieval_span.generation(
  name: "embedding-generation",
  model: "text-embedding-ada-002",
  input: "What is machine learning?",
  output: [0.1, 0.2, 0.3], # embedding vector
  usage: { prompt_tokens: 5, total_tokens: 5 }
)

# End the retrieval span
retrieval_span.end(
  output: { documents: ["ML is...", "Machine learning involves..."] }
)

# Create a span for answer generation
answer_span = trace.span(
  name: "answer-generation",
  input: { 
    query: "What is machine learning?",
    context: ["ML is...", "Machine learning involves..."]
  }
)

# Add LLM generation
llm_gen = answer_span.generation(
  name: "openai-completion",
  model: "gpt-3.5-turbo",
  input: [
    { role: "system", content: "Answer based on context" },
    { role: "user", content: "What is machine learning?" }
  ]
)

llm_gen.end(
  output: { answer: "Machine learning is a subset of AI..." },
  usage_details: { input: 50, output: 30, total: 80 }
)
answer_span.end(output: { answer: "Machine learning is a subset of AI..." })

Observation Types (v4)

Langfuse v4 queries observations directly. Use a specific type so traces filter and evaluate correctly. Helpers exist on Client, Trace, Span, and Generation. as_type: on #span does the same thing; an explicit as_type: passed to a typed helper cannot override that helper's type.

Helper langfuse.observation.type Use for
#span span generic timed work
#generation generation LLM calls (model, tokens, cost, prompt link)
#event event point-in-time logs
#agent agent orchestration / tool-calling loops
#tool tool a single function or API call
#chain chain stitching retrieval → generation, etc.
#retriever retriever vector store / DB lookups
#embedding embedding embedding model calls (model / usage go into metadata)
#evaluator / #evaluator_obs evaluator scoring functions (Client#evaluator is an alias of #evaluator_obs)
#guardrail guardrail safety / moderation
trace = client.trace(name: "support-agent", user_id: "u1")

agent = trace.agent(name: "planner", input: { question: "Reset my password" })
tool  = agent.tool(name: "lookup-user", input: { email: "a@example.com" })
tool.end(output: { user_id: "u1" })

guard = agent.guardrail(name: "content-filter", input: { text: "Reset my password" })
guard.end(output: { blocked: false })

gen = agent.generation(name: "reply", model: "gpt-4o", input: [...])
gen.end(output: "I can help with that.", usage_details: { input: 40, output: 12, total: 52 })
agent.end(output: { reply: "I can help with that." })

Events

Create generic events for custom application events and logging:

# Create events from trace
event = trace.event(
  name: "user_action",
  input: { action: "login", user_id: "123" },
  output: { success: true },
  metadata: { ip: "192.168.1.1" }
)

# Create events from spans or generations
validation_event = span.event(
  name: "validation_check",
  input: { rules: ["required", "format"] },
  output: { valid: true, warnings: [] }
)

# Direct event creation
event = client.event(
  trace_id: trace.id,
  name: "audit_log",
  input: { operation: "data_export" },
  output: { status: "completed" },
  level: "INFO"
)

Prompt Management

Get and Use Prompts

# Get a prompt
prompt = client.get_prompt("chat-prompt", version: 1)

# Prompt names with special characters are automatically URL-encoded
prompt = client.get_prompt("EXEMPLE/my-prompt")  # Works correctly!

# Compile prompt with variables
compiled = prompt.compile(
  user_name: "Alice",
  topic: "machine learning"
)

puts compiled
# Output: "Hello Alice! Let's discuss machine learning today."

Note: Prompt names containing special characters (like /, spaces, ?, etc.) are automatically URL-encoded. You don't need to manually encode them.

Fetched prompts are cached per client for cache_ttl_seconds (default 60). The cache is bounded (200 entries), TTLs use a monotonic clock, and if a refetch fails while an expired entry exists, the stale copy is served with a warning — a Langfuse outage does not break prompt resolution for prompts seen before.

Create Prompts

# Create a text prompt
text_prompt = client.create_prompt(
  name: "greeting-prompt",
  prompt: "Hello {{user_name}}! How can I help you with {{topic}} today?",
  labels: ["greeting", "customer-service"],
  config: { temperature: 0.7 }
)

# Create a chat prompt
chat_prompt = client.create_prompt(
  name: "chat-prompt",
  prompt: [
    { role: "system", content: "You are a helpful assistant specialized in {{domain}}." },
    { role: "user", content: "{{user_message}}" }
  ],
  labels: ["chat", "assistant"]
)

Prompt Templates

# Create prompt templates for reuse
template = Langfuse::PromptTemplate.from_template(
  "Translate the following text to {{language}}: {{text}}"
)

translated = template.format(
  language: "Spanish",
  text: "Hello, world!"
)

# Chat prompt templates
chat_template = Langfuse::ChatPromptTemplate.from_messages([
  { role: "system", content: "You are a {{role}} assistant." },
  { role: "user", content: "{{user_input}}" }
])

messages = chat_template.format(
  role: "helpful",
  user_input: "What is Ruby?"
)

Evaluation and Scoring

Built-in Evaluators

# Exact match evaluator
exact_match = Langfuse::Evaluators::ExactMatchEvaluator.new

result = exact_match.evaluate(
  input: "What is 2+2?",
  output: "4",
  expected: "4"
)
# => { name: "exact_match", value: 1, comment: "Exact match" }

# Similarity evaluator
similarity = Langfuse::Evaluators::SimilarityEvaluator.new

result = similarity.evaluate(
  input: "What is AI?",
  output: "Artificial Intelligence is...",
  expected: "AI is artificial intelligence..."
)
# => { name: "similarity", value: 0.85, comment: "Similarity: 85%" }

# Length evaluator
length = Langfuse::Evaluators::LengthEvaluator.new(min_length: 10, max_length: 100)

result = length.evaluate(
  input: "Explain AI",
  output: "AI is a field of computer science that focuses on creating intelligent machines."
)
# => { name: "length", value: 1, comment: "Length 80 within range" }

Custom Scoring

# Add scores to traces or observations
trace = client.trace(name: "qa-session")

# Score the entire trace
trace.score(
  name: "user-satisfaction",
  value: 0.9,
  comment: "User was very satisfied"
)

# Score specific generations
generation = trace.generation(
  name: "answer-generation",
  model: "gpt-3.5-turbo",
  output: { content: "The answer is 42." }
)

generation.score(
  name: "accuracy",
  value: 0.8,
  comment: "Mostly accurate answer"
)

generation.score(
  name: "helpfulness",
  value: 0.95,
  comment: "Very helpful response"
)

Advanced Usage

Tracing Environment, Sampling and Masking

# Tag all events with a tracing environment (also via LANGFUSE_TRACING_ENVIRONMENT)
client = Langfuse.new(
  public_key: "pk-lf-...",
  secret_key: "sk-lf-...",
  environment: "production"
)

# Sample a fraction of traces deterministically (also via LANGFUSE_SAMPLE_RATE)
# All events of a trace share the same keep/drop decision.
sampled_client = Langfuse.new(
  public_key: "pk-lf-...",
  secret_key: "sk-lf-...",
  sample_rate: 0.1
)

# Mask sensitive fields before sending (applied to input/output/metadata)
masked_client = Langfuse.new(
  public_key: "pk-lf-...",
  secret_key: "sk-lf-...",
  mask: ->(value) { value.to_s.gsub(/\b\d{16}\b/, "***CARD***") }
)

Batch flushing

Events are flushed in the background every flush_interval seconds, or as soon as flush_at events are queued (default 15, env LANGFUSE_FLUSH_AT). Batches are automatically split to respect the 3.5 MB ingestion API limit, and a process-wide at_exit hook flushes pending events on shutdown.

The queue is bounded by max_queue_size (default 10,000, env LANGFUSE_MAX_QUEUE_SIZE): while Langfuse is unreachable the oldest events are dropped with a warning instead of growing memory without bound, and batches that fail with a permanent error (4xx) are dropped rather than retried forever. shutdown lets the flush thread finish its current send before returning, and after a fork (e.g. Puma workers) each process recreates its own flush thread.

update and end send only the fields you changed, so ending a generation does not re-upload its prompt, input or model parameters.

client = Langfuse.new(
  public_key: "pk-lf-...",
  secret_key: "sk-lf-...",
  flush_at: 50,           # flush after 50 events
  flush_interval: 10,     # or every 10 seconds
  max_queue_size: 20_000, # drop the oldest events beyond this many queued
  shutdown_on_exit: true  # flush on process exit (default)
)

Scores with full fields

Scores can target a trace, an observation, a session, or a dataset run, and carry metadata, a config reference, and an annotation queue link:

# Trace-level score
trace.score(name: "accuracy", value: 0.9, comment: "good")

# Observation-level score (trace_id is set automatically on Span/Generation)
generation.score(name: "faithfulness", value: 0.8, data_type: "NUMERIC")

# Session-level score
client.score(name: "csat", value: 5, session_id: "sess-1", data_type: "NUMERIC")

# Dataset-run score with metadata and config link
client.score(
  name: "hallucination",
  value: 0.2,
  dataset_run_id: "run-1",
  trace_id: "trace-1",
  metadata: { evaluator: "llm-judge" },
  config_id: "cfg-abc",
  data_type: "NUMERIC"
)

# Categorical string value
client.score(name: "label", value: "good", trace_id: "t1", data_type: "CATEGORICAL")

Generation usage details, cost details and prompt linking (v4)

# New v4 usage model (arbitrary keys, e.g. cache tokens)
gen = trace.generation(
  name: "chat",
  model: "gpt-4o",
  usage_details: { input: 100, output: 50, cache_read: 30 },
  cost_details: { input: 0.001, output: 0.003, total: 0.004 }
)
gen.end(output: "response")

# Link a generation to a prompt version (accepts a Langfuse::Prompt or a hash)
prompt = Langfuse.get_prompt("chat-prompt")
gen = trace.generation(name: "chat", model: "gpt-4o", prompt: prompt)
# or: prompt: { name: "chat-prompt", version: 3 }

Error Handling

begin
  client = Langfuse.new(
    public_key: "invalid-key",
    secret_key: "invalid-secret"
  )
  
  trace = client.trace(name: "test")
  client.flush
rescue Langfuse::AuthenticationError => e
  puts "Authentication failed: #{e.message}"
rescue Langfuse::RateLimitError => e
  puts "Rate limit exceeded: #{e.message}"
rescue Langfuse::NetworkError => e
  puts "Network error: #{e.message}"
rescue Langfuse::APIError => e
  puts "API error: #{e.message}"
end

Configuration Options

client = Langfuse.new(
  public_key: "pk-lf-...",
  secret_key: "sk-lf-...",
  host: "https://us.cloud.langfuse.com", # US default; EU: cloud.langfuse.com
  ingestion_mode: :otel, # Langfuse v4 (default remains :legacy)
  debug: true,          # Enable debug logging (or LANGFUSE_DEBUG=true)
  timeout: 30,          # Request timeout in seconds
  retries: 3,           # Number of retry attempts
  flush_interval: 30,   # Event flush interval in seconds (default: 5)
  flush_at: 50,         # Flush once this many events are queued (default: 15)
  max_queue_size: 10_000, # Drop the oldest events beyond this many queued (default: 10_000)
  auto_flush: true,     # Enable automatic flushing (default: true)
  environment: "prod",  # Tracing environment (or LANGFUSE_TRACING_ENVIRONMENT)
  sample_rate: 0.5,     # Keep 50% of traces deterministically (or LANGFUSE_SAMPLE_RATE)
  mask: ->(v) { v },    # Callable applied to input/output/metadata
  shutdown_on_exit: true, # Flush pending events on process exit (default: true)
  http_adapter: :net_http_persistent # Faraday adapter (default: Faraday's default)
)

Retries

Timeouts, network errors, 429 and 5xx responses are retried up to retries times (default 3). A Retry-After header is honored (seconds or HTTP date, capped at 10 s); otherwise the delay grows exponentially from 0.5 s with ±50% jitter, so many processes do not retry in lockstep. Client errors such as 401 and 422 are not retried, since repeating them cannot help.

Connection reuse

By default every request opens a new TLS connection. To keep connections alive between flushes, add a pooling adapter to your Gemfile and select it with http_adapter:

# Gemfile
gem "faraday-net_http_persistent"

Langfuse.configure do |config|
  config.http_adapter = :net_http_persistent
end

If the adapter is not available, the client logs a warning and falls back to Faraday's default adapter instead of raising.

Environment Variables

You can also configure the client using environment variables:

export LANGFUSE_PUBLIC_KEY="pk-lf-..."
export LANGFUSE_SECRET_KEY="sk-lf-..."
export LANGFUSE_HOST="https://us.cloud.langfuse.com"  # or LANGFUSE_BASE_URL; EU: https://cloud.langfuse.com
export LANGFUSE_FLUSH_INTERVAL=5
export LANGFUSE_FLUSH_AT=15
export LANGFUSE_MAX_QUEUE_SIZE=10000
export LANGFUSE_AUTO_FLUSH=true
export LANGFUSE_TRACING_ENVIRONMENT="production"
export LANGFUSE_SAMPLE_RATE=0.5
export LANGFUSE_DEBUG=false
export LANGFUSE_INGESTION_MODE=otel     # v4; use `legacy` only for pre-v4 self-hosted

Automatic Flush Control

By default, the Langfuse client automatically flushes events to the server at regular intervals using a background thread. You can control this behavior:

Enable/Disable Auto Flush

# Enable automatic flushing (default)
client = Langfuse.new(
  public_key: "pk-lf-...",
  secret_key: "sk-lf-...",
  auto_flush: true,
  flush_interval: 5  # Flush every 5 seconds
)

# Disable automatic flushing for manual control
client = Langfuse.new(
  public_key: "pk-lf-...",
  secret_key: "sk-lf-...",
  auto_flush: false
)

# Manual flush when auto_flush is disabled
client.flush

Global Configuration

Langfuse.configure do |config|
  config.auto_flush = false  # Disable auto flush globally
  config.flush_interval = 10
end

Environment Variable

export LANGFUSE_AUTO_FLUSH=false

Use Cases

Auto Flush Enabled (Default)

  • Best for most applications
  • Events are sent automatically
  • No manual management required

Auto Flush Disabled

  • Better performance for batch operations
  • More control over when events are sent
  • Requires manual flush calls
  • Useful for high-frequency operations
# Example: Batch processing with manual flush
client = Langfuse.new(auto_flush: false)

# Process many items
1000.times do |i|
  trace = client.trace(name: "batch-item-#{i}")
  # ... process item
end

# Flush all events at once
client.flush

Shutdown

# Ensure all events are flushed before shutdown
client.shutdown

Framework Integration

Rails Integration

# config/initializers/langfuse.rb
Langfuse.configure do |config|
  config.public_key = Rails.application.credentials.langfuse_public_key
  config.secret_key = Rails.application.credentials.langfuse_secret_key
  config.ingestion_mode = :otel
  config.debug = Rails.env.development?
end

# In your controller or service
class ChatController < ApplicationController
  def create
    @client = Langfuse.new
    
    trace = @client.trace(
      name: "chat-request",
      user_id: current_user.id,
      session_id: session.id,
      input: params[:message],
      metadata: { 
        controller: self.class.name,
        action: action_name,
        ip: request.remote_ip
      }
    )
    
    # Your LLM logic here
    response = generate_response(params[:message])

    render json: { response: response }
  end
end

Sidekiq Integration

class LLMProcessingJob < ApplicationJob
  def perform(user_id, message)
    client = Langfuse.new

    trace = client.trace(
      name: "background-llm-processing",
      user_id: user_id,
      input: { message: message },
      metadata: { job_class: self.class.name }
    )

    # Process with LLM
    result = process_with_llm(message)

    # Ensure events are flushed
    client.flush
  end
end

Examples

Check out the examples/ directory for more comprehensive examples:

Documentation

Development

After checking out the repo, run:

bin/setup

To install dependencies. Then, run:

rake spec

To run the tests. You can also run:

bin/console

For an interactive prompt that will allow you to experiment.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/ai-firstly/langfuse-ruby.

License

The gem is available as open source under the terms of the MIT License.

Links