lex-agentic-learning 0.1.10 → 0.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a4da6d50340dafa221b40f6c63e1af34678d45c2729be6d3b2e67701ea6ee19a
4
- data.tar.gz: f485edff0dacee93438320daf14fb9171b5685a51afeba146e4c70f1fe10e0cb
3
+ metadata.gz: 0b9c9e5ffb94a852f69565d33096e38bc7314620ce3eb227c38f062b8ea79a13
4
+ data.tar.gz: a305ff18874b4ebac3878db8f18304f24c5a2f9575a5711d9bd73015b2dd6ee3
5
5
  SHA512:
6
- metadata.gz: c5cc7fb0ac840ea9b1f3f0914f26c395a36ca2a6f8cbc3e03dbc33ab13c0140bd5b5dba73e22fb7879e7fe7f5b6f82c4d2202c594cbcd781c1a6a292ea1f0fe1
7
- data.tar.gz: fb6446f01f7d2b0659b98e51971248c2f2d913df1c952cc0e4aa083973e3302aa205e0e5132e1ac47f6cc7723db6db76b55720f2a6e1a0e0d6bea36cc3075e74
6
+ metadata.gz: 78c10230805690d2207721eb815a8bce9bcbbc2c1d4c571d5c417dbd5d1480de5ce059fae848a6415d2a04c0c5bfbf9427ea2942f3d7f70c4a1f54e86277d987
7
+ data.tar.gz: 7fa6dce7d74b53af81283e29dc590697fd746321f031abe48d4b34f9d5c9c58c0f07eab6725e5b84dcec0abb94cbb15bcea7e529bc28ad960619f8490867709a
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.1.12] - 2026-05-15
4
+ ### Fixed
5
+ - Curiosity `store_insight_in_apollo` now calls `Legion::Apollo.ingest` directly (replacing `Legion::Extensions::Apollo.store` which was a non-existent method and silently failed on every call); adds `access_scope: 'private'` and `identity_principal_id: nil`.
6
+ - OutcomeListener `write_apollo_lesson` now calls `Legion::Apollo.ingest` directly (replacing `ingest_knowledge(content: ...)` which was a dormant `ArgumentError` in Ruby 3.x due to missing positional arg); adds `access_scope: 'private'` and `identity_principal_id: nil`.
7
+
8
+ ## [0.1.11] - 2026-05-07
9
+ ### Fixed
10
+ - Curiosity self-inquiry now uses native `Legion::LLM.chat` response hashes directly and avoids obsolete legacy LLM fallback calls after native chat succeeds.
11
+
3
12
  ## [0.1.10] - 2026-04-28
4
13
  ### Fixed
5
14
  - Curiosity self-inquiry now prefers the current `Legion::LLM.ask` router before legacy helper fallbacks, preventing old `lex(:llm, :complete)` failures from hiding native LLM availability.
@@ -136,20 +136,15 @@ module Legion
136
136
  def query_llm_for_wonder(question, domain)
137
137
  prompt = build_self_inquiry_prompt(question, domain)
138
138
 
139
- # Prefer the current legion-llm router; legacy helper paths are fallbacks only.
140
- if defined?(Legion::LLM) && Legion::LLM.respond_to?(:ask)
141
- result = Legion::LLM.ask(message: prompt) # rubocop:disable Legion/HelperMigration/DirectLlm
142
- text = extract_llm_text(result)
143
- return text if text && !text.empty?
144
- end
145
-
146
- if respond_to?(:lex, true)
147
- text = query_legacy_lex_llm(prompt)
148
- return text if text && !text.empty?
149
- end
150
-
151
- if defined?(Legion::LLM) && Legion::LLM.respond_to?(:complete)
152
- result = Legion::LLM.complete(prompt: prompt, max_tokens: 300)
139
+ if defined?(Legion::LLM) && Legion::LLM.respond_to?(:chat)
140
+ result = Legion::LLM.chat( # rubocop:disable Legion/HelperMigration/DirectLlm
141
+ message: prompt,
142
+ caller: {
143
+ extension: 'lex-agentic-learning',
144
+ operation: 'curiosity',
145
+ phase: 'self_inquiry'
146
+ }
147
+ )
153
148
  text = extract_llm_text(result)
154
149
  return text if text && !text.empty?
155
150
  end
@@ -160,19 +155,13 @@ module Legion
160
155
  nil
161
156
  end
162
157
 
163
- def query_legacy_lex_llm(prompt)
164
- result = lex(:llm, :complete, prompt: prompt, max_tokens: 300)
165
- extract_llm_text(result)
166
- rescue StandardError => e
167
- log.warn "[curiosity:self_inquiry] legacy lex LLM query failed: #{e.class}: #{e.message}"
168
- nil
169
- end
170
-
171
158
  def extract_llm_text(result)
172
159
  return result.strip if result.is_a?(String)
160
+ return result.content.to_s.strip if result.respond_to?(:content)
173
161
  return nil unless result.is_a?(Hash)
174
162
 
175
- text = result[:response] || result[:content] || result[:text] || result[:completion]
163
+ text = result[:response] || result[:content] || result[:text] || result[:completion] ||
164
+ result.dig(:message, :content) || result.dig('message', 'content')
176
165
  text&.to_s&.strip
177
166
  end
178
167
 
@@ -188,15 +177,16 @@ module Legion
188
177
  end
189
178
 
190
179
  def store_insight_in_apollo(question, insight, domain)
191
- return unless defined?(Legion::Extensions::Apollo)
180
+ return unless defined?(Legion::Apollo)
192
181
 
193
- Legion::Extensions::Apollo.store(
194
- content: "Self-inquiry insight [#{domain}]: #{question}\n\n#{insight}",
195
- content_type: :observation,
196
- tags: ['gaia-self-inquiry', "domain-#{domain}", 'autonomous-thought']
182
+ Legion::Apollo.ingest( # rubocop:disable Legion/HelperMigration/DirectKnowledge
183
+ content: "Self-inquiry insight [#{domain}]: #{question}\n\n#{insight}",
184
+ tags: ['gaia-self-inquiry', "domain-#{domain}", 'autonomous-thought'],
185
+ access_scope: 'private',
186
+ identity_principal_id: nil
197
187
  )
198
188
  rescue StandardError => e
199
- log.warn "[curiosity:self_inquiry] Apollo store failed: #{e.class}: #{e.message}"
189
+ log.warn "[curiosity:self_inquiry] Apollo ingest failed: #{e.class}: #{e.message}"
200
190
  end
201
191
 
202
192
  def create_wonders_from_gaps(gaps)
@@ -83,11 +83,15 @@ module Legion
83
83
  def write_apollo_lesson(lesson, source_agent)
84
84
  return unless defined?(Legion::Apollo)
85
85
 
86
- ingest_knowledge(content: json_generate(lesson),
87
- content_type: 'task_outcome_lesson',
88
- tags: ['task_outcome', lesson[:domain]],
89
- source_agent: source_agent,
90
- knowledge_domain: 'learning')
86
+ Legion::Apollo.ingest( # rubocop:disable Legion/HelperMigration/DirectKnowledge
87
+ content: json_generate(lesson),
88
+ tags: ['task_outcome', lesson[:domain]].compact,
89
+ source_agent: source_agent,
90
+ access_scope: 'private',
91
+ content_type: 'task_outcome_lesson',
92
+ knowledge_domain: 'learning',
93
+ identity_principal_id: nil
94
+ )
91
95
  rescue StandardError => e
92
96
  log.warn "[outcome_listener] apollo write failed: #{e.message}"
93
97
  end
@@ -4,7 +4,7 @@ module Legion
4
4
  module Extensions
5
5
  module Agentic
6
6
  module Learning
7
- VERSION = '0.1.10'
7
+ VERSION = '0.1.12'
8
8
  end
9
9
  end
10
10
  end
@@ -163,12 +163,12 @@ RSpec.describe Legion::Extensions::Agentic::Learning::Curiosity::Runners::Curios
163
163
  allow(client).to receive(:respond_to?).with(:lex, true).and_return(false)
164
164
  end
165
165
 
166
- it 'uses Legion::LLM.ask response hashes for current legion-llm' do
166
+ it 'uses Legion::LLM.chat response hashes for current legion-llm' do
167
167
  llm = Module.new do
168
- def self.ask(message:)
168
+ def self.chat(message:, **)
169
169
  raise 'missing prompt' if message.to_s.empty?
170
170
 
171
- { response: ' useful insight ' }
171
+ { content: ' useful insight ' }
172
172
  end
173
173
  end
174
174
  stub_const('Legion::LLM', llm)
@@ -176,33 +176,81 @@ RSpec.describe Legion::Extensions::Agentic::Learning::Curiosity::Runners::Curios
176
176
  expect(client.send(:query_llm_for_wonder, 'why?', :curiosity)).to eq('useful insight')
177
177
  end
178
178
 
179
- it 'does not let legacy lex complete failures hide current Legion::LLM.ask' do
179
+ it 'does not call legacy LLM paths when native chat succeeds' do
180
180
  allow(client).to receive(:respond_to?).with(:lex, true).and_return(true)
181
- allow(client).to receive(:lex).and_raise(StandardError, 'legacy unavailable')
181
+ expect(client).not_to receive(:lex)
182
182
 
183
183
  llm = Module.new do
184
- def self.ask(message:)
184
+ def self.chat(message:, **)
185
185
  raise 'missing prompt' if message.to_s.empty?
186
186
 
187
- { response: 'ask survived' }
187
+ { content: 'chat survived' }
188
188
  end
189
189
  end
190
190
  stub_const('Legion::LLM', llm)
191
191
 
192
- expect(client.send(:query_llm_for_wonder, 'why?', :curiosity)).to eq('ask survived')
192
+ expect(client.send(:query_llm_for_wonder, 'why?', :curiosity)).to eq('chat survived')
193
193
  end
194
194
 
195
- it 'keeps legacy Legion::LLM.complete fallback for older installs' do
196
- llm = Module.new do
197
- def self.complete(prompt:, max_tokens:)
198
- raise 'missing prompt' if prompt.to_s.empty? || max_tokens != 300
195
+ it 'returns nil when native chat is unavailable' do
196
+ llm = Module.new
197
+ stub_const('Legion::LLM', llm)
198
+
199
+ expect(client.send(:query_llm_for_wonder, 'why?', :curiosity)).to be_nil
200
+ end
201
+ end
199
202
 
200
- { content: 'legacy insight' }
203
+ describe '#store_insight_in_apollo' do
204
+ before do
205
+ mock_apollo = Module.new do
206
+ def self.started?
207
+ true
208
+ end
209
+
210
+ def self.ingest(**)
211
+ { success: true }
201
212
  end
202
213
  end
203
- stub_const('Legion::LLM', llm)
214
+ stub_const('Legion::Apollo', mock_apollo)
215
+ end
204
216
 
205
- expect(client.send(:query_llm_for_wonder, 'why?', :curiosity)).to eq('legacy insight')
217
+ it 'passes access_scope: private to Legion::Apollo.ingest' do
218
+ allow(Legion::Apollo).to receive(:ingest).and_return({ success: true })
219
+ client.send(:store_insight_in_apollo, 'How does Consul ACL work?', 'found it', 'consul')
220
+ expect(Legion::Apollo).to have_received(:ingest).with(
221
+ hash_including(access_scope: 'private')
222
+ )
223
+ end
224
+
225
+ it 'includes the domain and question in the ingested content' do
226
+ allow(Legion::Apollo).to receive(:ingest).and_return({ success: true })
227
+ client.send(:store_insight_in_apollo, 'What is raft?', 'distributed consensus', 'distributed_systems')
228
+ expect(Legion::Apollo).to have_received(:ingest).with(
229
+ hash_including(
230
+ content: a_string_including('distributed_systems'),
231
+ tags: array_including('gaia-self-inquiry', 'domain-distributed_systems')
232
+ )
233
+ )
234
+ end
235
+
236
+ it 'is a no-op when Legion::Apollo is not defined' do
237
+ hide_const('Legion::Apollo')
238
+ expect { client.send(:store_insight_in_apollo, 'q?', 'insight', 'test') }.not_to raise_error
239
+ end
240
+
241
+ it 'does not inject process identity as the owner' do
242
+ stub_const('Legion::Identity::Process', Module.new do
243
+ extend self
244
+
245
+ define_method(:identity_hash) do
246
+ { canonical_name: 'daemon', db_principal_id: 999, db_identity_id: 888 }
247
+ end
248
+ end)
249
+ allow(Legion::Apollo).to receive(:ingest).and_return({ success: true })
250
+ client.send(:store_insight_in_apollo, 'q?', 'insight', 'test')
251
+ expect(Legion::Apollo).to have_received(:ingest).with(
252
+ hash_including(identity_principal_id: nil)
253
+ )
206
254
  end
207
255
  end
208
256
  end
@@ -115,4 +115,64 @@ RSpec.describe Legion::Extensions::Agentic::Learning::OutcomeListener::Runners::
115
115
  expect(result[:domain]).to eq('unknown')
116
116
  end
117
117
  end
118
+
119
+ describe '#write_apollo_lesson' do
120
+ let(:lesson) do
121
+ { runner_class: 'Http::Get', function: 'fetch', status: 'task.completed',
122
+ domain: 'http', success: true, confidence: 0.8 }
123
+ end
124
+
125
+ before do
126
+ mock_apollo = Module.new do
127
+ def self.ingest(**)
128
+ { success: true }
129
+ end
130
+ end
131
+ stub_const('Legion::Apollo', mock_apollo)
132
+ end
133
+
134
+ it 'passes access_scope: private to Legion::Apollo.ingest' do
135
+ allow(Legion::Apollo).to receive(:ingest).and_return({ success: true })
136
+ client.send(:write_apollo_lesson, lesson, 'agent-1')
137
+ expect(Legion::Apollo).to have_received(:ingest).with(
138
+ hash_including(access_scope: 'private')
139
+ )
140
+ end
141
+
142
+ it 'includes source_agent in the ingest call' do
143
+ allow(Legion::Apollo).to receive(:ingest).and_return({ success: true })
144
+ client.send(:write_apollo_lesson, lesson, 'agent-99')
145
+ expect(Legion::Apollo).to have_received(:ingest).with(
146
+ hash_including(source_agent: 'agent-99')
147
+ )
148
+ end
149
+
150
+ it 'tags the entry with task_outcome and domain' do
151
+ allow(Legion::Apollo).to receive(:ingest).and_return({ success: true })
152
+ client.send(:write_apollo_lesson, lesson, 'agent-1')
153
+ expect(Legion::Apollo).to have_received(:ingest).with(
154
+ hash_including(tags: array_including('task_outcome', 'http'))
155
+ )
156
+ end
157
+
158
+ it 'is a no-op when Legion::Apollo is not defined' do
159
+ hide_const('Legion::Apollo')
160
+ expect { client.send(:write_apollo_lesson, lesson, 'agent-1') }.not_to raise_error
161
+ end
162
+
163
+ it 'does not inject process identity as the owner' do
164
+ stub_const('Legion::Identity::Process', Module.new do
165
+ extend self
166
+
167
+ define_method(:identity_hash) do
168
+ { canonical_name: 'daemon', db_principal_id: 999, db_identity_id: 888 }
169
+ end
170
+ end)
171
+ allow(Legion::Apollo).to receive(:ingest).and_return({ success: true })
172
+ client.send(:write_apollo_lesson, lesson, 'agent-1')
173
+ expect(Legion::Apollo).to have_received(:ingest).with(
174
+ hash_including(identity_principal_id: nil)
175
+ )
176
+ end
177
+ end
118
178
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lex-agentic-learning
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.10
4
+ version: 0.1.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity