Commit Graph

16 Commits

Author SHA1 Message Date
61b3bcb5ff feat: Erfolge (Achievements) – Unlock-Erkennung + Listing
- Tabelle user_achievements (Migration in db-migrate.js)
- src/lib/achievements.js: Definitionen + dedup-sichere Freischaltung
  (ON CONFLICT DO NOTHING … RETURNING → nur Neues), Listing mit Status
- /auth/progress liefert unlocked_achievements (defensiv gekapselt)
- neue Route GET /auth/achievements

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 21:53:49 +02:00
bb863640c0 feat: progressive Level-Kurve + atomarer /auth/progress-Vertrag
- levelForEp/levelInfo (Level 1 bei 20 EP statt fixer 500/Level), src/lib/leveling.js
- /auth/me liefert level + ep_into_level + ep_to_next_level
- /auth/progress liefert prev_level, streak_increased, daily_ep, daily_goal_ep, goal_just_reached
  (CTE fängt die Pre-Update-Werte, damit Level-Up/Streak-Up atomar erkennbar sind)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 21:43:36 +02:00
339a3ed27d fix: bessere Wort-Kategorisierung, weniger "Sonstiges"
- Taxonomie um "Eigenschaften" (Adjektive) und "Verben & Handlungen"
  ergänzt → Wortarten haben ein Zuhause statt Sonstiges.
- Klassifizierer geschärft: klare Wortart-/Themen-Regeln, "Sonstiges"
  nur als letzter Ausweg; Sofort-Pfad nutzt jetzt Beispielsätze und
  kleinere Batches (15) für deutlich genauere Treffer.
- ?reset=true: bestehende Zuordnungen verwerfen und neu klassifizieren.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 14:39:28 +02:00
d66cff3f61 feat: automatische Wort-Kategorisierung (Batches API + Sofort-Backfill)
Feste ~20er-Taxonomie geseedet (de/en/sv, published; bestehende
Kategorien werden wiederverwendet) + Tabelle category_batches.

src/lib/classifyWords.js: findet in Pairs verwendete Wörter ohne
Kategorie und klassifiziert sie per Haiku gegen die feste Liste.
- Stundenjob über die Message Batches API (asynchron, ~50% günstiger):
  submit/collect-Ticks, in index.js nach Boot + stündlich.
- Sofortiger synchroner One-Shot-Backfill (classifyWordsSync) für
  Live-Test ohne 24h-Verzug.
Beides materialisiert pair_categories via derivePairCategories.

POST /api/categories/auto-assign (admin): ?sync=true = Sofort-Backfill,
sonst ein Batch-Tick. Entkoppelt von generate-words und Publish.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 14:27:09 +02:00
9738d3e35a feat: Profil-Kategorien + Begrüßung in Zielsprache
languages.greeting (de/en/sv geseedet), neue pair_categories-Tabelle
(abgeleitet aus statement- und objektverknüpften Wörtern via
word_categories) inkl. Backfill für bereits veröffentlichte Pairs.
derivePairCategories() wird beim Publish (pairs + pipeline) aufgerufen.
/auth/me liefert language_target_greeting, /auth/stats liefert
categories[] mit Punkten je Kategorie fürs Profil.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 12:55:57 +02:00
e44d896f9e feat: Objekt-Token-Cleanup + schärferer LLM-Prompt (Kopf-Kompositum vs Bestimmungswort)
Der LLM-Fallback hatte Objektwörter auch dann verlinkt, wenn sie nur
Bestimmungswort eines anderen Dings waren (z.B. "jordgubbsfältet"/Erdbeerfeld
als Erdbeere). Regel jetzt explizit:
- behalten: Wort, Beugung/Mehrzahl/bestimmte Form, Kopf-Kompositum
  ("Landschildkröte"=Schildkröte), Synonym ("Stiefel"/"Lederstiefel"=Schuh)
- entfernen: Objektwort nur als Bestimmungswort ("Erdbeerfeld" != Erdbeere)

- locateSurfaceLLM-Prompt um diese Regel + Beispiele geschärft (verhindert
  künftiges Fehl-Tagging).
- Neuer cleanup-Modus: POST /api/pipeline/retag-objects {"cleanup":true}
  prüft bestehende Objekt-Tokens per LLM und entfernt die falschen. Eindeutig
  gute Formen (exakt/Lemma+Endung) werden ohne LLM behalten.
- Helfer in objectTagging.js: objectTokensInSentence, isSimpleObjectForm, untagToken.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 19:56:13 +02:00
434839e1d4 feat: Objekt-Wörter deterministisch tokenisieren (Forward + Backfill)
Objekt-Hervorhebung (Chip + Bildregion) hängt an {{label.o:uuid}}-Tokens im
Satz. Bisher entstanden die nur aus dem LLM-Nomen-Markup, das Haiku oft
ausließ -> Objekt blieb un-getokt (z.B. "ryggsäcken"/Rucksack), obwohl korrekt
verlinkt.

- src/lib/objectTagging.js: deterministischer, flexions-toleranter Tagger
  (schwed. bestimmte Form -en/-et/...), idempotent, schützt bestehende Tokens.
- generatePairs.resolveNounMarkup: Sweep als Sicherheitsnetz + titel_sv im Lookup.
- pipeline.retagPair/retagObjects: per-Pair Nachtokenisierung (Hybrid-LLM-Fallback
  nur für in anderer Sprache bestätigte Objekte), Backfill über Bild/alle Bilder.
- POST /api/pipeline/retag-objects (dry_run/use_llm/picture_id).

Ändert nur Satz-Textfelder -> Audio/Alignment bleiben gültig.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 19:37:12 +02:00
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
25d1e89446 feat: Deep-Delete für Pairs und Bilder (Fragen/Statements/Audios/Objekte kaskadieren)
DELETE /pairs/:id räumt jetzt unreferenzierte Fragen/Statements samt
Audio-Dateien (DB + S3) mit auf. DELETE /pictures/:id löscht zusätzlich
die nur mit diesem Bild verknüpften Objekte inkl. deren Pairs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 21:25:15 +02:00
ddbd879dab feat: KI-Review-Schritt in der Pipeline (Korrekturlesen vor Audio)
Alle Pairs eines Bildes (de/en/sv) gehen zusammen mit dem Bild an Sonnet
zur Prüfung von Rechtschreibung, Übersetzungs-Konsistenz und Plausibilität.
Korrekturen werden vor der Audio-Erzeugung angewendet; vorhandene Audios
korrigierter Zellen werden invalidiert. Review-Fehler sind nicht fatal.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 21:41:20 +02:00
f5b69a9213 feat: ElevenLabs-Voice-Liste + Fehlerdetails in Audio-Batch-Ergebnissen
- GET /api/tts-settings/voices/available listet die Account-Stimmen
  (Grundlage für Voice-Auswahl im CMT statt Freitext-IDs)
- Audio-Batch/-Fill-Fehler enthalten jetzt das ElevenLabs-Detail
  (z.B. voice_not_found) statt nur 'ElevenLabs error'

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 21:00:27 +02:00
985119bb03 fix: Übersetzungs-Retry + robuster Translate-Step + Nachhol-Endpoints
- callClaude: Retry mit Backoff bei Überlast/Rate-Limit/Netzfehler
  (429/500/503/529) — wahrscheinliche Ursache der fehlenden SV-Übersetzung
- Translate-Step pro Pair gekapselt: ein Fehler reißt nicht mehr den ganzen
  Lauf ab, Fehlversuche werden gezählt (pipeline_progress.translateFailures)
- translatePair als wiederverwendbarer Helfer extrahiert
- POST /pipeline/picture/:id/translate-fill: fehlende Übersetzungen
  (Sätze + Antwort-Wörter) eines Bildes nachholen

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 22:03:11 +02:00
fb93d2296e fix: Readiness pro answer_type + Objekt-Zuweisung & Audio-Nachholen im Publish-Flow
- computeReadiness: yes_no braucht keinen Positiv-Satz (nur answer-Flag),
  word-Pairs prüfen verlinkte Wörter (Titel + Audio) statt Statement-Sätze
  → behebt 'bei jedem Pair fehlt ein Audio' im Publish-Review
- Bundle liefert Placeholder-Kandidaten: Objekt-Wörter, die im deutschen
  Satz vorkommen (außerhalb bestehender Placeholder, inkl. Flexion)
- POST /pipeline/assign-object: Wort in allen 3 Sprachen als
  {{wort.o:objectId}} markieren (über die Wort-Übersetzungen)
- POST /pipeline/picture/:id/audio-fill: fehlende Audios nachgenerieren

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 21:48:03 +02:00
6af2428df5 feat: automatische Content-Pipeline (release → pairs → übersetzen → audio → ready)
- pictures.pipeline_* Spalten + app_settings Tabelle (Migration)
- lib/placeholders.js: Placeholder-Auflösung; TTS spricht keine UUIDs mehr
- lib/pairContent.js: geteilte Pair-Logik (Readiness mit Skip-Optionen)
- lib/generatePairs.js: Claude-Generierung (konfigurierbare Anzahl, nur
  Nomen/Adjektive bei word-Pairs) + serverseitige Persistenz inkl. object_pairs
- lib/pipeline.js: In-Process-Runner, idempotente Schritte, Boot-Resume
- routes/pipeline.js: release/retry/overview/bundle/settings + Bild-Publish
  (kaskadiert Fragen/Statements/Pairs/Wörter/Objekte/Bild)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 20:52:11 +02:00
29a260e351 feat: bessere Übersetzungsqualität (Sonnet + Wörter mit Kontext)
- Übersetzungs-Modell auf Sonnet (env TRANSLATE_MODEL, Default claude-sonnet-4-5).
- Neue translateWords(): übersetzt die Wörter eines word-Pairs gemeinsam in
  einem Call, mit der Frage als Kontext → korrekte Bedeutung mehrdeutiger
  Wörter (z.B. 'Ranke' → 'ranka' statt 'klänge'), konsistente Gruppe.
- POST /pairs/:id/translate nutzt translateWordGroup für word-Typ und nimmt
  { overwrite:true } entgegen, um falsche bestehende Zielsprachen neu zu
  übersetzen (Quellsprache bleibt unangetastet); fillMissingRow erhält
  overwrite-Flag.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 21:29:11 +02:00
8f9a48fa5a feat: Pro-Pair-Übersetzung + Review-Kaskade auf Objekt/Bild
- Übersetzungs-Kern (Claude + Platzhalter-Schutz) nach src/lib/translate.js
  ausgelagert; claude.js importiert von dort (Endpoints unverändert).
- Neuer Endpoint POST /pairs/:id/translate: füllt fehlende Sprachen für
  Frage, Statements bzw. (bei word-Typ) verlinkte Wörter und liefert das
  3-sprachige Inhalts-Bündel fürs Review-Modal.
- POST /pairs/:id/review hebt verlinkte Objekte + Bilder zusätzlich auf
  'reviewed' (idempotent).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 14:27:52 +02:00