Class: RailsErrorDashboard::Subscribers::LlmCallSubscriber

Inherits:
Object
  • Object
show all
Defined in:
lib/rails_error_dashboard/subscribers/llm_call_subscriber.rb

Overview

ActiveSupport::Notifications subscriber for manually-instrumented LLM calls. The Tier 3 path — for hosts that don’t run OpenTelemetry AND don’t use Faraday-based LLM SDKs (e.g., direct Net::HTTP, gRPC clients, custom adapters, or hosts that want to layer in extra LLM activity that the automatic paths can’t see, like local inference servers).

Usage in the host app:

ActiveSupport::Notifications.instrument("red.llm_call",
  provider: "ollama",
  model: "llama3:8b",
  input_tokens: 1200,
  output_tokens: 350
) do
  # ... call your LLM ...
end

# Tool execution
ActiveSupport::Notifications.instrument("red.llm_tool_call",
  tool_name: "search_database",
  tool_arguments: { query: "..." },
  tool_result: "[...]"
) do
  # ... execute tool ...
end

Payload contract = LlmCallEvent constructor kwargs:

:provider, :model, :status, :input_tokens, :output_tokens,
:duration_ms, :error_class, :error_message, :tool_name,
:tool_arguments, :tool_result, :cost_usd_estimate

Duration: defaults to ‘event.duration` (host wraps `.instrument` around the work); payload `:duration_ms` overrides if explicitly supplied. Cost: auto-estimated from provider/model/tokens unless payload supplies `:cost_usd_estimate`.

SAFETY RULES (HOST_APP_SAFETY.md):

  • Every callback wrapped in rescue => e; nil

  • Never raise from subscriber callbacks

  • Skip if buffer is nil (not in a request context)

  • Re-read config on every event (host may toggle at runtime)

Constant Summary collapse

CHAT_EVENT =
"red.llm_call"
TOOL_EVENT =
"red.llm_tool_call"
EVENTS =
[ CHAT_EVENT, TOOL_EVENT ].freeze

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.subscriptionsObject (readonly)

Returns the value of attribute subscriptions.



54
55
56
# File 'lib/rails_error_dashboard/subscribers/llm_call_subscriber.rb', line 54

def subscriptions
  @subscriptions
end

Class Method Details

.subscribe!Object

Idempotent — re-subscribing first tears down previous subscriptions so Spring reloads / repeated engine boots don’t pile up duplicates.



58
59
60
61
# File 'lib/rails_error_dashboard/subscribers/llm_call_subscriber.rb', line 58

def subscribe!
  unsubscribe!
  @subscriptions = EVENTS.map { |name| subscribe_event(name) }
end

.unsubscribe!Object



63
64
65
66
67
68
69
70
# File 'lib/rails_error_dashboard/subscribers/llm_call_subscriber.rb', line 63

def unsubscribe!
  (@subscriptions || []).each do |sub|
    ActiveSupport::Notifications.unsubscribe(sub) if sub
  rescue StandardError
    nil
  end
  @subscriptions = []
end