lex-apollo 0.4.22 → 0.4.24

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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -0
  3. data/lib/legion/extensions/apollo/actors/corroboration_checker.rb +3 -1
  4. data/lib/legion/extensions/apollo/actors/decay.rb +3 -1
  5. data/lib/legion/extensions/apollo/actors/entity_watchdog.rb +10 -21
  6. data/lib/legion/extensions/apollo/actors/expertise_aggregator.rb +3 -1
  7. data/lib/legion/extensions/apollo/actors/gas_subscriber.rb +1 -1
  8. data/lib/legion/extensions/apollo/actors/ingest.rb +1 -1
  9. data/lib/legion/extensions/apollo/actors/query_responder.rb +1 -1
  10. data/lib/legion/extensions/apollo/actors/writeback_store.rb +1 -1
  11. data/lib/legion/extensions/apollo/actors/writeback_vectorize.rb +2 -2
  12. data/lib/legion/extensions/apollo/api.rb +28 -22
  13. data/lib/legion/extensions/apollo/gaia_integration.rb +16 -13
  14. data/lib/legion/extensions/apollo/helpers/capability.rb +19 -17
  15. data/lib/legion/extensions/apollo/helpers/confidence.rb +5 -8
  16. data/lib/legion/extensions/apollo/helpers/data_models.rb +61 -0
  17. data/lib/legion/extensions/apollo/helpers/entity_watchdog.rb +8 -15
  18. data/lib/legion/extensions/apollo/helpers/similarity.rb +5 -6
  19. data/lib/legion/extensions/apollo/helpers/writeback.rb +13 -14
  20. data/lib/legion/extensions/apollo/runners/expertise.rb +10 -8
  21. data/lib/legion/extensions/apollo/runners/gas.rb +47 -27
  22. data/lib/legion/extensions/apollo/runners/knowledge.rb +95 -80
  23. data/lib/legion/extensions/apollo/runners/maintenance.rb +7 -5
  24. data/lib/legion/extensions/apollo/runners/request.rb +7 -1
  25. data/lib/legion/extensions/apollo/version.rb +1 -1
  26. data/lib/legion/extensions/apollo.rb +96 -0
  27. data/spec/legion/extensions/apollo/actors/writeback_vectorize_spec.rb +3 -3
  28. data/spec/legion/extensions/apollo/api_spec.rb +20 -0
  29. data/spec/legion/extensions/apollo/helpers/capability_spec.rb +4 -4
  30. data/spec/legion/extensions/apollo/runners/gas_anticipate_spec.rb +0 -3
  31. data/spec/legion/extensions/apollo/runners/gas_relate_spec.rb +0 -4
  32. data/spec/legion/extensions/apollo/runners/gas_synthesize_spec.rb +0 -11
  33. data/spec/legion/extensions/apollo/runners/knowledge_spec.rb +25 -15
  34. data/spec/legion/extensions/apollo/runners/maintenance_spec.rb +8 -8
  35. data/spec/legion/extensions/apollo/runners/request_spec.rb +8 -8
  36. data/spec/spec_helper.rb +4 -0
  37. metadata +2 -1
@@ -83,6 +83,26 @@ RSpec.describe Legion::Extensions::Apollo::Api do
83
83
  )
84
84
  end
85
85
 
86
+ context 'when legion-data exposes namespaced Apollo models' do
87
+ before do
88
+ hide_const('Legion::Data::Model::ApolloEntry') if defined?(Legion::Data::Model::ApolloEntry)
89
+ hide_const('Legion::Data::Model::ApolloRelation') if defined?(Legion::Data::Model::ApolloRelation)
90
+ stub_const('Legion::Data::Model::Apollo::Entry', entry_model)
91
+ stub_const('Legion::Data::Model::Apollo::Relation', relation_model)
92
+ end
93
+
94
+ it 'uses the namespaced models for stats' do
95
+ payload = described_class.stats_payload(now: Time.utc(2026, 4, 28, 12, 0, 0))
96
+
97
+ expect(payload).to include(
98
+ total_entries: 6,
99
+ recent_24h: 2,
100
+ avg_confidence: 0.812,
101
+ total_relations: 4
102
+ )
103
+ end
104
+ end
105
+
86
106
  it 'returns an apollo data error when the entry model is unavailable' do
87
107
  hide_const('Legion::Data::Model::ApolloEntry')
88
108
 
@@ -13,6 +13,7 @@ end
13
13
 
14
14
  RSpec.describe Legion::Extensions::Apollo::Helpers::Capability do
15
15
  before do
16
+ Legion::Settings[:extensions][:apollo] = Legion::Extensions::Apollo.default_settings
16
17
  described_class.instance_variable_set(:@apollo_write_privilege, nil)
17
18
  end
18
19
 
@@ -31,12 +32,12 @@ RSpec.describe Legion::Extensions::Apollo::Helpers::Capability do
31
32
 
32
33
  describe '.can_write?' do
33
34
  it 'returns false when apollo_write setting is false' do
34
- allow(Legion::Settings).to receive(:dig).with(:data, :apollo_write).and_return(false)
35
+ described_class.settings[:data][:apollo_write] = false
35
36
  expect(described_class.can_write?).to be false
36
37
  end
37
38
 
38
39
  it 'returns false when Data is not connected' do
39
- allow(Legion::Settings).to receive(:dig).with(:data, :apollo_write).and_return(true)
40
+ described_class.settings[:data][:apollo_write] = true
40
41
  allow(Legion::Data).to receive(:connected?).and_return(false) if defined?(Legion::Data)
41
42
  expect(described_class.can_write?).to be false
42
43
  end
@@ -44,12 +45,11 @@ RSpec.describe Legion::Extensions::Apollo::Helpers::Capability do
44
45
 
45
46
  describe '.apollo_write_enabled?' do
46
47
  it 'reads from settings' do
47
- allow(Legion::Settings).to receive(:dig).with(:data, :apollo_write).and_return(true)
48
+ described_class.settings[:data][:apollo_write] = true
48
49
  expect(described_class.apollo_write_enabled?).to be true
49
50
  end
50
51
 
51
52
  it 'defaults to false' do
52
- allow(Legion::Settings).to receive(:dig).with(:data, :apollo_write).and_return(nil)
53
53
  expect(described_class.apollo_write_enabled?).to be false
54
54
  end
55
55
  end
@@ -38,9 +38,6 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Gas, '.phase_anticipate' do
38
38
  before do
39
39
  stub_const('Legion::LLM::Pipeline::GaiaCaller', gaia_caller)
40
40
  allow(gaia_caller).to receive(:structured).and_return(mock_response)
41
- allow(Legion::JSON).to receive(:load).and_return(
42
- { 'questions' => ['How fast is pgvector HNSW search?', 'What distance metrics does pgvector support?'] }
43
- )
44
41
  end
45
42
 
46
43
  it 'generates anticipated questions' do
@@ -67,7 +67,6 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Gas, '.phase_relate' do
67
67
 
68
68
  before do
69
69
  stub_const('Legion::LLM::Pipeline::GaiaCaller', gaia_caller)
70
- stub_const('Legion::JSON', double(load: { 'relations' => [{ 'relation_type' => 'depends_on', 'confidence' => 0.85 }] }))
71
70
  allow(gaia_caller).to receive(:structured).and_return(mock_response)
72
71
  end
73
72
 
@@ -85,9 +84,6 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Gas, '.phase_relate' do
85
84
  }
86
85
  )
87
86
  allow(gaia_caller).to receive(:structured).and_return(low_conf_response)
88
- allow(Legion::JSON).to receive(:load).and_return(
89
- { 'relations' => [{ 'relation_type' => 'contradicts', 'confidence' => 0.3 }] }
90
- )
91
87
 
92
88
  result = described_class.phase_relate(facts, entities)
93
89
  # Low confidence relations should fall back to similar_to
@@ -39,17 +39,6 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Gas, '.phase_synthesize' do
39
39
  before do
40
40
  stub_const('Legion::LLM::Pipeline::GaiaCaller', gaia_caller)
41
41
  allow(gaia_caller).to receive(:structured).and_return(mock_response)
42
- allow(Legion::JSON).to receive(:load).and_return(
43
- {
44
- 'synthesis' => [
45
- {
46
- 'content' => 'pgvector achieves fast similarity search through HNSW logarithmic indexing',
47
- 'content_type' => 'inference',
48
- 'source_indices' => [0, 1]
49
- }
50
- ]
51
- }
52
- )
53
42
  end
54
43
 
55
44
  it 'generates derivative knowledge entries' do
@@ -18,10 +18,10 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Knowledge do
18
18
  before do
19
19
  embeddings_mod = Module.new do
20
20
  def self.generate(*, **)
21
- { vector: Array.new(1024, 0.0), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 }
21
+ { vector: Array.new(1024, 0.1), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 }
22
22
  end
23
23
  end
24
- stub_const('Legion::LLM::Embeddings', embeddings_mod)
24
+ stub_const('Legion::LLM::Call::Embeddings', embeddings_mod)
25
25
  end
26
26
 
27
27
  describe '#store_knowledge' do
@@ -158,8 +158,8 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Knowledge do
158
158
  stub_const('Legion::Data::Model::ApolloRelation', mock_relation_class)
159
159
  stub_const('Legion::Data::Model::ApolloExpertise', mock_expertise_class)
160
160
  stub_const('Legion::Data::Model::ApolloAccessLog', mock_access_log_class)
161
- allow(Legion::LLM::Embeddings).to receive(:generate)
162
- .and_return({ vector: Array.new(1024, 0.0), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 })
161
+ allow(Legion::LLM::Call::Embeddings).to receive(:generate)
162
+ .and_return({ vector: Array.new(1024, 0.1), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 })
163
163
 
164
164
  # Corroboration lookup chain
165
165
  allow(mock_entry_class).to receive(:where).and_return(double(exclude: double(limit: empty_dataset)))
@@ -183,6 +183,15 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Knowledge do
183
183
  expect(result[:corroborated]).to be false
184
184
  end
185
185
 
186
+ it 'defaults content_type to :observation when nil' do
187
+ expect(mock_entry_class).to receive(:create).with(
188
+ hash_including(content_type: 'observation')
189
+ ).and_return(mock_entry)
190
+ result = host.handle_ingest(content: 'gaia tick result', content_type: nil,
191
+ source_agent: 'gaia')
192
+ expect(result[:success]).to be true
193
+ end
194
+
186
195
  it 'creates expertise record for source agent' do
187
196
  expect(mock_expertise_class).to receive(:create).with(
188
197
  hash_including(agent_id: 'agent-1', domain: 'ruby')
@@ -318,8 +327,8 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Knowledge do
318
327
  context 'when Sequel raises an error' do
319
328
  before do
320
329
  stub_const('Legion::Data::Model::ApolloEntry', Class.new)
321
- allow(Legion::LLM::Embeddings).to receive(:generate)
322
- .and_return({ vector: Array.new(1024, 0.0), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 })
330
+ allow(Legion::LLM::Call::Embeddings).to receive(:generate)
331
+ .and_return({ vector: Array.new(1024, 0.1), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 })
323
332
  allow(Legion::Data::Model::ApolloEntry).to receive(:where)
324
333
  .and_raise(Sequel::Error, 'connection lost')
325
334
  end
@@ -348,9 +357,10 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Knowledge do
348
357
  expect(logger).to have_received(:warn).with(/early-return: content is required/)
349
358
  end
350
359
 
351
- it 'emits a warn log when content_type is nil' do
360
+ it 'does not emit a content_type warn when content_type is nil (defaults to :observation)' do
361
+ hide_const('Legion::Data::Model::ApolloEntry') if defined?(Legion::Data::Model::ApolloEntry)
352
362
  host.handle_ingest(content: 'something', content_type: nil)
353
- expect(logger).to have_received(:warn).with(/early-return: content_type is required/)
363
+ expect(logger).not_to have_received(:warn).with(/content_type is required/)
354
364
  end
355
365
 
356
366
  it 'emits a warn log when apollo_data_not_available' do
@@ -395,8 +405,8 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Knowledge do
395
405
  before do
396
406
  stub_const('Legion::Data::Model::ApolloEntry', mock_entry_class)
397
407
  stub_const('Legion::Data::Model::ApolloAccessLog', mock_access_log_class)
398
- allow(Legion::LLM::Embeddings).to receive(:generate)
399
- .and_return({ vector: Array.new(1024, 0.0), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 })
408
+ allow(Legion::LLM::Call::Embeddings).to receive(:generate)
409
+ .and_return({ vector: Array.new(1024, 0.1), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 })
400
410
  allow(mock_entry_class).to receive(:db).and_return(mock_db)
401
411
  allow(mock_db).to receive(:fetch).and_return(double(all: sample_entries))
402
412
  allow(mock_entry_class).to receive(:where).and_return(double(update: true))
@@ -430,8 +440,8 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Knowledge do
430
440
 
431
441
  before do
432
442
  stub_const('Legion::Data::Model::ApolloEntry', mock_entry_class)
433
- allow(Legion::LLM::Embeddings).to receive(:generate)
434
- .and_return({ vector: Array.new(1024, 0.0), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 })
443
+ allow(Legion::LLM::Call::Embeddings).to receive(:generate)
444
+ .and_return({ vector: Array.new(1024, 0.1), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 })
435
445
  allow(mock_entry_class).to receive(:db).and_return(mock_db)
436
446
  allow(mock_db).to receive(:fetch).and_return(double(all: []))
437
447
  end
@@ -463,7 +473,7 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Knowledge do
463
473
  end
464
474
 
465
475
  it 'lists recent non-archived entries without generating an embedding' do
466
- expect(Legion::LLM::Embeddings).not_to receive(:generate)
476
+ expect(Legion::LLM::Call::Embeddings).not_to receive(:generate)
467
477
 
468
478
  result = host.handle_query(query: 'x', limit: 50)
469
479
 
@@ -544,8 +554,8 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Knowledge do
544
554
 
545
555
  before do
546
556
  stub_const('Legion::Data::Model::ApolloEntry', mock_entry_class)
547
- allow(Legion::LLM::Embeddings).to receive(:generate)
548
- .and_return({ vector: Array.new(1024, 0.0), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 })
557
+ allow(Legion::LLM::Call::Embeddings).to receive(:generate)
558
+ .and_return({ vector: Array.new(1024, 0.1), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 })
549
559
  allow(mock_entry_class).to receive(:db).and_return(mock_db)
550
560
  allow(mock_db).to receive(:fetch).and_return(double(all: sample_entries))
551
561
  allow(mock_entry_class).to receive(:where).and_return(double(update: true))
@@ -107,9 +107,9 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Maintenance do
107
107
  before do
108
108
  stub_const('Legion::Data::Model::ApolloEntry', mock_entry_class)
109
109
  allow(mock_entry_class).to receive(:where).with(status: 'candidate')
110
- .and_return(double(exclude: double(all: [])))
110
+ .and_return(double(exclude: double(limit: double(all: []))))
111
111
  allow(mock_entry_class).to receive(:where).with(status: 'confirmed')
112
- .and_return(double(exclude: double(all: [])))
112
+ .and_return(double(exclude: double(limit: double(all: []))))
113
113
  end
114
114
 
115
115
  it 'returns zero promoted and scanned' do
@@ -136,9 +136,9 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Maintenance do
136
136
  stub_const('Legion::Data::Model::ApolloEntry', mock_entry_class)
137
137
  stub_const('Legion::Data::Model::ApolloRelation', mock_relation_class)
138
138
  allow(mock_entry_class).to receive(:where).with(status: 'candidate')
139
- .and_return(double(exclude: double(all: [candidate])))
139
+ .and_return(double(exclude: double(limit: double(all: [candidate]))))
140
140
  allow(mock_entry_class).to receive(:where).with(status: 'confirmed')
141
- .and_return(double(exclude: double(all: [confirmed_entry])))
141
+ .and_return(double(exclude: double(limit: double(all: [confirmed_entry]))))
142
142
  allow(mock_relation_class).to receive(:create)
143
143
  end
144
144
 
@@ -174,9 +174,9 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Maintenance do
174
174
  stub_const('Legion::Data::Model::ApolloEntry', mock_entry_class)
175
175
  stub_const('Legion::Data::Model::ApolloRelation', mock_relation_class)
176
176
  allow(mock_entry_class).to receive(:where).with(status: 'candidate')
177
- .and_return(double(exclude: double(all: [candidate])))
177
+ .and_return(double(exclude: double(limit: double(all: [candidate]))))
178
178
  allow(mock_entry_class).to receive(:where).with(status: 'confirmed')
179
- .and_return(double(exclude: double(all: [confirmed_entry])))
179
+ .and_return(double(exclude: double(limit: double(all: [confirmed_entry]))))
180
180
  end
181
181
 
182
182
  it 'does not promote even with different providers' do
@@ -198,9 +198,9 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Maintenance do
198
198
  before do
199
199
  stub_const('Legion::Data::Model::ApolloEntry', mock_entry_class)
200
200
  allow(mock_entry_class).to receive(:where).with(status: 'candidate')
201
- .and_return(double(exclude: double(all: [candidate])))
201
+ .and_return(double(exclude: double(limit: double(all: [candidate]))))
202
202
  allow(mock_entry_class).to receive(:where).with(status: 'confirmed')
203
- .and_return(double(exclude: double(all: [confirmed_entry])))
203
+ .and_return(double(exclude: double(limit: double(all: [confirmed_entry]))))
204
204
  end
205
205
 
206
206
  it 'does not promote' do
@@ -14,10 +14,10 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Request do
14
14
 
15
15
  embeddings_mod = Module.new do
16
16
  def self.generate(*, **)
17
- { vector: Array.new(1024, 0.0), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 }
17
+ { vector: Array.new(1024, 0.1), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 }
18
18
  end
19
19
  end
20
- stub_const('Legion::LLM::Embeddings', embeddings_mod)
20
+ stub_const('Legion::LLM::Call::Embeddings', embeddings_mod)
21
21
  end
22
22
 
23
23
  describe '.data_required?' do
@@ -39,8 +39,8 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Request do
39
39
  before do
40
40
  stub_const('Legion::Data::Model::ApolloEntry', mock_entry_class)
41
41
  stub_const('Legion::Data::Model::ApolloAccessLog', mock_access_log_class)
42
- allow(Legion::LLM::Embeddings).to receive(:generate)
43
- .and_return({ vector: Array.new(1024, 0.0), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 })
42
+ allow(Legion::LLM::Call::Embeddings).to receive(:generate)
43
+ .and_return({ vector: Array.new(1024, 0.1), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 })
44
44
  allow(mock_entry_class).to receive(:db).and_return(mock_db)
45
45
  allow(mock_db).to receive(:fetch).and_return(double(all: sample_entries))
46
46
  allow(mock_entry_class).to receive(:where).and_return(double(update: true))
@@ -103,8 +103,8 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Request do
103
103
 
104
104
  before do
105
105
  stub_const('Legion::Data::Model::ApolloEntry', mock_entry_class)
106
- allow(Legion::LLM::Embeddings).to receive(:generate)
107
- .and_return({ vector: Array.new(1024, 0.0), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 })
106
+ allow(Legion::LLM::Call::Embeddings).to receive(:generate)
107
+ .and_return({ vector: Array.new(1024, 0.1), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 })
108
108
  allow(mock_entry_class).to receive(:db).and_return(mock_db)
109
109
  allow(mock_db).to receive(:fetch).and_return(double(all: []))
110
110
  allow(mock_entry_class).to receive(:where).and_return(double(update: true))
@@ -142,8 +142,8 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Request do
142
142
  stub_const('Legion::Data::Model::ApolloEntry', mock_entry_class)
143
143
  stub_const('Legion::Data::Model::ApolloExpertise', mock_expertise_class)
144
144
  stub_const('Legion::Data::Model::ApolloAccessLog', mock_access_log_class)
145
- allow(Legion::LLM::Embeddings).to receive(:generate)
146
- .and_return({ vector: Array.new(1024, 0.0), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 })
145
+ allow(Legion::LLM::Call::Embeddings).to receive(:generate)
146
+ .and_return({ vector: Array.new(1024, 0.1), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 })
147
147
  allow(mock_entry_class).to receive(:where).and_return(double(exclude: double(limit: double(each: nil), first: nil)))
148
148
  allow(mock_entry_class).to receive(:exclude)
149
149
  .and_return(double(exclude: double(limit: double(all: []))))
data/spec/spec_helper.rb CHANGED
@@ -52,6 +52,10 @@ module Legion
52
52
  end
53
53
  end
54
54
 
55
+ require 'legion/extensions/apollo'
56
+
57
+ Legion::Settings[:extensions][:apollo] = Legion::Extensions::Apollo.default_settings
58
+
55
59
  RSpec.configure do |config|
56
60
  config.example_status_persistence_file_path = '.rspec_status'
57
61
  config.disable_monkey_patching!
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lex-apollo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.22
4
+ version: 0.4.24
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity
@@ -174,6 +174,7 @@ files:
174
174
  - lib/legion/extensions/apollo/gaia_integration.rb
175
175
  - lib/legion/extensions/apollo/helpers/capability.rb
176
176
  - lib/legion/extensions/apollo/helpers/confidence.rb
177
+ - lib/legion/extensions/apollo/helpers/data_models.rb
177
178
  - lib/legion/extensions/apollo/helpers/entity_watchdog.rb
178
179
  - lib/legion/extensions/apollo/helpers/graph_query.rb
179
180
  - lib/legion/extensions/apollo/helpers/similarity.rb