Skip to content

Commit 406c6bc

Browse files
committed
Fix load_all to infer embedding dimension from stored data
Previously hardcoded EMBEDDING_DIM (384) when reshaping the embedding matrix, causing ShapeError when the index was built with a remote model using a different dimension (e.g. 1024). Now infers dimension from the first embedding blob.
1 parent e528328 commit 406c6bc

2 files changed

Lines changed: 61 additions & 3 deletions

File tree

src/index.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,8 @@ impl Index {
214214

215215
let n = rows.len();
216216
let mut chunks = Vec::with_capacity(n);
217-
let mut embeddings_flat = Vec::with_capacity(n * EMBEDDING_DIM);
217+
let mut embeddings_flat = Vec::new();
218+
let mut dim = EMBEDDING_DIM;
218219

219220
for (text, start_line, end_line, blob, path) in rows {
220221
chunks.push(Chunk {
@@ -224,13 +225,17 @@ impl Index {
224225
end_line: end_line as usize,
225226
});
226227
let embedding = blob_to_embedding(&blob);
228+
if embeddings_flat.is_empty() {
229+
dim = embedding.len();
230+
embeddings_flat.reserve(n * dim);
231+
}
227232
embeddings_flat.extend_from_slice(&embedding);
228233
}
229234

230235
let embedding_matrix = if n > 0 {
231-
ndarray::Array2::from_shape_vec((n, EMBEDDING_DIM), embeddings_flat)?
236+
ndarray::Array2::from_shape_vec((n, dim), embeddings_flat)?
232237
} else {
233-
ndarray::Array2::zeros((0, EMBEDDING_DIM))
238+
ndarray::Array2::zeros((0, dim))
234239
};
235240

236241
Ok((chunks, embedding_matrix))

tests/integration.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,59 @@ fn test_default_mode_without_index_returns_no_results() {
274274
assert_eq!(output.status.code(), Some(1));
275275
}
276276

277+
// --- Embedding dimension tests ---
278+
279+
#[test]
280+
fn test_load_all_with_non_default_embedding_dim() {
281+
// Simulate an index built with a remote model (e.g., 1024-dim mxbai-embed-large)
282+
let index = Index::open_in_memory().unwrap();
283+
let dim = 1024; // Not the default 384
284+
285+
let chunks = vec![
286+
Chunk {
287+
file_path: "main.rs".to_string(),
288+
text: "fn main() {}".to_string(),
289+
start_line: 1,
290+
end_line: 1,
291+
},
292+
Chunk {
293+
file_path: "lib.rs".to_string(),
294+
text: "pub fn lib() {}".to_string(),
295+
start_line: 1,
296+
end_line: 1,
297+
},
298+
];
299+
300+
let embeddings = vec![make_embedding(dim, 1.0), make_embedding(dim, 2.0)];
301+
302+
index
303+
.upsert_file("main.rs", "hash1", &chunks[0..1], &embeddings[0..1])
304+
.unwrap();
305+
index
306+
.upsert_file("lib.rs", "hash2", &chunks[1..2], &embeddings[1..2])
307+
.unwrap();
308+
309+
// load_all should infer dim=1024 from stored blobs, not assume 384
310+
let (loaded_chunks, embedding_matrix) = index.load_all().unwrap();
311+
assert_eq!(loaded_chunks.len(), 2);
312+
assert_eq!(embedding_matrix.nrows(), 2);
313+
assert_eq!(embedding_matrix.ncols(), dim);
314+
315+
// Search should work with the correct dimension
316+
let results = search::search(&embeddings[0], &embedding_matrix, 2, 0.0, &loaded_chunks);
317+
assert!(!results.is_empty());
318+
assert!(results[0].score > 0.99); // should find itself
319+
}
320+
321+
#[test]
322+
fn test_load_all_empty_index_uses_default_dim() {
323+
let index = Index::open_in_memory().unwrap();
324+
let (chunks, matrix) = index.load_all().unwrap();
325+
assert!(chunks.is_empty());
326+
assert_eq!(matrix.nrows(), 0);
327+
assert_eq!(matrix.ncols(), EMBEDDING_DIM); // falls back to default
328+
}
329+
277330
// --- Index scoping tests ---
278331

279332
#[test]

0 commit comments

Comments
 (0)