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>
This commit is contained in:
2026-06-12 22:43:39 +02:00
parent 25d1e89446
commit 895d7c56a1
6 changed files with 199 additions and 18 deletions

View File

@@ -5,14 +5,25 @@ 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, resolvePlaceholdersToLabels };
module.exports = { PLACEHOLDER_RE, TOKEN_RE, stripLeakedTokens, resolvePlaceholdersToLabels };