Files
snakkimo-API/src/lib/placeholders.js
admin 895d7c56a1 feat: Placeholder in der Auto-Generierung + Token-Leak-Fix
- Pair-Generierung markiert Nomen per [surface|lemma]-Markup und löst sie zu
  {{label.o:objectId}} / {{label.w:wordId}} auf (Words werden auto-erstellt)
- Pipeline übersetzt + vertont Placeholder-Wörter aus den Sätzen mit
- translateText halluziniert keine ⟦PHn⟧-Tokens mehr (kein Token-Prompt ohne
  Tokens, defensives Strippen); TTS/Review lösen geleakte Tokens auf
- POST /api/pipeline/repair-tokens repariert bestehende Sätze + Audios

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 22:43:39 +02:00

30 lines
1.3 KiB
JavaScript

// Placeholder-Format in Satz-/Titel-Feldern: {{label.w:uuid}} (Wort) bzw. {{label.o:uuid}} (Objekt).
// Geteilt zwischen translate.js (Token-Schutz) und TTS (Auflösung in Sprechtext).
const PLACEHOLDER_RE = /\{\{([^.{}]+)\.(w|o):([0-9a-f-]{36})\}\}/g;
// Legacy-Form ohne Label: {{uuid}} — sollte migriert sein, defensiv trotzdem entfernen.
const LEGACY_PLACEHOLDER_RE = /\{\{\s*[0-9a-f-]{36}\s*\}\}/g;
// Schutz-Token während Übersetzung/Review: ⟦PHn:label⟧. Darf nie in der DB landen —
// falls doch (Claude-Halluzination), wird er überall defensiv zum Label aufgelöst.
const TOKEN_RE = /⟦(PH\d+):([^⟧]*)⟧/g;
// Entfernt geleakte ⟦PHn:label⟧-Tokens aus einem Text → nur das Label bleibt.
function stripLeakedTokens(text) {
if (!text) return text;
return String(text).replace(TOKEN_RE, (_, _key, label) => label.trim());
}
// Macht aus "Ist das ein {{Apfel.w:1234-…}}?" → "Ist das ein Apfel?" (für TTS/Anzeige).
function resolvePlaceholdersToLabels(text) {
if (!text) return '';
return String(text)
.replace(PLACEHOLDER_RE, (_, label) => label)
.replace(LEGACY_PLACEHOLDER_RE, '')
.replace(TOKEN_RE, (_, _key, label) => label.trim())
.replace(/\s{2,}/g, ' ')
.trim();
}
module.exports = { PLACEHOLDER_RE, TOKEN_RE, stripLeakedTokens, resolvePlaceholdersToLabels };