Class: Legion::CLI::Chat::Tools::Reflect

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

Constant Summary collapse

DEFAULT_PORT =
4567
DEFAULT_HOST =
'127.0.0.1'
EXTRACTION_PROMPT =
<<~PROMPT
  Extract discrete, reusable knowledge entries from the following text.
  Each entry should be a standalone fact, pattern, decision, or procedure
  that would be useful in future conversations.

  Rules:
  - One entry per line, prefixed with "- "
  - Be specific and actionable, not vague
  - Include context (file paths, module names, patterns)
  - Skip trivial observations
  - Maximum 5 entries

  Return ONLY the entries, no headers or commentary.
PROMPT

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



130
131
132
133
134
135
136
# File 'lib/legion/cli/chat/tools/reflect.rb', line 130

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

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

.call(text:, domain: nil) ⇒ Object



49
50
51
52
53
54
55
56
57
58
# File 'lib/legion/cli/chat/tools/reflect.rb', line 49

def self.call(text:, domain: nil)
  entries = extract_entries(text)
  return 'No actionable knowledge found to reflect on.' if entries.empty?

  results = ingest_entries(entries, domain)
  format_results(entries, results)
rescue StandardError => e
  Legion::Logging.warn("Reflect#execute failed: #{e.message}") if defined?(Legion::Logging)
  "Error during reflection: #{e.message}"
end

.extract_entries(text) ⇒ Object



60
61
62
63
64
65
66
67
68
69
70
# File 'lib/legion/cli/chat/tools/reflect.rb', line 60

def self.extract_entries(text)
  return [text] unless llm_available?

  response = Legion::LLM.chat_direct(
    message: "#{EXTRACTION_PROMPT}\n\nText:\n#{text}",
    model: nil, provider: nil
  )
  parse_entries(response.content)
rescue StandardError
  [text]
end

.format_results(entries, results) ⇒ Object



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

def self.format_results(entries, results)
  lines = ["Reflected on #{entries.size} knowledge entries:\n"]
  entries.each_with_index { |e, i| lines << "  #{i + 1}. #{e}" }
  lines << ''
  lines << "Saved: #{results[:apollo]} to Apollo, #{results[:memory]} to memory"
  lines.join("\n")
end

.ingest_entries(entries, domain) ⇒ Object



81
82
83
84
85
86
87
88
# File 'lib/legion/cli/chat/tools/reflect.rb', line 81

def self.ingest_entries(entries, domain)
  results = { apollo: 0, memory: 0 }
  entries.each do |entry|
    results[:apollo] += 1 if ingest_to_apollo(entry, domain)
    results[:memory] += 1 if save_to_memory(entry)
  end
  results
end

.ingest_to_apollo(content, domain) ⇒ Object



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/legion/cli/chat/tools/reflect.rb', line 90

def self.ingest_to_apollo(content, domain)
  uri = URI("http://#{DEFAULT_HOST}:#{api_port}/api/apollo/ingest")
  http = Net::HTTP.new(uri.host, uri.port)
  http.open_timeout = 2
  http.read_timeout = 5
  req = Net::HTTP::Post.new(uri.path, 'Content-Type' => 'application/json')
  req.body = ::JSON.dump({
                           content:          content,
                           content_type:     'observation',
                           tags:             %w[reflection auto-learned],
                           source_agent:     'chat',
                           source_channel:   'reflection',
                           knowledge_domain: domain
                         })
  response = http.request(req)
  response.is_a?(Net::HTTPSuccess)
rescue StandardError
  false
end

.llm_available?Boolean

Returns:

  • (Boolean)


126
127
128
# File 'lib/legion/cli/chat/tools/reflect.rb', line 126

def self.llm_available?
  defined?(Legion::LLM) && Legion::LLM.respond_to?(:chat_direct)
end

.parse_entries(content) ⇒ Object



72
73
74
75
76
77
78
79
# File 'lib/legion/cli/chat/tools/reflect.rb', line 72

def self.parse_entries(content)
  content.lines
         .map(&:strip)
         .select { |line| line.start_with?('- ') }
         .map { |line| line.sub(/\A- /, '').strip }
         .reject(&:empty?)
         .first(5)
end

.save_to_memory(entry) ⇒ Object



110
111
112
113
114
115
116
# File 'lib/legion/cli/chat/tools/reflect.rb', line 110

def self.save_to_memory(entry)
  require 'legion/cli/chat/memory_store'
  MemoryStore.add(entry, scope: :project)
  true
rescue StandardError
  false
end