From 5ab34fff6765d9d9152c896cabb8d085a967400c Mon Sep 17 00:00:00 2001 From: ghost <49853598+JSONbored@users.noreply.github.com> Date: Thu, 9 Jul 2026 01:23:23 -0700 Subject: [PATCH] fix(rag): reject invalid embed batch sizes --- src/review/rag.ts | 10 ++++++---- test/unit/rag.test.ts | 11 +++++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/review/rag.ts b/src/review/rag.ts index 38ddbf7593..60d7619485 100644 --- a/src/review/rag.ts +++ b/src/review/rag.ts @@ -122,8 +122,8 @@ export function ragDimensionsFromEnv(value: string | undefined): number { } export function ragEmbedBatchFromEnv(value: string | undefined): number { - const batch = Number(value); - return Number.isFinite(batch) && batch > 0 ? Math.floor(batch) : EMBED_BATCH; + const batch = Math.floor(Number(value)); + return Number.isFinite(batch) && batch > 0 ? batch : EMBED_BATCH; } // ── Filtering: index CODE, not content/data corpora (the primary free-tier cost guard) ─────────── @@ -298,10 +298,12 @@ export async function embedTexts( batchSize = EMBED_BATCH, ): Promise { if (!inference || texts.length === 0) return null; + const effectiveBatchSize = Math.floor(batchSize); + if (!Number.isFinite(effectiveBatchSize) || effectiveBatchSize < 1) return null; try { const out: number[][] = []; - for (let i = 0; i < texts.length; i += batchSize) { - const batch = texts.slice(i, i + batchSize); + for (let i = 0; i < texts.length; i += effectiveBatchSize) { + const batch = texts.slice(i, i + effectiveBatchSize); const res = (await inference.run(EMBED_MODEL, { text: batch })) as { data?: number[][] } | null; const data = res?.data; // Validate COUNT and DIMENSION: a self-host embedding endpoint can return a structurally-valid response diff --git a/test/unit/rag.test.ts b/test/unit/rag.test.ts index 7b6e87ffe3..434b677b8f 100644 --- a/test/unit/rag.test.ts +++ b/test/unit/rag.test.ts @@ -169,6 +169,7 @@ describe("ragEmbedBatchFromEnv", () => { expect(ragEmbedBatchFromEnv("")).toBe(96); expect(ragEmbedBatchFromEnv("not-a-number")).toBe(96); expect(ragEmbedBatchFromEnv("0")).toBe(96); + expect(ragEmbedBatchFromEnv("0.5")).toBe(96); expect(ragEmbedBatchFromEnv("-5")).toBe(96); }); }); @@ -733,6 +734,16 @@ describe("rag: embedTexts validation branches", () => { expect(calls).toEqual([96, 54]); // proves the batching loop ran twice }); + it("rejects an invalid batchSize before embedding so a zero step cannot hang", async () => { + const inference: InferenceAdapter = { + run: async () => { + throw new Error("must not call embedding provider for an invalid batch size"); + }, + }; + expect(await embedTexts(inference, ["hi"], RAG_DIMENSIONS, 0)).toBeNull(); + expect(await embedTexts(inference, ["hi"], RAG_DIMENSIONS, Number.NaN)).toBeNull(); + }); + it("honors a configured batchSize override instead of the EMBED_BATCH=96 default (self-host GPU tuning)", async () => { const calls: number[] = []; const inference: InferenceAdapter = {