Module: PlatformSdk::Observability::Langfuse

Defined in:
lib/platform_sdk/observability/langfuse.rb,
lib/platform_sdk/observability/langfuse.rb,
lib/platform_sdk/observability/langfuse/recorder.rb,
lib/platform_sdk/observability/langfuse/coercions.rb,
lib/platform_sdk/observability/langfuse/traceable.rb,
lib/platform_sdk/observability/langfuse/spec_support.rb,
lib/platform_sdk/observability/langfuse/configuration.rb,
lib/platform_sdk/observability/langfuse/openai_adapter.rb,
lib/platform_sdk/observability/langfuse/ruby_llm_adapter.rb,
lib/platform_sdk/observability/langfuse/sidekiq_lifecycle.rb,
lib/platform_sdk/observability/langfuse/null_span_exporter.rb,
lib/platform_sdk/observability/langfuse/trace_summarizable.rb,
lib/platform_sdk/observability/langfuse/bedrock_claude_adapter.rb,
lib/platform_sdk/observability/langfuse/notification_subscriber.rb

Defined Under Namespace

Modules: BedrockClaudeAdapter, Coercions, NotificationSubscriber, OpenAIAdapter, Recorder, RubyLLMAdapter, SidekiqLifecycle, SpecSupport, TraceSummarizable, Traceable Classes: Configuration, ConfigurationError, Error, NullSpanExporter

Constant Summary collapse

LLM_CALL_EVENT =

ActiveSupport::Notifications event name for LLM calls. Owned by the ‘Langfuse` namespace (rather than `NotificationSubscriber`) so the adapter can emit without requiring the subscriber to be loaded.

'llm_call.platform_sdk'
OWNED_THREAD_LOCALS_KEY =
:platform_sdk_langfuse_owned_thread_locals

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.configurationObject (readonly)

Returns the value of attribute configuration.



37
38
39
# File 'lib/platform_sdk/observability/langfuse.rb', line 37

def configuration
  @configuration
end

Class Method Details

.clear_tracked_thread_locals!Object

Clear every thread-local registered via ‘track_thread_local` on this thread, then drop the registry itself. Called from the Traceable wrapper’s ‘ensure` block.



130
131
132
133
134
135
136
# File 'lib/platform_sdk/observability/langfuse.rb', line 130

def clear_tracked_thread_locals!
  owned = Thread.current[OWNED_THREAD_LOCALS_KEY]
  return unless owned

  owned.each { |k| Thread.current[k] = nil }
  Thread.current[OWNED_THREAD_LOCALS_KEY] = nil
end

.configure(app_name:, environment: nil, prompt_label: nil, exporter: nil, autosubscribe: true) ⇒ Object



39
40
41
42
43
44
45
46
47
# File 'lib/platform_sdk/observability/langfuse.rb', line 39

def configure(app_name:, environment: nil, prompt_label: nil, exporter: nil, autosubscribe: true)
  @configuration&.force_flush_and_shutdown
  @configuration = Configuration.new(app_name:, environment:, prompt_label:, exporter:)
  if @configuration.enabled?
    SidekiqLifecycle.install!
    NotificationSubscriber.install! if autosubscribe
  end
  @configuration
end

.enabled?Boolean

Returns:

  • (Boolean)


49
50
51
# File 'lib/platform_sdk/observability/langfuse.rb', line 49

def enabled?
  !@configuration.nil? && @configuration.enabled?
end

.record_generation(**kwargs) ⇒ Object



59
60
61
# File 'lib/platform_sdk/observability/langfuse.rb', line 59

def record_generation(**kwargs)
  Recorder.record_generation(**kwargs)
end

.reset!Object



101
102
103
104
105
106
# File 'lib/platform_sdk/observability/langfuse.rb', line 101

def reset!
  @configuration&.force_flush_and_shutdown
  @configuration = nil
  SidekiqLifecycle.reset!
  NotificationSubscriber.reset!
end

.set_trace_output(value) ⇒ Object

Set the trace-level output on the active parent span. Application code calls this from inside a Traceable job (or any block running under an active span) to record a meaningful summary of the job’s result.

Why this is opt-in rather than auto-captured from ‘perform`’s return value: Sidekiq’s ‘perform` return is incidental — it’s often nil, an AR transaction callback array, or otherwise unrelated to anything a human reading a trace would find useful. Only the application knows what’s meaningful (e.g. a generated record’s ID).

Example:

class MyJob
  include Sidekiq::Job
  include PlatformSdk::Observability::Langfuse::Traceable

  def perform(args)
    component = generate_component(args)
    PlatformSdk::Observability::Langfuse.set_trace_output(
      { component_id: component.id, status: component.status }
    )
  end
end

No-ops when the SDK is disabled or no span is currently active. Never raises — observability failures must not break callers.



89
90
91
92
93
94
95
96
97
98
99
# File 'lib/platform_sdk/observability/langfuse.rb', line 89

def set_trace_output(value)
  return unless enabled?

  span = OpenTelemetry::Trace.current_span
  return unless span.context.valid?

  span.set_attribute('langfuse.trace.output', Recorder.stringify(value))
rescue StandardError, SystemStackError => e
  OpenTelemetry.handle_error(message: "Langfuse set_trace_output failed: #{e.class}: #{e.message[0, 200]}")
  nil
end

.tracerObject



53
54
55
56
57
# File 'lib/platform_sdk/observability/langfuse.rb', line 53

def tracer
  return nil unless enabled?

  @configuration.tracer
end

.track_thread_local(*keys) ⇒ Object

Register thread-local keys that should be cleared at the next Traceable job boundary. Consumers call this when they store per-trace state on ‘Thread.current` (e.g. the ID of the last recorded message) so the value can’t leak into the next job that lands on this Sidekiq thread.

Example:

PlatformSdk::Observability::Langfuse.track_thread_local(:my_app_last_message_id)
Thread.current[:my_app_last_message_id] = msg.id

Compared to a name-prefix convention, this requires explicit opt-in (so consumers can’t accidentally have unrelated state zeroed) and is O(owned keys) instead of O(all thread locals).



121
122
123
124
125
# File 'lib/platform_sdk/observability/langfuse.rb', line 121

def track_thread_local(*keys)
  list = (Thread.current[OWNED_THREAD_LOCALS_KEY] ||= [])
  keys.each { |k| list << k unless list.include?(k) }
  nil
end