lex-apollo 0.4.23 → 0.4.25

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: af5cbbe35e69f28430c0c4d2862294024e465b79a7fdba279bb50562bc0d81d4
4
- data.tar.gz: d2cf12984c06eb60752f7202935649a411377ca141e20d719d2faa2ec4c05b3b
3
+ metadata.gz: 96a0b3d72ad8895d264315b2da3710c323d31280cf08fe615f8db74c9c762e82
4
+ data.tar.gz: a6d938a7409cfd0f1b5f3a0dc6c7f1f3c0399dc93a8b7ba8ffcfff0c82536bc3
5
5
  SHA512:
6
- metadata.gz: a2bf398b3f6df58ec0e84f810f7ba4e48e68991c88f95ae8e5614ba4510ae7ab9be3994dc757f34138073797cb2af99c795d532f4e24dc9fb2f09ffa0b18d13f
7
- data.tar.gz: fad944b5457ef0574bb53b1d88251d5d4dce1a70b3cbe8ffadb25b3938877a0170cd36adbf41705d066d84f7036043914efa6b7473db5999daf24badde8a7f5d
6
+ metadata.gz: efd4519deb1935da01026f3149460fafd110be233f7cdaf522c1980c4cab70a2debcb4ca4ba8a7e3b5b8be0b409d94fdf566fdba6cc8bcdcc1a3ebef879ef6fe
7
+ data.tar.gz: b0b446a0af1010dde65247cff125c17d086ebba9cf9bd0269575fc7d94fed6eab06a2d836ee6419d742b4fe150aa7b055deefb11e6160b30bde83f79b5c63d71
data/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.4.25] - 2026-05-08
4
+
5
+ ### Fixed
6
+ - Entity watchdog now remembers processed task-log text during the process lifetime so unchanged logs do not trigger repeated structured LLM extraction on every watchdog interval.
7
+
8
+ ## [0.4.24] - 2026-05-07
9
+
10
+ ### Fixed
11
+ - Prevent zero-vector embeddings from being stored as valid semantic vectors when LLM embedding is unavailable.
12
+ - Avoid self-referential corroboration relations, bound corroboration maintenance scans, and preserve valid decay age filtering.
13
+ - Parse structured GAS LLM phase responses from `result[:data]` before falling back to JSON strings.
14
+ - Make Apollo write-privilege memoization thread-safe.
15
+
3
16
  ## [0.4.23] - 2026-05-06
4
17
 
5
18
  ### Fixed
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'digest'
3
4
  require 'legion/extensions/actors/every'
4
5
  require_relative '../runners/knowledge'
5
6
  require_relative '../runners/entity_extractor'
@@ -40,6 +41,8 @@ module Legion
40
41
 
41
42
  ingested = 0
42
43
  texts.each do |text|
44
+ next if task_log_text_processed?(text)
45
+
43
46
  result = extract_entities(
44
47
  text: text,
45
48
  entity_types: entity_types,
@@ -47,6 +50,7 @@ module Legion
47
50
  )
48
51
  next unless result[:success]
49
52
 
53
+ mark_task_log_text_processed(text) unless result[:source] == :unavailable
50
54
  result[:entities].each do |entity|
51
55
  next if entity_exists_in_apollo?(entity)
52
56
 
@@ -124,6 +128,28 @@ module Legion
124
128
  def dedup_similarity_threshold
125
129
  settings[:entity_watchdog][:dedup_threshold].to_f
126
130
  end
131
+
132
+ def task_log_text_processed?(text)
133
+ processed_task_log_hashes.key?(task_log_text_hash(text))
134
+ end
135
+
136
+ def mark_task_log_text_processed(text)
137
+ hashes = processed_task_log_hashes
138
+ hashes[task_log_text_hash(text)] = true
139
+ hashes.shift while hashes.size > processed_task_log_hash_limit
140
+ end
141
+
142
+ def processed_task_log_hashes
143
+ @processed_task_log_hashes ||= {}
144
+ end
145
+
146
+ def processed_task_log_hash_limit
147
+ [settings[:entity_watchdog][:log_limit].to_i * 4, 100].max
148
+ end
149
+
150
+ def task_log_text_hash(text)
151
+ Digest::SHA256.hexdigest(text.to_s)
152
+ end
127
153
  end
128
154
  end
129
155
  end
@@ -24,8 +24,9 @@ module Legion
24
24
  end
25
25
 
26
26
  def publishable?(insight)
27
- (insight[:confidence] || 0) > PUBLISH_CONFIDENCE_THRESHOLD &&
28
- (insight[:novelty] || 0) > PUBLISH_NOVELTY_THRESHOLD
27
+ confidence = (insight[:confidence] || 0).to_f
28
+ novelty = insight[:novelty] ? insight[:novelty].to_f : (1.0 - confidence)
29
+ confidence > PUBLISH_CONFIDENCE_THRESHOLD && novelty > PUBLISH_NOVELTY_THRESHOLD
29
30
  end
30
31
 
31
32
  def handle_mesh_departure(agent_id:)
@@ -9,6 +9,7 @@ module Legion
9
9
  extend Legion::Settings::Helper
10
10
 
11
11
  EMBEDDING_MODELS = %w[mxbai-embed-large bge-large snowflake-arctic-embed].freeze
12
+ PRIVILEGE_MUTEX = Mutex.new
12
13
 
13
14
  module_function
14
15
 
@@ -57,11 +58,13 @@ module Legion
57
58
  end
58
59
 
59
60
  def check_db_write_privilege
60
- return @apollo_write_privilege unless @apollo_write_privilege.nil?
61
+ PRIVILEGE_MUTEX.synchronize do
62
+ return @apollo_write_privilege unless @apollo_write_privilege.nil?
61
63
 
62
- @apollo_write_privilege = Legion::Data.connection
63
- .fetch("SELECT has_table_privilege(current_user, 'apollo_entries', 'INSERT') AS can_insert")
64
- .first[:can_insert] == true
64
+ @apollo_write_privilege = Legion::Data.connection
65
+ .fetch("SELECT has_table_privilege(current_user, 'apollo_entries', 'INSERT') AS can_insert")
66
+ .first[:can_insert] == true
67
+ end
65
68
  rescue StandardError => e
66
69
  handle_exception(e, level: :warn, operation: 'apollo.capability.check_db_write_privilege')
67
70
  @apollo_write_privilege = false
@@ -212,7 +212,7 @@ module Legion
212
212
  { from_content: fact[:content], to_id: entry[:id], relation_type: 'similar_to', confidence: fallback_confidence }
213
213
  end
214
214
 
215
- def llm_classify_relation(fact, entry)
215
+ def llm_classify_relation(fact, entry) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
216
216
  prompt = <<~PROMPT
217
217
  Classify the relationship between these two knowledge entries.
218
218
  Valid types: #{RELATION_TYPES.join(', ')}
@@ -245,9 +245,13 @@ module Legion
245
245
  phase: 'gas_relate'
246
246
  )
247
247
 
248
- content = result.respond_to?(:message) ? result.message[:content] : result.to_s
249
- parsed = json_load(content)
250
- rels = parsed.is_a?(Hash) ? (parsed[:relations] || parsed['relations'] || []) : []
248
+ rels = if result.is_a?(Hash) && result[:data].is_a?(Hash)
249
+ result[:data][:relations] || result.dig(:data, 'relations') || []
250
+ else
251
+ content = result.respond_to?(:message) ? result.message[:content] : result.to_s
252
+ parsed = json_load(content)
253
+ parsed.is_a?(Hash) ? (parsed[:relations] || parsed['relations'] || []) : []
254
+ end
251
255
  best = rels.max_by { |r| r[:confidence] || r['confidence'] || 0 }
252
256
 
253
257
  return fallback_relation(fact, entry) unless best
@@ -302,9 +306,13 @@ module Legion
302
306
  phase: 'gas_synthesize'
303
307
  )
304
308
 
305
- content = result.respond_to?(:message) ? result.message[:content] : result.to_s
306
- parsed = json_load(content)
307
- items = parsed.is_a?(Hash) ? (parsed[:synthesis] || parsed['synthesis'] || []) : []
309
+ items = if result.is_a?(Hash) && result[:data].is_a?(Hash)
310
+ result[:data][:synthesis] || result.dig(:data, 'synthesis') || []
311
+ else
312
+ content = result.respond_to?(:message) ? result.message[:content] : result.to_s
313
+ parsed = json_load(content)
314
+ parsed.is_a?(Hash) ? (parsed[:synthesis] || parsed['synthesis'] || []) : []
315
+ end
308
316
 
309
317
  items.map { |item| build_synthesis_entry(item, facts) }
310
318
  rescue StandardError => e
@@ -358,9 +366,13 @@ module Legion
358
366
  phase: 'gas_anticipate'
359
367
  )
360
368
 
361
- content = result.respond_to?(:message) ? result.message[:content] : result.to_s
362
- parsed = json_load(content)
363
- questions = parsed.is_a?(Hash) ? (parsed[:questions] || parsed['questions'] || []) : []
369
+ questions = if result.is_a?(Hash) && result[:data].is_a?(Hash)
370
+ result[:data][:questions] || result.dig(:data, 'questions') || []
371
+ else
372
+ content = result.respond_to?(:message) ? result.message[:content] : result.to_s
373
+ parsed = json_load(content)
374
+ parsed.is_a?(Hash) ? (parsed[:questions] || parsed['questions'] || []) : []
375
+ end
364
376
  questions = questions.first(max_anticipations)
365
377
 
366
378
  questions.map do |q|
@@ -426,9 +438,13 @@ module Legion
426
438
  phase: 'gas_comprehend'
427
439
  )
428
440
 
429
- content = result.respond_to?(:message) ? result.message[:content] : result.to_s
430
- parsed = json_load(content)
431
- facts_array = parsed.is_a?(Hash) ? (parsed[:facts] || parsed['facts'] || []) : Array(parsed)
441
+ facts_array = if result.is_a?(Hash) && result[:data].is_a?(Hash)
442
+ result[:data][:facts] || result.dig(:data, 'facts') || []
443
+ else
444
+ content = result.respond_to?(:message) ? result.message[:content] : result.to_s
445
+ parsed = json_load(content)
446
+ parsed.is_a?(Hash) ? (parsed[:facts] || parsed['facts'] || []) : Array(parsed)
447
+ end
432
448
  facts_array.map do |f|
433
449
  {
434
450
  content: f[:content] || f['content'],
@@ -133,7 +133,7 @@ module Legion
133
133
  { success: false, error: e.message }
134
134
  end
135
135
 
136
- def handle_query(query:, limit: Helpers::GraphQuery.default_query_limit, min_confidence: Helpers::GraphQuery.default_query_min_confidence, status: UNSET, tags: nil, domain: nil, agent_id: 'unknown', **) # rubocop:disable Layout/LineLength
136
+ def handle_query(query:, limit: Helpers::GraphQuery.default_query_limit, min_confidence: Helpers::GraphQuery.default_query_min_confidence, status: UNSET, tags: nil, domain: nil, agent_id: 'unknown', **) # rubocop:disable Layout/LineLength, Metrics/CyclomaticComplexity
137
137
  return { success: false, error: 'apollo_data_not_available' } unless Helpers::DataModels.apollo_entry_available?
138
138
 
139
139
  entry_model = Helpers::DataModels.apollo_entry
@@ -147,6 +147,12 @@ module Legion
147
147
  end
148
148
 
149
149
  embedding = embed_text(query)
150
+ if embedding.nil?
151
+ log.warn('Apollo Knowledge.handle_query embedding unavailable; falling back to browse query')
152
+ return list_entries_chronologically(query: query, limit: limit, status: requested_status,
153
+ status_defaulted: status_defaulted, tags: tags, domain: domain)
154
+ end
155
+
150
156
  sql = Helpers::GraphQuery.build_semantic_search_sql(
151
157
  limit: limit, min_confidence: min_confidence,
152
158
  statuses: Array(requested_status).map(&:to_s), tags: tags, domain: domain
@@ -255,6 +261,11 @@ module Legion
255
261
  return { success: true, entries: [], count: 0 } if query.nil? || query.to_s.strip.empty?
256
262
 
257
263
  embedding = embed_text(query)
264
+ if embedding.nil?
265
+ log.warn('Apollo Knowledge.retrieve_relevant embedding unavailable; returning empty result')
266
+ return { success: true, entries: [], count: 0, degraded: :no_embedding }
267
+ end
268
+
258
269
  sql = Helpers::GraphQuery.build_semantic_search_sql(
259
270
  limit: limit, min_confidence: min_confidence,
260
271
  statuses: %w[confirmed candidate], tags: tags, domain: domain
@@ -354,12 +365,6 @@ module Legion
354
365
  return { success: false, error: 'content is required' }
355
366
  end
356
367
 
357
- if content_type.nil?
358
- log.warn('[apollo][handle_ingest] early-return: content_type is required ' \
359
- "content_length=#{content.to_s.length}")
360
- return { success: false, error: 'content_type is required' }
361
- end
362
-
363
368
  return nil if Helpers::DataModels.apollo_entry_available?
364
369
 
365
370
  log.warn('[apollo][handle_ingest] early-return: apollo_data_not_available ' \
@@ -378,16 +383,16 @@ module Legion
378
383
  log.debug("Apollo Knowledge.embed_text text_length=#{text.length}")
379
384
  result = Legion::LLM::Call::Embeddings.generate(text: text)
380
385
  vector = result.is_a?(Hash) ? result[:vector] : result
381
- if vector.is_a?(Array) && vector.any?
386
+ if vector.is_a?(Array) && vector.any? && vector.any? { |v| v != 0.0 }
382
387
  log.debug("Apollo Knowledge.embed_text vector_dimensions=#{vector.length}")
383
388
  vector
384
389
  else
385
- log.warn('Apollo Knowledge.embed_text returned no vector; using zero-vector fallback')
386
- Array.new(1024, 0.0)
390
+ log.warn('Apollo Knowledge.embed_text returned no usable vector; skipping embedding')
391
+ nil
387
392
  end
388
393
  rescue StandardError => e
389
394
  handle_exception(e, level: :warn, operation: 'apollo.knowledge.embed_text')
390
- Array.new(1024, 0.0)
395
+ nil
391
396
  end
392
397
 
393
398
  def normalize_text_input(value)
@@ -460,7 +465,7 @@ module Legion
460
465
  submitted_by: metadata[:submitted_by],
461
466
  submitted_from: metadata[:submitted_from],
462
467
  content_hash: content_hash,
463
- embedding: Sequel.lit("'[#{embedding.join(',')}]'::vector")
468
+ embedding: embedding ? Sequel.lit("'[#{embedding.join(',')}]'::vector") : nil
464
469
  )
465
470
  log.info("Apollo Knowledge.handle_ingest created entry_id=#{new_entry.id} status=candidate domain=#{metadata[:domain]} source_agent=#{metadata[:source_agent]}") # rubocop:disable Layout/LineLength
466
471
  new_entry.id
@@ -584,6 +589,8 @@ module Legion
584
589
  end
585
590
 
586
591
  def find_corroboration(embedding, content_type_sym, source_agent, source_channel = nil)
592
+ return [false, nil] unless embedding
593
+
587
594
  scan_limit = Helpers::Confidence.apollo_setting(:corroboration, :scan_limit, default: 50)
588
595
  log.debug("Apollo Knowledge.find_corroboration content_type=#{content_type_sym} source_agent=#{source_agent} source_channel=#{source_channel || 'nil'} scan_limit=#{scan_limit}") # rubocop:disable Layout/LineLength
589
596
  existing = Helpers::DataModels.apollo_entry
@@ -611,13 +618,6 @@ module Legion
611
618
  confidence: Helpers::Confidence.apply_corroboration_boost(confidence: entry.confidence, weight: weight),
612
619
  updated_at: Time.now
613
620
  )
614
- Helpers::DataModels.apollo_relation.create(
615
- from_entry_id: entry.id,
616
- to_entry_id: entry.id,
617
- relation_type: 'similar_to',
618
- source_agent: source_agent,
619
- weight: sim
620
- )
621
621
  log.info("Apollo Knowledge.find_corroboration matched entry_id=#{entry.id} source_agent=#{source_agent} similarity=#{sim}")
622
622
  return [true, entry.id]
623
623
  end
@@ -43,7 +43,8 @@ module Legion
43
43
  )
44
44
 
45
45
  min_age_filter = Sequel.lit(
46
- "COALESCE(updated_at, created_at) < NOW() - INTERVAL '? hours'", min_age_hours
46
+ "COALESCE(updated_at, created_at) < NOW() - (? * INTERVAL '1 hour')",
47
+ min_age_hours
47
48
  )
48
49
 
49
50
  decayed = conn[:apollo_entries]
@@ -71,8 +72,8 @@ module Legion
71
72
  return { success: false, error: 'apollo_data_not_available' }
72
73
  end
73
74
 
74
- candidates = Helpers::DataModels.apollo_entry.where(status: 'candidate').exclude(embedding: nil).all
75
- confirmed = Helpers::DataModels.apollo_entry.where(status: 'confirmed').exclude(embedding: nil).all
75
+ candidates = Helpers::DataModels.apollo_entry.where(status: 'candidate').exclude(embedding: nil).limit(100).all
76
+ confirmed = Helpers::DataModels.apollo_entry.where(status: 'confirmed').exclude(embedding: nil).limit(500).all
76
77
  log.debug("Apollo Maintenance.check_corroboration candidates=#{candidates.size} confirmed=#{confirmed.size}")
77
78
 
78
79
  promoted = 0
@@ -3,7 +3,7 @@
3
3
  module Legion
4
4
  module Extensions
5
5
  module Apollo
6
- VERSION = '0.4.23'
6
+ VERSION = '0.4.25'
7
7
  end
8
8
  end
9
9
  end
@@ -67,6 +67,13 @@ RSpec.describe Legion::Extensions::Apollo::Actor::EntityWatchdog do
67
67
  expect(actor).to have_received(:publish_entity_ingest).once
68
68
  end
69
69
 
70
+ it 'does not extract entities from the same task log text twice' do
71
+ actor.scan_and_ingest
72
+ actor.scan_and_ingest
73
+
74
+ expect(actor).to have_received(:extract_entities).once
75
+ end
76
+
70
77
  context 'when entity already exists in Apollo (high similarity)' do
71
78
  let(:existing_match) do
72
79
  { success: true, entries: [{ id: 42, content: 'lex-synapse', distance: 0.02 }], count: 1 }
@@ -18,7 +18,7 @@ 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
24
  stub_const('Legion::LLM::Call::Embeddings', embeddings_mod)
@@ -159,7 +159,7 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Knowledge do
159
159
  stub_const('Legion::Data::Model::ApolloExpertise', mock_expertise_class)
160
160
  stub_const('Legion::Data::Model::ApolloAccessLog', mock_access_log_class)
161
161
  allow(Legion::LLM::Call::Embeddings).to receive(:generate)
162
- .and_return({ vector: Array.new(1024, 0.0), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 })
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)))
@@ -328,7 +328,7 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Knowledge do
328
328
  before do
329
329
  stub_const('Legion::Data::Model::ApolloEntry', Class.new)
330
330
  allow(Legion::LLM::Call::Embeddings).to receive(:generate)
331
- .and_return({ vector: Array.new(1024, 0.0), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 })
331
+ .and_return({ vector: Array.new(1024, 0.1), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 })
332
332
  allow(Legion::Data::Model::ApolloEntry).to receive(:where)
333
333
  .and_raise(Sequel::Error, 'connection lost')
334
334
  end
@@ -406,7 +406,7 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Knowledge do
406
406
  stub_const('Legion::Data::Model::ApolloEntry', mock_entry_class)
407
407
  stub_const('Legion::Data::Model::ApolloAccessLog', mock_access_log_class)
408
408
  allow(Legion::LLM::Call::Embeddings).to receive(:generate)
409
- .and_return({ vector: Array.new(1024, 0.0), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 })
409
+ .and_return({ vector: Array.new(1024, 0.1), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 })
410
410
  allow(mock_entry_class).to receive(:db).and_return(mock_db)
411
411
  allow(mock_db).to receive(:fetch).and_return(double(all: sample_entries))
412
412
  allow(mock_entry_class).to receive(:where).and_return(double(update: true))
@@ -441,7 +441,7 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Knowledge do
441
441
  before do
442
442
  stub_const('Legion::Data::Model::ApolloEntry', mock_entry_class)
443
443
  allow(Legion::LLM::Call::Embeddings).to receive(:generate)
444
- .and_return({ vector: Array.new(1024, 0.0), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 })
444
+ .and_return({ vector: Array.new(1024, 0.1), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 })
445
445
  allow(mock_entry_class).to receive(:db).and_return(mock_db)
446
446
  allow(mock_db).to receive(:fetch).and_return(double(all: []))
447
447
  end
@@ -555,7 +555,7 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Knowledge do
555
555
  before do
556
556
  stub_const('Legion::Data::Model::ApolloEntry', mock_entry_class)
557
557
  allow(Legion::LLM::Call::Embeddings).to receive(:generate)
558
- .and_return({ vector: Array.new(1024, 0.0), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 })
558
+ .and_return({ vector: Array.new(1024, 0.1), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 })
559
559
  allow(mock_entry_class).to receive(:db).and_return(mock_db)
560
560
  allow(mock_db).to receive(:fetch).and_return(double(all: sample_entries))
561
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,7 +14,7 @@ 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
20
  stub_const('Legion::LLM::Call::Embeddings', embeddings_mod)
@@ -40,7 +40,7 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Request 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
42
  allow(Legion::LLM::Call::Embeddings).to receive(:generate)
43
- .and_return({ vector: Array.new(1024, 0.0), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 })
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))
@@ -104,7 +104,7 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Request do
104
104
  before do
105
105
  stub_const('Legion::Data::Model::ApolloEntry', mock_entry_class)
106
106
  allow(Legion::LLM::Call::Embeddings).to receive(:generate)
107
- .and_return({ vector: Array.new(1024, 0.0), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 })
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))
@@ -143,7 +143,7 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Request do
143
143
  stub_const('Legion::Data::Model::ApolloExpertise', mock_expertise_class)
144
144
  stub_const('Legion::Data::Model::ApolloAccessLog', mock_access_log_class)
145
145
  allow(Legion::LLM::Call::Embeddings).to receive(:generate)
146
- .and_return({ vector: Array.new(1024, 0.0), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 })
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: []))))
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.23
4
+ version: 0.4.25
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity