Files
Momento/memento-note/lib/ai/services/exercise-generator.service.ts
Antigravity 104af3149f
Some checks failed
CI / Lint, Unit Tests & Build (push) Failing after 1m28s
CI / Deploy production (on server) (push) Has been skipped
feat: générateur d'exercices + planning de révision IA
- Générateur d'exercices : bouton dans menu note → IA crée 5 exercices
  - Niveaux variés (facile/moyen/difficile) avec emojis 🟢🟡🔴
  - Corrigés détaillés dans des toggles (cliquer pour révéler)
  - Callout warning pour le niveau
  - Notes créées dans le même carnet
- Planning de révision : bouton dans barre carnet → IA crée planning
  - Choix date d'examen
  - Répétition espacée (première lecture → revoir → révision globale)
  - Rappels automatiques ajoutés aux notes (9h le jour J)
  - Vue chronologique avec activités et notes par jour
- Services : exercise-generator.service.ts + study-planner.service.ts
- Endpoints : /api/ai/generate-exercises + /api/ai/study-plan
- i18n FR/EN complet
2026-06-14 19:57:21 +00:00

98 lines
2.9 KiB
TypeScript

import { getChatProvider } from '../factory'
import { getSystemConfig } from '@/lib/config'
export interface GeneratedExercise {
question: string
difficulty: 'facile' | 'moyen' | 'difficile'
answer: string
}
export class ExerciseGeneratorService {
async generate(
noteTitle: string,
noteContent: string,
options: { count?: number; language?: string }
): Promise<GeneratedExercise[]> {
const count = options.count || 5
const lang = options.language || 'fr'
const langName = lang === 'fr' ? 'français' : lang === 'fa' ? 'فارسی' : 'English'
// Strip HTML to plain text for the AI
const plainText = noteContent
.replace(/<[^>]+>/g, ' ')
.replace(/&nbsp;/g, ' ')
.replace(/\s+/g, ' ')
.trim()
.slice(0, 4000)
const prompt = `Tu es un professeur expert. Génère ${count} exercices basés sur ce cours.
COURS : "${noteTitle}"
CONTENU :
${plainText}
Langue : ${langName}
Génère ${count} exercices variés avec des niveaux de difficulté différents.
Pour chaque exercice : une question claire et détaillée, et un corrigé complet et expliqué.
FORMAT JSON UNIQUEMENT :
\`\`\`json
[
${Array.from({ length: count }, (_, i) => `{
"question": "Question détaillée numéro ${i + 1}...",
"difficulty": "${['facile', 'moyen', 'difficile'][i % 3]}",
"answer": "Corrigé complet et expliqué..."
}`).join(',\n ')}
]
\`\`\`
RÈGLES :
- Les questions doivent couvrir différents aspects du cours
- Mélange les types : application directe, analyse, synthèse
- Les corrigés doivent être détaillés avec les étapes
- Utilise des formules LaTeX avec la notation standard (\\frac, \\sum, etc.)`
const config = await getSystemConfig()
const provider = getChatProvider(config)
const raw = await provider.generateText(prompt)
return this.parseResponse(raw)
}
private parseResponse(raw: string): GeneratedExercise[] {
const jsonMatch = raw.match(/```json\s*([\s\S]+?)\s*```/)
let jsonStr = jsonMatch ? jsonMatch[1] : raw
const start = jsonStr.indexOf('[')
const end = jsonStr.lastIndexOf(']')
if (start >= 0 && end > start) {
jsonStr = jsonStr.slice(start, end + 1)
}
try {
const parsed = JSON.parse(jsonStr)
return parsed.map((e: any) => ({
question: String(e.question || ''),
difficulty: e.difficulty || 'moyen',
answer: String(e.answer || ''),
}))
} catch {
// Fix backslash escaping
try {
const fixed = jsonStr.replace(/\\(?!["\\\/bfnrtu])/g, '\\\\')
const parsed = JSON.parse(fixed)
return parsed.map((e: any) => ({
question: String(e.question || ''),
difficulty: e.difficulty || 'moyen',
answer: String(e.answer || ''),
}))
} catch {
throw new Error('Failed to parse exercises')
}
}
}
}
export const exerciseGeneratorService = new ExerciseGeneratorService()