From bfaacc557f07f1096ddc11aa6c0cbbfd423584dd Mon Sep 17 00:00:00 2001 From: Antigravity Date: Fri, 22 May 2026 18:30:11 +0000 Subject: [PATCH] fix(chart): simplify to single insert_chart tool that does everything --- memento-note/app/api/chat/route.ts | 10 +- memento-note/lib/ai/tools/chart.tool.ts | 141 +++++++++--------------- memento-note/lib/ai/tools/registry.ts | 2 +- 3 files changed, 59 insertions(+), 94 deletions(-) diff --git a/memento-note/app/api/chat/route.ts b/memento-note/app/api/chat/route.ts index e686350..9bb60d8 100644 --- a/memento-note/app/api/chat/route.ts +++ b/memento-note/app/api/chat/route.ts @@ -220,13 +220,12 @@ 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, note_find_and_update, document_search, task_extract, web_search, web_scrape, generate_chart, insert_chart_in_note. +You have access to: note_search, note_read, note_find_and_update, document_search, task_extract, web_search, web_scrape, insert_chart. 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. - note_find_and_update: Finds a note by search query and appends/prepends/replaces content. Use when the user says "find the note about X and add Y to it". -- generate_chart: Generates a chart (bar, line, area, pie, radar, funnel, gauge) from data and returns it as markdown. Use when the user asks for a chart, graph, visualization, or wants to see data visually. -- insert_chart_in_note: Creates a chart and directly inserts it into a note. Use when the user says "make a chart", "create a graph", "visualize this data" in the context of editing a note.`, +- insert_chart: Generates a chart (bar, line, area, pie, radar) and inserts it directly into the note. Use when the user asks "make a chart", "create a graph", "visualize this data", "show me a chart of X". Chart types: bar (comparisons), horizontal-bar (long labels), line/area (trends), pie (proportions), radar (comparisons).`, }, fr: { 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.`, @@ -257,12 +256,11 @@ 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, note_find_and_update, document_search, task_extract, web_search, web_scrape, generate_chart, insert_chart_in_note. +Tu as accès à : note_search, note_read, note_find_and_update, document_search, task_extract, web_search, web_scrape, insert_chart. - 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. - note_find_and_update : Trouve une note par recherche textuelle et ajoute/prépose/remplace du contenu. Utilise quand l'utilisateur dit "trouve la note sur X et ajoute-y Y". -- generate_chart : Génère un graphique (barres, ligne, aire, circulaire, radar, entonnoir, jauge) à partir de données et le retourne en markdown. Utilise quand l'utilisateur demande un graphique, une visualisation, ou veut voir des données visuellement. -- insert_chart_in_note : Crée un graphique et l'insère directement dans une note. Utilise quand l'utilisateur dit "fais un graphique", "crée un chart", "visualise ces données" dans le contexte d'édition d'une note.`, +- insert_chart : Génère un graphique (barres, ligne, aire, circulaire, radar) et l'insère directement dans la note. Utilise quand l'utilisateur demande "fais un graphique", "crée un chart", "visualise ces données", "montre-moi un chart de X". Types : bar (comparaisons), horizontal-bar (labels longs), line/area (tendances), pie (proportions), radar (comparaisons).`, }, fa: { contextWithNotes: `## یادداشت‌های کاربر\n\n${contextNotes}\n\nهنگام استفاده از اطلاعات یادداشت‌های بالا، عنوان یادداشت منبع را در پرانتز ذکر کنید.`, diff --git a/memento-note/lib/ai/tools/chart.tool.ts b/memento-note/lib/ai/tools/chart.tool.ts index 7ddce1e..943ddbf 100644 --- a/memento-note/lib/ai/tools/chart.tool.ts +++ b/memento-note/lib/ai/tools/chart.tool.ts @@ -6,111 +6,78 @@ import { tool } from 'ai' import { z } from 'zod' import { toolRegistry } from './registry' +import { prisma } from '@/lib/prisma' -// Chart generation tool - inserts chart as markdown code block +// Simple chart insertion tool - does everything in one call toolRegistry.register({ - name: 'generate_chart', - description: 'Generate an inline chart from data and insert it into a note as markdown. The chart will be rendered directly in the note.', + name: 'insert_chart', + description: 'Generate a chart and insert it directly into a note. Use when the user asks for a chart, graph, or visualization.', isInternal: true, buildTool: (ctx) => tool({ - description: `Generate an inline chart from data and return it as a markdown code block that can be inserted into a note. + description: `Generate a chart and insert it directly into the note content. Available chart types: -- "bar": Vertical bar chart -- "horizontal-bar": Horizontal bar chart -- "line": Line chart for trends over time -- "area": Area chart (filled line) -- "pie": Pie/donut chart for proportions -- "radar": Radar chart for comparing multiple dimensions -- "funnel": Funnel chart for stages/conversions -- "gauge": Gauge/meter for a single value +- "bar": Vertical bar chart (default for comparisons) +- "horizontal-bar": Horizontal bar chart (use when labels are long) +- "line": Line chart (use for time series or trends) +- "area": Area chart (filled line chart) +- "pie": Pie chart (use for proportions/percentages) +- "radar": Radar chart (use for comparing multiple dimensions) -Data format: Array of objects with "label" (string) and "value" (number). +IMPORTANT: When the user asks for a chart/graph/visualization: +1. Extract the data from the note or user request +2. Choose the appropriate chart type based on the data +3. Generate the chart markdown using this format: -IMPORTANT: -- Extract data from the note content when possible -- Keep labels short (max 20 characters) -- Round values to reasonable precision (max 2 decimal places) -- For time series, use short date formats (Jan, Feb, Mar or 2023, 2024) -- Maximum 12 data points for readability -- The output will be rendered as a visual chart in the note`, +\`\`\`chart +{chartType} +{title} +{label1}: {value1} +{label2}: {value2} +... +\`\`\` + +Example for "show sales by month": +\`\`\`chart +bar +Sales by Month +Jan: 5000 +Feb: 7500 +Mar: 6200 +\`\`\` + +4. Call this tool with the noteId, the chart markdown, and where to insert it`, inputSchema: z.object({ - chartType: z.enum(['bar', 'horizontal-bar', 'line', 'area', 'pie', 'radar', 'funnel', 'gauge']).describe('Type of chart to generate'), - title: z.string().optional().describe('Optional title for the chart'), - data: z.array(z.object({ - label: z.string().describe('Label for the data point (e.g., month name, category)'), - value: z.number().describe('Numeric value for the data point'), - })).describe('Array of data points with label and value'), - insertLocation: z.enum(['append', 'prepend', 'replace']).default('append').describe('Where to insert the chart in the note'), - targetNoteId: z.string().optional().describe('Optional: specific note ID to update. If not provided, use the current note.'), + noteId: z.string().describe('The note ID to update'), + chartMarkdown: z.string().describe('The complete chart markdown block to insert (including the ```chart fences)'), + insertLocation: z.enum(['append', 'prepend']).default('append').describe('append: add to end, prepend: add to start'), }), - execute: async ({ chartType, title, data, insertLocation, targetNoteId }) => { + execute: async ({ noteId, chartMarkdown, insertLocation }) => { try { - // Generate the markdown code block for the chart - const chartData = data.map(d => `${d.label}: ${d.value}`).join('\n') - const chartMarkdown = `\`\`\`chart -${chartType}${title ? `\n${title}` : ''} -${chartData} -\`\`\`\n` + const note = await prisma.note.findFirst({ + where: { id: noteId, userId: ctx.userId }, + select: { content: true }, + }) + + if (!note) return { error: 'Note not found' } + + const updatedContent = insertLocation === 'append' + ? `${note.content}\n\n${chartMarkdown}` + : `${chartMarkdown}\n\n${note.content}` + + await prisma.note.update({ + where: { id: noteId }, + data: { content: updatedContent }, + }) return { success: true, + message: `Chart ${insertLocation === 'append' ? 'appended' : 'prepended'} to note.`, chartMarkdown, - chartType, - dataPointCount: data.length, - message: `Chart generated with ${data.length} data points. Insert this markdown into the note.`, } } catch (e: any) { - return { success: false, error: `Chart generation failed: ${e.message}` } - } - }, - }), -}) - -// Quick chart insert tool - directly updates a note with a chart -toolRegistry.register({ - name: 'insert_chart_in_note', - description: 'Insert a chart directly into a note. Reads the note, extracts relevant data, generates a chart, and updates the note content.', - isInternal: true, - buildTool: (ctx) => - tool({ - description: `Insert a chart directly into a note. This tool will: -1. Read the current note content -2. Extract relevant data from the note (sales, metrics, comparisons, etc.) -3. Generate an appropriate chart type based on the data -4. Insert the chart markdown into the note - -Use this when the user says "make a chart", "create a graph", "visualize this data", etc. - -Choose the chart type based on the data: -- Use "bar" for comparing values across categories (default) -- Use "horizontal-bar" when labels are long -- Use "line" or "area" for time series or trends -- Use "pie" for showing proportions/percentages -- Use "radar" for comparing multiple attributes -- Use "funnel" for stages or conversion data -- Use "gauge" for progress/KPI (single value)`, - inputSchema: z.object({ - noteId: z.string().describe('The note ID to read and update'), - chartHint: z.string().optional().describe('Optional hint about what to chart (e.g., "sales by month", "comparison of products")'), - insertLocation: z.enum(['append', 'prepend', 'before-section', 'after-section']).default('append').describe('Where to insert the chart'), - sectionMarker: z.string().optional().describe('For before-section/after-section: the heading text to find (e.g., "## Sales Data")'), - }), - execute: async ({ noteId, chartHint, insertLocation, sectionMarker }) => { - try { - // We'll return the instructions for the AI to format the chart - // The actual note update will be done by note_find_and_update or note_update - return { - success: true, - instructions: 'Generate a chart markdown block using the generate_chart tool, then use note_update or note_find_and_update to insert it into the note.', - noteId, - chartHint, - insertLocation, - sectionMarker, - } - } catch (e: any) { - return { success: false, error: `Failed to prepare chart insertion: ${e.message}` } + return { success: false, error: `Failed: ${e.message}` } } }, }), diff --git a/memento-note/lib/ai/tools/registry.ts b/memento-note/lib/ai/tools/registry.ts index c4007d9..15ebe98 100644 --- a/memento-note/lib/ai/tools/registry.ts +++ b/memento-note/lib/ai/tools/registry.ts @@ -52,7 +52,7 @@ class ToolRegistry { * When webOnly is true, only web tools are included (no note access). */ buildToolsForChat(ctx: ToolContext & { webOnly?: boolean }): Record { - const toolNames: string[] = ctx.webOnly ? [] : ['note_search', 'note_read', 'note_find_and_update', 'document_search', 'task_extract', 'generate_chart', 'insert_chart_in_note'] + const toolNames: string[] = ctx.webOnly ? [] : ['note_search', 'note_read', 'note_find_and_update', 'document_search', 'task_extract', 'insert_chart'] // Add web tools only when user toggled web search AND config is present if (ctx.webSearch) {