diff --git a/src/components/AIChat/memory/AIMemoryManagerModal.vue b/src/components/AIChat/memory/AIMemoryManagerModal.vue new file mode 100644 index 00000000..589515d3 --- /dev/null +++ b/src/components/AIChat/memory/AIMemoryManagerModal.vue @@ -0,0 +1,321 @@ + + + diff --git a/src/i18n/locales/en-US/ai.json b/src/i18n/locales/en-US/ai.json index 2a006255..67010be4 100644 --- a/src/i18n/locales/en-US/ai.json +++ b/src/i18n/locales/en-US/ai.json @@ -279,6 +279,37 @@ "input": { "placeholder": "Ask a question, or type {'@'} to select contacts or groups..." }, + "memory": { + "button": "Memory", + "title": "Long-term memory", + "description": "Manage preferences and relationship context that AI can use across conversations.", + "preferences": "Preferences", + "entities": "Contacts and groups", + "count": "{count} items", + "empty": "No memories yet. You can ask AI to remember something in a conversation.", + "proactive": { + "title": "Allow proactive AI memory", + "description": "Let AI save long-term preferences and relationship insights. Explicit requests to remember still work when this is off." + }, + "source": { + "user": "User confirmed", + "ai": "AI generated" + }, + "clearCurrent": "Clear current category", + "clearAll": "Clear all", + "confirmDelete": "Delete this memory?", + "confirmClearView": "Clear every memory in the current category?", + "confirmClearAll": "Clear all long-term memories? This cannot be undone.", + "toast": { + "loadFailed": "Failed to load memories", + "saved": "Memory updated", + "saveFailed": "Failed to update memory", + "deleted": "Memory deleted", + "deleteFailed": "Failed to delete memory", + "cleared": "Memories cleared", + "clearFailed": "Failed to clear memories" + } + }, "entityPicker": { "title": "Select contacts or groups", "add": "Select scope", diff --git a/src/i18n/locales/ja-JP/ai.json b/src/i18n/locales/ja-JP/ai.json index dfad22bd..0248ac7f 100644 --- a/src/i18n/locales/ja-JP/ai.json +++ b/src/i18n/locales/ja-JP/ai.json @@ -279,6 +279,37 @@ "input": { "placeholder": "質問を入力、または {'@'} で連絡先やグループを選択..." }, + "memory": { + "button": "メモリ", + "title": "長期メモリ", + "description": "会話をまたいで AI が利用する設定や関係性の背景を管理します。", + "preferences": "設定", + "entities": "連絡先とグループ", + "count": "{count} 件", + "empty": "メモリはまだありません。会話で AI に覚えるよう依頼できます。", + "proactive": { + "title": "AI による自発的な記憶を許可", + "description": "長期的に役立つ設定や関係性の知見を AI が保存できます。オフでも、明示的に記憶を依頼した内容は保存されます。" + }, + "source": { + "user": "ユーザー確認済み", + "ai": "AI が生成" + }, + "clearCurrent": "現在の分類を消去", + "clearAll": "すべて消去", + "confirmDelete": "このメモリを削除しますか?", + "confirmClearView": "現在の分類にあるメモリをすべて消去しますか?", + "confirmClearAll": "長期メモリをすべて消去しますか?この操作は元に戻せません。", + "toast": { + "loadFailed": "メモリを読み込めませんでした", + "saved": "メモリを更新しました", + "saveFailed": "メモリを更新できませんでした", + "deleted": "メモリを削除しました", + "deleteFailed": "メモリを削除できませんでした", + "cleared": "メモリを消去しました", + "clearFailed": "メモリを消去できませんでした" + } + }, "entityPicker": { "title": "連絡先またはグループを選択", "add": "対象を選択", diff --git a/src/i18n/locales/zh-CN/ai.json b/src/i18n/locales/zh-CN/ai.json index 79633c4f..49e21bcb 100644 --- a/src/i18n/locales/zh-CN/ai.json +++ b/src/i18n/locales/zh-CN/ai.json @@ -279,6 +279,37 @@ "input": { "placeholder": "输入问题,输入 {'@'} 选择联系人或群聊..." }, + "memory": { + "button": "记忆", + "title": "长期记忆", + "description": "管理 AI 在不同对话间持续使用的偏好和关系背景。", + "preferences": "偏好", + "entities": "联系人和群聊", + "count": "{count} 条", + "empty": "暂无记忆,你可以在对话中让 AI 记住。", + "proactive": { + "title": "允许 AI 主动记忆", + "description": "允许 AI 保存有长期价值的偏好和关系结论;关闭后仍会保存你明确要求记住的内容。" + }, + "source": { + "user": "用户确认", + "ai": "AI 形成" + }, + "clearCurrent": "清空当前分类", + "clearAll": "清空全部", + "confirmDelete": "确定删除这条记忆吗?", + "confirmClearView": "确定清空当前分类中的全部记忆吗?", + "confirmClearAll": "确定清空全部长期记忆吗?此操作无法撤销。", + "toast": { + "loadFailed": "加载记忆失败", + "saved": "记忆已更新", + "saveFailed": "更新记忆失败", + "deleted": "记忆已删除", + "deleteFailed": "删除记忆失败", + "cleared": "记忆已清空", + "clearFailed": "清空记忆失败" + } + }, "entityPicker": { "title": "选择联系人或群聊", "add": "选择对象", diff --git a/src/i18n/locales/zh-TW/ai.json b/src/i18n/locales/zh-TW/ai.json index cb0eefe9..c839c7c9 100644 --- a/src/i18n/locales/zh-TW/ai.json +++ b/src/i18n/locales/zh-TW/ai.json @@ -279,6 +279,37 @@ "input": { "placeholder": "輸入問題,輸入 {'@'} 選擇聯絡人或群聊..." }, + "memory": { + "button": "記憶", + "title": "長期記憶", + "description": "管理 AI 在不同對話間持續使用的偏好與關係背景。", + "preferences": "偏好", + "entities": "聯絡人和群聊", + "count": "{count} 條", + "empty": "暫無記憶,你可以在對話中讓 AI 記住。", + "proactive": { + "title": "允許 AI 主動記憶", + "description": "允許 AI 儲存有長期價值的偏好與關係結論;關閉後仍會儲存你明確要求記住的內容。" + }, + "source": { + "user": "使用者確認", + "ai": "AI 形成" + }, + "clearCurrent": "清空目前分類", + "clearAll": "清空全部", + "confirmDelete": "確定刪除這條記憶嗎?", + "confirmClearView": "確定清空目前分類中的全部記憶嗎?", + "confirmClearAll": "確定清空全部長期記憶嗎?此操作無法復原。", + "toast": { + "loadFailed": "載入記憶失敗", + "saved": "記憶已更新", + "saveFailed": "更新記憶失敗", + "deleted": "記憶已刪除", + "deleteFailed": "刪除記憶失敗", + "cleared": "記憶已清空", + "clearFailed": "清空記憶失敗" + } + }, "entityPicker": { "title": "選擇聯絡人或群聊", "add": "選擇對象", diff --git a/src/pages/global-ai/index.vue b/src/pages/global-ai/index.vue index f599fa6e..9a0f5728 100644 --- a/src/pages/global-ai/index.vue +++ b/src/pages/global-ai/index.vue @@ -10,6 +10,7 @@ import ChatMessage from '@/components/AIChat/chat/ChatMessage.vue' import AIThinkingIndicator from '@/components/AIChat/chat/AIThinkingIndicator.vue' import ChatStatusBar from '@/components/AIChat/chat/ChatStatusBar.vue' import AIChatInput from '@/components/AIChat/input/AIChatInput.vue' +import AIMemoryManagerModal from '@/components/AIChat/memory/AIMemoryManagerModal.vue' import { useChatScroll } from '@/components/AIChat/composables/useChatScroll' import { useProgressiveChatHistory } from '@/components/AIChat/composables/useProgressiveChatHistory' import { groupMessagesToQAPairs } from '@/components/AIChat/utils/chatMessages' @@ -43,6 +44,7 @@ function isGlobalPageActive(): boolean { return !isUnmounted && route.name === 'global-ai' } const estimatedContextTokens = ref(0) +const showMemoryManager = ref(false) const pairs = computed(() => groupMessagesToQAPairs(messages.value)) const { messagesContainer, showScrollToBottom, handleScrollToBottom, scrollToBottom } = useChatScroll( @@ -238,6 +240,15 @@ onUnmounted(() => { size="compact" >