fix(t1-live): remove questionnaire dependency from T1 Live session
Some checks are pending
CI / quality (push) Waiting to run
Some checks are pending
CI / quality (push) Waiting to run
- buildT1SystemPrompt() now static (no reponses param); examiner formulates questions from what it hears in real-time audio stream - Remove context guard + close 4004 CONTEXT_MISSING; Gemini session opens immediately after auth (aligns with T2 flow) - Remove parseT1Context, validateReponses import from route - Unknown WS message types silently ignored (debug log + return) - Update Prompt_t1live.md and CHANGELOG-backend - Tests: 309/309 green
This commit is contained in:
parent
01707c0b74
commit
74770b6402
6 changed files with 105 additions and 209 deletions
|
|
@ -6,6 +6,28 @@ Format basé sur [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/).
|
|||
|
||||
---
|
||||
|
||||
## [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
|
||||
|
|
|
|||
|
|
@ -31,12 +31,12 @@ d'immigration au Canada) sous forme de **monologue**, et l'examinateur le
|
|||
|
||||
**Différence structurelle avec le T2 :**
|
||||
|
||||
| Axe | T1 (entretien dirigé) | T2 (interaction de service) |
|
||||
| ----------------- | -------------------------------- | ---------------------------- |
|
||||
| Qui mène | L'examinateur relance | Le candidat mène |
|
||||
| Questions de l'IA | **Obligatoires** (relances) | **Interdites** (rôle passif) |
|
||||
| Forme candidat | Monologue + relances | Dialogue |
|
||||
| Subject-based | **Non** (questionnaire candidat) | Oui (table `sujets`) |
|
||||
| Axe | T1 (entretien dirigé) | T2 (interaction de service) |
|
||||
| ----------------- | ------------------------------ | ---------------------------- |
|
||||
| Qui mène | L'examinateur relance | Le candidat mène |
|
||||
| Questions de l'IA | **Obligatoires** (relances) | **Interdites** (rôle passif) |
|
||||
| Forme candidat | Monologue + relances | Dialogue |
|
||||
| Subject-based | **Non** (écoute en temps réel) | Oui (table `sujets`) |
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -53,7 +53,7 @@ L'IA joue un **examinateur bienveillant** du TCF Canada. Son comportement :
|
|||
interne. Le backend ne lit PAS la transcription partielle pour décider
|
||||
(Modèle 1 acté — cf. ROADMAP / `geminiLiveT1.ts`).
|
||||
3. **Relance courte et unique.** Une seule question de 10 à 20 mots, liée à ce
|
||||
que le candidat vient de dire ou à son contexte. Jamais d'enchaînement.
|
||||
que le candidat vient de dire. Jamais d'enchaînement.
|
||||
4. **Ton bienveillant et professionnel**, français B2-C1.
|
||||
5. **N'évalue jamais** le candidat, ne corrige pas ses erreurs, ne commente pas
|
||||
sa langue.
|
||||
|
|
@ -63,25 +63,21 @@ L'IA joue un **examinateur bienveillant** du TCF Canada. Son comportement :
|
|||
|
||||
## 3. Prompt système (source : `buildT1SystemPrompt`)
|
||||
|
||||
Les variables `${...}` sont substituées dynamiquement depuis les réponses du
|
||||
**questionnaire candidat** (`PresentationReponses`) — il n'existe pas de sujet T1
|
||||
en base (T1 EO n'est PAS subject-based).
|
||||
Le prompt est **statique** : aucune variable substituée. L'examinateur formule
|
||||
ses relances à partir de ce qu'il **entend en temps réel** (son contexte audio
|
||||
interne) — il n'existe ni sujet T1 en base, ni questionnaire pré-rempli (T1 EO
|
||||
n'est PAS subject-based).
|
||||
|
||||
```
|
||||
RÔLE : Tu es un examinateur bienveillant de l'épreuve d'Expression Orale du TCF Canada (Tâche 1, entretien dirigé). Le candidat se présente en monologue : identité, parcours, situation familiale, loisirs, et projet d'immigration au Canada.
|
||||
|
||||
CONTEXTE DU CANDIDAT (pour formuler des relances pertinentes et personnalisées) :
|
||||
- Identité : ${reponses.prenom_age_ville}
|
||||
- Formation / métier : ${reponses.formation_metier}
|
||||
- Situation familiale : ${reponses.situation_familiale}
|
||||
- Loisirs : ${reponses.loisirs}
|
||||
- Projet Canada : ${reponses.motivation_canada}
|
||||
É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.
|
||||
|
||||
RÈGLES :
|
||||
1. Tu parles TOUJOURS en français naturel et courant, niveau B2-C1, sur un ton bienveillant et professionnel.
|
||||
2. Tu RESTES SILENCIEUX par défaut. Tant que le candidat parle, tu n'interviens JAMAIS de ta propre initiative.
|
||||
3. Tu prends la parole UNIQUEMENT lorsqu'on te le signale, et alors UNIQUEMENT pour relancer le candidat par UNE question.
|
||||
4. Ta relance est COURTE : une seule question de 10 à 20 mots, liée à ce que le candidat vient de dire ou à son contexte ci-dessus.
|
||||
4. Ta relance est COURTE : une seule question de 10 à 20 mots, liée à ce que le candidat vient de dire.
|
||||
5. Tu PEUX et tu DOIS poser des questions : c'est le cœur de ton rôle d'examinateur en Tâche 1. Utilise le point d'interrogation normalement.
|
||||
6. Une seule question à la fois. Jamais de liste, jamais d'enchaînement de plusieurs questions dans la même prise de parole.
|
||||
7. Tu ne corriges JAMAIS les erreurs du candidat et tu ne commentes jamais sa langue, ses erreurs ou sa performance.
|
||||
|
|
@ -91,13 +87,6 @@ RÈGLES :
|
|||
> **⚠ Spécificité T1 — règle 5 :** elle est l'**exact inverse** de la règle 7 du
|
||||
> T2. Toute fusion des deux prompts est interdite (TD-22 / TD-23).
|
||||
|
||||
**Variables à substituer dynamiquement** (depuis le questionnaire candidat, pas
|
||||
d'un sujet en base) :
|
||||
|
||||
- `prenom_age_ville`, `formation_metier`, `situation_familiale`, `loisirs`,
|
||||
`motivation_canada` — validés par `validateReponses`
|
||||
(`presentationController.ts`).
|
||||
|
||||
---
|
||||
|
||||
## 4. Contrat WebSocket T1 (figé — la suite Sprint 7b en dépend)
|
||||
|
|
@ -106,13 +95,17 @@ Route : **`WS /t1/live?token=<jwt>`**
|
|||
Auth : JWT Supabase + permission Premium `oral_t2_live` (réutilise
|
||||
`authenticate` de `t2live.ts` — cf. dette de nommage TD-24).
|
||||
|
||||
La session Gemini s'ouvre **immédiatement après l'auth** (pas de message de
|
||||
contexte ni de questionnaire). Le client envoie directement son audio. Tout
|
||||
message non reconnu (ni `audio` ni `end`) est **ignoré silencieusement** (log
|
||||
debug + return) — jamais de close.
|
||||
|
||||
### 4.1 Client → Backend
|
||||
|
||||
| Message | Forme | Effet |
|
||||
| ---------------------------------- | ------------------------------------------ | --------------------------------------------------------------------------------------------- |
|
||||
| Contexte (1er message obligatoire) | `{type:'context', reponses}` | Validé par `validateReponses` ; démarre la session Gemini. Absent/invalide → close `4004`. |
|
||||
| Audio candidat | `{type:'audio', data}` (PCM 16 kHz base64) | Relayé à Gemini tant qu'un tour candidat est ouvert et qu'aucune interruption n'est en cours. |
|
||||
| Fin de session | `{type:'end'}` | Déclenche `endSession()` (flush terminal + correction). |
|
||||
| Message | Forme | Effet |
|
||||
| -------------- | ------------------------------------------ | --------------------------------------------------------------------------------------------- |
|
||||
| Audio candidat | `{type:'audio', data}` (PCM 16 kHz base64) | Relayé à Gemini tant qu'un tour candidat est ouvert et qu'aucune interruption n'est en cours. |
|
||||
| Fin de session | `{type:'end'}` | Déclenche `endSession()` (flush terminal + correction). |
|
||||
|
||||
### 4.2 Backend → Client
|
||||
|
||||
|
|
@ -127,15 +120,14 @@ Auth : JWT Supabase + permission Premium `oral_t2_live` (réutilise
|
|||
|
||||
### 4.3 Codes de fermeture WebSocket
|
||||
|
||||
| Close code | Cause | Origine |
|
||||
| ---------- | -------------------------------------------------------- | ------------------------- |
|
||||
| 1000 | Fin normale + rapport prêt (ou transcript vide) | `runT1LiveCorrection` |
|
||||
| 1011 | `PERSISTENCE_FAILED` / `CORRECTION_FAILED` | `runT1LiveCorrection` |
|
||||
| 4001 | `AUTH_REQUIRED` (JWT absent/invalide) | `authenticate` |
|
||||
| 4003 | `PLAN_INSUFFICIENT` (pas Premium) | `authenticate` |
|
||||
| 4004 | `CONTEXT_MISSING` (1er message contexte absent/invalide) | route `t1live.ts` |
|
||||
| 4005 | `GEMINI_CONFIG` (clé API Gemini manquante côté serveur) | `openGeminiLiveT1Session` |
|
||||
| 4006 | `GEMINI_DISCONNECTED` (WS Gemini fermé/erreur) | `openGeminiLiveT1Session` |
|
||||
| Close code | Cause | Origine |
|
||||
| ---------- | ------------------------------------------------------- | ------------------------- |
|
||||
| 1000 | Fin normale + rapport prêt (ou transcript vide) | `runT1LiveCorrection` |
|
||||
| 1011 | `PERSISTENCE_FAILED` / `CORRECTION_FAILED` | `runT1LiveCorrection` |
|
||||
| 4001 | `AUTH_REQUIRED` (JWT absent/invalide) | `authenticate` |
|
||||
| 4003 | `PLAN_INSUFFICIENT` (pas Premium) | `authenticate` |
|
||||
| 4005 | `GEMINI_CONFIG` (clé API Gemini manquante côté serveur) | `openGeminiLiveT1Session` |
|
||||
| 4006 | `GEMINI_DISCONNECTED` (WS Gemini fermé/erreur) | `openGeminiLiveT1Session` |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue