/** * Appels API du domaine `report`. * * Toutes les requêtes passent par `apiFetch` (Règle J / Règle F). * Timeout 30 s : DeepSeek (EE) et Gemini (EO) peuvent mettre jusqu'à 20-25 s. * Retry désactivé : POST non-idempotent — une double soumission créerait * deux corrections facturées sur le quota Free. * * Erreurs notables : SIMULATION_NOT_FOUND (404), AUTH_REQUIRED (401), * QUOTA_REACHED (403 — côté simulation, pas correction). */ import { apiFetch } from '@/shared/lib/api-client' import { getSimulationState } from '@/entities/production/api' import type { CorrectEePayload, CorrectEoPayload, Report } from './types' /** * Récupère un rapport existant. Endpoint : `GET /simulations/:id`. * * FTD-21 : depuis l'assouplissement de `getById` côté backend (tolère `rapport=null` * pour permettre le resume), on unwrap le champ `rapport` du `SimulationState`. * Si la simulation est encore en cours (`rapport === null`), on lève une erreur * typée `REPORT_NOT_READY` que RapportPage catche pour rediriger vers /simulation/ee. */ export function getReport(id: string): Promise { return getSimulationState(id).then((state) => { if (state.rapport === null) { throw { error: true, code: 'REPORT_NOT_READY', message: 'Simulation en cours — rédaction pas encore corrigée.', } } // Sprint 3.6b : reconstruit un Report en combinant rapport (correction) + // exercices / modele (jobs fire-and-forget, portés par SimulationState). return { ...state.rapport, simulation_id: state.simulation_id, tache: state.tache, erreurs_codes: state.rapport.erreurs_codes as Report['erreurs_codes'], exercices: state.exercices as Report['exercices'], exercices_status: state.exercices_status, modele: state.modele as Report['modele'], modele_status: state.modele_status, } }) } // Sprint 3.6a — le nouveau prompt maître (taxonomie + revelation + diagnostic + // criteres×6 champs + conseil_nclc + erreurs_codes) produit un JSON long ; // DeepSeek met typiquement 25-45 s pour répondre. Backend abort à 55 s. const CORRECTION_EE_TIMEOUT_MS = 60_000 // Sprint 4b.3 — EO en mode audio enchaîne Gemini transcribe (jusqu'à 60 s, // 30 s + 1 retry de 30 s) puis DeepSeek correction (55 s côté backend). // Pire cas serveur ≈ 115 s : on alloue 120 s côté client pour ne pas couper // avant que la mutation aboutisse (le rapport apparaissait sinon dans // l'historique sans navigation vers /rapport/:id). const CORRECTION_EO_TIMEOUT_MS = 120_000 /** Soumet une production écrite pour correction. Endpoint : `POST /corrections/ee`. * Payload : { simulationId, contenu, tache } */ export function correctEe(payload: CorrectEePayload): Promise { return apiFetch('/corrections/ee', { method: 'POST', body: payload, timeoutMs: CORRECTION_EE_TIMEOUT_MS, }) } /** * Soumet une production orale pour correction. Endpoint : `POST /corrections/eo`. * Payload : { simulationId, transcript, tache } — transcript implémenté Sprint 4. */ export function correctEo(payload: CorrectEoPayload): Promise { return apiFetch('/corrections/eo', { method: 'POST', body: payload, timeoutMs: CORRECTION_EO_TIMEOUT_MS, }) } const IDEES_TIMEOUT_MS = 15_000 /** * Récupère 5 suggestions d'idées DeepSeek pour prolonger la rédaction en cours. * Endpoint : `POST /sujets/idees`. Tâche G5. * Contraintes backend : sujet_consigne non vide + contenu_partiel ≥ 30 mots. */ export function getIdees(consigne: string, contenu: string): Promise { return apiFetch<{ idees: string[] }>('/sujets/idees', { method: 'POST', body: { sujet_consigne: consigne, contenu_partiel: contenu }, timeoutMs: IDEES_TIMEOUT_MS, }).then((res) => res.idees) }