expria-backend/docs/CHANGELOG-backend.md
Hermann_Kitio 5263372839
Some checks are pending
CI / quality (push) Waiting to run
feat(infra): route Gemini WS through SOCKS5 proxy (WARP)
- Add socks-proxy-agent dependency
- Add resolveGeminiProxyAgent() helper reading GEMINI_PROXY_URL env
- Apply agent to T1 and T2 Gemini WS factory defaults
- No proxy when GEMINI_PROXY_URL is unset (local dev unchanged)
- Tests: 311/311 green
2026-06-30 20:30:15 +03:00

18 KiB
Raw Blame History

Changelog — Expria Backend

Toutes les modifications notables du backend sont documentées dans ce fichier.

Format basé sur Keep a Changelog.


[Unreleased] — 2026-06-30 — Proxy SOCKS5 (Cloudflare WARP) pour les WS Gemini Live

Added

  • resolveGeminiProxyAgent() (geminiLive.ts) — helper qui lit la variable d'environnement optionnelle GEMINI_PROXY_URL et renvoie un SocksProxyAgent si elle est définie, sinon undefined (connexion directe). Contexte : l'IP du VPS de production (datacenter) est bloquée par Google ; Cloudflare WARP tourne en mode proxy SOCKS5 sur le VPS (socks5://127.0.0.1:40000). Seul le trafic WS Gemini est routé via ce proxy ; Supabase, DeepSeek et les clients restent en direct.
  • Dépendance socks-proxy-agent (package.json).
  • GEMINI_PROXY_URL ajoutée à .env.example (vide par défaut → dev local inchangé).
  • Tests geminiLive.test.ts — 2 tests pour resolveGeminiProxyAgent (absente → undefined ; socks5://… → instance SocksProxyAgent).

Changed

  • Factory WS par défaut de openGeminiLiveSession (T2, geminiLive.ts) et openGeminiLiveT1Session (T1, geminiLiveT1.ts) — passe désormais { agent } au constructeur new WebSocket(url, options) quand un proxy est résolu. La factory injectée par les tests (clientFactory) n'est pas affectée. Prompt système, interruption, flush, runT1/T2LiveCorrection et close codes inchangés.

Notes

  • Tests backend : 311/311 verts. tsc --noEmit OK.
  • Activation en prod : définir GEMINI_PROXY_URL=socks5://127.0.0.1:40000 côté VPS/Render (config d'env, hors code).

[Unreleased] — 2026-06-30 — Sprint 7a-patch — T1 Live : suppression de la dépendance au questionnaire

Changed

  • buildT1SystemPrompt (geminiLiveT1.ts) — prompt système T1 désormais statique (signature sans argument). La section « CONTEXTE DU CANDIDAT » (5 variables ${reponses.*}) est retirée et remplacée par une consigne d'écoute : « Écoute attentivement ce que le candidat dit. Quand on te le signale, formule UNE question de relance courte (10-20 mots) liée à ce que le candidat vient de dire. » Les 8 règles sont conservées (silence par défaut, relance sur signal, ton bienveillant, jamais d'évaluation/hors-rôle, règle 5 « DOIS poser des questions »). Règle 4 : retrait de « ou à son contexte ci-dessus ».
  • openGeminiLiveT1Session (geminiLiveT1.ts) — option reponses retirée de OpenGeminiLiveT1SessionOptions et de la signature. Le handler de message client ignore désormais explicitement (log debug + return) tout message non reconnu (ni audio ni end) — un éventuel {type:'context'} d'un ancien front ne provoque ni crash ni close.
  • WS /t1/live (t1live.ts) — la session Gemini s'ouvre immédiatement après l'auth (calque T2), dans onOpen. Le flux client devient {type:'audio', data} puis {type:'end'}.
  • docs/Prompt_t1live.md — §1 (table « Subject-based »), §2 (règle 3), §3 (prompt statique, retrait des variables et de la section « Variables à substituer »), §4.1 (retrait du message de contexte), §4.3 (retrait du close 4004).
  • Tests geminiLiveT1.test.ts / t1live.test.ts — appels buildT1SystemPrompt() sans argument, retrait de reponses des appels de session ; suppression du test d'intégration des réponses (remplacé par un test « écoute / plus de CONTEXTE DU CANDIDAT ») et du bloc parseT1Context ; tests interruption, flush terminal, timeout et correction inchangés.

Removed

  • Message client {type:'context', reponses} (1er message obligatoire) et close 4004 CONTEXT_MISSING — la route ne lit plus de contexte.
  • Fonction parseT1Context (t1live.ts) et ses imports validateReponses / PresentationReponses.

Notes

  • Tests backend : 309/309 verts. tsc --noEmit OK.
  • Suivi frontend (hors scope) : le frontend Sprint 7b (non commité) envoie encore {type:'context'} et possède un QuestionnaireT1Page Live ; ce message est désormais inoffensif côté backend (ignoré). Le questionnaire Live devient sémantiquement obsolète — à retirer dans une session frontend dédiée.

[Unreleased] — 2026-06-28 — Sprint 6d — T2 Live : durcissement prompt + VAD + cleanup SDK

Changed

  • buildT2SystemPrompt (geminiLive.ts) — prompt système T2 durci : 13 règles absolues (Bug 1). Interdiction stricte de poser des questions (aucun point d'interrogation), rôle passif/inerte, silence total après réponse, aucune formule de politesse de fin. Objectif : stopper la relance systématique de l'examinateur IA. Réf TD-22. ⚠ Spécifique T2 — l'interdiction du « ? » ne doit pas être propagée au prompt T1 (Sprint 7).
  • buildSetupFrame (geminiLive.ts) — realtimeInputConfig.automaticActivityDetection (VAD) réintégré dans le setup frame Gemini (Bug 2), 4 champs : disabled:false, startOfSpeechSensitivity:START_SENSITIVITY_LOW, endOfSpeechSensitivity:END_SENSITIVITY_LOW, silenceDurationMs:2000.
  • geminiLive.test.ts — assertion VAD mise à jour (realtimeInputConfig : toBeUndefined → présence + valeurs des 4 champs).

Removed

  • Dépendance @google/genai retirée de package.json / package-lock.json et script de debug test-gemini-live.js supprimé (Bug 8) — SDK abandonné au profit du WebSocket brut (ws).

Notes

  • Tests backend : 292/292 verts (0 échec).
  • Validé au test manuel Golden Dataset Groupe D — Bug 1 (plus de relance systématique) et Bug 2 (VAD, pauses de réflexion respectées) confirmés en conditions réelles.
  • TD-22 (🟡 ouvert) — comportement T2 garanti uniquement par prompt engineering ; pas de garantie déterministe sur modèle Flash Live. À revisiter si la relance persiste.

[Unreleased] — 2026-04-26 — Sprint 6a — Backend T2 Live

Added

  • buildT2SystemPrompt({role, contexte}) dans geminiLive.ts — prompt dynamique conforme Prompt_t2live.md §3, remplace la constante T2_SYSTEM_PROMPT (agent immobilier).
  • Accumulation transcripts pendant la session WS : inputTranscription[] + outputTranscription[] parsés depuis les messages Gemini, reconstruits en transcript chronologique à la fin.
  • VAD config dans le setup frame Gemini : endOfSpeechSensitivity: END_SENSITIVITY_LOW, startOfSpeechSensitivity: START_SENSITIVITY_LOW, silenceDurationMs: 2000.
  • Timeout session 210 s (3 min 30) + warning client à 180 s (30 s restantes).
  • Signal client {type:'end'} pour fin anticipée du dialogue.
  • Close codes : 4005 GEMINI_CONFIG, 4006 GEMINI_DISCONNECTED.
  • Orchestration t2live.ts : fetch sujet par UUID (?sujet=<uuid>, validation mode='EO' + tache=2), close 4004 SUJET_NOT_FOUND si absent.
  • Post-session : runT2LiveCorrection — insert productions(tache='EO_T2_LIVE'), appel deepseekCorrectEO(transcript, 'EO_T2'), PHONOLOGY_STUB (TD-08), persist rapport + score + nclc, envoi {type:'report'} au client, close 1000.
  • TacheEO étendu avec 'EO_T2' dans deepseek.ts + VALID_TACHES_EO dans corrections.ts.
  • 10 tests d'intégration t2live.test.ts (auth, sujet, pipeline correction nominal + erreurs).
  • 11 tests geminiLive.test.ts (7 réécrits + 4 nouveaux : prompt builder, accumulation, timeout/warning, end signal).

Changed

  • geminiLive.ts réécrit — setup frame paramétrable, inputAudioTranscription + outputAudioTranscription activés, callback onSessionEnd(transcript).
  • corrections.tsVALID_TACHES_EO inclut 'EO_T2'.

Notes

  • Tests backend : 292/292 verts (+15 vs baseline 277).
  • Phonologie T2 Live = 0 (TD-08 — pas d'audio brut pour évaluation phonologique).
  • Le frontend n'est pas encore connecté — test e2e au Sprint 6c.

[Unreleased] — 2026-04-26 — Sprint 5a — Backend billing cleanup

Added

  • supabase/migrations/007_sprint_5a_stripe_webhook_events.sql — table stripe_webhook_events(id TEXT PRIMARY KEY, processed_at TIMESTAMPTZ NOT NULL DEFAULT NOW()) + index sur processed_at. Idempotente (CREATE TABLE IF NOT EXISTS).
  • src/lib/stripeWebhookEvents.ts — helpers isEventProcessed / markEventProcessed (insert idempotent, conflit unique 23505 avalé silencieusement).
  • src/lib/__tests__/stripeWebhookEvents.test.ts — 8 tests (lecture, écriture, edge cases vide/erreur DB).
  • src/lib/__tests__/createBillingPortalSession.test.ts — 4 tests (succès, customerId vide, returnUrl vide, URL Stripe vide).
  • POST /stripe/customer-portal — endpoint authentifié qui crée une Stripe Billing Portal Session (gestion abonnement self-service) et redirige l'utilisateur. 400 NO_ACTIVE_SUBSCRIPTION si pas de stripe_customer_id ; return_url = ${APP_URL}/dashboard.

Changed

  • POST /stripe/webhook — déduplication explicite des events Stripe (TD-13 résolu) : check isEventProcessed(event.id) avant traitement → early return 200 { received: true, replayed: true } ; markEventProcessed après succès uniquement (pas si exception, pour permettre rejeu Stripe).
  • src/lib/stripe.ts — nouvelle fonction createBillingPortalSession({ customerId, returnUrl }) (mirror de createCheckoutSession).
  • src/routes/__tests__/stripe.test.ts — 5 nouveaux tests (2 idempotency webhook + 3 customer-portal route).
  • docs/ARCHITECTURE-backend.md — §3 commentaire plans.ts corrigé (POST /plans/upgrade-prorata au lieu de POST /plans/upgrade qui n'existait pas) ; §6 retrait de la ligne dupliquée POST /plans/upgrade (la création d'abonnement passe par POST /stripe/checkout) ; §6 ajout POST /stripe/customer-portal.

Fixed

  • src/lib/stripe.tscancel_url Stripe Checkout corrigé : ${APP_URL}/tarifs?upgrade=cancelled${APP_URL}/plan?upgrade=cancelled. La route /tarifs n'existe pas côté frontend (route réelle : /plan) ; les checkouts annulés aboutissaient sur un 404. Bug détecté lors du Sprint 5c frontend (gestion des retours post-Checkout) et corrigé en cross-repo.

Resolved

  • TD-13 🔴 → Résolu : Webhook Stripe idempotent (table stripe_webhook_events + helpers + wiring route + 10 tests).

Notes

  • Tests : 261 → 278 verts (+17).
  • Aucun changement code frontend dans ce sprint — Sprint 5b/5c/5d (frontend billing) livrés en parallèle.

[Unreleased] — 2026-04-26 — Sprint 4.8 — Phonologie EO

Added

  • src/lib/geminiPhonology.ts — évaluation phonologique via Gemini 2.5 Flash (audio brut, JSON strict, timeout 45s + 1 retry). PHONOLOGY_STUB pour Mode A (transcript sans audio).
  • src/lib/__tests__/geminiPhonology.test.ts — 9 tests (parse, cap 0..4, retry, erreurs HTTP).
  • src/controllers/__tests__/correctionEoPhonology.test.ts — 4 tests (injection 5e critère, stub Mode A, fallback Gemini down, score max 20).

Changed

  • POST /corrections/eo — passe de 4 critères × /5 à 5 critères × /4 (score total /20 inchangé). Phonologie évaluée par Gemini en parallèle de la transcription (Mode B audio). Fallback stub si Gemini phonologie échoue.
  • src/lib/deepseek.ts — prompt EO : cap critère /5 → /4, libellés officiels TCF Canada, mention phonologie évaluée séparément. Cap EE inchangé /5.
  • TD-08 partiellement résolu (T1/T3). T2 Live reste à 0 (Sprint 6).

Notes

  • ⚠️ Breaking change frontend : criteres.length passe de 4 à 5, échelle /5 → /4.
  • Tests : 248 → 261 verts (+13).

[Unreleased] — 2026-04-25 — Sprint 4a/4b — Backend EO

Added

  • POST /corrections/eo aligné format Sprint 3.6a : revelation, diagnostic, criteres enrichis (exemple/suggestion/astuce), conseil_nclc adaptatif (4 niveaux selon score vs cible), erreurs_codes, jobs fire-and-forget modèle + exercices
  • POST /presentations/generate — génération présentation T1 via DeepSeek (220-260 mots, registre oral NCLC 7-8, 5 champs)
  • POST /transcriptions/token — token Deepgram éphémère (600s TTL, dormant côté frontend MVP)
  • src/lib/deepgram.ts — client Deepgram /v1/auth/grant (scope Member requis)
  • src/lib/audioStorage.ts — supprimé (audio non stocké côté serveur)
  • Migration 006_sprint_4a_eo.sql — documentation bucket Storage (no-op)

Changed

  • correctEO : accepte audioBase64+mimeType (Gemini batch) OU transcript texte
  • MIME normalisé avant validation (audio/webm;codecs=opus → audio/webm)
  • sanitizeJsonContent : gère single-quote JSON DeepSeek
  • Gemini timeout 30s → 45s, DeepSeek correctEO 55s → 90s
  • gemini-2.0-flashgemini-2.5-flash
  • conseil_nclc adaptatif EE + EO (4 niveaux : dépassé / atteint / proche / loin)
  • Tests : 205 → 248 verts (+43)

[Unreleased] — 2026-04-25 — Fix health check keepalive Supabase

Changed

  • Route GET / : ajout d'un ping Supabase (profiles.select('id', { head: true }).limit(1)) à chaque appel. Garde le pool de connexions DB actif via les pings UptimeRobot (toutes les 5 min). Réponse enrichie : { message, db: 'connected' | 'error' }. Toujours 200 (liveness, pas readiness).

[Unreleased] — 2026-04-22 — Sprint 3.6a — Qualité correction Backend

Added

  • Nouveaux prompts DeepSeek spécifiés dans docs/Prompt_maître.md et docs/Prompt_production_modèle.md — builders dynamiques buildCorrectionPrompt, buildModelPrompt, buildExercicesPrompt dans src/lib/deepseek.ts.
  • expria-frontend/docs/TAXONOMIE_ERREURS.md — 63 codes d'erreurs TCF Canada sur 4 critères + 4 codes « autre ». Validation runtime via src/lib/taxonomieErreurs.ts (isValidCode, isValidCritere, buildTaxonomyPromptSection). Codes invalides retournés par DeepSeek sont filtrés ; le code autre sans description est rejeté.
  • Génération parallèle correction + modèle — option (b) : generateProductionModele démarre en même temps que correctEE avec nclcObtenu = nclcCible - 1 comme estimation provisoire, await uniquement sur la correction pour répondre à la requête HTTP.
  • Exercices personnalisés fire-and-forget déclenchés après la résolution de la correction (dépendent de rapport.erreurs_codes et rapport.criteres). Format aligné sur les captures d'écran : {difficulte, theme, diagnostic, consigne, extrait, indice, correction, explication}.
  • Nouveaux champs dans productions : revelation (JSONB), diagnostic (TEXT), conseil_nclc (JSONB), erreurs_codes (JSONB), exercices (JSONB), modele (JSONB), nclc_cible (INTEGER), exercices_status / modele_status (TEXT, 'pending'/'ready'/'error').
  • Migration SQL supabase/migrations/004_sprint_3_6a_qualite_correction.sql — première migration versionnée du projet (cf. backend TD-06) ; idempotente grâce à IF NOT EXISTS.
  • Paramètre nclc_cible optionnel sur POST /corrections/ee (défaut 9, valeurs acceptées : 9 ou 10 ; sinon 400 VALIDATION_ERROR).
  • Index GIN sur erreurs_codes pour préparer l'agrégation du Sprint 3.6c (analyse patterns).
  • Nouveau fichier de tests src/controllers/__tests__/correctionController.test.ts — 8 tests (parallélisme option b, statuts ready/error, nclc_cible propagé, simulation introuvable, autre utilisateur).
  • 2 tests ajoutés à simulationController.test.tsgetById renvoie nclc_cible, exercices, modele + statuts.
  • Logs d'erreur détaillés : callDeepSeek classifie TIMEOUT / ABORT / JSON_PARSE / NETWORK / OTHER ; correctionController.correctEE logue {simulationId, tache, nclcCible, message, stack} avant de retourner 500.
  • FTD-23 🟡 ajoutée dans expria-frontend/docs/TECH_DEBT.mduseAutosave peut fire un PATCH /simulations/:id/contenu après correction, ce qui retourne 400 VALIDATION_ERROR. À corriger dans une session dédiée (préexistant au Sprint 3.6a, détecté lors des tests manuels).

Changed

  • correctEE dans src/lib/deepseek.ts — nouvelle signature correctEE(CorrectionInput) (contenu, tache, sujet, sourceDoc1/2, nclcCible) et nouvelle forme de retour CorrectionRapport (revelation, diagnostic, criteres avec exemple/suggestion/astuce, conseil_nclc, erreurs_codes). EERapport devient alias de CorrectionRapport. EO inchangé.
  • correctionController.correctEE — charge le sujet + documents T3 depuis Supabase pour alimenter le prompt maître ; persiste les nouveaux champs (revelation, diagnostic, conseil_nclc, erreurs_codes, nclc_cible) + statuts pending initiaux ; lance runModeleJob en parallèle (option b) et runExercicesJob après correction.
  • simulationController.getById — retourne désormais nclc_cible, exercices, exercices_status, modele, modele_status en plus du rapport enrichi ; fallback 'pending' si les colonnes sont absentes (compat avec productions pré-migration).
  • Timeout DeepSeek côté backend : callDeepSeek abort à 55 s via AbortSignal.timeout(55_000) (avant : aucun timeout) ; timeout frontend corrections monte de 30 s à 60 s — marge de 5 s entre abort backend et abort client.
  • Routes /simulations/* : réorganisation défensive — les PATCH /:id/contenu et PATCH /:id/sujet sont déclarées avant GET /:id pour éviter tout risque de masquage.
  • deepseek.test.ts réécrit (25 tests) — couvre correctEE nouvelle signature, generateProductionModele, generateExercices, helpers post-traitement, EO inchangé.

Notes

  • Option A retenue pour la compatibilité frontend : backend renvoie uniquement la nouvelle forme. Le Sprint 3.6b (frontend) est immédiatement suivant et corrige l'écran blanc sur RapportPage.
  • Option (b) retenue pour le parallélisme : modèle en parallèle avec correction (nclcObtenu estimé), exercices strictement après correction.
  • Migration SQL à exécuter manuellement via supabase db push ou SQL Editor du dashboard (cf. Règle F) — aucune exécution automatique.
  • Tests : 174 tests verts (+19 vs baseline 155), 18 fichiers de tests.
  • TD-15 🟡 ouvert : si le process redémarre pendant un job fire-and-forget (modèle/exercices), le statut reste pending indéfiniment. À traiter après observation en production.