Class: Legion::CLI::Chat::Tools::ModelComparison

Inherits:
Tools::Base
  • Object
show all
Defined in:
lib/legion/cli/chat/tools/model_comparison.rb

Class Method Summary collapse

Methods inherited from Tools::Base

deferred, deferred?, description, error_response, extension, handle_exception, input_schema, log, mcp_category, mcp_tier, runner, sticky, tags, text_response, tool_name, trigger_words

Class Method Details

.call(models: nil, tokens: 1000) ⇒ Object



19
20
21
22
23
24
25
# File 'lib/legion/cli/chat/tools/model_comparison.rb', line 19

def self.call(models: nil, tokens: 1000)
  pricing = load_pricing
  selected = filter_models(pricing, models)
  return 'No matching models found.' if selected.empty?

  format_comparison(selected, tokens.to_i)
end

.cost_tracker_pricingObject



34
35
36
37
38
39
40
41
42
43
# File 'lib/legion/cli/chat/tools/model_comparison.rb', line 34

def self.cost_tracker_pricing
  return {} unless defined?(Legion::LLM::CostTracker)

  Legion::LLM::CostTracker::DEFAULT_PRICING.transform_values do |v|
    { input: v[:input], output: v[:output] }
  end
rescue StandardError => e
  Legion::Logging.debug("ModelComparison#cost_tracker_pricing failed: #{e.message}") if defined?(Legion::Logging)
  {}
end

.default_pricingObject



45
46
47
48
49
50
51
52
53
# File 'lib/legion/cli/chat/tools/model_comparison.rb', line 45

def self.default_pricing
  {
    'claude-sonnet-4-6' => { input: 3.0,  output: 15.0 },
    'claude-haiku-4-5'  => { input: 0.80, output: 4.0  },
    'claude-opus-4-6'   => { input: 15.0, output: 75.0 },
    'gpt-4o'            => { input: 2.50, output: 10.0 },
    'gpt-4o-mini'       => { input: 0.15, output: 0.60 }
  }
end

.estimate_cost(price, tokens) ⇒ Object



88
89
90
# File 'lib/legion/cli/chat/tools/model_comparison.rb', line 88

def self.estimate_cost(price, tokens)
  ((tokens * price[:input] / 1_000_000.0) + (tokens * price[:output] / 1_000_000.0)).round(6)
end

.filter_models(pricing, models_str) ⇒ Object



55
56
57
58
59
60
# File 'lib/legion/cli/chat/tools/model_comparison.rb', line 55

def self.filter_models(pricing, models_str)
  return pricing if models_str.nil? || models_str.strip.empty?

  names = models_str.split(',').map(&:strip).map(&:downcase)
  pricing.select { |k, _| names.any? { |n| k.downcase.include?(n) } }
end

.format_comparison(selected, tokens) ⇒ Object



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
# File 'lib/legion/cli/chat/tools/model_comparison.rb', line 62

def self.format_comparison(selected, tokens)
  lines = ["Model Comparison (per 1M tokens pricing):\n"]
  lines << '  Model                        Input/$   Output/$    Est. Cost'
  lines << "  #{'' * 59}"

  sorted = selected.sort_by { |_, v| v[:input] }
  sorted.each do |name, price|
    est = estimate_cost(price, tokens)
    lines << format('  %<name>-25s %<inp>9.2f %<out>10.2f   $%<est>.6f',
                    name: name, inp: price[:input], out: price[:output], est: est)
  end

  lines << ''
  lines << format('  Estimate based on %<t>d input + %<t>d output tokens.', t: tokens)

  if sorted.size > 1
    cheapest = sorted.first
    priciest = sorted.last
    ratio = priciest[1][:input] / cheapest[1][:input]
    lines << format('  %<exp>s is %<r>.1fx more expensive than %<chp>s (input rate).',
                    exp: priciest[0], r: ratio, chp: cheapest[0])
  end

  lines.join("\n")
end

.load_pricingObject



27
28
29
30
31
32
# File 'lib/legion/cli/chat/tools/model_comparison.rb', line 27

def self.load_pricing
  base = cost_tracker_pricing
  return base unless base.empty?

  default_pricing
end