Class: Legion::CLI::Chat::Tools::DetectAnomalies

Inherits:
Tools::Base
  • Object
show all
Defined in:
lib/legion/cli/chat/tools/detect_anomalies.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



65
66
67
68
69
70
71
72
# File 'lib/legion/cli/chat/tools/detect_anomalies.rb', line 65

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



74
75
76
77
78
79
80
# File 'lib/legion/cli/chat/tools/detect_anomalies.rb', line 74

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

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

.call(threshold: 2.0) ⇒ Object



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

def self.call(threshold: 2.0)
  data = api_get("/api/traces/anomalies?threshold=#{threshold.to_f}")
  return "API error: #{data[:error][:message]}" if data[:error]

  format_report(data[:data] || data)
rescue Errno::ECONNREFUSED
  'Legion daemon not running (cannot reach anomaly detection API).'
rescue StandardError => e
  Legion::Logging.warn("DetectAnomalies#execute failed: #{e.message}") if defined?(Legion::Logging)
  "Error detecting anomalies: #{e.message}"
end

.format_report(data) ⇒ Object



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/legion/cli/chat/tools/detect_anomalies.rb', line 44

def self.format_report(data)
  anomalies = data[:anomalies] || []
  lines = ["Anomaly Report (threshold: #{data[:threshold] || '2.0'}x)\n"]
  lines << "  Recent period:   #{data[:recent_period] || 'last 1 hour'} (#{data[:recent_count] || 0} records)"
  lines << "  Baseline period: #{data[:baseline_period] || 'previous 23 hours'} (#{data[:baseline_count] || 0} records)"
  lines << ''

  if anomalies.empty?
    lines << 'No anomalies detected. All metrics within normal range.'
  else
    lines << "#{anomalies.size} anomal#{anomalies.size == 1 ? 'y' : 'ies'} detected:\n"
    anomalies.each_with_index do |a, i|
      severity = (a[:severity] || 'warning').upcase
      lines << "  #{i + 1}. [#{severity}] #{a[:metric]}"
      lines << "     Recent: #{a[:recent]} | Baseline: #{a[:baseline]} | Ratio: #{a[:ratio]}x"
    end
  end

  lines.join("\n")
end