Class: Langfuse::BaseObservation Abstract

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

Overview

This class is abstract.

Subclass and pass type: to super to create concrete observation types

Base class for all Langfuse observation wrappers.

Provides unified functionality for spans, generations, events, and specialized observation types. Wraps OpenTelemetry spans with Langfuse-specific functionality. Uses unified ‘start_observation()` method with `as_type` parameter, aligning with langfuse-js architecture.

Examples:

Block-based API (auto-ends)

Langfuse.observe("parent-operation", input: { query: "test" }) do |span|
  # Child span
  span.start_observation("data-processing", input: { step: "fetch" }) do |child|
    result = fetch_data
    child.update(output: result)
  end

  # Child generation (LLM call)
  span.start_observation("llm-call", { model: "gpt-4", input: [{ role: "user", content: "Hello" }] }, as_type: :generation) do |gen|
    response = call_llm
    gen.update(output: response, usage_details: { prompt_tokens: 100, completion_tokens: 50 })
  end
end

Stateful API (manual end)

span = Langfuse.start_observation("parent-operation", { input: { query: "test" } })

# Child span
child_span = span.start_observation("data-validation", { input: { data: result } })
validate_data
child_span.update(output: { valid: true })
child_span.end

# Child generation (LLM call)
gen = span.start_observation("llm-summary", {
  model: "gpt-3.5-turbo",
  input: [{ role: "user", content: "Summarize" }]
}, as_type: :generation)
summary = call_llm
gen.update(output: summary, usage_details: { prompt_tokens: 50, completion_tokens: 25 })
gen.end

span.end

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(otel_span, otel_tracer, attributes: nil, type: nil) ⇒ BaseObservation

Returns a new instance of BaseObservation.

Parameters:

  • otel_span (OpenTelemetry::SDK::Trace::Span)

    The underlying OTel span

  • otel_tracer (OpenTelemetry::SDK::Trace::Tracer)

    The OTel tracer

  • attributes (Hash, Types::SpanAttributes, Types::GenerationAttributes, nil) (defaults to: nil)

    Optional initial attributes

  • type (String) (defaults to: nil)

    Observation type (e.g., “span”, “generation”, “event”)



67
68
69
70
71
72
73
74
75
76
# File 'lib/langfuse/observations.rb', line 67

def initialize(otel_span, otel_tracer, attributes: nil, type: nil)
  @otel_span = otel_span
  @otel_tracer = otel_tracer
  @type = type || raise(ArgumentError, "type must be provided")

  # Set initial attributes if provided
  return unless attributes

  update_observation_attributes(attributes.to_h)
end

Instance Attribute Details

#otel_spanObject (readonly)

Returns the value of attribute otel_span.



61
62
63
# File 'lib/langfuse/observations.rb', line 61

def otel_span
  @otel_span
end

#otel_tracerObject (readonly)

Returns the value of attribute otel_tracer.



61
62
63
# File 'lib/langfuse/observations.rb', line 61

def otel_tracer
  @otel_tracer
end

#typeObject (readonly)

Returns the value of attribute type.



61
62
63
# File 'lib/langfuse/observations.rb', line 61

def type
  @type
end

Instance Method Details

#current_spanOpenTelemetry::SDK::Trace::Span

Returns:

  • (OpenTelemetry::SDK::Trace::Span)


192
193
194
# File 'lib/langfuse/observations.rb', line 192

def current_span
  @otel_span
end

#end(end_time: nil) ⇒ Object

Parameters:

  • end_time (Time, Integer, nil) (defaults to: nil)

    Optional end time (Time object or Unix timestamp in nanoseconds)



99
100
101
# File 'lib/langfuse/observations.rb', line 99

def end(end_time: nil)
  @otel_span.finish(end_timestamp: end_time)
end

#event(name:, input: nil, level: "default") ⇒ Object

Parameters:

  • name (String)

    Event name

  • input (Object, nil) (defaults to: nil)

    Optional event data

  • level (String) (defaults to: "default")

    Log level (debug, default, warning, error)



182
183
184
185
186
187
188
189
# File 'lib/langfuse/observations.rb', line 182

def event(name:, input: nil, level: "default")
  attributes = {
    "langfuse.observation.input" => input&.to_json,
    "langfuse.observation.level" => level
  }.compact

  @otel_span.add_event(name, attributes: attributes)
end

#idString

Returns Hex-encoded span ID (16 hex characters).

Returns:

  • (String)

    Hex-encoded span ID (16 hex characters)



79
80
81
# File 'lib/langfuse/observations.rb', line 79

def id
  @otel_span.context.span_id.unpack1("H*")
end

#input=(value) ⇒ Object

Sets observation-level input attributes.

Parameters:

  • value (Object)

    Input value (will be JSON-encoded)



157
158
159
# File 'lib/langfuse/observations.rb', line 157

def input=(value)
  update_observation_attributes(input: value)
end

#level=(value) ⇒ Object

Parameters:

  • value (String)

    Level (DEBUG, DEFAULT, WARNING, ERROR)



174
175
176
# File 'lib/langfuse/observations.rb', line 174

def level=(value)
  update_observation_attributes(level: value)
end

#metadata=(value) ⇒ Object

Parameters:

  • value (Hash)

    Metadata hash (expanded into individual langfuse.observation.metadata.* attributes)



169
170
171
# File 'lib/langfuse/observations.rb', line 169

def metadata=(value)
  update_observation_attributes(metadata: value)
end

#output=(value) ⇒ Object

Sets observation-level output attributes.

Parameters:

  • value (Object)

    Output value (will be JSON-encoded)



164
165
166
# File 'lib/langfuse/observations.rb', line 164

def output=(value)
  update_observation_attributes(output: value)
end

#start_observation(name, attrs = {}, as_type: :span) {|observation| ... } ⇒ BaseObservation, Object

Creates a child observation within this observation’s context.

Supports block-based (auto-ends) and stateful (manual end) APIs. Events auto-end when created without a block.

Parameters:

  • name (String)

    Descriptive name for the child observation

  • attrs (Hash, Types::SpanAttributes, Types::GenerationAttributes, nil) (defaults to: {})

    Observation attributes

  • as_type (Symbol, String) (defaults to: :span)

    Observation type (:span, :generation, :event, etc.). Defaults to ‘:span`.

Yields:

  • (observation)

    Optional block that receives the observation object

Returns:

  • (BaseObservation, Object)

    The child observation (or block return value if block given)



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/langfuse/observations.rb', line 124

def start_observation(name, attrs = {}, as_type: :span, &block)
  # Call module-level factory with parent context
  # Skip validation to allow unknown types to fall back to Span
  child = Langfuse.start_observation(
    name,
    attrs,
    as_type: as_type,
    parent_span_context: @otel_span.context,
    skip_validation: true
  )

  if block
    # Block-based API: auto-ends when block completes
    # Set context and execute block
    current_context = OpenTelemetry::Context.current
    result = OpenTelemetry::Context.with_current(
      OpenTelemetry::Trace.context_with_span(child.otel_span, parent_context: current_context)
    ) do
      block.call(child)
    end
    # Only end if not already ended (events auto-end in start_observation)
    child.end unless as_type.to_s == OBSERVATION_TYPES[:event]
    result
  else
    # Stateful API - return observation
    # Events already auto-ended in start_observation
    child
  end
end

#trace_idString

Returns Hex-encoded trace ID (32 hex characters).

Returns:

  • (String)

    Hex-encoded trace ID (32 hex characters)



84
85
86
# File 'lib/langfuse/observations.rb', line 84

def trace_id
  @otel_span.context.trace_id.unpack1("H*")
end

#trace_urlString

Returns URL to view this trace in Langfuse UI.

Examples:

span = Langfuse.observe("operation") do |obs|
  puts "View trace: #{obs.trace_url}"
end

Returns:

  • (String)

    URL to view this trace in Langfuse UI



94
95
96
# File 'lib/langfuse/observations.rb', line 94

def trace_url
  Langfuse.client.trace_url(trace_id)
end

#update_trace(attrs) ⇒ self

Updates trace-level attributes (user_id, session_id, tags, etc.) for the entire trace.

Parameters:

Returns:

  • (self)


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

def update_trace(attrs)
  return self unless @otel_span.recording?

  otel_attrs = OtelAttributes.create_trace_attributes(attrs.to_h)
  otel_attrs.each { |key, value| @otel_span.set_attribute(key, value) }
  self
end