(options: { rootDir: string; relativePaths: string[] })
| 192 | } |
| 193 | |
| 194 | export async function refreshFileSearchEmbeddings(options: { rootDir: string; relativePaths: string[] }): Promise<number> { |
| 195 | const uniquePaths = Array.from(new Set(options.relativePaths.map(normalizeRelativePath).filter(Boolean))); |
| 196 | if (uniquePaths.length === 0) return 0; |
| 197 | |
| 198 | const cache = await loadEmbeddingCache(options.rootDir, SEARCH_CACHE_FILE); |
| 199 | const pending: { path: string; hash: string; text: string }[] = []; |
| 200 | |
| 201 | for (const relativePath of uniquePaths) { |
| 202 | const doc = await buildSearchDocumentForFile(options.rootDir, relativePath); |
| 203 | if (!doc) { |
| 204 | delete cache[relativePath]; |
| 205 | continue; |
| 206 | } |
| 207 | |
| 208 | const text = `${doc.header} ${doc.symbols.join(" ")} ${doc.content}`; |
| 209 | const hash = hashContent(text); |
| 210 | if (cache[relativePath]?.hash === hash) continue; |
| 211 | pending.push({ path: relativePath, hash, text }); |
| 212 | } |
| 213 | |
| 214 | if (pending.length > 0) { |
| 215 | const batchSize = getEmbeddingBatchSize(); |
| 216 | for (let i = 0; i < pending.length; i += batchSize) { |
| 217 | const batch = pending.slice(i, i + batchSize); |
| 218 | const vectors = await fetchEmbedding(batch.map((entry) => entry.text)); |
| 219 | for (let j = 0; j < batch.length; j++) { |
| 220 | cache[batch[j].path] = { hash: batch[j].hash, vector: vectors[j] }; |
| 221 | } |
| 222 | } |
| 223 | } |
| 224 | |
| 225 | await saveEmbeddingCache(options.rootDir, cache, SEARCH_CACHE_FILE); |
| 226 | invalidateSearchCache(); |
| 227 | return pending.length; |
| 228 | } |
no test coverage detected