feat(ux): epic UX design improvements across agents, chat, notes, and i18n
Comprehensive UI/UX updates including agent card redesign, chat container improvements, note editor enhancements, memory echo notifications, and updated translations for all 15 locales. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,8 @@ import { semanticSearchService } from '@/lib/ai/services/semantic-search.service
|
||||
import { prisma } from '@/lib/prisma'
|
||||
import { auth } from '@/auth'
|
||||
import { loadTranslations, getTranslationValue, SupportedLanguage } from '@/lib/i18n'
|
||||
import { toolRegistry } from '@/lib/ai/tools'
|
||||
import { stepCountIs } from 'ai'
|
||||
|
||||
export const maxDuration = 60
|
||||
|
||||
@@ -45,11 +47,12 @@ export async function POST(req: Request) {
|
||||
|
||||
// 2. Parse request body — messages arrive as UIMessage[] from DefaultChatTransport
|
||||
const body = await req.json()
|
||||
const { messages: rawMessages, conversationId, notebookId, language } = body as {
|
||||
const { messages: rawMessages, conversationId, notebookId, language, webSearch } = body as {
|
||||
messages: UIMessage[]
|
||||
conversationId?: string
|
||||
notebookId?: string
|
||||
language?: string
|
||||
webSearch?: boolean
|
||||
}
|
||||
|
||||
// Convert UIMessages to CoreMessages for streamText
|
||||
@@ -143,7 +146,19 @@ export async function POST(req: Request) {
|
||||
- Natural tone, neither corporate nor too casual.
|
||||
- No unnecessary intro phrases ("Here's what I found", "Based on your notes"). Answer directly.
|
||||
- No upsell questions at the end ("Would you like me to...", "Do you want..."). If you have useful additional info, just give it.
|
||||
- If the user says "Momento" they mean Memento (this app).`,
|
||||
- If the user says "Momento" they mean Memento (this app).
|
||||
|
||||
## Available tools
|
||||
You have access to these tools for deeper research:
|
||||
- **note_search**: Search the user's notes by keyword or meaning. Use when the initial context above is insufficient or when the user asks about specific content in their notes. If a notebook is selected, pass its ID to restrict results.
|
||||
- **note_read**: Read a specific note by ID. Use when note_search returns a note you need the full content of.
|
||||
- **web_search**: Search the web for information. Use when the user asks about something not in their notes.
|
||||
- **web_scrape**: Scrape a web page and return its content as markdown. Use when web_search returns a URL you need to read.
|
||||
|
||||
## Tool usage rules
|
||||
- You already have context from the user's notes above. Only use tools if you need more specific or additional information.
|
||||
- Never invent note IDs, URLs, or notebook IDs. Use the IDs provided in the context or from tool results.
|
||||
- For simple conversational questions (greetings, opinions, general knowledge), answer directly without using any tools.`,
|
||||
},
|
||||
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. Si les notes ne couvrent pas le sujet, dis-le et complète avec tes connaissances générales.`,
|
||||
@@ -159,7 +174,19 @@ export async function POST(req: Request) {
|
||||
- Ton naturel, ni corporate ni trop familier.
|
||||
- Pas de phrase d'intro inutile ("Voici ce que j'ai trouvé", "Basé sur vos notes"). Réponds directement.
|
||||
- Pas de question upsell à la fin ("Souhaitez-vous que je...", "Acceptez-vous que..."). Si tu as une info complémentaire utile, donne-la.
|
||||
- Si l'utilisateur dit "Momento" il parle de Memento (cette application).`,
|
||||
- Si l'utilisateur dit "Momento" il parle de Memento (cette application).
|
||||
|
||||
## Outils disponibles
|
||||
Tu as accès à ces outils pour des recherches approfondies :
|
||||
- **note_search** : Cherche dans les notes de l'utilisateur par mot-clé ou sens. Utilise quand le contexte initial ci-dessus est insuffisant ou quand l'utilisateur demande du contenu spécifique dans ses notes. Si un carnet est sélectionné, passe son ID pour restreindre les résultats.
|
||||
- **note_read** : Lit une note spécifique par son ID. Utilise quand note_search retourne une note dont tu as besoin du contenu complet.
|
||||
- **web_search** : Recherche sur le web. Utilise quand l'utilisateur demande quelque chose qui n'est pas dans ses notes.
|
||||
- **web_scrape** : Scrape une page web et retourne son contenu en markdown. Utilise quand web_search retourne une URL que tu veux lire.
|
||||
|
||||
## Règles d'utilisation des outils
|
||||
- Tu as déjà du contexte des notes de l'utilisateur ci-dessus. Utilise les outils seulement si tu as besoin d'informations plus spécifiques.
|
||||
- N'invente jamais d'IDs de notes, d'URLs ou d'IDs de carnet. Utilise les IDs fournis dans le contexte ou les résultats d'outils.
|
||||
- Pour les questions conversationnelles simples (salutations, opinions, connaissances générales), réponds directement sans utiliser d'outils.`,
|
||||
},
|
||||
fa: {
|
||||
contextWithNotes: `## یادداشتهای کاربر\n\n${contextNotes}\n\nهنگام استفاده از اطلاعات یادداشتهای بالا، عنوان یادداشت منبع را در پرانتز ذکر کنید. کپی نکنید — بازنویسی کنید. اگر یادداشتها موضوع را پوشش نمیدهند، بگویید و با دانش عمومی خود تکمیل کنید.`,
|
||||
@@ -175,7 +202,18 @@ export async function POST(req: Request) {
|
||||
- لحن طبیعی، نه رسمی بیش از حد و نه خیلی غیررسمی.
|
||||
- بدون جمله مقدمه اضافی. مستقیم پاسخ دهید.
|
||||
- بدون سؤال فروشی در انتها. اگر اطلاعات تکمیلی مفید دارید، مستقیم بدهید.
|
||||
- اگر کاربر "Momento" میگوید، منظورش Memento (این برنامه) است.`,
|
||||
- اگر کاربر "Momento" میگوید، منظورش Memento (این برنامه) است.
|
||||
|
||||
## ابزارهای موجود
|
||||
- **note_search**: جستجو در یادداشتهای کاربر با کلیدواژه یا معنی. زمانی استفاده کنید که زمینه اولیه کافی نباشد. اگر دفترچه انتخاب شده، شناسه آن را ارسال کنید.
|
||||
- **note_read**: خواندن یک یادداشت خاص با شناسه. زمانی استفاده کنید که note_search یادداشتی برگرداند که محتوای کامل آن را نیاز دارید.
|
||||
- **web_search**: جستجو در وب. زمانی استفاده کنید که کاربر درباره چیزی خارج از یادداشتهایش میپرسد.
|
||||
- **web_scrape**: استخراج محتوای صفحه وب. زمانی استفاده کنید که web_search نشانیای برگرداند که میخواهید بخوانید.
|
||||
|
||||
## قوانین استفاده از ابزارها
|
||||
- شما از قبل زمینهای از یادداشتهای کاربر دارید. فقط در صورت نیاز به اطلاعات بیشتر از ابزارها استفاده کنید.
|
||||
- هرگز شناسه یادداشت، نشانی یا شناسه دفترچه نسازید. از شناسههای موجود در زمینه یا نتایج ابزار استفاده کنید.
|
||||
- برای سؤالات مکالمهای ساده (سلام، نظرات، دانش عمومی)، مستقیم پاسخ دهید.`,
|
||||
},
|
||||
es: {
|
||||
contextWithNotes: `## Notas del usuario\n\n${contextNotes}\n\nCuando uses información de las notas anteriores, cita el título de la nota fuente entre paréntesis. No copies palabra por palabra — reformula. Si las notas no cubren el tema, dilo y complementa con tu conocimiento general.`,
|
||||
@@ -190,7 +228,18 @@ export async function POST(req: Request) {
|
||||
## Reglas de tono
|
||||
- Tono natural, ni corporativo ni demasiado informal.
|
||||
- Sin frases de introducción innecesarias. Responde directamente.
|
||||
- Sin preguntas de venta al final. Si tienes información complementaria útil, dala directamente.`,
|
||||
- Sin preguntas de venta al final. Si tienes información complementaria útil, dala directamente.
|
||||
|
||||
## Herramientas disponibles
|
||||
- **note_search**: Busca en las notas del usuario por palabra clave o significado. Úsalo cuando el contexto inicial sea insuficiente. Si hay una libreta seleccionada, pasa su ID para restringir los resultados.
|
||||
- **note_read**: Lee una nota específica por su ID. Úsalo cuando note_search devuelva una nota cuyo contenido completo necesites.
|
||||
- **web_search**: Busca en la web. Úsalo cuando el usuario pregunte sobre algo que no está en sus notas.
|
||||
- **web_scrape**: Extrae el contenido de una página web como markdown. Úsalo cuando web_search devuelva una URL que quieras leer.
|
||||
|
||||
## Reglas de uso de herramientas
|
||||
- Ya tienes contexto de las notas del usuario arriba. Solo usa herramientas si necesitas información más específica.
|
||||
- Nunca inventes IDs de notas, URLs o IDs de libreta. Usa los IDs proporcionados en el contexto o en los resultados de herramientas.
|
||||
- Para preguntas conversacionales simples (saludos, opiniones, conocimiento general), responde directamente sin herramientas.`,
|
||||
},
|
||||
de: {
|
||||
contextWithNotes: `## Notizen des Benutzers\n\n${contextNotes}\n\nWenn du Infos aus den obigen Notizen verwendest, zitiere den Titel der Quellnotiz in Klammern. Nicht Wort für Wort kopieren — umformulieren. Wenn die Notizen das Thema nicht abdecken, sag es und ergänze mit deinem Allgemeinwissen.`,
|
||||
@@ -205,7 +254,18 @@ export async function POST(req: Request) {
|
||||
## Tonregeln
|
||||
- Natürlicher Ton, weder zu geschäftsmäßig noch zu umgangssprachlich.
|
||||
- Keine unnötigen Einleitungssätze. Antworte direkt.
|
||||
- Keine Upsell-Fragen am Ende. Gib nützliche Zusatzinfos einfach direkt.`,
|
||||
- Keine Upsell-Fragen am Ende. Gib nützliche Zusatzinfos einfach direkt.
|
||||
|
||||
## Verfügbare Werkzeuge
|
||||
- **note_search**: Durchsuche die Notizen des Benutzers nach Schlagwort oder Bedeutung. Verwende es, wenn der obige Kontext unzureichend ist. Wenn ein Notizbuch ausgewählt ist, gib dessen ID an, um die Ergebnisse einzuschränken.
|
||||
- **note_read**: Lese eine bestimmte Notiz anhand ihrer ID. Verwende es, wenn note_search eine Notiz zurückgibt, deren vollständigen Inhalt du benötigst.
|
||||
- **web_search**: Suche im Web. Verwende es, wenn der Benutzer nach etwas fragt, das nicht in seinen Notizen steht.
|
||||
- **web_scrape**: Lese eine Webseite und gib den Inhalt als Markdown zurück. Verwende es, wenn web_search eine URL zurückgibt, die du lesen möchtest.
|
||||
|
||||
## Werkzeugregeln
|
||||
- Du hast bereits Kontext aus den Notizen des Benutzers oben. Verwende Werkzeuge nur, wenn du spezifischere Informationen benötigst.
|
||||
- Erfinde niemals Notiz-IDs, URLs oder Notizbuch-IDs. Verwende die im Kontext oder in Werkzeugergebnissen bereitgestellten IDs.
|
||||
- Bei einfachen Gesprächsfragen (Begrüßungen, Meinungen, Allgemeinwissen) antworte direkt ohne Werkzeuge.`,
|
||||
},
|
||||
it: {
|
||||
contextWithNotes: `## Note dell'utente\n\n${contextNotes}\n\nQuando usi informazioni dalle note sopra, cita il titolo della nota fonte tra parentesi. Non copiare parola per parola — riformula. Se le note non coprono l'argomento, dillo e integra con la tua conoscenza generale.`,
|
||||
@@ -220,7 +280,18 @@ export async function POST(req: Request) {
|
||||
## Regole di tono
|
||||
- Tono naturale, né aziendale né troppo informale.
|
||||
- Nessuna frase introduttiva non necessaria. Rispondi direttamente.
|
||||
- Nessuna domanda di upsell alla fine. Se hai informazioni aggiuntive utili, dalle direttamente.`,
|
||||
- Nessuna domanda di upsell alla fine. Se hai informazioni aggiuntive utili, dalle direttamente.
|
||||
|
||||
## Strumenti disponibili
|
||||
- **note_search**: Cerca nelle note dell'utente per parola chiave o significato. Usa quando il contesto iniziale è insufficiente. Se un quaderno è selezionato, passa il suo ID per restringere i risultati.
|
||||
- **note_read**: Leggi una nota specifica per ID. Usa quando note_search restituisce una nota di cui hai bisogno del contenuto completo.
|
||||
- **web_search**: Cerca sul web. Usa quando l'utente chiede qualcosa che non è nelle sue note.
|
||||
- **web_scrape**: Estrai il contenuto di una pagina web come markdown. Usa quando web_search restituisce un URL che vuoi leggere.
|
||||
|
||||
## Regole di utilizzo degli strumenti
|
||||
- Hai già contesto dalle note dell'utente sopra. Usa gli strumenti solo se hai bisogno di informazioni più specifiche.
|
||||
- Non inventare mai ID di note, URL o ID di quaderno. Usa gli ID forniti nel contesto o nei risultati degli strumenti.
|
||||
- Per domande conversazionali semplici (saluti, opinioni, conoscenza generale), rispondi direttamente senza strumenti.`,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -260,6 +331,16 @@ ${lang === 'en' ? 'Respond in the user\'s language.' : lang === 'fr' ? 'Réponds
|
||||
const provider = getChatProvider(config)
|
||||
const model = provider.getModel()
|
||||
|
||||
// 7b. Build chat tools
|
||||
const chatToolContext = {
|
||||
userId,
|
||||
conversationId: conversation.id,
|
||||
notebookId,
|
||||
webSearch: !!webSearch,
|
||||
config,
|
||||
}
|
||||
const chatTools = toolRegistry.buildToolsForChat(chatToolContext)
|
||||
|
||||
// 8. Save user message to DB before streaming
|
||||
if (isNewMessage && lastIncoming) {
|
||||
await prisma.chatMessage.create({
|
||||
@@ -276,6 +357,8 @@ ${lang === 'en' ? 'Respond in the user\'s language.' : lang === 'fr' ? 'Réponds
|
||||
model,
|
||||
system: systemPrompt,
|
||||
messages: allMessages,
|
||||
tools: chatTools,
|
||||
stopWhen: stepCountIs(5),
|
||||
async onFinish({ text }) {
|
||||
// Save assistant message to DB after streaming completes
|
||||
await prisma.chatMessage.create({
|
||||
|
||||
Reference in New Issue
Block a user