From afdb5ad0366f060cd004539139e84585a59430b4 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 27 Jul 2026 14:36:27 +0000 Subject: [PATCH] Fix embed() calls silently timing out, dropping single-source items forever MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reported symptom: articles that never got AI-merged (single source, nothing else to combine with) simply never published at all. Root cause: the same default-5-minute-fetch-timeout bug fixed for generate() earlier was never applied to embed(). Ollama serves one inference request at a time (n_slots = 1) — an embed() call issued while a slow generate() call is in flight has to wait in queue for that same slot, and on this CPU-only hardware a generate() call can easily run past 5 minutes. That wait alone was enough to trip Node's default fetch timeout on the embed request. embedPendingItems() catches that failure and just drops the item from its result (logged, not thrown) — clusterItems() only ever sees items that already have an embedding, so a dropped item never joins a cluster, never gets assignCluster() called, and stays "unclustered" forever, retried every cycle with the same failure for as long as Ollama stays busy. An item that happened to embed during an idle window still merges or publishes fine — which is exactly the split reported: synthesized articles show up, standalone ones don't. Fix: embed() now uses the same noTimeoutDispatcher already wired into generate(). Verified the request completes correctly end-to-end against a real HTTP server that delays its response. --- backend/src/inference/ollama-provider.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/backend/src/inference/ollama-provider.ts b/backend/src/inference/ollama-provider.ts index 7173922..002f68b 100644 --- a/backend/src/inference/ollama-provider.ts +++ b/backend/src/inference/ollama-provider.ts @@ -79,8 +79,17 @@ export class OllamaProvider implements InferenceProvider { const res = await fetch(`${this.base()}/api/embeddings`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ model: opts.model, prompt: text }) - }); + body: JSON.stringify({ model: opts.model, prompt: text }), + // Ollama serves one inference request at a time (n_slots = 1) — an embed call + // queued behind a slow generate() call waits for that same slot, and on this + // CPU-only hardware a generate() call can easily run past 5 minutes. Without + // this, that wait alone was enough to trip the same default fetch timeout + // generate() had (see noTimeoutDispatcher above), silently dropping the item + // from embedPendingItems — it never got clustered, so a single-source item + // unlucky enough to be embedded while Ollama was busy never published at all, + // retried every cycle with the same result for as long as Ollama stayed busy. + dispatcher: noTimeoutDispatcher + } as RequestInit); if (!res.ok) throw new Error(`Ollama embed failed: ${res.status} ${await res.text()}`); const data = (await res.json()) as { embedding: number[] }; return data.embedding;