From f67bd2dc9520b322f3d6b6039c3b028bab1a2c3a Mon Sep 17 00:00:00 2001 From: Hermann_Kitio Date: Mon, 20 Apr 2026 04:28:06 +0300 Subject: [PATCH] =?UTF-8?q?feat(corrections):=20aligner=20sch=C3=A9ma=20ra?= =?UTF-8?q?pport=20DeepSeek=20=E2=80=94=20renommages=20+=20feedback=5Fcour?= =?UTF-8?q?t?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/__tests__/deepseek.test.ts | 39 +++++++++++++++++++++++------- src/lib/deepseek.ts | 28 +++++++++++++++------ 2 files changed, 50 insertions(+), 17 deletions(-) diff --git a/src/lib/__tests__/deepseek.test.ts b/src/lib/__tests__/deepseek.test.ts index 3c37daa..c55c2f1 100644 --- a/src/lib/__tests__/deepseek.test.ts +++ b/src/lib/__tests__/deepseek.test.ts @@ -3,6 +3,8 @@ import { describe, it, expect, vi, beforeEach } from 'vitest' const VALID_RAPPORT = { score: 14.5, nclc: 8, + feedback_court: + 'Bonne production générale. Quelques points à améliorer sur le lexique et la morphosyntaxe.', criteres: [ { nom: 'Coherence et cohesion', score: 4, commentaire: 'Bonne organisation.' }, { nom: 'Lexique', score: 3, commentaire: 'Vocabulaire correct mais limite.' }, @@ -10,8 +12,8 @@ const VALID_RAPPORT = { { nom: 'Pertinence', score: 3.5, commentaire: 'Adequation partielle a la consigne.' }, ], erreurs: ['Connecteurs logiques insuffisants', 'Quelques fautes accord'], - production_modele: 'Texte modele corrige ici.', - suggestions_idees: ['Developper argumentation', 'Ajouter des exemples concrets'], + modele: 'Texte modele corrige ici.', + idees: ['Developper argumentation', 'Ajouter des exemples concrets'], exercices: ['Exercice connecteurs logiques', 'Exercice accords sujet-verbe'], } @@ -41,14 +43,17 @@ describe('deepseek.correctEE', () => { expect(rapport).toHaveProperty('score') expect(rapport).toHaveProperty('nclc') + expect(rapport).toHaveProperty('feedback_court') expect(rapport).toHaveProperty('criteres') expect(rapport).toHaveProperty('erreurs') - expect(rapport).toHaveProperty('production_modele') - expect(rapport).toHaveProperty('suggestions_idees') + expect(rapport).toHaveProperty('modele') + expect(rapport).toHaveProperty('idees') expect(rapport).toHaveProperty('exercices') expect(rapport.criteres).toHaveLength(4) + expect(typeof rapport.feedback_court).toBe('string') + expect(rapport.feedback_court.length).toBeGreaterThan(0) expect(Array.isArray(rapport.erreurs)).toBe(true) - expect(Array.isArray(rapport.suggestions_idees)).toBe(true) + expect(Array.isArray(rapport.idees)).toBe(true) expect(Array.isArray(rapport.exercices)).toBe(true) }) @@ -86,6 +91,17 @@ describe('deepseek.correctEE', () => { await expect(correctEE('Texte', 'EE_T1')).rejects.toThrow('NCLC invalide') }) + it('lance une erreur si feedback_court est absent ou vide', async () => { + // Cas 1 : champ absent (JSON.stringify drop les undefined) + mockFetchSuccess({ ...VALID_RAPPORT, feedback_court: undefined }) + const { correctEE } = await import('../deepseek') + await expect(correctEE('Texte', 'EE_T1')).rejects.toThrow('feedback_court invalide') + + // Cas 2 : chaîne vide (whitespace uniquement) + mockFetchSuccess({ ...VALID_RAPPORT, feedback_court: ' ' }) + await expect(correctEE('Texte', 'EE_T1')).rejects.toThrow('feedback_court invalide') + }) + it('erreur HTTP depuis DeepSeek API', async () => { vi.stubGlobal( 'fetch', @@ -132,6 +148,8 @@ describe('deepseek.correctEE', () => { const VALID_RAPPORT_EO = { score: 12, nclc: 7, + feedback_court: + 'Bonne production générale. Quelques points à améliorer sur le lexique et la morphosyntaxe.', criteres: [ { nom: 'Coherence et cohesion', score: 4, commentaire: 'Discours structure.' }, { nom: 'Lexique', score: 4, commentaire: 'Vocabulaire varie.' }, @@ -139,8 +157,8 @@ const VALID_RAPPORT_EO = { { nom: 'Phonologie', score: 0, commentaire: 'Non evalue sur transcription textuelle.' }, ], erreurs: ['Hesitations frequentes', 'Registre parfois familier'], - production_modele: 'Transcription corrigee ici.', - suggestions_idees: ['Structurer les reponses', 'Enrichir le vocabulaire'], + modele: 'Transcription corrigee ici.', + idees: ['Structurer les reponses', 'Enrichir le vocabulaire'], exercices: ['Exercice fluidite orale', 'Exercice registre formel'], } @@ -158,12 +176,15 @@ describe('deepseek.correctEO', () => { expect(rapport).toHaveProperty('score') expect(rapport).toHaveProperty('nclc') + expect(rapport).toHaveProperty('feedback_court') expect(rapport).toHaveProperty('criteres') expect(rapport.criteres).toHaveLength(4) expect(rapport).toHaveProperty('erreurs') - expect(rapport).toHaveProperty('production_modele') - expect(rapport).toHaveProperty('suggestions_idees') + expect(rapport).toHaveProperty('modele') + expect(rapport).toHaveProperty('idees') expect(rapport).toHaveProperty('exercices') + expect(typeof rapport.feedback_court).toBe('string') + expect(rapport.feedback_court.length).toBeGreaterThan(0) }) it('phonologie est a 0', async () => { diff --git a/src/lib/deepseek.ts b/src/lib/deepseek.ts index fb2b319..05cd0c3 100644 --- a/src/lib/deepseek.ts +++ b/src/lib/deepseek.ts @@ -10,10 +10,11 @@ export interface EECritere { export interface EERapport { score: number nclc: number + feedback_court: string criteres: EECritere[] erreurs: string[] - production_modele: string - suggestions_idees: string[] + modele: string + idees: string[] exercices: string[] } @@ -26,10 +27,11 @@ export interface EOCritere { export interface EORapport { score: number nclc: number + feedback_court: string criteres: EOCritere[] erreurs: string[] - production_modele: string - suggestions_idees: string[] + modele: string + idees: string[] exercices: string[] } @@ -44,6 +46,7 @@ Tu dois retourner un JSON strict avec cette structure exacte : { "score": , "nclc": , + "feedback_court": "<2 à 3 lignes de feedback global, orientées action>", "criteres": [ { "nom": "Cohérence et cohésion", "score": , "commentaire": "" }, { "nom": "Lexique", "score": , "commentaire": "" }, @@ -51,14 +54,15 @@ Tu dois retourner un JSON strict avec cette structure exacte : { "nom": "Pertinence", "score": , "commentaire": "" } ], "erreurs": ["", "", ...], - "production_modele": "", - "suggestions_idees": ["", "", ...], + "modele": "", + "idees": ["", "", ...], "exercices": ["", "", ...] } Règles : - score est la note globale sur 20 - nclc est le niveau NCLC estimé (entre 4 et 12) +- feedback_court est un résumé de 2 à 3 lignes, toujours renseigné (visible pour tous les plans) - Chaque critère a un score de 0 à 5 - Retourne UNIQUEMENT le JSON, sans texte avant ni après` @@ -104,6 +108,9 @@ export async function correctEE(contenu: string, tache: string): Promise 12) { throw new Error(`NCLC invalide: ${rapport.nclc} (attendu 4-12)`) } + if (typeof rapport.feedback_court !== 'string' || rapport.feedback_court.trim().length === 0) { + throw new Error('feedback_court invalide: attendu une chaîne non vide') + } return rapport } @@ -119,6 +126,7 @@ Tu dois retourner un JSON strict avec cette structure exacte : { "score": , "nclc": , + "feedback_court": "<2 à 3 lignes de feedback global, orientées action>", "criteres": [ { "nom": "Cohérence et cohésion", "score": , "commentaire": "" }, { "nom": "Lexique", "score": , "commentaire": "" }, @@ -126,14 +134,15 @@ Tu dois retourner un JSON strict avec cette structure exacte : { "nom": "Phonologie", "score": 0, "commentaire": "Non évalué sur transcription textuelle." } ], "erreurs": ["", "", ...], - "production_modele": "", - "suggestions_idees": ["", "", ...], + "modele": "", + "idees": ["", "", ...], "exercices": ["", "", ...] } Règles : - score est la note globale sur 20 (basée uniquement sur les 3 critères évalués) - nclc est le niveau NCLC estimé (entre 4 et 12) +- feedback_court est un résumé de 2 à 3 lignes, toujours renseigné (visible pour tous les plans) - Phonologie est toujours à 0 avec le commentaire "Non évalué sur transcription textuelle." - Retourne UNIQUEMENT le JSON, sans texte avant ni après` @@ -179,6 +188,9 @@ export async function correctEO(transcript: string, tache: string): Promise 12) { throw new Error(`NCLC invalide: ${rapport.nclc} (attendu 4-12)`) } + if (typeof rapport.feedback_court !== 'string' || rapport.feedback_court.trim().length === 0) { + throw new Error('feedback_court invalide: attendu une chaîne non vide') + } return rapport }