Class: RailsErrorDashboard::Services::LlmSummary
- Inherits:
-
Object
- Object
- RailsErrorDashboard::Services::LlmSummary
- Defined in:
- lib/rails_error_dashboard/services/llm_summary.rb
Overview
Pure algorithm: Roll up LLM breadcrumbs into a per-error summary.
Operates on already-captured breadcrumb data at display time only —zero runtime cost. Same pattern as CacheAnalyzer / NplusOneDetector.
NOTE on string coercion: BreadcrumbCollector#truncate_metadata stringifies every metadata value (input_tokens “42”, cost_usd “0.0003”, etc.). This service does the ‘.to_i` / `.to_f` itself so callers don’t have to.
Class Method Summary collapse
-
.call(breadcrumbs) ⇒ Hash?
Summary hash, or nil if no LLM breadcrumbs present.
Class Method Details
.call(breadcrumbs) ⇒ Hash?
Returns Summary hash, or nil if no LLM breadcrumbs present.
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/rails_error_dashboard/services/llm_summary.rb', line 25 def self.call() return nil unless .is_a?(Array) llm_crumbs = .select { |c| c.is_a?(Hash) && c["c"] == "llm" } tool_crumbs = .select { |c| c.is_a?(Hash) && c["c"] == "llm_tool" } return nil if llm_crumbs.empty? && tool_crumbs.empty? total_input = 0 total_output = 0 total_cost = 0.0 total_duration = 0.0 error_count = 0 providers = {} by_model = {} llm_crumbs.each do |crumb| = crumb["meta"].is_a?(Hash) ? crumb["meta"] : {} provider = ["provider"].to_s model = ["model"].to_s input = ["input_tokens"].to_i output = ["output_tokens"].to_i cost = ["cost_usd"].to_f duration = crumb["d"].to_f status = ["status"].to_s total_input += input total_output += output total_cost += cost total_duration += duration error_count += 1 unless status == "success" || status == "" providers[provider] = true unless provider.empty? key = [ provider, model ] by_model[key] ||= { provider: provider, model: model, calls: 0, tokens: 0, cost_usd: 0.0 } by_model[key][:calls] += 1 by_model[key][:tokens] += input + output by_model[key][:cost_usd] += cost end # Tool calls also contribute to duration (visible request impact) tool_crumbs.each do |crumb| total_duration += crumb["d"].to_f end { total_calls: llm_crumbs.size, total_tool_calls: tool_crumbs.size, total_input_tokens: total_input, total_output_tokens: total_output, total_tokens: total_input + total_output, total_cost_usd: total_cost.round(6), error_count: error_count, total_duration_ms: total_duration.round(1), providers: providers.keys.sort, by_model: by_model.values.sort_by { |row| -row[:calls] }.map { |row| row[:cost_usd] = row[:cost_usd].round(6) row } } rescue => e RailsErrorDashboard::Logger.debug("[RailsErrorDashboard] LlmSummary.call failed: #{e.}") nil end |