Some checks are pending
CI / quality (push) Waiting to run
- 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
18 KiB
18 KiB
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 optionnelleGEMINI_PROXY_URLet renvoie unSocksProxyAgentsi elle est définie, sinonundefined(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_URLajoutée à.env.example(vide par défaut → dev local inchangé).- Tests
geminiLive.test.ts— 2 tests pourresolveGeminiProxyAgent(absente →undefined;socks5://…→ instanceSocksProxyAgent).
Changed
- Factory WS par défaut de
openGeminiLiveSession(T2,geminiLive.ts) etopenGeminiLiveT1Session(T1,geminiLiveT1.ts) — passe désormais{ agent }au constructeurnew 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/T2LiveCorrectionet close codes inchangés.
Notes
- Tests backend : 311/311 verts.
tsc --noEmitOK. - Activation en prod : définir
GEMINI_PROXY_URL=socks5://127.0.0.1:40000cô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) — optionreponsesretirée deOpenGeminiLiveT1SessionOptionset de la signature. Le handler de message client ignore désormais explicitement (log debug + return) tout message non reconnu (niaudioniend) — 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), dansonOpen. 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— appelsbuildT1SystemPrompt()sans argument, retrait dereponsesdes appels de session ; suppression du test d'intégration des réponses (remplacé par un test « écoute / plus de CONTEXTE DU CANDIDAT ») et du blocparseT1Context; tests interruption, flush terminal, timeout et correction inchangés.
Removed
- Message client
{type:'context', reponses}(1er message obligatoire) et close 4004CONTEXT_MISSING— la route ne lit plus de contexte. - Fonction
parseT1Context(t1live.ts) et ses importsvalidateReponses/PresentationReponses.
Notes
- Tests backend : 309/309 verts.
tsc --noEmitOK. - Suivi frontend (hors scope) : le frontend Sprint 7b (non commité) envoie encore
{type:'context'}et possède unQuestionnaireT1PageLive ; 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/genairetirée depackage.json/package-lock.jsonet script de debugtest-gemini-live.jssupprimé (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})dansgeminiLive.ts— prompt dynamique conformePrompt_t2live.md §3, remplace la constanteT2_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, 4006GEMINI_DISCONNECTED. - Orchestration
t2live.ts: fetch sujet par UUID (?sujet=<uuid>, validationmode='EO'+tache=2), close 4004SUJET_NOT_FOUNDsi absent. - Post-session :
runT2LiveCorrection— insertproductions(tache='EO_T2_LIVE'), appeldeepseekCorrectEO(transcript, 'EO_T2'),PHONOLOGY_STUB(TD-08), persist rapport + score + nclc, envoi{type:'report'}au client, close 1000. TacheEOétendu avec'EO_T2'dansdeepseek.ts+VALID_TACHES_EOdanscorrections.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.tsréécrit — setup frame paramétrable,inputAudioTranscription+outputAudioTranscriptionactivés, callbackonSessionEnd(transcript).corrections.ts—VALID_TACHES_EOinclut'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— tablestripe_webhook_events(id TEXT PRIMARY KEY, processed_at TIMESTAMPTZ NOT NULL DEFAULT NOW())+ index surprocessed_at. Idempotente (CREATE TABLE IF NOT EXISTS).src/lib/stripeWebhookEvents.ts— helpersisEventProcessed/markEventProcessed(insert idempotent, conflit unique23505avalé 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. 400NO_ACTIVE_SUBSCRIPTIONsi pas destripe_customer_id; return_url =${APP_URL}/dashboard.
Changed
POST /stripe/webhook— déduplication explicite des events Stripe (TD-13 résolu) : checkisEventProcessed(event.id)avant traitement → early return200 { received: true, replayed: true };markEventProcessedaprès succès uniquement (pas si exception, pour permettre rejeu Stripe).src/lib/stripe.ts— nouvelle fonctioncreateBillingPortalSession({ customerId, returnUrl })(mirror decreateCheckoutSession).src/routes/__tests__/stripe.test.ts— 5 nouveaux tests (2 idempotency webhook + 3 customer-portal route).docs/ARCHITECTURE-backend.md— §3 commentaireplans.tscorrigé (POST /plans/upgrade-prorataau lieu dePOST /plans/upgradequi n'existait pas) ; §6 retrait de la ligne dupliquéePOST /plans/upgrade(la création d'abonnement passe parPOST /stripe/checkout) ; §6 ajoutPOST /stripe/customer-portal.
Fixed
src/lib/stripe.ts—cancel_urlStripe Checkout corrigé :${APP_URL}/tarifs?upgrade=cancelled→${APP_URL}/plan?upgrade=cancelled. La route/tarifsn'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_STUBpour 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/eoaligné 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 + exercicesPOST /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-flash→gemini-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.mdetdocs/Prompt_production_modèle.md— builders dynamiquesbuildCorrectionPrompt,buildModelPrompt,buildExercicesPromptdanssrc/lib/deepseek.ts. expria-frontend/docs/TAXONOMIE_ERREURS.md— 63 codes d'erreurs TCF Canada sur 4 critères + 4 codes « autre ». Validation runtime viasrc/lib/taxonomieErreurs.ts(isValidCode,isValidCritere,buildTaxonomyPromptSection). Codes invalides retournés par DeepSeek sont filtrés ; le codeautresans description est rejeté.- Génération parallèle correction + modèle — option (b) :
generateProductionModeledémarre en même temps quecorrectEEavecnclcObtenu = nclcCible - 1comme estimation provisoire,awaituniquement 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_codesetrapport.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_cibleoptionnel surPOST /corrections/ee(défaut 9, valeurs acceptées : 9 ou 10 ; sinon 400 VALIDATION_ERROR). - Index GIN sur
erreurs_codespour 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.ts—getByIdrenvoienclc_cible,exercices,modele+ statuts. - Logs d'erreur détaillés :
callDeepSeekclassifie TIMEOUT / ABORT / JSON_PARSE / NETWORK / OTHER ;correctionController.correctEElogue{simulationId, tache, nclcCible, message, stack}avant de retourner 500. - FTD-23 🟡 ajoutée dans
expria-frontend/docs/TECH_DEBT.md—useAutosavepeut fire un PATCH/simulations/:id/contenuaprè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
correctEEdanssrc/lib/deepseek.ts— nouvelle signaturecorrectEE(CorrectionInput)(contenu, tache, sujet, sourceDoc1/2, nclcCible) et nouvelle forme de retourCorrectionRapport(revelation, diagnostic, criteres avec exemple/suggestion/astuce, conseil_nclc, erreurs_codes).EERapportdevient alias deCorrectionRapport. 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 ; lancerunModeleJoben parallèle (option b) etrunExercicesJobaprès correction.simulationController.getById— retourne désormaisnclc_cible,exercices,exercices_status,modele,modele_statusen plus durapportenrichi ; fallback'pending'si les colonnes sont absentes (compat avec productions pré-migration).- Timeout DeepSeek côté backend :
callDeepSeekabort à 55 s viaAbortSignal.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 — lesPATCH /:id/contenuetPATCH /:id/sujetsont déclarées avantGET /:idpour éviter tout risque de masquage. deepseek.test.tsréé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 pushou 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
pendingindéfiniment. À traiter après observation en production.