feat: brainstorm sessions, PDF document Q&A, embedding fixes, and UI improvements
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 7s

- Add brainstorm feature with collaborative canvas, AI idea generation, live cursors, playback, and export
- Add PDF upload/extraction/ingestion pipeline with pgvector document search (RAG)
- Add document Q&A overlay with streaming chat and PDF preview
- Add note attachments UI with status polling, grid layout, and auto-scroll
- Add task extraction AI tool and agent executor improvements
- Fix NoteEmbedding missing updatedAt column, re-index 66 notes with 1536-dim embeddings
- Fix brainstorm 'Create Note' button: add success toast and redirect to created note
- Fix memory echo notification infinite polling
- Fix chat route to always include document_search tool
- Add brainstorm i18n keys across all 14 locales
- Add socket server for real-time brainstorm collaboration
- Add hierarchical notebook selector and organize notebook dialog improvements
- Add sidebar brainstorm section with session management
- Update prisma schema with brainstorm tables, attachments, and document chunks
This commit is contained in:
Antigravity
2026-05-14 17:43:21 +00:00
parent 195e845f0a
commit 1fcea6ed7d
228 changed files with 57656 additions and 1059 deletions

View File

@@ -48,7 +48,7 @@ export async function POST(req: Request) {
// 2. Parse request body
const body = await req.json()
const { messages: rawMessages, conversationId, notebookId, language, webSearch, noteContext, format } = body as {
const { messages: rawMessages, conversationId, notebookId, language, webSearch, noteContext, format, noteId } = body as {
messages: UIMessage[]
conversationId?: string
notebookId?: string
@@ -56,6 +56,7 @@ export async function POST(req: Request) {
webSearch?: boolean
noteContext?: { title: string; content: string; tone: string; images?: string[] }
format?: 'html' | 'markdown'
noteId?: string
}
const incomingMessages = toCoreMessages(rawMessages)
@@ -107,17 +108,60 @@ export async function POST(req: Request) {
let searchResults: any[] = []
try {
searchResults = await semanticSearchService.search(currentMessage, {
notebookId,
limit: notebookId ? 10 : 5,
threshold: notebookId ? 0.3 : 0.5,
defaultTitle: untitledText,
})
const documentMention = currentMessage.match(
/\b(pdf|document|fichier|pi[eè]ce jointe|attachment|file)\b/i
)
if (documentMention) {
const docResults = await semanticSearchService.searchWithDocuments(
userId, currentMessage, {
notebookId,
limit: notebookId ? 10 : 5,
threshold: notebookId ? 0.3 : 0.5,
includeDocuments: true,
defaultTitle: untitledText,
}
)
searchResults = docResults
} else {
searchResults = await semanticSearchService.search(currentMessage, {
notebookId,
limit: notebookId ? 10 : 5,
threshold: notebookId ? 0.3 : 0.5,
defaultTitle: untitledText,
})
}
} catch {}
searchNotes = searchResults
.map((r) => `NOTE [${r.title || untitledText}]: ${r.content}`)
.map((r) => {
if ((r as any).source === 'document') {
return `DOCUMENT [${(r as any).fileName} p.${(r as any).pageNumber}] (from note: ${r.title || untitledText}):\n${r.content}`
}
return `NOTE [${r.title || untitledText}]: ${r.content}`
})
.join('\n\n---\n\n')
} else if (noteId) {
try {
const docResults = await semanticSearchService.searchWithDocuments(
userId, currentMessage, {
noteId,
limit: 8,
threshold: 0.3,
includeDocuments: true,
defaultTitle: untitledText,
}
)
searchNotes = docResults
.map((r) => {
if ((r as any).source === 'document') {
return `DOCUMENT [${(r as any).fileName} p.${(r as any).pageNumber}]:\n${r.content}`
}
return ''
})
.filter(Boolean)
.join('\n\n---\n\n')
} catch {}
}
const contextNotes = [notebookContext, searchNotes].filter(Boolean).join('\n\n---\n\n')
@@ -125,7 +169,7 @@ export async function POST(req: Request) {
// 5. System prompt synthesis
const promptLang: Record<string, { contextWithNotes: string; contextNoNotes: string; system: string }> = {
en: {
contextWithNotes: `## User's notes\n\n${contextNotes}\n\nWhen using info from the notes above, cite the source note title in parentheses, e.g.: "Deployment is done via Docker (💻 Development Guide)". Don't copy word for word — rephrase. If the notes don't cover the topic, say so and supplement with your general knowledge.`,
contextWithNotes: `## User's notes & documents\n\n${contextNotes}\n\nWhen using info from the notes above, cite the source note title in parentheses, e.g.: "Deployment is done via Docker (💻 Development Guide)". For document passages, cite the filename and page number, e.g.: "The revenue was $5M (📄 report.pdf p.12)". Don't copy word for word — rephrase. If the notes don't cover the topic, say so and supplement with your general knowledge.`,
contextNoNotes: "No relevant notes found for this question. Answer with your general knowledge.",
system: `You are the AI assistant of Memento. The user asks you questions about their projects, technical docs, and notes. You must respond in a structured and helpful way.
@@ -159,11 +203,13 @@ Momento is an intelligent note-taking application. Key features include:
- **Lab**: Experimental AI tools for data analysis and deeper insights.
## Available tools
You have access to: note_search, note_read, web_search, web_scrape.
Only use tools if you need more information. Never invent note IDs or URLs.`,
You have access to: note_search, note_read, document_search, task_extract, web_search, web_scrape.
Only use tools if you need more information. Never invent note IDs or URLs.
- document_search: Searches attached PDF documents for the current note/notebook. Use when the user asks about documents or files.
- task_extract: Extracts action items from notes and creates a synthesis note. Use when the user asks to extract tasks or TODOs.`,
},
fr: {
contextWithNotes: `## Notes de l'utilisateur\n\n${contextNotes}\n\nQuand tu utilises une info venant des notes ci-dessus, cite le titre de la note source entre parenthèses, ex: "Le déploiement se fait via Docker (💻 Development Guide)". Ne recopie pas mot pour mot — reformule.`,
contextWithNotes: `## Notes et documents de l'utilisateur\n\n${contextNotes}\n\nQuand tu utilises une info venant des notes ci-dessus, cite le titre de la note source entre parenthèses, ex: "Le déploiement se fait via Docker (💻 Development Guide)". Pour les documents PDF, cite le nom du fichier et la page, ex: "Le chiffre d'affaires est de 5M$ (📄 rapport.pdf p.12)". Ne recopie pas mot pour mot — reformule.`,
contextNoNotes: "Aucune note pertinente trouvée pour cette question. Réponds avec tes connaissances générales.",
system: `Tu es l'assistant IA de Memento. L'utilisateur te pose des questions sur ses projets, sa doc technique, ses notes. Tu dois répondre de façon structurée et utile.
@@ -191,7 +237,9 @@ Only use tools if you need more information. Never invent note IDs or URLs.`,
Momento est une application de prise de notes intelligente. Ses fonctionnalités : Éditeur Markdown riche, Copilot IA, Organisation par Carnets, Recherche sémantique, Agents IA, Lab.
## Outils disponibles
Tu as accès à : note_search, note_read, web_search, web_scrape.`,
Tu as accès à : note_search, note_read, document_search, task_extract, web_search, web_scrape.
- document_search : Recherche dans les documents PDF attachés à la note/au carnet.
- task_extract : Extrait les tâches/action items des notes et crée une note de synthèse.`,
},
fa: {
contextWithNotes: `## یادداشت‌های کاربر\n\n${contextNotes}\n\nهنگام استفاده از اطلاعات یادداشت‌های بالا، عنوان یادداشت منبع را در پرانتز ذکر کنید.`,
@@ -276,7 +324,7 @@ Focus ONLY on this note unless asked otherwise.`
// 6. Execute stream
const sysConfig = await getSystemConfig()
const chatTools = noteContext
? toolRegistry.buildToolsForChat({ userId, config: sysConfig, webSearch, webOnly: true })
? toolRegistry.buildToolsForChat({ userId, config: sysConfig, webSearch, notebookId: notebookId || undefined })
: toolRegistry.buildToolsForChat({ userId, config: sysConfig, webSearch, notebookId: notebookId || undefined })
const provider = getChatProvider(sysConfig)