Class: Legion::CLI::Chat::Tools::CostSummary

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

Constant Summary collapse

DEFAULT_PORT =
4567
DEFAULT_HOST =
'127.0.0.1'

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

.api_get(path) ⇒ Object



104
105
106
107
108
109
110
111
# File 'lib/legion/cli/chat/tools/cost_summary.rb', line 104

def self.api_get(path)
  uri = URI("http://#{DEFAULT_HOST}:#{api_port}#{path}")
  http = Net::HTTP.new(uri.host, uri.port)
  http.open_timeout = 2
  http.read_timeout = 5
  response = http.get(uri.request_uri)
  ::JSON.parse(response.body, symbolize_names: true)
end

.api_portObject



113
114
115
116
117
118
119
# File 'lib/legion/cli/chat/tools/cost_summary.rb', line 113

def self.api_port
  return DEFAULT_PORT unless defined?(Legion::Settings)

  Legion::Settings[:api]&.dig(:port) || DEFAULT_PORT
rescue StandardError
  DEFAULT_PORT
end

.call(action: 'summary', worker_id: nil, limit: 5) ⇒ Object



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/legion/cli/chat/tools/cost_summary.rb', line 34

def self.call(action: 'summary', worker_id: nil, limit: 5)
  case action.to_s
  when 'top'
    handle_top(limit.to_i.clamp(1, 20))
  when 'worker'
    return 'worker_id is required for the "worker" action.' if worker_id.nil? || worker_id.strip.empty?

    handle_worker(worker_id.strip)
  else
    handle_summary
  end
rescue Errno::ECONNREFUSED
  'Legion daemon not running (cannot reach cost API).'
rescue StandardError => e
  Legion::Logging.warn("CostSummary#execute failed: #{e.message}") if defined?(Legion::Logging)
  "Error fetching cost data: #{e.message}"
end

.fetch_worker_cost(worker_id) ⇒ Object



96
97
98
99
100
101
102
# File 'lib/legion/cli/chat/tools/cost_summary.rb', line 96

def self.fetch_worker_cost(worker_id)
  data = api_get("/api/workers/#{worker_id}/value")
  data = data[:data] || data
  (data[:total_cost_usd] || 0).to_f
rescue StandardError
  0.0
end

.handle_summaryObject



52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/legion/cli/chat/tools/cost_summary.rb', line 52

def self.handle_summary
  data = api_get('/api/costs/summary?period=month')
  return "API error: #{data[:error]}" if data[:error]

  data = data[:data] || data
  lines = ["Cost Summary:\n"]
  lines << format('  Today:      $%.4f', (data[:today] || 0).to_f)
  lines << format('  This Week:  $%.4f', (data[:week] || 0).to_f)
  lines << format('  This Month: $%.4f', (data[:month] || 0).to_f)
  lines << "  Workers:    #{data[:workers] || 0}"
  lines.join("\n")
end

.handle_top(limit) ⇒ Object



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/legion/cli/chat/tools/cost_summary.rb', line 65

def self.handle_top(limit)
  data = api_get('/api/workers')
  return "API error: #{data[:error]}" if data[:error]

  workers = data[:data] || data
  workers = Array(workers).first(limit)
  return 'No workers found.' if workers.empty?

  lines = ["Top #{workers.size} Cost Consumers:\n"]
  workers.each_with_index do |w, i|
    id = w[:worker_id] || w[:id] || 'unknown'
    cost = fetch_worker_cost(id)
    lines << format('  %<rank>d. %-20<id>s $%<cost>.4f', rank: i + 1, id: id, cost: cost)
  end
  lines.join("\n")
end

.handle_worker(worker_id) ⇒ Object



82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/legion/cli/chat/tools/cost_summary.rb', line 82

def self.handle_worker(worker_id)
  data = api_get("/api/workers/#{worker_id}/value")
  return "API error: #{data[:error]}" if data[:error]

  data = data[:data] || data
  return "No cost data for worker #{worker_id}." if data.nil? || data.empty?

  lines = ["Worker: #{worker_id}\n"]
  data.each do |key, val|
    lines << "  #{key}: #{val}" unless key == :worker_id
  end
  lines.join("\n")
end