fix: cancel abandoned highlight() semantic work and harden WASM engine recovery

highlight() used Promise.race with a 10s timeout, but the losing
semantic phase promise continued running 25 WASM micro-batches,
saturating the event loop and degrading all subsequent operations
(find() going from ~200ms to ~10,000ms).

Add AbortController to highlight() so the semantic phase stops
immediately on timeout or error. Pass abort signal through
embedBatch() → EmbeddingManager → micro-batch loop.

Also add defensive hardening:
- CandleEmbeddingEngine: try/catch around WASM calls resets engine
  state on failure so next call triggers re-initialization
- WASMEmbeddingEngine: initialize() now checks underlying Candle
  engine state, not just its own flag, completing the recovery chain
This commit is contained in:
David Snelling 2026-01-27 18:26:37 -08:00
parent 279fccebfe
commit f8dd93c93c
4 changed files with 54 additions and 23 deletions

View file

@ -219,7 +219,7 @@ export class EmbeddingManager {
* @param texts Array of strings to embed
* @returns Array of embedding vectors (384 dimensions each)
*/
async embedBatch(texts: string[]): Promise<number[][]> {
async embedBatch(texts: string[], options?: { signal?: AbortSignal }): Promise<number[][]> {
if (texts.length === 0) return []
const isTestMode =
@ -248,6 +248,10 @@ export class EmbeddingManager {
// so other requests (HTTP, timers, I/O) can proceed
const allResults: number[][] = []
for (let i = 0; i < texts.length; i += MICRO_BATCH_SIZE) {
if (options?.signal?.aborted) {
return allResults
}
const batch = texts.slice(i, i + MICRO_BATCH_SIZE)
const batchResults = await this.engine.embedBatch(batch)
allResults.push(...batchResults)