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/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/notification_subscriber.rb
Defined Under Namespace
Modules: Coercions, NotificationSubscriber, 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
-
.configuration ⇒ Object
readonly
Returns the value of attribute configuration.
Class Method Summary collapse
-
.clear_tracked_thread_locals! ⇒ Object
Clear every thread-local registered via ‘track_thread_local` on this thread, then drop the registry itself.
- .configure(app_name:, environment: nil, prompt_label: nil, exporter: nil, autosubscribe: true) ⇒ Object
- .enabled? ⇒ Boolean
- .record_generation(**kwargs) ⇒ Object
- .reset! ⇒ Object
-
.set_trace_output(value) ⇒ Object
Set the trace-level output on the active parent span.
- .tracer ⇒ Object
-
.track_thread_local(*keys) ⇒ Object
Register thread-local keys that should be cleared at the next Traceable job boundary.
Class Attribute Details
.configuration ⇒ Object (readonly)
Returns the value of attribute configuration.
35 36 37 |
# File 'lib/platform_sdk/observability/langfuse.rb', line 35 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.
128 129 130 131 132 133 134 |
# File 'lib/platform_sdk/observability/langfuse.rb', line 128 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
37 38 39 40 41 42 43 44 45 |
# File 'lib/platform_sdk/observability/langfuse.rb', line 37 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
47 48 49 |
# File 'lib/platform_sdk/observability/langfuse.rb', line 47 def enabled? !@configuration.nil? && @configuration.enabled? end |
.record_generation(**kwargs) ⇒ Object
57 58 59 |
# File 'lib/platform_sdk/observability/langfuse.rb', line 57 def record_generation(**kwargs) Recorder.record_generation(**kwargs) end |
.reset! ⇒ Object
99 100 101 102 103 104 |
# File 'lib/platform_sdk/observability/langfuse.rb', line 99 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.
87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/platform_sdk/observability/langfuse.rb', line 87 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.[0, 200]}") nil end |
.tracer ⇒ Object
51 52 53 54 55 |
# File 'lib/platform_sdk/observability/langfuse.rb', line 51 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).
119 120 121 122 123 |
# File 'lib/platform_sdk/observability/langfuse.rb', line 119 def track_thread_local(*keys) list = (Thread.current[OWNED_THREAD_LOCALS_KEY] ||= []) keys.each { |k| list << k unless list.include?(k) } nil end |