(docs: SearchDocument[], rootDir: string)
| 441 | private documents: SearchDocument[] = []; |
| 442 | private vectors: number[][] = []; |
| 443 | async index(docs: SearchDocument[], rootDir: string): Promise<void> { |
| 444 | this.documents = docs; |
| 445 | const cache = await loadCache(rootDir); |
| 446 | const uncached: { idx: number; text: string; hash: string }[] = []; |
| 447 | |
| 448 | this.vectors = new Array(docs.length); |
| 449 | |
| 450 | for (let i = 0; i < docs.length; i++) { |
| 451 | const doc = docs[i]; |
| 452 | const rawText = `${doc.header} ${doc.symbols.join(" ")} ${doc.content}`; |
| 453 | const hash = hashContent(rawText); |
| 454 | |
| 455 | if (cache[doc.path]?.hash === hash) { |
| 456 | this.vectors[i] = cache[doc.path].vector; |
| 457 | } else { |
| 458 | uncached.push({ idx: i, text: rawText, hash }); |
| 459 | } |
| 460 | } |
| 461 | |
| 462 | if (uncached.length > 0) { |
| 463 | const batchSize = getEmbeddingBatchSize(); |
| 464 | for (let b = 0; b < uncached.length; b += batchSize) { |
| 465 | const batch = uncached.slice(b, b + batchSize); |
| 466 | try { |
| 467 | const embeddings = await fetchEmbedding(batch.map((u) => u.text)); |
| 468 | for (let j = 0; j < batch.length; j++) { |
| 469 | this.vectors[batch[j].idx] = embeddings[j]; |
| 470 | cache[docs[batch[j].idx].path] = { hash: batch[j].hash, vector: embeddings[j] }; |
| 471 | } |
| 472 | } catch (error) { |
| 473 | if (!isContextLengthError(error)) throw error; |
| 474 | for (const item of batch) { |
| 475 | try { |
| 476 | const [vector] = await fetchEmbedding(item.text); |
| 477 | this.vectors[item.idx] = vector; |
| 478 | cache[docs[item.idx].path] = { hash: item.hash, vector }; |
| 479 | } catch (itemError) { |
| 480 | if (!isContextLengthError(itemError)) throw itemError; |
| 481 | delete cache[docs[item.idx].path]; |
| 482 | } |
| 483 | } |
| 484 | } |
| 485 | } |
| 486 | await saveCache(rootDir, cache); |
| 487 | } |
| 488 | } |
| 489 | |
| 490 | async search(query: string, optionsOrTopK?: number | SearchQueryOptions): Promise<SearchResult[]> { |
| 491 | const options = resolveSearchOptions(optionsOrTopK); |
no test coverage detected