381 lines
11 KiB
TypeScript
381 lines
11 KiB
TypeScript
import { prisma } from '@/lib/prisma'
|
||
import { getAIProvider } from '@/lib/ai/factory'
|
||
import { getSystemConfig } from '@/lib/config'
|
||
|
||
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, language: string = 'en'): Promise<NotebookSummary | null> {
|
||
// 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, language)
|
||
|
||
// 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, language: string): Promise<string> {
|
||
// 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, language)
|
||
|
||
try {
|
||
const config = await getSystemConfig()
|
||
const provider = getAIProvider(config)
|
||
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 (localized)
|
||
*/
|
||
private buildPrompt(notesSummary: string, notebookName: string, language: string = 'en'): string {
|
||
const instructions: Record<string, string> = {
|
||
fr: `
|
||
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 DOIT ÊTRE EN FRANÇAIS
|
||
|
||
Ta réponse :
|
||
`.trim(),
|
||
en: `
|
||
You are an assistant that generates structured summaries of notebooks.
|
||
|
||
NOTEBOOK: ${notebookName}
|
||
|
||
NOTEBOOK NOTES:
|
||
${notesSummary}
|
||
|
||
TASK:
|
||
Generate a structured and organized summary of this notebook by analyzing all notes.
|
||
|
||
RESPONSE FORMAT (Markdown with emojis):
|
||
|
||
# 📊 Summary of Notebook ${notebookName}
|
||
|
||
## 🌍 Main Themes
|
||
• Identify 3-5 recurring themes or topics covered
|
||
|
||
## 📝 Statistics
|
||
• Total number of notes analyzed
|
||
• Main content categories
|
||
|
||
## 📅 Temporal Elements
|
||
• Important dates or periods mentioned
|
||
• Planned vs past events
|
||
|
||
## ⚠️ Action Items / Attention Points
|
||
• Tasks or actions identified in notes
|
||
• Important reminders or deadlines
|
||
• Items requiring special attention
|
||
|
||
## 💡 Key Insights
|
||
• Summary of most important information
|
||
• Observed trends or patterns
|
||
• Connections between different notes
|
||
|
||
RULES:
|
||
- Use Markdown format with emojis as in the example
|
||
- Be concise and organize information clearly
|
||
- Identify real trends, do not invent information
|
||
- If a section is not relevant, use "N/A" or omit it
|
||
- Tone: professional but accessible
|
||
- YOUR RESPONSE MUST BE IN ENGLISH
|
||
|
||
Your response:
|
||
`.trim(),
|
||
fa: `
|
||
شما یک دستیار هستید که خلاصههای ساختاریافته از دفترچههای یادداشت تولید میکنید.
|
||
|
||
دفترچه: ${notebookName}
|
||
|
||
یادداشتهای دفترچه:
|
||
${notesSummary}
|
||
|
||
وظیفه:
|
||
یک خلاصه ساختاریافته و منظم از این دفترچه با تحلیل تمام یادداشتها تولید کنید.
|
||
|
||
فرمت پاسخ (مارکداون با ایموجی):
|
||
|
||
# 📊 خلاصه دفترچه ${notebookName}
|
||
|
||
## 🌍 موضوعات اصلی
|
||
• ۳-۵ موضوع تکرارشونده یا مبحث پوشش داده شده را شناسایی کنید
|
||
|
||
## 📝 آمار
|
||
• تعداد کل یادداشتهای تحلیل شده
|
||
• دستهبندیهای اصلی محتوا
|
||
|
||
## 📅 عناصر زمانی
|
||
• تاریخها یا دورههای مهم ذکر شده
|
||
• رویدادهای برنامهریزی شده در مقابل گذشته
|
||
|
||
## ⚠️ موارد اقدام / نقاط توجه
|
||
• وظایف یا اقدامات شناسایی شده در یادداشتها
|
||
• یادآوریها یا مهلتهای مهم
|
||
• مواردی که نیاز به توجه ویژه دارند
|
||
|
||
## 💡 بینشهای کلیدی
|
||
• خلاصه مهمترین اطلاعات
|
||
• روندها یا الگوهای مشاهده شده
|
||
• ارتباطات بین یادداشتهای مختلف
|
||
|
||
قوانین:
|
||
- از فرمت مارکداون با ایموجی مانند مثال استفاده کنید
|
||
- مختصر باشید و اطلاعات را به وضوح سازماندهی کنید
|
||
- روندهای واقعی را شناسایی کنید، اطلاعات اختراع نکنید
|
||
- اگر بخش مرتبط نیست، از "N/A" استفاده کنید یا آن را حذف کنید
|
||
- لحن: حرفهای اما قابل دسترس
|
||
- پاسخ شما باید به زبان فارسی باشد
|
||
|
||
پاسخ شما:
|
||
`.trim(),
|
||
es: `
|
||
Eres un asistente que genera resúmenes estructurados de cuadernos de notas.
|
||
|
||
CUADERNO: ${notebookName}
|
||
|
||
NOTAS DEL CUADERNO:
|
||
${notesSummary}
|
||
|
||
TAREA:
|
||
Genera un resumen estructurado y organizado de este cuaderno analizando todas las notas.
|
||
|
||
FORMATO DE RESPUESTA (Markdown con emojis):
|
||
|
||
# 📊 Resumen del Cuaderno ${notebookName}
|
||
|
||
## 🌍 Temas Principales
|
||
• Identifica 3-5 temas recurrentes o tópicos cubiertos
|
||
|
||
## 📝 Estadísticas
|
||
• Número total de notas analizadas
|
||
• Categorías principales de contenido
|
||
|
||
## 📅 Elementos Temporales
|
||
• Fechas o periodos importantes mencionados
|
||
• Eventos planificados vs pasados
|
||
|
||
## ⚠️ Puntos de Atención / Acciones Requeridas
|
||
• Tareas o acciones identificadas en las notas
|
||
• Recordatorios o plazos importantes
|
||
• Elementos que requieren atención especial
|
||
|
||
## 💡 Insights Clave
|
||
• Resumen de la información más importante
|
||
• Tendencias o patrones observados
|
||
• Conexiones entre las diferentes notas
|
||
|
||
REGLAS:
|
||
- Usa formato Markdown con emojis como en el ejemplo
|
||
- Sé conciso y organiza la información claramente
|
||
- Identifica tendencias reales, no inventes información
|
||
- Si una sección no es relevante, usa "N/A" u omítela
|
||
- Tono: profesional pero accesible
|
||
- TU RESPUESTA DEBE SER EN ESPAÑOL
|
||
|
||
Tu respuesta:
|
||
`.trim(),
|
||
de: `
|
||
Du bist ein Assistent, der strukturierte Zusammenfassungen von Notizbüchern erstellt.
|
||
|
||
NOTIZBUCH: ${notebookName}
|
||
|
||
NOTIZBUCH-NOTIZEN:
|
||
${notesSummary}
|
||
|
||
AUFGABE:
|
||
Erstelle eine strukturierte und organisierte Zusammenfassung dieses Notizbuchs, indem du alle Notizen analysierst.
|
||
|
||
ANTWORTFORMAT (Markdown mit Emojis):
|
||
|
||
# 📊 Zusammenfassung des Notizbuchs ${notebookName}
|
||
|
||
## 🌍 Hauptthemen
|
||
• Identifiziere 3-5 wiederkehrende Themen
|
||
|
||
## 📝 Statistiken
|
||
• Gesamtzahl der analysierten Notizen
|
||
• Hauptinhaltkategorien
|
||
|
||
## 📅 Zeitliche Elemente
|
||
• Wichtige erwähnte Daten oder Zeiträume
|
||
• Geplante vs. vergangene Ereignisse
|
||
|
||
## ⚠️ Handlungspunkte / Aufmerksamkeitspunkte
|
||
• In Notizen identifizierte Aufgaben oder Aktionen
|
||
• Wichtige Erinnerungen oder Fristen
|
||
• Elemente, die besondere Aufmerksamkeit erfordern
|
||
|
||
## 💡 Wichtige Erkenntnisse
|
||
• Zusammenfassung der wichtigsten Informationen
|
||
• Beobachtete Trends oder Muster
|
||
• Verbindungen zwischen verschiedenen Notizen
|
||
|
||
REGELN:
|
||
- Verwende Markdown-Format mit Emojis wie im Beispiel
|
||
- Sei prägnant und organisiere Informationen klar
|
||
- Identifiziere echte Trends, erfinde keine Informationen
|
||
- Wenn ein Abschnitt nicht relevant ist, verwende "N/A" oder lass ihn weg
|
||
- Ton: professionell aber zugänglich
|
||
- DEINE ANTWORT MUSS AUF DEUTSCH SEIN
|
||
|
||
Deine Antwort:
|
||
`.trim()
|
||
}
|
||
|
||
return instructions[language] || instructions['en'] || instructions['fr']
|
||
}
|
||
}
|
||
|
||
// Export singleton instance
|
||
export const notebookSummaryService = new NotebookSummaryService()
|