import { prisma } from '@/lib/prisma' import { getAIProvider } from '@/lib/ai/factory' export interface NotebookSummary { notebookId: string notebookName: string notebookIcon: string | null summary: string // Markdown formatted summary stats: { totalNotes: number totalLabels: number labelsUsed: string[] } generatedAt: Date } /** * Service for generating AI-powered notebook summaries * (Story 5.6 - IA6) */ export class NotebookSummaryService { /** * Generate a summary for a notebook * @param notebookId - Notebook ID * @param userId - User ID (for authorization) * @returns Notebook summary or null */ async generateSummary(notebookId: string, userId: string): Promise { // 1. Get notebook with notes and labels const notebook = await prisma.notebook.findFirst({ where: { id: notebookId, userId, }, include: { labels: { select: { id: true, name: true, }, }, _count: { select: { notes: true }, }, }, }) if (!notebook) { throw new Error('Notebook not found') } // Get all notes in this notebook const notes = await prisma.note.findMany({ where: { notebookId, userId, }, select: { id: true, title: true, content: true, createdAt: true, updatedAt: true, labelRelations: { select: { name: true, }, }, }, orderBy: { updatedAt: 'desc', }, take: 100, // Limit to 100 most recent notes for summary }) if (notes.length === 0) { return null } // 2. Generate summary using AI const summary = await this.generateAISummary(notes, notebook) // 3. Get labels used in this notebook const labelsUsed = Array.from( new Set( notes.flatMap(note => note.labelRelations.map(l => l.name) ) ) ) return { notebookId: notebook.id, notebookName: notebook.name, notebookIcon: notebook.icon, summary, stats: { totalNotes: notebook._count.notes, totalLabels: notebook.labels.length, labelsUsed, }, generatedAt: new Date(), } } /** * Use AI to generate notebook summary */ private async generateAISummary(notes: any[], notebook: any): Promise { // Build notes summary for AI const notesSummary = notes .map((note, index) => { const title = note.title || 'Sans titre' const content = note.content.substring(0, 200) // Limit content length const labels = note.labelRelations.map((l: any) => l.name).join(', ') const date = new Date(note.createdAt).toLocaleDateString('fr-FR') return `[${index + 1}] **${title}** (${date}) ${labels ? `Labels: ${labels}` : ''} ${content}...` }) .join('\n\n') const prompt = this.buildPrompt(notesSummary, notebook.name) try { const provider = getAIProvider() const summary = await provider.generateText(prompt) return summary.trim() } catch (error) { console.error('Failed to generate notebook summary:', error) throw error } } /** * Build prompt for AI (always in French - interface language) */ private buildPrompt(notesSummary: string, notebookName: string): string { return ` Tu es un assistant qui génère des synthèses structurées de carnets de notes. CARNET: ${notebookName} NOTES DU CARNET: ${notesSummary} TÂCHE: Génère une synthèse structurée et organisée de ce carnet en analysant toutes les notes. FORMAT DE LA RÉPONSE (Markdown avec emojis): # 📊 Synthèse du Carnet ${notebookName} ## 🌍 Thèmes Principaux • Identifie 3-5 thèmes récurrents ou sujets abordés ## 📝 Statistiques • Nombre total de notes analysées • Principales catégories de contenu ## 📅 Éléments Temporels • Dates ou périodes importantes mentionnées • Événements planifiés vs passés ## ⚠️ Points d'Attention / Actions Requises • Tâches ou actions identifiées dans les notes • Rappels ou échéances importantes • Éléments nécessitant une attention particulière ## 💡 Insights Clés • Résumé des informations les plus importantes • Tendances ou patterns observés • Connexions entre les différentes notes RÈGLES: - Utilise le format Markdown avec emojis comme dans l'exemple - Sois concis et organise l'information de manière claire - Identifie les vraies tendances, ne pas inventer d'informations - Si une section n'est pas pertinente, utilise "N/A" ou omets-la - Ton: professionnel mais accessible Ta réponse : `.trim() } } // Export singleton instance export const notebookSummaryService = new NotebookSummaryService()