From 8c536a10dad2fd0b54aab6b76d45967836d8403c Mon Sep 17 00:00:00 2001 From: Peyton-Spencer Date: Mon, 10 Aug 2026 06:01:09 -0400 Subject: [PATCH] fix: scale translation output token ceiling with batch size Provider-default output limits (often ~4k tokens) truncated large batch responses mid-JSON, surfacing as 'Unterminated string in JSON' or 'model response contained no JSON object' failures on 50-key batches. Set maxTokens proportional to the number of keys (batch) or document length (markdown), capped at 32k. Co-Authored-By: Claude Fable 5 --- src/translate.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/translate.ts b/src/translate.ts index 583e304..497124f 100644 --- a/src/translate.ts +++ b/src/translate.ts @@ -125,16 +125,20 @@ export async function translateBatch( JSON.stringify(entries, null, 2), ].join("\n"); - const attempt = async (prompt: string) => { + const attempt = async (prompt: string, keyCount: number) => { const { text } = await generateText({ model, system: systemPrompt || defaultSystem, prompt, + // Provider-default output ceilings (often 4k tokens) truncate large + // batches mid-JSON ("Unterminated string in JSON"). Scale the ceiling + // with batch size so the full object always fits. + maxTokens: Math.min(32_000, 2_000 + 400 * keyCount), }); return collectBatchTranslations(extractJsonObject(text), keys); }; - let { translations, missing } = await attempt(basePrompt); + let { translations, missing } = await attempt(basePrompt, keys.length); if (missing.length > 0) { // One corrective retry for just the missing keys, then hard-fail so the @@ -149,6 +153,7 @@ export async function translateBatch( contextSection, JSON.stringify(retryEntries, null, 2), ].join("\n"), + missing.length, ); translations = { ...translations, ...retry.translations }; missing = retry.missing; @@ -194,6 +199,9 @@ export async function translateMarkdown( translated: z.string(), }), system: systemPrompt || defaultSystem, + // Scale the output ceiling with document size so long documents are not + // truncated by provider-default limits. + maxTokens: Math.min(32_000, 2_000 + Math.ceil(content.length / 2)), prompt: [ `Translate this Markdown/MDX content from "${sourceLocale}" to "${targetLocale}".`, `Return the complete translated document.`,