Module: Observ::Concerns::ObservableService

Extended by:
ActiveSupport::Concern
Included in:
ModerationGuardrailService
Defined in:
app/services/observ/concerns/observable_service.rb

Overview

Concern for adding observability to service objects

This module provides automatic observability session management for services that perform LLM operations. It handles session creation, lifecycle management, and chat instrumentation.

Usage:

class MyService
include Observ::Concerns::ObservableService

def initialize(observability_session: nil, moderate: false)
  initialize_observability(
    observability_session,
    service_name: "my_service",
    metadata: { custom: "data" },
    moderate: moderate
  )
end

def perform(input)
  with_observability do |session|
    # Your service logic here
    # Session automatically finalized on success/error
    # If moderate: true, content moderation runs after finalization
  end
end
end

Instance Method Summary collapse

Instance Method Details

#initialize_observability(session_or_false = nil, service_name:, metadata: {}, moderate: false) ⇒ Object

Initialize observability for the service

Parameters:

  • session_or_false (Observ::Session, false, nil) (defaults to: nil)

    Session to use, false to disable, nil to auto-create

  • service_name (String)

    Name of the service (used in session metadata)

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

    Additional metadata to include in the session

  • moderate (Boolean) (defaults to: false)

    Whether to run content moderation after session finalization



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'app/services/observ/concerns/observable_service.rb', line 45

def initialize_observability(session_or_false = nil, service_name:, metadata: {}, moderate: false)
  @moderate_on_complete = moderate

  if session_or_false == false
    # Explicitly disable observability
    @observability = nil
    @owns_session = false
  elsif session_or_false
    # Use provided session
    @observability = session_or_false
    @owns_session = false
  else
    # Auto-create session for standalone use
    @observability = create_service_session(service_name, )
    @owns_session = @observability.present?
  end
end

#instrument_chat(chat, context: {}) ⇒ Observ::ChatInstrumenter?

Instrument a RubyLLM chat instance for observability

This wraps the chat's ask method to automatically create traces and track LLM calls within the observability session.

Examples:

chat = RubyLLM.chat(model: "gpt-4")
instrument_chat(chat, context: { operation: "summarize" })
response = chat.ask("Summarize this text")

Parameters:

  • chat (RubyLLM::Chat)

    The chat instance to instrument

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

    Additional context to include in traces

Returns:



104
105
106
107
108
# File 'app/services/observ/concerns/observable_service.rb', line 104

def instrument_chat(chat, context: {})
  return unless @observability

  @observability.instrument_chat(chat, context: context)
end

#instrument_embedding(context: {}) ⇒ Observ::EmbeddingInstrumenter?

Instrument RubyLLM.embed for observability

This wraps the RubyLLM.embed class method to automatically create traces and track embedding calls within the observability session.

Examples:

instrument_embedding(context: { operation: "semantic_search" })
embedding = RubyLLM.embed("Search query")

Parameters:

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

    Additional context to include in traces

Returns:



121
122
123
124
125
# File 'app/services/observ/concerns/observable_service.rb', line 121

def instrument_embedding(context: {})
  return unless @observability

  @observability.instrument_embedding(context: context)
end

#instrument_image_generation(context: {}) ⇒ Observ::ImageGenerationInstrumenter?

Instrument RubyLLM.paint for observability

This wraps the RubyLLM.paint class method to automatically create traces and track image generation calls within the observability session.

Examples:

instrument_image_generation(context: { operation: "product_image" })
image = RubyLLM.paint("A modern logo")

Parameters:

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

    Additional context to include in traces

Returns:



138
139
140
141
142
# File 'app/services/observ/concerns/observable_service.rb', line 138

def instrument_image_generation(context: {})
  return unless @observability

  @observability.instrument_image_generation(context: context)
end

#instrument_moderation(context: {}) ⇒ Observ::ModerationInstrumenter?

Instrument RubyLLM.moderate for observability

This wraps the RubyLLM.moderate class method to automatically create traces and track moderation calls within the observability session.

Examples:

instrument_moderation(context: { operation: "user_input_check" })
result = RubyLLM.moderate(user_input)

Parameters:

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

    Additional context to include in traces

Returns:



172
173
174
175
176
# File 'app/services/observ/concerns/observable_service.rb', line 172

def instrument_moderation(context: {})
  return unless @observability

  @observability.instrument_moderation(context: context)
end

#instrument_transcription(context: {}) ⇒ Observ::TranscriptionInstrumenter?

Instrument RubyLLM.transcribe for observability

This wraps the RubyLLM.transcribe class method to automatically create traces and track transcription calls within the observability session.

Examples:

instrument_transcription(context: { operation: "meeting_notes" })
transcript = RubyLLM.transcribe("meeting.wav")

Parameters:

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

    Additional context to include in traces

Returns:



155
156
157
158
159
# File 'app/services/observ/concerns/observable_service.rb', line 155

def instrument_transcription(context: {})
  return unless @observability

  @observability.instrument_transcription(context: context)
end

#with_observability {|session| ... } ⇒ Object

Execute a block with automatic session lifecycle management

The session will be finalized automatically after the block completes, whether it succeeds or raises an error. Only sessions owned by this service instance (i.e., auto-created sessions) will be finalized.

If moderate: true was passed to initialize_observability, content moderation will be enqueued after the session is finalized.

Examples:

with_observability do |session|
  # Your service logic here
  process_data(session)
end

Yields:

  • (session)

    The observability session (may be nil if disabled)

Returns:

  • The result of the block



80
81
82
83
84
85
86
87
88
89
# File 'app/services/observ/concerns/observable_service.rb', line 80

def with_observability(&block)
  result = block.call(@observability)
  finalize_service_session if @owns_session
  enqueue_moderation if should_moderate?
  result
rescue StandardError
  finalize_service_session if @owns_session
  enqueue_moderation if should_moderate?
  raise
end