feat(corrections): aligner schéma rapport DeepSeek — renommages + feedback_court

This commit is contained in:
Hermann_Kitio 2026-04-20 04:28:06 +03:00
parent c65a2a0157
commit f67bd2dc95
2 changed files with 50 additions and 17 deletions

View file

@ -3,6 +3,8 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'
const VALID_RAPPORT = { const VALID_RAPPORT = {
score: 14.5, score: 14.5,
nclc: 8, nclc: 8,
feedback_court:
'Bonne production générale. Quelques points à améliorer sur le lexique et la morphosyntaxe.',
criteres: [ criteres: [
{ nom: 'Coherence et cohesion', score: 4, commentaire: 'Bonne organisation.' }, { nom: 'Coherence et cohesion', score: 4, commentaire: 'Bonne organisation.' },
{ nom: 'Lexique', score: 3, commentaire: 'Vocabulaire correct mais limite.' }, { 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.' }, { nom: 'Pertinence', score: 3.5, commentaire: 'Adequation partielle a la consigne.' },
], ],
erreurs: ['Connecteurs logiques insuffisants', 'Quelques fautes accord'], erreurs: ['Connecteurs logiques insuffisants', 'Quelques fautes accord'],
production_modele: 'Texte modele corrige ici.', modele: 'Texte modele corrige ici.',
suggestions_idees: ['Developper argumentation', 'Ajouter des exemples concrets'], idees: ['Developper argumentation', 'Ajouter des exemples concrets'],
exercices: ['Exercice connecteurs logiques', 'Exercice accords sujet-verbe'], exercices: ['Exercice connecteurs logiques', 'Exercice accords sujet-verbe'],
} }
@ -41,14 +43,17 @@ describe('deepseek.correctEE', () => {
expect(rapport).toHaveProperty('score') expect(rapport).toHaveProperty('score')
expect(rapport).toHaveProperty('nclc') expect(rapport).toHaveProperty('nclc')
expect(rapport).toHaveProperty('feedback_court')
expect(rapport).toHaveProperty('criteres') expect(rapport).toHaveProperty('criteres')
expect(rapport).toHaveProperty('erreurs') expect(rapport).toHaveProperty('erreurs')
expect(rapport).toHaveProperty('production_modele') expect(rapport).toHaveProperty('modele')
expect(rapport).toHaveProperty('suggestions_idees') expect(rapport).toHaveProperty('idees')
expect(rapport).toHaveProperty('exercices') expect(rapport).toHaveProperty('exercices')
expect(rapport.criteres).toHaveLength(4) 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.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) expect(Array.isArray(rapport.exercices)).toBe(true)
}) })
@ -86,6 +91,17 @@ describe('deepseek.correctEE', () => {
await expect(correctEE('Texte', 'EE_T1')).rejects.toThrow('NCLC invalide') 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 () => { it('erreur HTTP depuis DeepSeek API', async () => {
vi.stubGlobal( vi.stubGlobal(
'fetch', 'fetch',
@ -132,6 +148,8 @@ describe('deepseek.correctEE', () => {
const VALID_RAPPORT_EO = { const VALID_RAPPORT_EO = {
score: 12, score: 12,
nclc: 7, nclc: 7,
feedback_court:
'Bonne production générale. Quelques points à améliorer sur le lexique et la morphosyntaxe.',
criteres: [ criteres: [
{ nom: 'Coherence et cohesion', score: 4, commentaire: 'Discours structure.' }, { nom: 'Coherence et cohesion', score: 4, commentaire: 'Discours structure.' },
{ nom: 'Lexique', score: 4, commentaire: 'Vocabulaire varie.' }, { 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.' }, { nom: 'Phonologie', score: 0, commentaire: 'Non evalue sur transcription textuelle.' },
], ],
erreurs: ['Hesitations frequentes', 'Registre parfois familier'], erreurs: ['Hesitations frequentes', 'Registre parfois familier'],
production_modele: 'Transcription corrigee ici.', modele: 'Transcription corrigee ici.',
suggestions_idees: ['Structurer les reponses', 'Enrichir le vocabulaire'], idees: ['Structurer les reponses', 'Enrichir le vocabulaire'],
exercices: ['Exercice fluidite orale', 'Exercice registre formel'], exercices: ['Exercice fluidite orale', 'Exercice registre formel'],
} }
@ -158,12 +176,15 @@ describe('deepseek.correctEO', () => {
expect(rapport).toHaveProperty('score') expect(rapport).toHaveProperty('score')
expect(rapport).toHaveProperty('nclc') expect(rapport).toHaveProperty('nclc')
expect(rapport).toHaveProperty('feedback_court')
expect(rapport).toHaveProperty('criteres') expect(rapport).toHaveProperty('criteres')
expect(rapport.criteres).toHaveLength(4) expect(rapport.criteres).toHaveLength(4)
expect(rapport).toHaveProperty('erreurs') expect(rapport).toHaveProperty('erreurs')
expect(rapport).toHaveProperty('production_modele') expect(rapport).toHaveProperty('modele')
expect(rapport).toHaveProperty('suggestions_idees') expect(rapport).toHaveProperty('idees')
expect(rapport).toHaveProperty('exercices') expect(rapport).toHaveProperty('exercices')
expect(typeof rapport.feedback_court).toBe('string')
expect(rapport.feedback_court.length).toBeGreaterThan(0)
}) })
it('phonologie est a 0', async () => { it('phonologie est a 0', async () => {

View file

@ -10,10 +10,11 @@ export interface EECritere {
export interface EERapport { export interface EERapport {
score: number score: number
nclc: number nclc: number
feedback_court: string
criteres: EECritere[] criteres: EECritere[]
erreurs: string[] erreurs: string[]
production_modele: string modele: string
suggestions_idees: string[] idees: string[]
exercices: string[] exercices: string[]
} }
@ -26,10 +27,11 @@ export interface EOCritere {
export interface EORapport { export interface EORapport {
score: number score: number
nclc: number nclc: number
feedback_court: string
criteres: EOCritere[] criteres: EOCritere[]
erreurs: string[] erreurs: string[]
production_modele: string modele: string
suggestions_idees: string[] idees: string[]
exercices: string[] exercices: string[]
} }
@ -44,6 +46,7 @@ Tu dois retourner un JSON strict avec cette structure exacte :
{ {
"score": <number 0-20>, "score": <number 0-20>,
"nclc": <number 4-12>, "nclc": <number 4-12>,
"feedback_court": "<2 à 3 lignes de feedback global, orientées action>",
"criteres": [ "criteres": [
{ "nom": "Cohérence et cohésion", "score": <number 0-5>, "commentaire": "<string>" }, { "nom": "Cohérence et cohésion", "score": <number 0-5>, "commentaire": "<string>" },
{ "nom": "Lexique", "score": <number 0-5>, "commentaire": "<string>" }, { "nom": "Lexique", "score": <number 0-5>, "commentaire": "<string>" },
@ -51,14 +54,15 @@ Tu dois retourner un JSON strict avec cette structure exacte :
{ "nom": "Pertinence", "score": <number 0-5>, "commentaire": "<string>" } { "nom": "Pertinence", "score": <number 0-5>, "commentaire": "<string>" }
], ],
"erreurs": ["<erreur 1>", "<erreur 2>", ...], "erreurs": ["<erreur 1>", "<erreur 2>", ...],
"production_modele": "<texte modèle corrigé>", "modele": "<texte modèle corrigé>",
"suggestions_idees": ["<idée 1>", "<idée 2>", ...], "idees": ["<idée 1>", "<idée 2>", ...],
"exercices": ["<exercice recommandé 1>", "<exercice recommandé 2>", ...] "exercices": ["<exercice recommandé 1>", "<exercice recommandé 2>", ...]
} }
Règles : Règles :
- score est la note globale sur 20 - score est la note globale sur 20
- nclc est le niveau NCLC estimé (entre 4 et 12) - 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 - Chaque critère a un score de 0 à 5
- Retourne UNIQUEMENT le JSON, sans texte avant ni après` - Retourne UNIQUEMENT le JSON, sans texte avant ni après`
@ -104,6 +108,9 @@ export async function correctEE(contenu: string, tache: string): Promise<EERappo
if (rapport.nclc < 4 || rapport.nclc > 12) { if (rapport.nclc < 4 || rapport.nclc > 12) {
throw new Error(`NCLC invalide: ${rapport.nclc} (attendu 4-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 return rapport
} }
@ -119,6 +126,7 @@ Tu dois retourner un JSON strict avec cette structure exacte :
{ {
"score": <number 0-20>, "score": <number 0-20>,
"nclc": <number 4-12>, "nclc": <number 4-12>,
"feedback_court": "<2 à 3 lignes de feedback global, orientées action>",
"criteres": [ "criteres": [
{ "nom": "Cohérence et cohésion", "score": <number 0-5>, "commentaire": "<string>" }, { "nom": "Cohérence et cohésion", "score": <number 0-5>, "commentaire": "<string>" },
{ "nom": "Lexique", "score": <number 0-5>, "commentaire": "<string>" }, { "nom": "Lexique", "score": <number 0-5>, "commentaire": "<string>" },
@ -126,14 +134,15 @@ Tu dois retourner un JSON strict avec cette structure exacte :
{ "nom": "Phonologie", "score": 0, "commentaire": "Non évalué sur transcription textuelle." } { "nom": "Phonologie", "score": 0, "commentaire": "Non évalué sur transcription textuelle." }
], ],
"erreurs": ["<erreur 1>", "<erreur 2>", ...], "erreurs": ["<erreur 1>", "<erreur 2>", ...],
"production_modele": "<version corrigée de la transcription>", "modele": "<version corrigée de la transcription>",
"suggestions_idees": ["<idée 1>", "<idée 2>", ...], "idees": ["<idée 1>", "<idée 2>", ...],
"exercices": ["<exercice recommandé 1>", "<exercice recommandé 2>", ...] "exercices": ["<exercice recommandé 1>", "<exercice recommandé 2>", ...]
} }
Règles : Règles :
- score est la note globale sur 20 (basée uniquement sur les 3 critères évalués) - 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) - 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." - Phonologie est toujours à 0 avec le commentaire "Non évalué sur transcription textuelle."
- Retourne UNIQUEMENT le JSON, sans texte avant ni après` - Retourne UNIQUEMENT le JSON, sans texte avant ni après`
@ -179,6 +188,9 @@ export async function correctEO(transcript: string, tache: string): Promise<EORa
if (rapport.nclc < 4 || rapport.nclc > 12) { if (rapport.nclc < 4 || rapport.nclc > 12) {
throw new Error(`NCLC invalide: ${rapport.nclc} (attendu 4-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 return rapport
} }