fix(gate): robust isReserved strips model Altro/other variants (no duplicate free-text option)

This commit is contained in:
2026-07-03 13:05:21 +02:00
parent 3de1a784ce
commit 168a72372b
2 changed files with 59 additions and 7 deletions
+26 -7
View File
@@ -10,15 +10,34 @@ export const QUIT_LABEL = "Esci da Pi (/quit)";
export const BACK_LABEL = "Torna indietro (fase precedente)";
export const CONTROL_LABELS = new Set([QUIT_LABEL, BACK_LABEL]);
const RESERVED = new Set([ALTRO, QUIT_LABEL, BACK_LABEL]);
// true se l'etichetta e' una di quelle che il gate aggiunge da se'.
export function isReserved(label) {
return RESERVED.has(label);
// Normalize a label to lowercase ASCII tokens: strip diacritics, turn every run
// of punctuation/space into a single space, trim. "Altro — specifica…" -> "altro specifica".
function normalize(label) {
return String(label)
.normalize("NFD")
.replace(/[̀-ͯ]/g, "")
.toLowerCase()
.replace(/[^a-z0-9]+/g, " ")
.trim();
}
// rimuove da una lista di etichette ogni voce riservata (Altro + controlli),
// preservando ordine e voci normali. Idempotente.
const NORM_QUIT = normalize(QUIT_LABEL);
const NORM_BACK = normalize(BACK_LABEL);
// true if the label is one the gate adds itself. Robust to the variants models
// emit ("Altro - specificare", "altro", English "Other — specify"): reserved when
// the FIRST normalized token is exactly "altro"/"other", or the whole normalized
// label equals the canonical quit/back labels. First-token match keeps real
// options like "altrove" out of the reserved set.
export function isReserved(label) {
const n = normalize(label);
if (!n) return false;
const first = n.split(" ")[0];
return first === "altro" || first === "other" || n === NORM_QUIT || n === NORM_BACK;
}
// removes every reserved entry from a list of labels, preserving order and normal
// entries. Idempotent.
export function stripReserved(labels) {
return labels.filter((label) => !isReserved(label));
}