End the hint prompt in a full stop: an unterminated list made Whisper drop punctuation and loop

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Lucas Winther
2026-10-29 21:55:48 +01:00
co-authored by Claude Opus 5.5
parent 1c5952f212
commit 8236ea3504
4 changed files with 39 additions and 17 deletions
+12 -5
View File
@@ -72,10 +72,13 @@ function defaultStorage(): StorageLike | null {
* Token ids for the hint as a Whisper prompt (without `<|startofprev|>`),
* truncated predictably to `budget` tokens (D-WS2a):
*
* 1. Encode `" " + normalizeHint(hint)` (Whisper prompts start with a space).
* 1. Prompt text = `" " + normalizeHint(hint)` (Whisper prompts start with a
* space), ending in a full stop unless it already ends in . ? or ! —
* measured: an unterminated list makes Whisper drop punctuation and, on
* smaller models, fall into repetition loops (D-WS2a).
* 2. Over budget → keep the longest prefix of whole space-separated words that
* fits, with a dangling "," / ";" removed: the start of the list is kept,
* the end dropped, never mid-word.
* fits (full stop included), a dangling "," / ";" removed: the start of the
* list is kept, the end dropped, never mid-word.
* 3. Not even the first word fits → its first `budget` tokens.
*
* `encode` must add no special tokens. Mirrored by `fit_prompt_ids` in
@@ -88,12 +91,12 @@ export function fitHintToBudget(
): number[] {
const text = normalizeHint(hint);
if (text.length === 0) return [];
const full = encode(` ${text}`);
const full = encode(promptSentence(text));
if (full.length <= budget) return full;
const words = text.split(" ");
const encodePrefix = (count: number) =>
encode(` ${words.slice(0, count).join(" ").replace(/[,;]+$/, "")}`);
encode(promptSentence(words.slice(0, count).join(" ").replace(/[,;]+$/, "")));
// Largest word count whose encoding fits; token count grows with the
// prefix, so binary search needs O(log n) encodes even for huge pastes.
let low = 0;
@@ -106,3 +109,7 @@ export function fitHintToBudget(
if (low === 0) return encode(` ${words[0]}`).slice(0, budget);
return encodePrefix(low);
}
function promptSentence(text: string): string {
return /[.?!]$/.test(text) ? ` ${text}` : ` ${text}.`;
}