Class: Legion::CLI::Chat::Tools::ManageTasks

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

Constant Summary collapse

VALID_ACTIONS =
%w[list show logs trigger].freeze
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



146
147
148
149
150
151
152
153
# File 'lib/legion/cli/chat/tools/manage_tasks.rb', line 146

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

.api_portObject



166
167
168
169
170
171
172
# File 'lib/legion/cli/chat/tools/manage_tasks.rb', line 166

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

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

.api_post(path, body) ⇒ Object



155
156
157
158
159
160
161
162
163
164
# File 'lib/legion/cli/chat/tools/manage_tasks.rb', line 155

def self.api_post(path, body)
  uri = URI("http://#{DEFAULT_HOST}:#{api_port}#{path}")
  http = Net::HTTP.new(uri.host, uri.port)
  http.open_timeout = 3
  http.read_timeout = 15
  request = Net::HTTP::Post.new(uri.request_uri, 'Content-Type' => 'application/json')
  request.body = ::JSON.generate(body)
  response = http.request(request)
  ::JSON.parse(response.body, symbolize_names: true)
end

.call(action:) ⇒ Object



40
41
42
43
44
45
46
47
48
49
50
# File 'lib/legion/cli/chat/tools/manage_tasks.rb', line 40

def self.call(action:, **)
  action = action.to_s.strip
  return "Invalid action: #{action}. Use: #{VALID_ACTIONS.join(', ')}" unless VALID_ACTIONS.include?(action)

  send(:"handle_#{action}", **)
rescue Errno::ECONNREFUSED
  'Legion daemon not running (cannot reach task API).'
rescue StandardError => e
  Legion::Logging.warn("ManageTasks#execute failed: #{e.message}") if defined?(Legion::Logging)
  "Error managing tasks: #{e.message}"
end

.format_task_detail(task) ⇒ Object



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/legion/cli/chat/tools/manage_tasks.rb', line 115

def self.format_task_detail(task)
  lines = ["Task ##{task[:id]}\n"]
  lines << "  Status: #{task[:status]}"
  lines << "  Runner: #{task[:runner_class]}"
  lines << "  Function: #{task[:function]}" if task[:function]
  lines << "  Created: #{task[:created_at]}"
  lines << "  Updated: #{task[:updated_at]}" if task[:updated_at]

  if task[:metering]
    m = task[:metering]
    lines << "\n  Metering:"
    lines << "    Total tokens: #{m[:total_tokens]}"
    lines << "    Input/Output: #{m[:input_tokens]}/#{m[:output_tokens]}"
    lines << "    Calls: #{m[:total_calls]}"
    lines << "    Avg latency: #{m[:avg_latency_ms]}ms"
    lines << "    Provider: #{Array(m[:provider]).join(', ')}" if m[:provider]
    lines << "    Model: #{Array(m[:model]).join(', ')}" if m[:model]
  end

  lines.join("\n")
end

.format_task_list(tasks) ⇒ Object



106
107
108
109
110
111
112
113
# File 'lib/legion/cli/chat/tools/manage_tasks.rb', line 106

def self.format_task_list(tasks)
  lines = ["Recent Tasks (#{tasks.size}):\n"]
  tasks.each do |t|
    status_str = t[:status] || 'unknown'
    lines << "  ##{t[:id]} [#{status_str}] #{t[:runner_class]}##{t[:function]} (#{t[:created_at]})"
  end
  lines.join("\n")
end

.format_task_logs(task_id, logs) ⇒ Object



137
138
139
140
141
142
143
144
# File 'lib/legion/cli/chat/tools/manage_tasks.rb', line 137

def self.format_task_logs(task_id, logs)
  lines = ["Logs for Task ##{task_id} (#{logs.size} entries):\n"]
  logs.each do |log|
    ts = log[:created_at] || log[:timestamp]
    lines << "  [#{ts}] #{log[:level] || 'info'}: #{log[:message]}"
  end
  lines.join("\n")
end

.handle_list(status: nil, limit: nil) ⇒ Object



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

def self.handle_list(status: nil, limit: nil, **)
  path = '/api/tasks'
  params = []
  params << "status=#{status}" if status
  params << "per_page=#{limit || 10}"
  path += "?#{params.join('&')}" unless params.empty?

  data = api_get(path)
  return "API error: #{data[:error]}" if data[:error]

  tasks = data[:data] || data[:items] || data
  tasks = [tasks] if tasks.is_a?(Hash)
  return 'No tasks found.' if !tasks.is_a?(Array) || tasks.empty?

  format_task_list(tasks)
end

.handle_logs(task_id: nil) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/legion/cli/chat/tools/manage_tasks.rb', line 79

def self.handle_logs(task_id: nil, **)
  return 'task_id is required for "logs"' unless task_id

  data = api_get("/api/tasks/#{task_id}/logs")
  return "API error: #{data[:error]}" if data[:error]

  logs = data[:data] || data[:items] || data
  logs = [logs] if logs.is_a?(Hash)
  return "No logs found for task #{task_id}." if !logs.is_a?(Array) || logs.empty?

  format_task_logs(task_id, logs)
end

.handle_show(task_id: nil) ⇒ Object



69
70
71
72
73
74
75
76
77
# File 'lib/legion/cli/chat/tools/manage_tasks.rb', line 69

def self.handle_show(task_id: nil, **)
  return 'task_id is required for "show"' unless task_id

  data = api_get("/api/tasks/#{task_id}")
  return "API error: #{data[:error]}" if data[:error]

  task = data[:data] || data
  format_task_detail(task)
end

.handle_trigger(runner_class: nil, function: nil, payload: nil) ⇒ Object



92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/legion/cli/chat/tools/manage_tasks.rb', line 92

def self.handle_trigger(runner_class: nil, function: nil, payload: nil, **)
  return 'runner_class is required for "trigger"' unless runner_class
  return 'function is required for "trigger"' unless function

  body = { runner_class: runner_class, function: function }
  body.merge!(::JSON.parse(payload, symbolize_names: true)) if payload

  data = api_post('/api/tasks', body)
  return "API error: #{data[:error]}" if data[:error]

  result = data[:data] || data
  "Task triggered successfully.\n  Task ID: #{result[:task_id]}\n  Runner: #{runner_class}\n  Function: #{function}"
end