lex-apollo 0.4.28 → 0.4.44

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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +107 -0
  3. data/README.md +42 -5
  4. data/lib/legion/extensions/apollo/actors/contradiction_scanner.rb +63 -0
  5. data/lib/legion/extensions/apollo/actors/entity_watchdog.rb +1 -1
  6. data/lib/legion/extensions/apollo/actors/gas_subscriber.rb +4 -6
  7. data/lib/legion/extensions/apollo/api.rb +19 -9
  8. data/lib/legion/extensions/apollo/helpers/capability.rb +8 -20
  9. data/lib/legion/extensions/apollo/helpers/confidence.rb +11 -18
  10. data/lib/legion/extensions/apollo/helpers/entity_watchdog.rb +1 -1
  11. data/lib/legion/extensions/apollo/helpers/graph_query.rb +37 -51
  12. data/lib/legion/extensions/apollo/helpers/writeback.rb +9 -13
  13. data/lib/legion/extensions/apollo/runners/entity_extractor.rb +1 -1
  14. data/lib/legion/extensions/apollo/runners/expertise.rb +3 -3
  15. data/lib/legion/extensions/apollo/runners/gas.rb +95 -25
  16. data/lib/legion/extensions/apollo/runners/knowledge.rb +177 -113
  17. data/lib/legion/extensions/apollo/runners/maintenance.rb +135 -36
  18. data/lib/legion/extensions/apollo/runners/request.rb +4 -5
  19. data/lib/legion/extensions/apollo/transport.rb +20 -0
  20. data/lib/legion/extensions/apollo/version.rb +1 -1
  21. data/lib/legion/extensions/apollo.rb +2 -0
  22. data/spec/legion/extensions/apollo/actors/contradiction_scanner_spec.rb +38 -0
  23. data/spec/legion/extensions/apollo/actors/entity_watchdog_spec.rb +1 -1
  24. data/spec/legion/extensions/apollo/actors/gas_subscriber_spec.rb +4 -14
  25. data/spec/legion/extensions/apollo/api_spec.rb +11 -21
  26. data/spec/legion/extensions/apollo/contradiction_spec.rb +0 -9
  27. data/spec/legion/extensions/apollo/helpers/capability_spec.rb +2 -2
  28. data/spec/legion/extensions/apollo/helpers/writeback_spec.rb +31 -3
  29. data/spec/legion/extensions/apollo/runners/corroboration_integration_spec.rb +191 -0
  30. data/spec/legion/extensions/apollo/runners/entity_extractor_spec.rb +2 -2
  31. data/spec/legion/extensions/apollo/runners/gas_deposit_integration_spec.rb +92 -0
  32. data/spec/legion/extensions/apollo/runners/gas_spec.rb +27 -33
  33. data/spec/legion/extensions/apollo/runners/ingest_transaction_integration_spec.rb +114 -0
  34. data/spec/legion/extensions/apollo/runners/knowledge_spec.rb +129 -57
  35. data/spec/legion/extensions/apollo/runners/lifecycle_integration_spec.rb +165 -0
  36. data/spec/legion/extensions/apollo/runners/maintenance_spec.rb +22 -108
  37. data/spec/legion/extensions/apollo/runners/request_spec.rb +43 -26
  38. metadata +35 -2
  39. data/spec/legion/extensions/apollo/helpers/graph_query_spec.rb +0 -100
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'spec_helper'
4
+ require 'open3'
5
+ require 'rbconfig'
4
6
  require 'legion/extensions/apollo/helpers/confidence'
5
7
  require 'legion/extensions/apollo/helpers/similarity'
6
8
  require 'legion/extensions/apollo/helpers/graph_query'
@@ -55,30 +57,43 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Request do
55
57
  end
56
58
 
57
59
  context 'when only transport is available' do
58
- let(:mock_transport) do
59
- Module.new do
60
- def self.connected?
61
- true
62
- end
63
-
64
- def self.respond_to?(method, *args)
65
- method == :connected? || super
66
- end
67
- end
68
- end
69
- let(:mock_message) { double('message', publish: true) }
70
-
71
- before do
72
- hide_const('Legion::Data::Model::ApolloEntry') if defined?(Legion::Data::Model::ApolloEntry)
73
- stub_const('Legion::Transport', mock_transport)
74
- stub_const('Legion::Extensions::Apollo::Transport::Messages::Query',
75
- double('QueryMsg', new: mock_message))
76
- end
77
-
78
- it 'publishes via transport' do
79
- result = described_class.query(text: 'test query')
80
- expect(result[:success]).to be true
81
- expect(result[:dispatched]).to eq(:transport)
60
+ it 'delivers all request operations through the real message classes and local transport' do
61
+ script = <<~'RUBY'
62
+ require 'rspec'
63
+ require 'spec_helper'
64
+ require 'json'
65
+ Legion::Transport::Connection.setup
66
+ channel = Legion::Transport::Connection.session.create_channel
67
+ Legion::Extensions::Apollo::Transport::Exchanges::Apollo.new('legion.apollo', channel: channel)
68
+ queries = []
69
+ ingests = []
70
+ Legion::Transport::Local.subscribe('legion.apollo.query') { |payload| queries << Legion::JSON.load(payload) }
71
+ Legion::Transport::Local.subscribe('legion.apollo.ingest') { |payload| ingests << Legion::JSON.load(payload) }
72
+ request = Legion::Extensions::Apollo::Runners::Request
73
+ results = [
74
+ request.query(text: 'remote query'),
75
+ request.retrieve(text: 'remote retrieval'),
76
+ request.traverse(entry_id: 'entry-1'),
77
+ request.ingest(content: 'remote fact', content_type: 'fact')
78
+ ]
79
+ Legion::Transport::Connection.shutdown
80
+ puts "APOLLO_RESULT=#{JSON.generate(results: results, queries: queries, ingests: ingests)}"
81
+ RUBY
82
+ stdout, stderr, status = Open3.capture3(
83
+ { 'LEGION_MODE' => 'lite' }, RbConfig.ruby, '-Ilib', '-Ispec', '-e', script,
84
+ chdir: File.expand_path('../../../../..', __dir__)
85
+ )
86
+ line = stdout.lines.find { |output| output.start_with?('APOLLO_RESULT=') }
87
+ raise "Request process failed: #{stderr}\n#{stdout}" unless status.success? && line
88
+
89
+ state = Legion::JSON.load(line.delete_prefix('APOLLO_RESULT='))
90
+ expect(state[:results]).to all(include(success: true, dispatched: 'transport'))
91
+ expect(state[:queries]).to contain_exactly(
92
+ include(action: 'query', query: 'remote query'),
93
+ include(action: 'query', query: 'remote retrieval'),
94
+ include(action: 'traverse', entry_id: 'entry-1')
95
+ )
96
+ expect(state[:ingests]).to contain_exactly(include(content: 'remote fact', content_type: 'fact'))
82
97
  end
83
98
  end
84
99
 
@@ -137,7 +152,7 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Request do
137
152
  let(:mock_expertise_class) { double('ApolloExpertise') }
138
153
  let(:mock_access_log_class) { double('ApolloAccessLog') }
139
154
  let(:mock_entry) { double('entry', id: 'uuid-123', embedding: nil) }
140
-
155
+ let(:mock_db) { double('db') }
141
156
  before do
142
157
  stub_const('Legion::Data::Model::ApolloEntry', mock_entry_class)
143
158
  stub_const('Legion::Data::Model::ApolloExpertise', mock_expertise_class)
@@ -147,7 +162,9 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Request do
147
162
  allow(mock_entry_class).to receive(:where).and_return(double(exclude: double(limit: double(each: nil), first: nil)))
148
163
  allow(mock_entry_class).to receive(:exclude)
149
164
  .and_return(double(exclude: double(limit: double(all: []))))
150
- allow(mock_entry_class).to receive(:db).and_return(double(fetch: double(all: [])))
165
+ allow(mock_entry_class).to receive(:db).and_return(mock_db)
166
+ allow(mock_db).to receive(:fetch).and_return(double(all: []))
167
+ allow(mock_db).to receive(:transaction) { |**_options, &block| block.call }
151
168
  allow(mock_entry_class).to receive(:create).and_return(mock_entry)
152
169
  allow(mock_expertise_class).to receive(:where).and_return(double(first: nil))
153
170
  allow(mock_expertise_class).to receive(:create)
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.28
4
+ version: 0.4.44
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity
@@ -149,6 +149,34 @@ dependencies:
149
149
  - - ">="
150
150
  - !ruby/object:Gem::Version
151
151
  version: '0'
152
+ - !ruby/object:Gem::Dependency
153
+ name: sinatra
154
+ requirement: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - "~>"
157
+ - !ruby/object:Gem::Version
158
+ version: '4.0'
159
+ type: :development
160
+ prerelease: false
161
+ version_requirements: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - "~>"
164
+ - !ruby/object:Gem::Version
165
+ version: '4.0'
166
+ - !ruby/object:Gem::Dependency
167
+ name: sqlite3
168
+ requirement: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ version: '2.0'
173
+ type: :development
174
+ prerelease: false
175
+ version_requirements: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - ">="
178
+ - !ruby/object:Gem::Version
179
+ version: '2.0'
152
180
  description: Durable shared knowledge with vector search, concept graph, and expertise
153
181
  tracking
154
182
  email:
@@ -160,6 +188,7 @@ files:
160
188
  - CHANGELOG.md
161
189
  - README.md
162
190
  - lib/legion/extensions/apollo.rb
191
+ - lib/legion/extensions/apollo/actors/contradiction_scanner.rb
163
192
  - lib/legion/extensions/apollo/actors/corroboration_checker.rb
164
193
  - lib/legion/extensions/apollo/actors/decay.rb
165
194
  - lib/legion/extensions/apollo/actors/entity_watchdog.rb
@@ -198,6 +227,7 @@ files:
198
227
  - lib/legion/extensions/apollo/transport/queues/writeback_store.rb
199
228
  - lib/legion/extensions/apollo/transport/queues/writeback_vectorize.rb
200
229
  - lib/legion/extensions/apollo/version.rb
230
+ - spec/legion/extensions/apollo/actors/contradiction_scanner_spec.rb
201
231
  - spec/legion/extensions/apollo/actors/decay_spec.rb
202
232
  - spec/legion/extensions/apollo/actors/entity_watchdog_spec.rb
203
233
  - spec/legion/extensions/apollo/actors/expertise_aggregator_spec.rb
@@ -212,18 +242,21 @@ files:
212
242
  - spec/legion/extensions/apollo/helpers/capability_spec.rb
213
243
  - spec/legion/extensions/apollo/helpers/confidence_spec.rb
214
244
  - spec/legion/extensions/apollo/helpers/entity_watchdog_spec.rb
215
- - spec/legion/extensions/apollo/helpers/graph_query_spec.rb
216
245
  - spec/legion/extensions/apollo/helpers/similarity_spec.rb
217
246
  - spec/legion/extensions/apollo/helpers/tag_normalizer_spec.rb
218
247
  - spec/legion/extensions/apollo/helpers/writeback_spec.rb
248
+ - spec/legion/extensions/apollo/runners/corroboration_integration_spec.rb
219
249
  - spec/legion/extensions/apollo/runners/decay_cycle_spec.rb
220
250
  - spec/legion/extensions/apollo/runners/entity_extractor_spec.rb
221
251
  - spec/legion/extensions/apollo/runners/expertise_spec.rb
222
252
  - spec/legion/extensions/apollo/runners/gas_anticipate_spec.rb
253
+ - spec/legion/extensions/apollo/runners/gas_deposit_integration_spec.rb
223
254
  - spec/legion/extensions/apollo/runners/gas_relate_spec.rb
224
255
  - spec/legion/extensions/apollo/runners/gas_spec.rb
225
256
  - spec/legion/extensions/apollo/runners/gas_synthesize_spec.rb
257
+ - spec/legion/extensions/apollo/runners/ingest_transaction_integration_spec.rb
226
258
  - spec/legion/extensions/apollo/runners/knowledge_spec.rb
259
+ - spec/legion/extensions/apollo/runners/lifecycle_integration_spec.rb
227
260
  - spec/legion/extensions/apollo/runners/maintenance_spec.rb
228
261
  - spec/legion/extensions/apollo/runners/request_spec.rb
229
262
  - spec/legion/extensions/apollo/transport/messages/ingest_spec.rb
@@ -1,100 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
- require 'legion/extensions/apollo/helpers/graph_query'
5
-
6
- RSpec.describe Legion::Extensions::Apollo::Helpers::GraphQuery do
7
- describe 'constants' do
8
- it 'defines SPREAD_FACTOR' do
9
- expect(described_class::SPREAD_FACTOR).to eq(0.6)
10
- end
11
-
12
- it 'defines DEFAULT_DEPTH' do
13
- expect(described_class::DEFAULT_DEPTH).to eq(2)
14
- end
15
-
16
- it 'defines MIN_ACTIVATION' do
17
- expect(described_class::MIN_ACTIVATION).to eq(0.1)
18
- end
19
- end
20
-
21
- describe '.build_traversal_sql' do
22
- it 'returns SQL string with entry_id placeholder' do
23
- sql = described_class.build_traversal_sql(depth: 2)
24
- expect(sql).to include('apollo_entries')
25
- expect(sql).to include('apollo_relations')
26
- expect(sql).to include('WITH RECURSIVE')
27
- expect(sql).to include(':entry_id')
28
- end
29
-
30
- it 'includes relation type filter when specified' do
31
- sql = described_class.build_traversal_sql(depth: 2, relation_types: %w[causes depends_on])
32
- expect(sql).to include("'causes'")
33
- expect(sql).to include("'depends_on'")
34
- end
35
-
36
- it 'respects custom depth' do
37
- sql = described_class.build_traversal_sql(depth: 3)
38
- expect(sql).to include('g.depth < 3')
39
- end
40
-
41
- it 'applies spread factor and min activation' do
42
- sql = described_class.build_traversal_sql(depth: 2)
43
- expect(sql).to include('0.6')
44
- expect(sql).to include('0.1')
45
- end
46
- end
47
-
48
- describe '.build_semantic_search_sql' do
49
- it 'returns SQL with vector placeholder' do
50
- sql = described_class.build_semantic_search_sql(limit: 5, min_confidence: 0.3)
51
- expect(sql).to include('apollo_entries')
52
- expect(sql).to include(':embedding')
53
- expect(sql).to include('<=>')
54
- expect(sql).to include('LIMIT 5')
55
- end
56
-
57
- it 'includes status filter' do
58
- sql = described_class.build_semantic_search_sql(limit: 10, statuses: %w[confirmed])
59
- expect(sql).to include("'confirmed'")
60
- end
61
-
62
- it 'includes tag filter when specified' do
63
- sql = described_class.build_semantic_search_sql(limit: 10, tags: %w[vault dns])
64
- expect(sql).to include("'vault'")
65
- expect(sql).to include("'dns'")
66
- expect(sql).to include('&&')
67
- end
68
-
69
- context 'without requesting_principal_id' do
70
- it 'includes no access_scope filter' do
71
- sql = described_class.build_semantic_search_sql(limit: 5, min_confidence: 0.3)
72
- expect(sql).not_to include('access_scope')
73
- end
74
- end
75
-
76
- context 'with requesting_principal_id' do
77
- let(:sql) { described_class.build_semantic_search_sql(limit: 5, min_confidence: 0.3, requesting_principal_id: 42) }
78
-
79
- it 'allows global entries unconditionally' do
80
- expect(sql).to include("access_scope = 'global'")
81
- end
82
-
83
- it 'allows private entries owned by the principal via principal FK' do
84
- expect(sql).to include('e.identity_principal_id = 42')
85
- end
86
-
87
- it 'allows private entries owned by the principal via identity subquery (multi-provider auth)' do
88
- expect(sql).to include('SELECT id FROM identities WHERE principal_id = 42')
89
- end
90
-
91
- it 'allows team entries when principal shares a group with the submitter' do
92
- expect(sql).to include('identity_group_memberships')
93
- end
94
-
95
- it 'wraps the entire access_scope condition in parentheses so it does not break AND chaining' do
96
- expect(sql).to match(/AND \(.*access_scope.*\)/m)
97
- end
98
- end
99
- end
100
- end