From 229c3cc242b0c96548b1eaf6d1b8013cec75fdaa Mon Sep 17 00:00:00 2001 From: vikingowl Date: Wed, 7 Jan 2026 15:02:20 +0100 Subject: [PATCH] fix: add timeout to embedding generation to prevent page freeze - Add 30 second timeout to generateEmbedding and generateEmbeddings - Abort controller cancels request if it takes too long - Clear error message when embedding model isn't available --- frontend/src/lib/memory/embeddings.ts | 54 +++++++++++++++++++++------ 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/frontend/src/lib/memory/embeddings.ts b/frontend/src/lib/memory/embeddings.ts index 770e693..9f139d5 100644 --- a/frontend/src/lib/memory/embeddings.ts +++ b/frontend/src/lib/memory/embeddings.ts @@ -32,14 +32,27 @@ export async function generateEmbedding( text: string, model: string = DEFAULT_EMBEDDING_MODEL ): Promise { - const response = await ollamaClient.embed({ - model, - input: text - }); + const TIMEOUT_MS = 30000; // 30 second timeout + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), TIMEOUT_MS); - // Ollama returns an array of embeddings (one per input) - // We're only passing one input, so take the first - return response.embeddings[0]; + try { + const response = await ollamaClient.embed({ + model, + input: text + }, controller.signal); + + // Ollama returns an array of embeddings (one per input) + // We're only passing one input, so take the first + return response.embeddings[0]; + } catch (error) { + if (error instanceof Error && error.name === 'AbortError') { + throw new Error(`Embedding generation timed out. Is the model "${model}" available?`); + } + throw error; + } finally { + clearTimeout(timeoutId); + } } /** @@ -53,13 +66,30 @@ export async function generateEmbeddings( const BATCH_SIZE = 10; const results: number[][] = []; + // Create abort controller with timeout + const TIMEOUT_MS = 30000; // 30 second timeout per batch + for (let i = 0; i < texts.length; i += BATCH_SIZE) { const batch = texts.slice(i, i + BATCH_SIZE); - const response = await ollamaClient.embed({ - model, - input: batch - }); - results.push(...response.embeddings); + + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), TIMEOUT_MS); + + try { + const response = await ollamaClient.embed({ + model, + input: batch + }, controller.signal); + results.push(...response.embeddings); + } catch (error) { + clearTimeout(timeoutId); + if (error instanceof Error && error.name === 'AbortError') { + throw new Error(`Embedding generation timed out. Is the model "${model}" available?`); + } + throw error; + } finally { + clearTimeout(timeoutId); + } } return results;