lex-apollo 0.4.7 → 0.4.8

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: 4ed5f6ac36b031c41850cae2f4e4c6768666c4151c730b7557f7d5f21ac24df7
4
- data.tar.gz: 870ffad7838f068a01aa2b03af51ba2ae7df1cfe30debda9a8d3275705ef775a
3
+ metadata.gz: ef4d63cf3e8b7a9ce86f11af1150b851a911fa7383ca2233d753bc328be31cb5
4
+ data.tar.gz: 8d4e3f73d3d7d04c1dff338eb226a3385d0977d57d77c2378d41723b3fff80e6
5
5
  SHA512:
6
- metadata.gz: '0583ae3da3f19c3e852929e97947bf49b94e89d37f918d65fcc2507719049ac641ab6867c1e2b7eee899c923c1cb52ec75670c3cab345b159506d34a01099463'
7
- data.tar.gz: 4f77f0e210baa01f3e77b06808ddfe06456d4f10358b8add5e4d602b8669be9b8223e6b69c30f2b56b2d18f20e5d0eabde504b05a8426812236d9a5918631bc4
6
+ metadata.gz: fae193470f3da5dedcdb5ee91310ac824064aa3ebd60de9f21e9d9a6c5eb770db23ad21603cb6c8d7337fb7fb84b1f85f9c3155f1ff38cac2bd426a3270eaabf
7
+ data.tar.gz: 6d6827a8defe1e41724a3033193f774034830cddf28bd34096622410f736d233a79853567f71b5e2f7547e48194ff3d23ce685633b55f832747ad37026c54df5
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.4.8] - 2026-03-25
4
+
5
+ ### Changed
6
+ - Removed `Helpers::Embedding` direct Ollama bypass; all embedding generation now routes through `Legion::LLM::Embeddings.generate`
7
+ - `Runners::Knowledge`, `Helpers::Writeback`, and `Actor::WritebackVectorize` all use `Legion::LLM::Embeddings.generate` and extract `[:vector]` from the result hash
8
+ - Zero-vector fallback (`Array.new(1024, 0.0)`) preserved when LLM is unavailable or returns nil vector
9
+
10
+ ### Removed
11
+ - `lib/legion/extensions/apollo/helpers/embedding.rb` — direct Faraday/Ollama embedding bypass (superseded by `Legion::LLM` provider routing)
12
+ - `spec/legion/extensions/apollo/helpers/embedding_spec.rb` — tests for removed helper
13
+
3
14
  ## [0.4.7] - 2026-03-25
4
15
 
5
16
  ### Added
@@ -14,7 +14,9 @@ module Legion
14
14
 
15
15
  def handle_vectorize(payload)
16
16
  payload = symbolize(payload)
17
- embedding = Helpers::Embedding.generate(text: payload[:content])
17
+ result = Legion::LLM::Embeddings.generate(text: payload[:content])
18
+ vector = result.is_a?(Hash) ? result[:vector] : result
19
+ embedding = vector.is_a?(Array) && vector.any? ? vector : Array.new(1024, 0.0)
18
20
  enriched = payload.merge(embedding: embedding)
19
21
 
20
22
  if Helpers::Capability.can_write?
@@ -64,8 +64,9 @@ module Legion
64
64
  can_write = Helpers::Capability.can_write?
65
65
 
66
66
  if can_embed
67
- embedding = Helpers::Embedding.generate(text: payload[:content])
68
- payload[:embedding] = embedding
67
+ result = Legion::LLM::Embeddings.generate(text: payload[:content])
68
+ vector = result.is_a?(Hash) ? result[:vector] : result
69
+ payload[:embedding] = vector.is_a?(Array) && vector.any? ? vector : Array.new(1024, 0.0)
69
70
  end
70
71
 
71
72
  if can_write && can_embed
@@ -2,7 +2,6 @@
2
2
 
3
3
  require 'json'
4
4
  require_relative '../helpers/confidence'
5
- require_relative '../helpers/embedding'
6
5
 
7
6
  module Legion
8
7
  module Extensions
@@ -75,7 +74,7 @@ module Legion
75
74
  end
76
75
  end
77
76
 
78
- embedding = Helpers::Embedding.generate(text: content)
77
+ embedding = embed_text(content)
79
78
  content_type_sym = content_type.to_s
80
79
  tag_array = defined?(Helpers::TagNormalizer) ? Helpers::TagNormalizer.normalize_all(tags) : Array(tags)
81
80
  domain = knowledge_domain || tag_array.first || 'general'
@@ -119,7 +118,7 @@ module Legion
119
118
  def handle_query(query:, limit: Helpers::GraphQuery.default_query_limit, min_confidence: Helpers::GraphQuery.default_query_min_confidence, status: [:confirmed], tags: nil, domain: nil, agent_id: 'unknown', **) # rubocop:disable Metrics/ParameterLists, Layout/LineLength
120
119
  return { success: false, error: 'apollo_data_not_available' } unless defined?(Legion::Data::Model::ApolloEntry)
121
120
 
122
- embedding = Helpers::Embedding.generate(text: query)
121
+ embedding = embed_text(query)
123
122
  sql = Helpers::GraphQuery.build_semantic_search_sql(
124
123
  limit: limit, min_confidence: min_confidence,
125
124
  statuses: Array(status).map(&:to_s), tags: tags, domain: domain
@@ -228,7 +227,7 @@ module Legion
228
227
 
229
228
  return { success: true, entries: [], count: 0 } if query.nil? || query.to_s.strip.empty?
230
229
 
231
- embedding = Helpers::Embedding.generate(text: query.to_s)
230
+ embedding = embed_text(query.to_s)
232
231
  sql = Helpers::GraphQuery.build_semantic_search_sql(
233
232
  limit: limit, min_confidence: min_confidence,
234
233
  statuses: ['confirmed'], tags: tags, domain: domain
@@ -310,6 +309,14 @@ module Legion
310
309
 
311
310
  private
312
311
 
312
+ def embed_text(text)
313
+ result = Legion::LLM::Embeddings.generate(text: text)
314
+ vector = result.is_a?(Hash) ? result[:vector] : result
315
+ vector.is_a?(Array) && vector.any? ? vector : Array.new(1024, 0.0)
316
+ rescue StandardError
317
+ Array.new(1024, 0.0)
318
+ end
319
+
313
320
  def allowed_domains_for(target_domain)
314
321
  rules = if defined?(Legion::Settings) && Legion::Settings.dig(:apollo, :domain_isolation)
315
322
  Legion::Settings.dig(:apollo, :domain_isolation)
@@ -2,7 +2,6 @@
2
2
 
3
3
  require_relative '../helpers/confidence'
4
4
  require_relative '../helpers/similarity'
5
- require_relative '../helpers/embedding'
6
5
 
7
6
  module Legion
8
7
  module Extensions
@@ -3,7 +3,7 @@
3
3
  module Legion
4
4
  module Extensions
5
5
  module Apollo
6
- VERSION = '0.4.7'
6
+ VERSION = '0.4.8'
7
7
  end
8
8
  end
9
9
  end
@@ -39,7 +39,6 @@ unless defined?(Legion::Transport::Message)
39
39
  $LOADED_FEATURES << 'legion/transport/exchange' unless $LOADED_FEATURES.include?('legion/transport/exchange')
40
40
  end
41
41
 
42
- require 'legion/extensions/apollo/helpers/embedding'
43
42
  require 'legion/extensions/apollo/helpers/capability'
44
43
  require 'legion/extensions/apollo/transport/exchanges/apollo'
45
44
  require 'legion/extensions/apollo/transport/messages/writeback'
@@ -48,6 +47,15 @@ require 'legion/extensions/apollo/actors/writeback_vectorize'
48
47
  RSpec.describe Legion::Extensions::Apollo::Actor::WritebackVectorize do
49
48
  subject(:actor) { described_class.new }
50
49
 
50
+ before do
51
+ embeddings_mod = Module.new do
52
+ def self.generate(*, **)
53
+ { vector: Array.new(1024, 0.1), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 }
54
+ end
55
+ end
56
+ stub_const('Legion::LLM::Embeddings', embeddings_mod)
57
+ end
58
+
51
59
  describe '#runner_function' do
52
60
  it 'returns handle_vectorize' do
53
61
  expect(actor.runner_function).to eq('handle_vectorize')
@@ -58,7 +66,8 @@ RSpec.describe Legion::Extensions::Apollo::Actor::WritebackVectorize do
58
66
  let(:payload) { { content: 'test content', content_type: 'observation', tags: %w[test] } }
59
67
 
60
68
  before do
61
- allow(Legion::Extensions::Apollo::Helpers::Embedding).to receive(:generate).and_return([0.1] * 1024)
69
+ allow(Legion::LLM::Embeddings).to receive(:generate)
70
+ .and_return({ vector: [0.1] * 1024, model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 })
62
71
  allow(Legion::Extensions::Apollo::Helpers::Capability).to receive(:can_write?).and_return(false)
63
72
  end
64
73
 
@@ -83,7 +92,7 @@ RSpec.describe Legion::Extensions::Apollo::Actor::WritebackVectorize do
83
92
  end
84
93
 
85
94
  it 'returns error hash on failure' do
86
- allow(Legion::Extensions::Apollo::Helpers::Embedding).to receive(:generate).and_raise(RuntimeError, 'boom')
95
+ allow(Legion::LLM::Embeddings).to receive(:generate).and_raise(RuntimeError, 'boom')
87
96
 
88
97
  result = actor.handle_vectorize(payload)
89
98
  expect(result[:success]).to be false
@@ -3,7 +3,6 @@
3
3
  require 'spec_helper'
4
4
  require 'legion/extensions/apollo/helpers/confidence'
5
5
  require 'legion/extensions/apollo/helpers/similarity'
6
- require 'legion/extensions/apollo/helpers/embedding'
7
6
  require 'legion/extensions/apollo/helpers/graph_query'
8
7
  require 'legion/extensions/apollo/helpers/tag_normalizer'
9
8
  require 'legion/extensions/apollo/helpers/writeback'
@@ -16,6 +15,15 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Knowledge do
16
15
  obj
17
16
  end
18
17
 
18
+ before do
19
+ embeddings_mod = Module.new do
20
+ def self.generate(*, **)
21
+ { vector: Array.new(1024, 0.0), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 }
22
+ end
23
+ end
24
+ stub_const('Legion::LLM::Embeddings', embeddings_mod)
25
+ end
26
+
19
27
  describe '#store_knowledge' do
20
28
  it 'returns a message payload hash' do
21
29
  result = runner.store_knowledge(
@@ -119,8 +127,8 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Knowledge do
119
127
  stub_const('Legion::Data::Model::ApolloRelation', mock_relation_class)
120
128
  stub_const('Legion::Data::Model::ApolloExpertise', mock_expertise_class)
121
129
  stub_const('Legion::Data::Model::ApolloAccessLog', mock_access_log_class)
122
- allow(Legion::Extensions::Apollo::Helpers::Embedding).to receive(:generate)
123
- .and_return(Array.new(1536, 0.0))
130
+ allow(Legion::LLM::Embeddings).to receive(:generate)
131
+ .and_return({ vector: Array.new(1024, 0.0), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 })
124
132
 
125
133
  # Corroboration lookup chain
126
134
  allow(mock_entry_class).to receive(:where).and_return(double(exclude: double(limit: empty_dataset)))
@@ -249,8 +257,8 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Knowledge do
249
257
  context 'when Sequel raises an error' do
250
258
  before do
251
259
  stub_const('Legion::Data::Model::ApolloEntry', Class.new)
252
- allow(Legion::Extensions::Apollo::Helpers::Embedding).to receive(:generate)
253
- .and_return(Array.new(1536, 0.0))
260
+ allow(Legion::LLM::Embeddings).to receive(:generate)
261
+ .and_return({ vector: Array.new(1024, 0.0), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 })
254
262
  allow(Legion::Data::Model::ApolloEntry).to receive(:where)
255
263
  .and_raise(Sequel::Error, 'connection lost')
256
264
  end
@@ -289,8 +297,8 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Knowledge do
289
297
  before do
290
298
  stub_const('Legion::Data::Model::ApolloEntry', mock_entry_class)
291
299
  stub_const('Legion::Data::Model::ApolloAccessLog', mock_access_log_class)
292
- allow(Legion::Extensions::Apollo::Helpers::Embedding).to receive(:generate)
293
- .and_return(Array.new(1536, 0.0))
300
+ allow(Legion::LLM::Embeddings).to receive(:generate)
301
+ .and_return({ vector: Array.new(1024, 0.0), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 })
294
302
  allow(mock_entry_class).to receive(:db).and_return(mock_db)
295
303
  allow(mock_db).to receive(:fetch).and_return(double(all: sample_entries))
296
304
  allow(mock_entry_class).to receive(:where).and_return(double(update: true))
@@ -324,8 +332,8 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Knowledge do
324
332
 
325
333
  before do
326
334
  stub_const('Legion::Data::Model::ApolloEntry', mock_entry_class)
327
- allow(Legion::Extensions::Apollo::Helpers::Embedding).to receive(:generate)
328
- .and_return(Array.new(1536, 0.0))
335
+ allow(Legion::LLM::Embeddings).to receive(:generate)
336
+ .and_return({ vector: Array.new(1024, 0.0), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 })
329
337
  allow(mock_entry_class).to receive(:db).and_return(mock_db)
330
338
  allow(mock_db).to receive(:fetch).and_return(double(all: []))
331
339
  end
@@ -380,8 +388,8 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Knowledge do
380
388
 
381
389
  before do
382
390
  stub_const('Legion::Data::Model::ApolloEntry', mock_entry_class)
383
- allow(Legion::Extensions::Apollo::Helpers::Embedding).to receive(:generate)
384
- .and_return(Array.new(1536, 0.0))
391
+ allow(Legion::LLM::Embeddings).to receive(:generate)
392
+ .and_return({ vector: Array.new(1024, 0.0), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 })
385
393
  allow(mock_entry_class).to receive(:db).and_return(mock_db)
386
394
  allow(mock_db).to receive(:fetch).and_return(double(all: sample_entries))
387
395
  allow(mock_entry_class).to receive(:where).and_return(double(update: true))
@@ -3,7 +3,6 @@
3
3
  require 'spec_helper'
4
4
  require 'legion/extensions/apollo/helpers/confidence'
5
5
  require 'legion/extensions/apollo/helpers/similarity'
6
- require 'legion/extensions/apollo/helpers/embedding'
7
6
  require 'legion/extensions/apollo/runners/maintenance'
8
7
 
9
8
  RSpec.describe Legion::Extensions::Apollo::Runners::Maintenance do
@@ -3,7 +3,6 @@
3
3
  require 'spec_helper'
4
4
  require 'legion/extensions/apollo/helpers/confidence'
5
5
  require 'legion/extensions/apollo/helpers/similarity'
6
- require 'legion/extensions/apollo/helpers/embedding'
7
6
  require 'legion/extensions/apollo/helpers/graph_query'
8
7
  require 'legion/extensions/apollo/runners/knowledge'
9
8
  require 'legion/extensions/apollo/runners/request'
@@ -12,6 +11,13 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Request do
12
11
  before do
13
12
  # Clear cached knowledge_host between examples
14
13
  described_class.instance_variable_set(:@knowledge_host, nil)
14
+
15
+ embeddings_mod = Module.new do
16
+ def self.generate(*, **)
17
+ { vector: Array.new(1024, 0.0), model: 'test', provider: :ollama, dimensions: 1024, tokens: 0 }
18
+ end
19
+ end
20
+ stub_const('Legion::LLM::Embeddings', embeddings_mod)
15
21
  end
16
22
 
17
23
  describe '.data_required?' do
@@ -33,8 +39,8 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Request do
33
39
  before do
34
40
  stub_const('Legion::Data::Model::ApolloEntry', mock_entry_class)
35
41
  stub_const('Legion::Data::Model::ApolloAccessLog', mock_access_log_class)
36
- allow(Legion::Extensions::Apollo::Helpers::Embedding).to receive(:generate)
37
- .and_return(Array.new(1536, 0.0))
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 })
38
44
  allow(mock_entry_class).to receive(:db).and_return(mock_db)
39
45
  allow(mock_db).to receive(:fetch).and_return(double(all: sample_entries))
40
46
  allow(mock_entry_class).to receive(:where).and_return(double(update: true))
@@ -97,8 +103,8 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Request do
97
103
 
98
104
  before do
99
105
  stub_const('Legion::Data::Model::ApolloEntry', mock_entry_class)
100
- allow(Legion::Extensions::Apollo::Helpers::Embedding).to receive(:generate)
101
- .and_return(Array.new(1536, 0.0))
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 })
102
108
  allow(mock_entry_class).to receive(:db).and_return(mock_db)
103
109
  allow(mock_db).to receive(:fetch).and_return(double(all: []))
104
110
  allow(mock_entry_class).to receive(:where).and_return(double(update: true))
@@ -136,8 +142,8 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Request do
136
142
  stub_const('Legion::Data::Model::ApolloEntry', mock_entry_class)
137
143
  stub_const('Legion::Data::Model::ApolloExpertise', mock_expertise_class)
138
144
  stub_const('Legion::Data::Model::ApolloAccessLog', mock_access_log_class)
139
- allow(Legion::Extensions::Apollo::Helpers::Embedding).to receive(:generate)
140
- .and_return(Array.new(1536, 0.0))
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 })
141
147
  allow(mock_entry_class).to receive(:where).and_return(double(exclude: double(limit: double(each: nil), first: nil)))
142
148
  allow(mock_entry_class).to receive(:exclude)
143
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.7
4
+ version: 0.4.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity
@@ -160,7 +160,6 @@ files:
160
160
  - lib/legion/extensions/apollo/gaia_integration.rb
161
161
  - lib/legion/extensions/apollo/helpers/capability.rb
162
162
  - lib/legion/extensions/apollo/helpers/confidence.rb
163
- - lib/legion/extensions/apollo/helpers/embedding.rb
164
163
  - lib/legion/extensions/apollo/helpers/entity_watchdog.rb
165
164
  - lib/legion/extensions/apollo/helpers/graph_query.rb
166
165
  - lib/legion/extensions/apollo/helpers/similarity.rb
@@ -197,7 +196,6 @@ files:
197
196
  - spec/legion/extensions/apollo/gaia_integration_spec.rb
198
197
  - spec/legion/extensions/apollo/helpers/capability_spec.rb
199
198
  - spec/legion/extensions/apollo/helpers/confidence_spec.rb
200
- - spec/legion/extensions/apollo/helpers/embedding_spec.rb
201
199
  - spec/legion/extensions/apollo/helpers/entity_watchdog_spec.rb
202
200
  - spec/legion/extensions/apollo/helpers/graph_query_spec.rb
203
201
  - spec/legion/extensions/apollo/helpers/similarity_spec.rb
@@ -1,104 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Legion
4
- module Extensions
5
- module Apollo
6
- module Helpers
7
- module Embedding
8
- DEFAULT_DIMENSION = 1024
9
-
10
- LOCAL_EMBEDDING_MODELS = %w[mxbai-embed-large bge-large snowflake-arctic-embed].freeze
11
-
12
- module_function
13
-
14
- def generate(text:, **)
15
- unless defined?(Legion::LLM) && Legion::LLM.started?
16
- Legion::Logging.debug('[apollo] embedding fallback: LLM not started') if defined?(Legion::Logging)
17
- return zero_vector
18
- end
19
-
20
- local_model = detect_local_model
21
- vector = if local_model
22
- ollama_embed(text, local_model)
23
- else
24
- opts = cloud_embedding_opts
25
- result = Legion::LLM.embed(text, **opts)
26
- result.is_a?(Hash) ? result[:vector] : result
27
- end
28
-
29
- if vector.is_a?(Array) && vector.any?
30
- @dimension = vector.size
31
- vector
32
- else
33
- Legion::Logging.warn('[apollo] embedding fallback: LLM returned no vector') if defined?(Legion::Logging)
34
- zero_vector
35
- end
36
- end
37
-
38
- def dimension
39
- @dimension || configured_dimension
40
- end
41
-
42
- def configured_dimension
43
- return DEFAULT_DIMENSION unless defined?(Legion::Settings) && !Legion::Settings[:apollo].nil?
44
-
45
- Legion::Settings[:apollo].dig(:embedding, :dimension) || DEFAULT_DIMENSION
46
- rescue StandardError
47
- DEFAULT_DIMENSION
48
- end
49
-
50
- def ollama_embed(text, model)
51
- require 'faraday'
52
- base_url = ollama_base_url
53
- Legion::Logging.debug("[apollo] embedding via local Ollama: #{model}") if defined?(Legion::Logging)
54
- conn = Faraday.new(url: base_url) { |f| f.options.timeout = 10 }
55
- response = conn.post('/api/embed', { model: model, input: text }.to_json,
56
- 'Content-Type' => 'application/json')
57
- return nil unless response.success?
58
-
59
- parsed = ::JSON.parse(response.body)
60
- parsed['embeddings']&.first
61
- rescue StandardError => e
62
- Legion::Logging.warn("[apollo] local Ollama embed failed: #{e.message}") if defined?(Legion::Logging)
63
- nil
64
- end
65
-
66
- def ollama_base_url
67
- return 'http://localhost:11434' unless defined?(Legion::Settings)
68
-
69
- Legion::Settings[:llm].dig(:providers, :ollama, :base_url) || 'http://localhost:11434'
70
- rescue StandardError
71
- 'http://localhost:11434'
72
- end
73
-
74
- def cloud_embedding_opts
75
- return {} unless defined?(Legion::Settings) && !Legion::Settings[:apollo].nil?
76
-
77
- embedding = Legion::Settings[:apollo][:embedding] || {}
78
- opts = {}
79
- opts[:provider] = embedding[:provider].to_sym if embedding[:provider]
80
- opts[:model] = embedding[:model] if embedding[:model]
81
- opts
82
- rescue StandardError
83
- {}
84
- end
85
-
86
- def detect_local_model
87
- return nil unless defined?(Legion::LLM::Discovery::Ollama)
88
-
89
- LOCAL_EMBEDDING_MODELS.find do |m|
90
- Legion::LLM::Discovery::Ollama.model_available?(m) ||
91
- Legion::LLM::Discovery::Ollama.model_available?("#{m}:latest")
92
- end
93
- rescue StandardError
94
- nil
95
- end
96
-
97
- def zero_vector
98
- Array.new(dimension, 0.0)
99
- end
100
- end
101
- end
102
- end
103
- end
104
- end
@@ -1,127 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
- require 'legion/extensions/apollo/helpers/embedding'
5
-
6
- RSpec.describe Legion::Extensions::Apollo::Helpers::Embedding do
7
- describe '.generate' do
8
- context 'when Legion::LLM is not defined' do
9
- before do
10
- hide_const('Legion::LLM') if defined?(Legion::LLM)
11
- end
12
-
13
- it 'returns a zero vector of the correct dimension' do
14
- result = described_class.generate(text: 'hello world')
15
- expect(result).to eq(Array.new(1024, 0.0))
16
- expect(result.size).to eq(1024)
17
- end
18
- end
19
-
20
- context 'when Legion::LLM is defined but not started' do
21
- before do
22
- stub_const('Legion::LLM', Module.new { def self.started? = false })
23
- end
24
-
25
- it 'returns a zero vector' do
26
- result = described_class.generate(text: 'hello world')
27
- expect(result).to eq(Array.new(1024, 0.0))
28
- end
29
- end
30
-
31
- context 'when Legion::LLM is available and started' do
32
- let(:mock_vector) { Array.new(1024) { rand(-1.0..1.0) } }
33
-
34
- before do
35
- llm = Module.new do
36
- define_method(:started?) { true }
37
- define_method(:embed) { |_text, **| nil }
38
- extend self
39
- end
40
- stub_const('Legion::LLM', llm)
41
- allow(Legion::LLM).to receive(:embed).and_return({ vector: mock_vector, model: 'text-embedding-3-small' })
42
- end
43
-
44
- it 'returns the vector from the LLM response hash' do
45
- result = described_class.generate(text: 'hello world')
46
- expect(result).to eq(mock_vector)
47
- expect(Legion::LLM).to have_received(:embed).with('hello world', **{})
48
- end
49
- end
50
-
51
- context 'when Legion::LLM returns a short embedding' do
52
- before do
53
- llm = Module.new do
54
- define_method(:started?) { true }
55
- define_method(:embed) { |_text, **| nil }
56
- extend self
57
- end
58
- stub_const('Legion::LLM', llm)
59
- allow(Legion::LLM).to receive(:embed).and_return({ vector: [0.1, 0.2], model: 'test' })
60
- end
61
-
62
- it 'accepts the embedding and updates dimension' do
63
- result = described_class.generate(text: 'hello world')
64
- expect(result).to eq([0.1, 0.2])
65
- expect(described_class.dimension).to eq(2)
66
- end
67
- end
68
-
69
- context 'when Legion::LLM returns nil vector' do
70
- before do
71
- llm = Module.new do
72
- define_method(:started?) { true }
73
- define_method(:embed) { |_text, **| nil }
74
- extend self
75
- end
76
- stub_const('Legion::LLM', llm)
77
- allow(Legion::LLM).to receive(:embed).and_return({ vector: nil, model: 'test', error: 'failed' })
78
- end
79
-
80
- it 'returns a zero vector as fallback' do
81
- result = described_class.generate(text: 'hello world')
82
- expect(result).to be_an(Array)
83
- expect(result.all?(&:zero?)).to be true
84
- end
85
- end
86
-
87
- context 'when Legion::LLM returns nil' do
88
- before do
89
- llm = Module.new do
90
- define_method(:started?) { true }
91
- define_method(:embed) { |_text, **| nil }
92
- extend self
93
- end
94
- stub_const('Legion::LLM', llm)
95
- allow(Legion::LLM).to receive(:embed).and_return(nil)
96
- end
97
-
98
- it 'returns a zero vector as fallback' do
99
- result = described_class.generate(text: 'hello world')
100
- expect(result).to be_an(Array)
101
- expect(result.all?(&:zero?)).to be true
102
- end
103
- end
104
- end
105
-
106
- describe '.configured_dimension' do
107
- context 'when Settings has apollo.embedding.dimension' do
108
- before do
109
- stub_const('Legion::Settings', { apollo: { embedding: { dimension: 768 } } })
110
- end
111
-
112
- it 'returns the configured dimension' do
113
- expect(described_class.configured_dimension).to eq(768)
114
- end
115
- end
116
-
117
- context 'when Settings has no apollo key' do
118
- before do
119
- stub_const('Legion::Settings', { apollo: nil })
120
- end
121
-
122
- it 'returns the default dimension' do
123
- expect(described_class.configured_dimension).to eq(1024)
124
- end
125
- end
126
- end
127
- end