Fix embed() calls silently timing out, dropping single-source items forever

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.
This commit is contained in:
Claude
2026-07-27 14:36:27 +00:00
parent ecfcce9eb1
commit afdb5ad036
+11 -2
View File
@@ -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;