Class: Legion::CLI::Chat::Tools::MemoryStatus

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

.api_portObject



162
163
164
# File 'lib/legion/cli/chat/tools/memory_status.rb', line 162

def self.api_port
  (defined?(Legion::Settings) && Legion::Settings[:api] && Legion::Settings[:api][:port]) || 4567
end

.apollo_available?Boolean

Returns:

  • (Boolean)


144
145
146
# File 'lib/legion/cli/chat/tools/memory_status.rb', line 144

def self.apollo_available?
  defined?(Legion::Data)
end

.apollo_statsObject



126
127
128
129
130
131
132
133
134
135
# File 'lib/legion/cli/chat/tools/memory_status.rb', line 126

def self.apollo_stats
  return nil unless apollo_available?

  data = safe_fetch('/api/apollo/stats')
  return nil unless data

  data[:data] || data
rescue StandardError
  nil
end

.call(action: 'overview') ⇒ Object



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

def self.call(action: 'overview')
  case action.to_s
  when 'memories' then format_memories
  when 'apollo'   then format_apollo
  when 'sessions' then format_sessions
  else format_overview
  end
end

.format_apolloObject



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/legion/cli/chat/tools/memory_status.rb', line 76

def self.format_apollo
  stats = apollo_stats
  return 'Apollo knowledge store is not available.' unless stats

  lines = ["Apollo Knowledge Store:\n"]
  lines << format('  Total Entries:  %<v>d', v: stats[:total] || 0)
  lines << format('  Confirmed:      %<v>d', v: stats[:confirmed] || 0)
  lines << format('  Candidates:     %<v>d', v: stats[:candidates] || 0)
  lines << format('  Disputed:       %<v>d', v: stats[:disputed] || 0)
  lines << format('  Recent (24h):   %<v>d', v: stats[:recent_24h] || 0)
  lines << format('  Avg Confidence: %<v>.2f', v: stats[:avg_confidence] || 0.0)

  if stats[:domains]
    lines << ''
    lines << '  Domains:'
    stats[:domains].each do |domain, count|
      lines << format('    %<d>-20s %<c>d entries', d: domain, c: count)
    end
  end

  lines.join("\n")
end

.format_memoriesObject



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

def self.format_memories
  require 'legion/cli/chat/memory_store'
  lines = ["Persistent Memory Detail:\n"]

  project = Chat::MemoryStore.list(scope: :project)
  lines << '  Project Memory:'
  if project.empty?
    lines << '    (no entries)'
  else
    project.each_with_index do |entry, i|
      lines << format('    %<i>d. %<e>s', i: i + 1, e: truncate(entry, 100))
    end
  end

  lines << ''
  global = Chat::MemoryStore.list(scope: :global)
  lines << '  Global Memory:'
  if global.empty?
    lines << '    (no entries)'
  else
    global.each_with_index do |entry, i|
      lines << format('    %<i>d. %<e>s', i: i + 1, e: truncate(entry, 100))
    end
  end

  lines.join("\n")
end

.format_overviewObject



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/legion/cli/chat/tools/memory_status.rb', line 28

def self.format_overview
  lines = ["Memory & Knowledge Overview:\n"]

  mem = memory_stats
  lines << format('  Local Memory:   %<p>d project, %<g>d global entries', p: mem[:project], g: mem[:global])

  apollo = apollo_stats
  lines << if apollo
             format('  Apollo Store:   %<t>d entries (%<c>d confirmed, %<d>d disputed)',
                    t: apollo[:total] || 0, c: apollo[:confirmed] || 0, d: apollo[:disputed] || 0)
           else
             '  Apollo Store:   not available'
           end

  sessions = session_list
  lines << format('  Saved Sessions: %<c>d', c: sessions.size)

  lines.join("\n")
end

.format_sessionsObject



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/legion/cli/chat/tools/memory_status.rb', line 99

def self.format_sessions
  require 'legion/cli/chat/session_store'
  sessions = Chat::SessionStore.list
  return 'No saved sessions found.' if sessions.empty?

  lines = [format("Saved Sessions (%<c>d):\n", c: sessions.size)]
  sessions.first(10).each do |s|
    age = time_ago(s[:modified])
    lines << format('  %<n>-20s %<m>3d msgs  %<a>s  %<s>s',
                    n: s[:name], m: s[:message_count] || 0, a: age, s: s[:model] || '')
    lines << format('    %<v>s', v: truncate(s[:summary].to_s, 80)) if s[:summary]
  end
  lines << format('  ... and %<n>d more', n: sessions.size - 10) if sessions.size > 10

  lines.join("\n")
end

.memory_statsObject



116
117
118
119
120
121
122
123
124
# File 'lib/legion/cli/chat/tools/memory_status.rb', line 116

def self.memory_stats
  require 'legion/cli/chat/memory_store'
  {
    project: Chat::MemoryStore.list(scope: :project).size,
    global:  Chat::MemoryStore.list(scope: :global).size
  }
rescue StandardError
  { project: 0, global: 0 }
end

.safe_fetch(path) ⇒ Object



148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/legion/cli/chat/tools/memory_status.rb', line 148

def self.safe_fetch(path)
  require 'net/http'
  uri = URI("http://127.0.0.1:#{api_port}#{path}")
  http = Net::HTTP.new(uri.host, uri.port)
  http.open_timeout = 2
  http.read_timeout = 5
  response = http.request(Net::HTTP::Get.new(uri))
  return nil unless response.is_a?(Net::HTTPSuccess)

  Legion::JSON.load(response.body)
rescue StandardError
  nil
end

.session_listObject



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

def self.session_list
  require 'legion/cli/chat/session_store'
  Chat::SessionStore.list
rescue StandardError
  []
end

.time_ago(time) ⇒ Object



170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/legion/cli/chat/tools/memory_status.rb', line 170

def self.time_ago(time)
  return '?' unless time

  seconds = Time.now - time
  if seconds < 3600
    format('%<m>dm ago', m: (seconds / 60).to_i)
  elsif seconds < 86_400
    format('%<h>dh ago', h: (seconds / 3600).to_i)
  else
    format('%<d>dd ago', d: (seconds / 86_400).to_i)
  end
end

.truncate(str, max) ⇒ Object



166
167
168
# File 'lib/legion/cli/chat/tools/memory_status.rb', line 166

def self.truncate(str, max)
  str.length > max ? "#{str[0, max]}..." : str
end