kreuzberg 4.3.5-aarch64-linux → 4.3.7-aarch64-linux
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 +4 -4
- data/Gemfile.lock +5 -5
- data/README.md +1 -1
- data/kreuzberg.gemspec +1 -1
- data/lib/kreuzberg/config.rb +13 -3
- data/lib/kreuzberg/result.rb +32 -2
- data/lib/kreuzberg/types.rb +20 -0
- data/lib/kreuzberg/version.rb +1 -1
- data/lib/kreuzberg_rb.so +0 -0
- data/sig/kreuzberg.rbs +28 -2
- data/spec/binding/embeddings_spec.rb +0 -682
- data/spec/binding/images_spec.rb +0 -577
- data/spec/binding/keywords_extraction_spec.rb +0 -548
- data/spec/binding/pages_extraction_spec.rb +0 -449
- data/spec/binding/tables_spec.rb +0 -467
- data/spec/smoke/package_spec.rb +22 -0
- metadata +3 -3
|
@@ -1,479 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
RSpec.describe 'Embeddings Vector Generation' do
|
|
4
|
-
describe 'vector generation correctness' do
|
|
5
|
-
it 'generates embedding vectors with correct dimensions' do
|
|
6
|
-
config = Kreuzberg::Config::Extraction.new(
|
|
7
|
-
chunking: Kreuzberg::Config::Chunking.new(
|
|
8
|
-
enabled: true,
|
|
9
|
-
max_chars: 500,
|
|
10
|
-
embedding: Kreuzberg::Config::Embedding.new(
|
|
11
|
-
model: { type: :preset, name: 'balanced' },
|
|
12
|
-
normalize: true,
|
|
13
|
-
batch_size: 32
|
|
14
|
-
)
|
|
15
|
-
)
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
text = 'Machine learning transforms technology. Artificial intelligence enables automation and prediction across industries.'
|
|
19
|
-
result = Kreuzberg.extract_bytes_sync(data: text, mime_type: 'text/plain', config: config)
|
|
20
|
-
|
|
21
|
-
expect(result).not_to be_nil
|
|
22
|
-
expect(result.chunks).not_to be_nil
|
|
23
|
-
unless result.chunks.empty?
|
|
24
|
-
first_chunk = result.chunks.first
|
|
25
|
-
expect(first_chunk.embedding).not_to be_nil if first_chunk.embedding
|
|
26
|
-
if first_chunk.embedding.is_a?(Array) && !first_chunk.embedding.empty?
|
|
27
|
-
dimension = first_chunk.embedding.length
|
|
28
|
-
expect(dimension).to(satisfy { |d| [384, 512, 768, 1024].include?(d) })
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
it 'produces numeric embedding vectors' do
|
|
34
|
-
config = Kreuzberg::Config::Extraction.new(
|
|
35
|
-
chunking: Kreuzberg::Config::Chunking.new(
|
|
36
|
-
enabled: true,
|
|
37
|
-
embedding: Kreuzberg::Config::Embedding.new(
|
|
38
|
-
model: { type: :preset, name: 'balanced' },
|
|
39
|
-
normalize: true
|
|
40
|
-
)
|
|
41
|
-
)
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
text = 'Deep learning neural networks enable complex pattern recognition.'
|
|
45
|
-
result = Kreuzberg.extract_bytes_sync(data: text, mime_type: 'text/plain', config: config)
|
|
46
|
-
|
|
47
|
-
expect(result).not_to be_nil
|
|
48
|
-
if result.chunks && !result.chunks.empty? && result.chunks.first.embedding
|
|
49
|
-
embedding = result.chunks.first.embedding
|
|
50
|
-
expect(embedding).to be_a(Array)
|
|
51
|
-
expect(embedding).to all(be_a(Numeric))
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
it 'generates consistent vectors for same input' do
|
|
56
|
-
config = Kreuzberg::Config::Extraction.new(
|
|
57
|
-
chunking: Kreuzberg::Config::Chunking.new(
|
|
58
|
-
enabled: true,
|
|
59
|
-
embedding: Kreuzberg::Config::Embedding.new(
|
|
60
|
-
model: { type: :preset, name: 'balanced' }
|
|
61
|
-
)
|
|
62
|
-
)
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
text = 'Artificial intelligence transforms technology development.'
|
|
66
|
-
result1 = Kreuzberg.extract_bytes_sync(data: text, mime_type: 'text/plain', config: config)
|
|
67
|
-
result2 = Kreuzberg.extract_bytes_sync(data: text, mime_type: 'text/plain', config: config)
|
|
68
|
-
|
|
69
|
-
expect(result1).not_to be_nil
|
|
70
|
-
expect(result2).not_to be_nil
|
|
71
|
-
|
|
72
|
-
if result1.chunks && result2.chunks && !result1.chunks.empty? && !result2.chunks.empty? && result1.chunks.first.embedding && result2.chunks.first.embedding
|
|
73
|
-
expect(result1.chunks.first.embedding).to eq(result2.chunks.first.embedding)
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
describe 'embedding dimension verification' do
|
|
79
|
-
it 'validates embedding vector length consistency within batch' do
|
|
80
|
-
config = Kreuzberg::Config::Extraction.new(
|
|
81
|
-
chunking: Kreuzberg::Config::Chunking.new(
|
|
82
|
-
enabled: true,
|
|
83
|
-
max_chars: 300,
|
|
84
|
-
embedding: Kreuzberg::Config::Embedding.new(
|
|
85
|
-
model: { type: :preset, name: 'balanced' },
|
|
86
|
-
batch_size: 8
|
|
87
|
-
)
|
|
88
|
-
)
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
text = 'Machine learning techniques evolve continuously. Neural networks improve performance. Data science enables insights.'
|
|
92
|
-
result = Kreuzberg.extract_bytes_sync(data: text, mime_type: 'text/plain', config: config)
|
|
93
|
-
|
|
94
|
-
expect(result).not_to be_nil
|
|
95
|
-
if result.chunks && result.chunks.length > 1
|
|
96
|
-
embedding_dims = result.chunks
|
|
97
|
-
.select { |chunk| chunk.embedding.is_a?(Array) }
|
|
98
|
-
.map { |chunk| chunk.embedding.length }
|
|
99
|
-
.uniq
|
|
100
|
-
|
|
101
|
-
# All embeddings should have consistent dimensions
|
|
102
|
-
expect(embedding_dims.length).to eq(1)
|
|
103
|
-
end
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
it 'respects configured batch size for embeddings' do
|
|
107
|
-
batch_sizes = [8, 16, 32]
|
|
108
|
-
|
|
109
|
-
batch_sizes.each do |batch_size|
|
|
110
|
-
config = Kreuzberg::Config::Extraction.new(
|
|
111
|
-
chunking: Kreuzberg::Config::Chunking.new(
|
|
112
|
-
enabled: true,
|
|
113
|
-
max_chars: 200,
|
|
114
|
-
embedding: Kreuzberg::Config::Embedding.new(
|
|
115
|
-
model: { type: :preset, name: 'balanced' },
|
|
116
|
-
batch_size: batch_size
|
|
117
|
-
)
|
|
118
|
-
)
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
text = 'Technology transforms industries. Machine learning advances AI. Neural networks improve models. Data analysis drives decisions.'
|
|
122
|
-
result = Kreuzberg.extract_bytes_sync(data: text, mime_type: 'text/plain', config: config)
|
|
123
|
-
|
|
124
|
-
expect(result).not_to be_nil
|
|
125
|
-
expect(result.chunks).not_to be_nil
|
|
126
|
-
end
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
it 'confirms embedding dimensions match model specification' do
|
|
130
|
-
config = Kreuzberg::Config::Extraction.new(
|
|
131
|
-
chunking: Kreuzberg::Config::Chunking.new(
|
|
132
|
-
enabled: true,
|
|
133
|
-
embedding: Kreuzberg::Config::Embedding.new(
|
|
134
|
-
model: { type: :preset, name: 'balanced' },
|
|
135
|
-
normalize: true
|
|
136
|
-
)
|
|
137
|
-
)
|
|
138
|
-
)
|
|
139
|
-
|
|
140
|
-
text = 'Embeddings capture semantic meaning in vector space representations.'
|
|
141
|
-
result = Kreuzberg.extract_bytes_sync(data: text, mime_type: 'text/plain', config: config)
|
|
142
|
-
|
|
143
|
-
if result.chunks && !result.chunks.empty? && result.chunks.first.embedding
|
|
144
|
-
embedding = result.chunks.first.embedding
|
|
145
|
-
dimension = embedding.length
|
|
146
|
-
# Verify dimension is positive
|
|
147
|
-
expect(dimension).to be > 0
|
|
148
|
-
# Verify dimension is in expected range for balanced model
|
|
149
|
-
expect(dimension).to be_within(1024).of(512)
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
it 'handles empty chunk gracefully' do
|
|
154
|
-
config = Kreuzberg::Config::Extraction.new(
|
|
155
|
-
chunking: Kreuzberg::Config::Chunking.new(
|
|
156
|
-
enabled: true,
|
|
157
|
-
embedding: Kreuzberg::Config::Embedding.new(
|
|
158
|
-
model: { type: :preset, name: 'balanced' }
|
|
159
|
-
)
|
|
160
|
-
)
|
|
161
|
-
)
|
|
162
|
-
|
|
163
|
-
text = 'a'
|
|
164
|
-
result = Kreuzberg.extract_bytes_sync(data: text, mime_type: 'text/plain', config: config)
|
|
165
|
-
|
|
166
|
-
expect(result).not_to be_nil
|
|
167
|
-
end
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
describe 'performance with batch operations' do
|
|
171
|
-
it 'processes multiple chunks efficiently' do
|
|
172
|
-
config = Kreuzberg::Config::Extraction.new(
|
|
173
|
-
chunking: Kreuzberg::Config::Chunking.new(
|
|
174
|
-
enabled: true,
|
|
175
|
-
max_chars: 250,
|
|
176
|
-
embedding: Kreuzberg::Config::Embedding.new(
|
|
177
|
-
model: { type: :preset, name: 'balanced' },
|
|
178
|
-
batch_size: 16
|
|
179
|
-
)
|
|
180
|
-
)
|
|
181
|
-
)
|
|
182
|
-
|
|
183
|
-
text = 'Machine learning transforms industries. Neural networks enable deep learning. ' \
|
|
184
|
-
'Artificial intelligence drives automation. Data science enables insights. ' \
|
|
185
|
-
'Computer vision processes images. Natural language processing understands text. ' \
|
|
186
|
-
'Reinforcement learning optimizes decisions. Transfer learning improves efficiency.'
|
|
187
|
-
|
|
188
|
-
start_time = Time.now
|
|
189
|
-
result = Kreuzberg.extract_bytes_sync(data: text, mime_type: 'text/plain', config: config)
|
|
190
|
-
elapsed = Time.now - start_time
|
|
191
|
-
|
|
192
|
-
expect(result).not_to be_nil
|
|
193
|
-
expect(elapsed).to be < 30.0
|
|
194
|
-
expect(result.chunks.length).to be > 0
|
|
195
|
-
end
|
|
196
|
-
|
|
197
|
-
it 'maintains consistent embedding quality across batch sizes' do
|
|
198
|
-
text = 'Deep learning networks process data efficiently through parallel computation architecture.'
|
|
199
|
-
|
|
200
|
-
results = []
|
|
201
|
-
[8, 16, 32].each do |batch_size|
|
|
202
|
-
config = Kreuzberg::Config::Extraction.new(
|
|
203
|
-
chunking: Kreuzberg::Config::Chunking.new(
|
|
204
|
-
enabled: true,
|
|
205
|
-
embedding: Kreuzberg::Config::Embedding.new(
|
|
206
|
-
model: { type: :preset, name: 'balanced' },
|
|
207
|
-
batch_size: batch_size
|
|
208
|
-
)
|
|
209
|
-
)
|
|
210
|
-
)
|
|
211
|
-
|
|
212
|
-
result = Kreuzberg.extract_bytes_sync(data: text, mime_type: 'text/plain', config: config)
|
|
213
|
-
results << result if result
|
|
214
|
-
end
|
|
215
|
-
|
|
216
|
-
expect(results.length).to be > 0
|
|
217
|
-
results.each do |result|
|
|
218
|
-
expect(result).not_to be_nil
|
|
219
|
-
expect(result.chunks).to be_a(Array)
|
|
220
|
-
end
|
|
221
|
-
end
|
|
222
|
-
|
|
223
|
-
it 'handles large text with many chunks' do
|
|
224
|
-
config = Kreuzberg::Config::Extraction.new(
|
|
225
|
-
chunking: Kreuzberg::Config::Chunking.new(
|
|
226
|
-
enabled: true,
|
|
227
|
-
max_chars: 300,
|
|
228
|
-
embedding: Kreuzberg::Config::Embedding.new(
|
|
229
|
-
model: { type: :preset, name: 'balanced' },
|
|
230
|
-
batch_size: 32
|
|
231
|
-
)
|
|
232
|
-
)
|
|
233
|
-
)
|
|
234
|
-
|
|
235
|
-
text = ('Machine learning and artificial intelligence are transforming technology. ' * 10)
|
|
236
|
-
result = Kreuzberg.extract_bytes_sync(data: text, mime_type: 'text/plain', config: config)
|
|
237
|
-
|
|
238
|
-
expect(result).not_to be_nil
|
|
239
|
-
expect(result.chunks.length).to be >= 1
|
|
240
|
-
expect(result.chunk_count).to eq(result.chunks.length)
|
|
241
|
-
end
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
describe 'format-specific embedding handling' do
|
|
245
|
-
it 'generates embeddings from plain text' do
|
|
246
|
-
config = Kreuzberg::Config::Extraction.new(
|
|
247
|
-
chunking: Kreuzberg::Config::Chunking.new(
|
|
248
|
-
enabled: true,
|
|
249
|
-
embedding: Kreuzberg::Config::Embedding.new(
|
|
250
|
-
model: { type: :preset, name: 'balanced' }
|
|
251
|
-
)
|
|
252
|
-
)
|
|
253
|
-
)
|
|
254
|
-
|
|
255
|
-
text = 'Machine learning enables advanced data analysis and predictions.'
|
|
256
|
-
result = Kreuzberg.extract_bytes_sync(data: text, mime_type: 'text/plain', config: config)
|
|
257
|
-
|
|
258
|
-
expect(result).not_to be_nil
|
|
259
|
-
expect(result.mime_type).to include('text')
|
|
260
|
-
expect(result.chunks).not_to be_nil
|
|
261
|
-
end
|
|
262
|
-
|
|
263
|
-
it 'handles extraction without embeddings when disabled' do
|
|
264
|
-
config = Kreuzberg::Config::Extraction.new(
|
|
265
|
-
chunking: Kreuzberg::Config::Chunking.new(
|
|
266
|
-
enabled: true,
|
|
267
|
-
embedding: nil
|
|
268
|
-
)
|
|
269
|
-
)
|
|
270
|
-
|
|
271
|
-
text = 'Data science transforms business decisions with insights.'
|
|
272
|
-
result = Kreuzberg.extract_bytes_sync(data: text, mime_type: 'text/plain', config: config)
|
|
273
|
-
|
|
274
|
-
expect(result).not_to be_nil
|
|
275
|
-
expect(result.chunks).not_to be_nil
|
|
276
|
-
end
|
|
277
|
-
|
|
278
|
-
it 'respects extraction config for embeddings' do
|
|
279
|
-
config = Kreuzberg::Config::Extraction.new(
|
|
280
|
-
chunking: Kreuzberg::Config::Chunking.new(
|
|
281
|
-
enabled: true,
|
|
282
|
-
max_chars: 400,
|
|
283
|
-
embedding: Kreuzberg::Config::Embedding.new(
|
|
284
|
-
model: { type: :preset, name: 'balanced' },
|
|
285
|
-
normalize: true,
|
|
286
|
-
batch_size: 16
|
|
287
|
-
)
|
|
288
|
-
)
|
|
289
|
-
)
|
|
290
|
-
|
|
291
|
-
text = 'Artificial intelligence enables automation across industries.'
|
|
292
|
-
result = Kreuzberg.extract_bytes_sync(data: text, mime_type: 'text/plain', config: config)
|
|
293
|
-
|
|
294
|
-
expect(result).not_to be_nil
|
|
295
|
-
expect(result.chunks).to be_a(Array)
|
|
296
|
-
end
|
|
297
|
-
end
|
|
298
|
-
|
|
299
|
-
describe 'similarity score validation' do
|
|
300
|
-
it 'generates vectors suitable for cosine similarity' do
|
|
301
|
-
config = Kreuzberg::Config::Extraction.new(
|
|
302
|
-
chunking: Kreuzberg::Config::Chunking.new(
|
|
303
|
-
enabled: true,
|
|
304
|
-
embedding: Kreuzberg::Config::Embedding.new(
|
|
305
|
-
model: { type: :preset, name: 'balanced' },
|
|
306
|
-
normalize: true
|
|
307
|
-
)
|
|
308
|
-
)
|
|
309
|
-
)
|
|
310
|
-
|
|
311
|
-
text = 'Machine learning enables pattern recognition. Artificial intelligence drives innovation.'
|
|
312
|
-
result = Kreuzberg.extract_bytes_sync(data: text, mime_type: 'text/plain', config: config)
|
|
313
|
-
|
|
314
|
-
if result.chunks && result.chunks.length >= 2
|
|
315
|
-
embeddings = result.chunks.select { |c| c.embedding.is_a?(Array) }.map(&:embedding)
|
|
316
|
-
|
|
317
|
-
if embeddings.length >= 2
|
|
318
|
-
vec1 = embeddings[0]
|
|
319
|
-
vec2 = embeddings[1]
|
|
320
|
-
|
|
321
|
-
dot_product = vec1.zip(vec2).sum { |a, b| a * b }
|
|
322
|
-
norm1 = Math.sqrt(vec1.sum { |x| x * x })
|
|
323
|
-
norm2 = Math.sqrt(vec2.sum { |x| x * x })
|
|
324
|
-
similarity = dot_product / (norm1 * norm2) if norm1 > 0 && norm2 > 0
|
|
325
|
-
|
|
326
|
-
if similarity
|
|
327
|
-
expect(similarity).to be >= -1.0
|
|
328
|
-
expect(similarity).to be <= 1.0
|
|
329
|
-
end
|
|
330
|
-
end
|
|
331
|
-
end
|
|
332
|
-
end
|
|
333
|
-
|
|
334
|
-
it 'normalized embeddings have unit or near-unit magnitude' do
|
|
335
|
-
config = Kreuzberg::Config::Extraction.new(
|
|
336
|
-
chunking: Kreuzberg::Config::Chunking.new(
|
|
337
|
-
enabled: true,
|
|
338
|
-
embedding: Kreuzberg::Config::Embedding.new(
|
|
339
|
-
model: { type: :preset, name: 'balanced' },
|
|
340
|
-
normalize: true
|
|
341
|
-
)
|
|
342
|
-
)
|
|
343
|
-
)
|
|
344
|
-
|
|
345
|
-
text = 'Embeddings capture semantic information in vector space.'
|
|
346
|
-
result = Kreuzberg.extract_bytes_sync(data: text, mime_type: 'text/plain', config: config)
|
|
347
|
-
|
|
348
|
-
if result.chunks && !result.chunks.empty? && result.chunks.first.embedding
|
|
349
|
-
embedding = result.chunks.first.embedding
|
|
350
|
-
magnitude = Math.sqrt(embedding.sum { |x| x * x })
|
|
351
|
-
|
|
352
|
-
expect(magnitude).to be > 0.0
|
|
353
|
-
end
|
|
354
|
-
end
|
|
355
|
-
|
|
356
|
-
it 'computes reasonable similarity between related texts' do
|
|
357
|
-
config = Kreuzberg::Config::Extraction.new(
|
|
358
|
-
chunking: Kreuzberg::Config::Chunking.new(
|
|
359
|
-
enabled: true,
|
|
360
|
-
embedding: Kreuzberg::Config::Embedding.new(
|
|
361
|
-
model: { type: :preset, name: 'balanced' },
|
|
362
|
-
normalize: true
|
|
363
|
-
)
|
|
364
|
-
)
|
|
365
|
-
)
|
|
366
|
-
|
|
367
|
-
text1 = 'Machine learning enables data analysis'
|
|
368
|
-
text2 = 'Artificial intelligence enables learning'
|
|
369
|
-
|
|
370
|
-
result1 = Kreuzberg.extract_bytes_sync(data: text1, mime_type: 'text/plain', config: config)
|
|
371
|
-
result2 = Kreuzberg.extract_bytes_sync(data: text2, mime_type: 'text/plain', config: config)
|
|
372
|
-
|
|
373
|
-
if result1.chunks && result2.chunks && !result1.chunks.empty? && !result2.chunks.empty?
|
|
374
|
-
emb1 = result1.chunks.first.embedding
|
|
375
|
-
emb2 = result2.chunks.first.embedding
|
|
376
|
-
|
|
377
|
-
if emb1.is_a?(Array) && emb2.is_a?(Array)
|
|
378
|
-
dot_product = emb1.zip(emb2).sum { |a, b| a * b }
|
|
379
|
-
norm1 = Math.sqrt(emb1.sum { |x| x * x })
|
|
380
|
-
norm2 = Math.sqrt(emb2.sum { |x| x * x })
|
|
381
|
-
similarity = dot_product / (norm1 * norm2) if norm1 > 0 && norm2 > 0
|
|
382
|
-
|
|
383
|
-
expect(similarity).not_to be_nil if similarity
|
|
384
|
-
# Cosine similarity must be in valid range [-1, 1]
|
|
385
|
-
expect(similarity).to be >= -1.0 if similarity
|
|
386
|
-
expect(similarity).to be <= 1.0 if similarity
|
|
387
|
-
# Related texts should have positive similarity
|
|
388
|
-
expect(similarity).to be > 0 if similarity
|
|
389
|
-
end
|
|
390
|
-
end
|
|
391
|
-
end
|
|
392
|
-
end
|
|
393
|
-
|
|
394
|
-
describe 'normalization correctness' do
|
|
395
|
-
it 'respects normalization configuration' do
|
|
396
|
-
config_normalized = Kreuzberg::Config::Extraction.new(
|
|
397
|
-
chunking: Kreuzberg::Config::Chunking.new(
|
|
398
|
-
enabled: true,
|
|
399
|
-
embedding: Kreuzberg::Config::Embedding.new(
|
|
400
|
-
model: { type: :preset, name: 'balanced' },
|
|
401
|
-
normalize: true
|
|
402
|
-
)
|
|
403
|
-
)
|
|
404
|
-
)
|
|
405
|
-
|
|
406
|
-
text = 'Normalization ensures consistent vector magnitudes.'
|
|
407
|
-
result = Kreuzberg.extract_bytes_sync(data: text, mime_type: 'text/plain', config: config_normalized)
|
|
408
|
-
|
|
409
|
-
expect(result).not_to be_nil
|
|
410
|
-
expect(result.chunks).not_to be_nil
|
|
411
|
-
end
|
|
412
|
-
|
|
413
|
-
it 'handles embedding with custom normalization settings' do
|
|
414
|
-
normalize_options = [true, false]
|
|
415
|
-
|
|
416
|
-
normalize_options.each do |should_normalize|
|
|
417
|
-
config = Kreuzberg::Config::Extraction.new(
|
|
418
|
-
chunking: Kreuzberg::Config::Chunking.new(
|
|
419
|
-
enabled: true,
|
|
420
|
-
embedding: Kreuzberg::Config::Embedding.new(
|
|
421
|
-
model: { type: :preset, name: 'balanced' },
|
|
422
|
-
normalize: should_normalize
|
|
423
|
-
)
|
|
424
|
-
)
|
|
425
|
-
)
|
|
426
|
-
|
|
427
|
-
text = 'Embeddings can be normalized or unnormalized depending on use case.'
|
|
428
|
-
result = Kreuzberg.extract_bytes_sync(data: text, mime_type: 'text/plain', config: config)
|
|
429
|
-
|
|
430
|
-
expect(result).not_to be_nil
|
|
431
|
-
expect(result.chunks).to be_a(Array)
|
|
432
|
-
end
|
|
433
|
-
end
|
|
434
|
-
|
|
435
|
-
it 'validates embedding values within expected range' do
|
|
436
|
-
config = Kreuzberg::Config::Extraction.new(
|
|
437
|
-
chunking: Kreuzberg::Config::Chunking.new(
|
|
438
|
-
enabled: true,
|
|
439
|
-
embedding: Kreuzberg::Config::Embedding.new(
|
|
440
|
-
model: { type: :preset, name: 'balanced' },
|
|
441
|
-
normalize: true
|
|
442
|
-
)
|
|
443
|
-
)
|
|
444
|
-
)
|
|
445
|
-
|
|
446
|
-
text = 'Normalized embeddings typically have values between -1 and 1.'
|
|
447
|
-
result = Kreuzberg.extract_bytes_sync(data: text, mime_type: 'text/plain', config: config)
|
|
448
|
-
|
|
449
|
-
if result.chunks && !result.chunks.empty? && result.chunks.first.embedding
|
|
450
|
-
embedding = result.chunks.first.embedding
|
|
451
|
-
expect(embedding).to all(be >= -2.0)
|
|
452
|
-
expect(embedding).to all(be <= 2.0)
|
|
453
|
-
end
|
|
454
|
-
end
|
|
455
|
-
|
|
456
|
-
it 'ensures numerical stability in embeddings' do
|
|
457
|
-
config = Kreuzberg::Config::Extraction.new(
|
|
458
|
-
chunking: Kreuzberg::Config::Chunking.new(
|
|
459
|
-
enabled: true,
|
|
460
|
-
embedding: Kreuzberg::Config::Embedding.new(
|
|
461
|
-
model: { type: :preset, name: 'balanced' },
|
|
462
|
-
normalize: true
|
|
463
|
-
)
|
|
464
|
-
)
|
|
465
|
-
)
|
|
466
|
-
|
|
467
|
-
text = 'Numerical stability prevents overflow and underflow in floating point computation.'
|
|
468
|
-
result = Kreuzberg.extract_bytes_sync(data: text, mime_type: 'text/plain', config: config)
|
|
469
|
-
|
|
470
|
-
if result.chunks && !result.chunks.empty? && result.chunks.first.embedding
|
|
471
|
-
embedding = result.chunks.first.embedding
|
|
472
|
-
expect(embedding).to all(be_finite)
|
|
473
|
-
end
|
|
474
|
-
end
|
|
475
|
-
end
|
|
476
|
-
|
|
477
4
|
describe 'embedding configuration validation' do
|
|
478
5
|
it 'creates Embedding config with default values' do
|
|
479
6
|
embedding = Kreuzberg::Config::Embedding.new
|
|
@@ -547,88 +74,6 @@ RSpec.describe 'Embeddings Vector Generation' do
|
|
|
547
74
|
end
|
|
548
75
|
end
|
|
549
76
|
|
|
550
|
-
describe 'edge cases and error handling' do
|
|
551
|
-
it 'handles very short text with embeddings' do
|
|
552
|
-
config = Kreuzberg::Config::Extraction.new(
|
|
553
|
-
chunking: Kreuzberg::Config::Chunking.new(
|
|
554
|
-
enabled: true,
|
|
555
|
-
embedding: Kreuzberg::Config::Embedding.new(
|
|
556
|
-
model: { type: :preset, name: 'balanced' }
|
|
557
|
-
)
|
|
558
|
-
)
|
|
559
|
-
)
|
|
560
|
-
|
|
561
|
-
text = 'AI'
|
|
562
|
-
result = Kreuzberg.extract_bytes_sync(data: text, mime_type: 'text/plain', config: config)
|
|
563
|
-
|
|
564
|
-
expect(result).not_to be_nil
|
|
565
|
-
expect(result.chunks).not_to be_nil
|
|
566
|
-
end
|
|
567
|
-
|
|
568
|
-
it 'handles text with special characters in embeddings' do
|
|
569
|
-
config = Kreuzberg::Config::Extraction.new(
|
|
570
|
-
chunking: Kreuzberg::Config::Chunking.new(
|
|
571
|
-
enabled: true,
|
|
572
|
-
embedding: Kreuzberg::Config::Embedding.new(
|
|
573
|
-
model: { type: :preset, name: 'balanced' }
|
|
574
|
-
)
|
|
575
|
-
)
|
|
576
|
-
)
|
|
577
|
-
|
|
578
|
-
text = 'Machine learning & AI. Data science -> insights. Deep learning @ scale.'
|
|
579
|
-
result = Kreuzberg.extract_bytes_sync(data: text, mime_type: 'text/plain', config: config)
|
|
580
|
-
|
|
581
|
-
expect(result).not_to be_nil
|
|
582
|
-
expect(result.chunks).not_to be_nil
|
|
583
|
-
end
|
|
584
|
-
|
|
585
|
-
it 'handles unicode text in embeddings' do
|
|
586
|
-
config = Kreuzberg::Config::Extraction.new(
|
|
587
|
-
chunking: Kreuzberg::Config::Chunking.new(
|
|
588
|
-
enabled: true,
|
|
589
|
-
embedding: Kreuzberg::Config::Embedding.new(
|
|
590
|
-
model: { type: :preset, name: 'balanced' }
|
|
591
|
-
)
|
|
592
|
-
)
|
|
593
|
-
)
|
|
594
|
-
|
|
595
|
-
text = 'Machine learning transforms technology. Aprendizaje automático transforma.'
|
|
596
|
-
result = Kreuzberg.extract_bytes_sync(data: text, mime_type: 'text/plain', config: config)
|
|
597
|
-
|
|
598
|
-
expect(result).not_to be_nil
|
|
599
|
-
end
|
|
600
|
-
|
|
601
|
-
it 'handles repeated text in embeddings' do
|
|
602
|
-
config = Kreuzberg::Config::Extraction.new(
|
|
603
|
-
chunking: Kreuzberg::Config::Chunking.new(
|
|
604
|
-
enabled: true,
|
|
605
|
-
embedding: Kreuzberg::Config::Embedding.new(
|
|
606
|
-
model: { type: :preset, name: 'balanced' }
|
|
607
|
-
)
|
|
608
|
-
)
|
|
609
|
-
)
|
|
610
|
-
|
|
611
|
-
text = 'machine machine machine learning learning learning'
|
|
612
|
-
result = Kreuzberg.extract_bytes_sync(data: text, mime_type: 'text/plain', config: config)
|
|
613
|
-
|
|
614
|
-
expect(result).not_to be_nil
|
|
615
|
-
end
|
|
616
|
-
|
|
617
|
-
it 'handles empty embedding result gracefully' do
|
|
618
|
-
config = Kreuzberg::Config::Extraction.new(
|
|
619
|
-
chunking: Kreuzberg::Config::Chunking.new(
|
|
620
|
-
enabled: false
|
|
621
|
-
)
|
|
622
|
-
)
|
|
623
|
-
|
|
624
|
-
text = 'This text will not be chunked.'
|
|
625
|
-
result = Kreuzberg.extract_bytes_sync(data: text, mime_type: 'text/plain', config: config)
|
|
626
|
-
|
|
627
|
-
expect(result).not_to be_nil
|
|
628
|
-
expect(result.chunks).to be_a(Array)
|
|
629
|
-
end
|
|
630
|
-
end
|
|
631
|
-
|
|
632
77
|
describe 'Chunking with Embedding integration' do
|
|
633
78
|
it 'integrates embedding with chunking configuration' do
|
|
634
79
|
chunking = Kreuzberg::Config::Chunking.new(
|
|
@@ -686,131 +131,4 @@ RSpec.describe 'Embeddings Vector Generation' do
|
|
|
686
131
|
expect(hash[:embedding]).to be_nil
|
|
687
132
|
end
|
|
688
133
|
end
|
|
689
|
-
|
|
690
|
-
describe 'mathematical properties and error handling' do
|
|
691
|
-
it 'validates embedding vector values are valid floats' do
|
|
692
|
-
config = Kreuzberg::Config::Extraction.new(
|
|
693
|
-
chunking: Kreuzberg::Config::Chunking.new(
|
|
694
|
-
enabled: true,
|
|
695
|
-
embedding: Kreuzberg::Config::Embedding.new(
|
|
696
|
-
model: { type: :preset, name: 'balanced' },
|
|
697
|
-
normalize: true
|
|
698
|
-
)
|
|
699
|
-
)
|
|
700
|
-
)
|
|
701
|
-
|
|
702
|
-
text = 'Validating floating-point properties of embedding values.'
|
|
703
|
-
result = Kreuzberg.extract_bytes_sync(data: text, mime_type: 'text/plain', config: config)
|
|
704
|
-
|
|
705
|
-
if result.chunks && !result.chunks.empty? && result.chunks.first.embedding
|
|
706
|
-
embedding = result.chunks.first.embedding
|
|
707
|
-
expect(embedding).to all(be_a(Float))
|
|
708
|
-
expect(embedding).to all(be_finite)
|
|
709
|
-
end
|
|
710
|
-
end
|
|
711
|
-
|
|
712
|
-
it 'ensures no dead embeddings (all-zero vectors)' do
|
|
713
|
-
config = Kreuzberg::Config::Extraction.new(
|
|
714
|
-
chunking: Kreuzberg::Config::Chunking.new(
|
|
715
|
-
enabled: true,
|
|
716
|
-
embedding: Kreuzberg::Config::Embedding.new(
|
|
717
|
-
model: { type: :preset, name: 'balanced' },
|
|
718
|
-
normalize: true
|
|
719
|
-
)
|
|
720
|
-
)
|
|
721
|
-
)
|
|
722
|
-
|
|
723
|
-
text = 'Testing for dead embeddings and zero vectors.'
|
|
724
|
-
result = Kreuzberg.extract_bytes_sync(data: text, mime_type: 'text/plain', config: config)
|
|
725
|
-
|
|
726
|
-
if result.chunks && !result.chunks.empty? && result.chunks.first.embedding
|
|
727
|
-
embedding = result.chunks.first.embedding
|
|
728
|
-
magnitude = embedding.sum(&:abs)
|
|
729
|
-
expect(magnitude).to be > 0.1
|
|
730
|
-
end
|
|
731
|
-
end
|
|
732
|
-
|
|
733
|
-
it 'validates identical vectors have similarity 1.0' do
|
|
734
|
-
config = Kreuzberg::Config::Extraction.new(
|
|
735
|
-
chunking: Kreuzberg::Config::Chunking.new(
|
|
736
|
-
enabled: true,
|
|
737
|
-
embedding: Kreuzberg::Config::Embedding.new(
|
|
738
|
-
model: { type: :preset, name: 'balanced' },
|
|
739
|
-
normalize: true
|
|
740
|
-
)
|
|
741
|
-
)
|
|
742
|
-
)
|
|
743
|
-
|
|
744
|
-
text = 'Testing identical vector similarity.'
|
|
745
|
-
result = Kreuzberg.extract_bytes_sync(data: text, mime_type: 'text/plain', config: config)
|
|
746
|
-
|
|
747
|
-
if result.chunks && !result.chunks.empty? && result.chunks.first.embedding
|
|
748
|
-
embedding = result.chunks.first.embedding
|
|
749
|
-
# Vector with itself should have similarity 1.0
|
|
750
|
-
dot_product = embedding.zip(embedding).sum { |a, b| a * b }
|
|
751
|
-
norm_sq = embedding.sum { |x| x * x }
|
|
752
|
-
similarity = dot_product / norm_sq if norm_sq > 0
|
|
753
|
-
|
|
754
|
-
expect(similarity).to be_within(0.0001).of(1.0) if similarity
|
|
755
|
-
end
|
|
756
|
-
end
|
|
757
|
-
|
|
758
|
-
it 'validates embedding consistency across multiple runs' do
|
|
759
|
-
config = Kreuzberg::Config::Extraction.new(
|
|
760
|
-
chunking: Kreuzberg::Config::Chunking.new(
|
|
761
|
-
enabled: true,
|
|
762
|
-
embedding: Kreuzberg::Config::Embedding.new(
|
|
763
|
-
model: { type: :preset, name: 'balanced' },
|
|
764
|
-
normalize: true
|
|
765
|
-
)
|
|
766
|
-
)
|
|
767
|
-
)
|
|
768
|
-
|
|
769
|
-
text = 'Testing deterministic embedding generation.'
|
|
770
|
-
|
|
771
|
-
result1 = Kreuzberg.extract_bytes_sync(data: text, mime_type: 'text/plain', config: config)
|
|
772
|
-
result2 = Kreuzberg.extract_bytes_sync(data: text, mime_type: 'text/plain', config: config)
|
|
773
|
-
|
|
774
|
-
if result1.chunks && result2.chunks && !result1.chunks.empty? && !result2.chunks.empty?
|
|
775
|
-
emb1 = result1.chunks.first.embedding
|
|
776
|
-
emb2 = result2.chunks.first.embedding
|
|
777
|
-
|
|
778
|
-
expect(emb1).to eq(emb2) if emb1.is_a?(Array) && emb2.is_a?(Array)
|
|
779
|
-
end
|
|
780
|
-
end
|
|
781
|
-
|
|
782
|
-
it 'handles dimension consistency across batch operations' do
|
|
783
|
-
config = Kreuzberg::Config::Extraction.new(
|
|
784
|
-
chunking: Kreuzberg::Config::Chunking.new(
|
|
785
|
-
enabled: true,
|
|
786
|
-
max_chars: 150,
|
|
787
|
-
embedding: Kreuzberg::Config::Embedding.new(
|
|
788
|
-
model: { type: :preset, name: 'balanced' },
|
|
789
|
-
batch_size: 4
|
|
790
|
-
)
|
|
791
|
-
)
|
|
792
|
-
)
|
|
793
|
-
|
|
794
|
-
texts = [
|
|
795
|
-
'First document about machine learning.',
|
|
796
|
-
'Second document about neural networks.',
|
|
797
|
-
'Third document about deep learning.'
|
|
798
|
-
]
|
|
799
|
-
|
|
800
|
-
dimensions = []
|
|
801
|
-
|
|
802
|
-
texts.each do |text|
|
|
803
|
-
result = Kreuzberg.extract_bytes_sync(data: text, mime_type: 'text/plain', config: config)
|
|
804
|
-
|
|
805
|
-
next unless result.chunks
|
|
806
|
-
|
|
807
|
-
result.chunks.each do |chunk|
|
|
808
|
-
dimensions << chunk.embedding.length if chunk.embedding.is_a?(Array)
|
|
809
|
-
end
|
|
810
|
-
end
|
|
811
|
-
|
|
812
|
-
# All dimensions should be consistent
|
|
813
|
-
expect(dimensions.uniq.length).to be <= 1 if dimensions.any?
|
|
814
|
-
end
|
|
815
|
-
end
|
|
816
134
|
end
|