From 3fe6aee5b8cd52bb0ac922054b59928177c911be Mon Sep 17 00:00:00 2001 From: Camol Date: Sat, 10 May 2025 21:34:48 +0800 Subject: [PATCH 1/7] =?UTF-8?q?style:=20remove=20small=20size=20prop=20fro?= =?UTF-8?q?m=20Switch=20component=20in=20AssistantModel=E2=80=A6=20(#5833)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit style: remove small size prop from Switch component in AssistantModelSettings Co-authored-by: kanweiwei --- .../pages/settings/AssistantSettings/AssistantModelSettings.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/renderer/src/pages/settings/AssistantSettings/AssistantModelSettings.tsx b/src/renderer/src/pages/settings/AssistantSettings/AssistantModelSettings.tsx index 9ea4559b47..1c494b2ee2 100644 --- a/src/renderer/src/pages/settings/AssistantSettings/AssistantModelSettings.tsx +++ b/src/renderer/src/pages/settings/AssistantSettings/AssistantModelSettings.tsx @@ -381,7 +381,6 @@ const AssistantModelSettings: FC = ({ assistant, updateAssistant, updateA { setEnableToolUse(checked) From b80b63e20767267c1a0b463f83e616083e46360b Mon Sep 17 00:00:00 2001 From: George Zhao <38124587+CreatorZZY@users.noreply.github.com> Date: Sat, 10 May 2025 21:57:57 +0800 Subject: [PATCH 2/7] feat: Agent Regular Phrases (#5775) * feat: add regular prompts settings component and integrate into assistant settings. * feat: add regular prompts to assistant creation and update QuickPhrasesButton to utilize them. * feat: add regular prompts settings and update localization for multiple languages. * fix: update assistantObj type to Assistant and clean up unused console logs. * feat: enhance QuickPhrasesButton with modal for adding phrases and localization updates. * feat: update localization for regular prompts to regular phrases in English, Simplified Chinese, and Traditional Chinese. * fix: update localization for assistant prompts to assistant phrases in English and Simplified Chinese. * feat: add regular prompts to new agents during import. * refactor: rename regular_prompts to regular_phrases across localization and components * feat: enhance QuickPhrasesButton with icons for location selection options * perf: optimize loadQuickListPhrases with useCallback and update icon sizes in QuickPhrasesButton --------- Co-authored-by: George Zhao --- src/renderer/src/i18n/locales/en-us.json | 18 +- src/renderer/src/i18n/locales/ja-jp.json | 16 +- src/renderer/src/i18n/locales/ru-ru.json | 18 +- src/renderer/src/i18n/locales/zh-cn.json | 18 +- src/renderer/src/i18n/locales/zh-tw.json | 16 +- .../agents/components/ImportAgentPopup.tsx | 3 +- .../src/pages/home/Inputbar/Inputbar.tsx | 1 + .../home/Inputbar/QuickPhrasesButton.tsx | 145 ++++++++++++-- .../AssistantRegularPromptsSettings.tsx | 179 ++++++++++++++++++ .../settings/AssistantSettings/index.tsx | 10 +- src/renderer/src/services/AssistantService.ts | 6 +- src/renderer/src/types/index.ts | 1 + 12 files changed, 400 insertions(+), 31 deletions(-) create mode 100644 src/renderer/src/pages/settings/AssistantSettings/AssistantRegularPromptsSettings.tsx diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index e70133d759..50d98803e0 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -84,7 +84,18 @@ "settings.knowledge_base.recognition.tip": "The assistant will use the large model's intent recognition capability to determine whether to use the knowledge base for answering. This feature will depend on the model's capabilities", "settings.knowledge_base.recognition": "Use Knowledge Base", "settings.knowledge_base.recognition.off": "Force Search", - "settings.knowledge_base.recognition.on": "Intent Recognition" + "settings.knowledge_base.recognition.on": "Intent Recognition", + "settings.regular_phrases": { + "title": "Regular Phrase", + "add": "Add Phrase", + "edit": "Edit Phrase", + "delete": "Delete Phrase", + "deleteConfirm": "Are you sure to delete this phrase?", + "titleLabel": "Title", + "titlePlaceholder": "Enter title", + "contentLabel": "Content", + "contentPlaceholder": "Please enter phrase content, support using variables, and press Tab to quickly locate the variable to modify. For example: \nHelp me plan a route from ${from} to ${to}, and send it to ${email}." + } }, "auth": { "error": "API key automatically obtained failed, please get it manually", @@ -1586,7 +1597,10 @@ "titlePlaceholder": "Please enter phrase title", "contentPlaceholder": "Please enter phrase content, support using variables, and press Tab to quickly locate the variable to modify. For example: \nHelp me plan a route from ${from} to ${to}, and send it to ${email}.", "delete": "Delete Phrase", - "deleteConfirm": "The phrase cannot be recovered after deletion, continue?" + "deleteConfirm": "The phrase cannot be recovered after deletion, continue?", + "locationLabel": "Add Location", + "global": "Global Phrases", + "assistant": "Assistant Phrases" }, "quickPanel": { "title": "Quick Menu", diff --git a/src/renderer/src/i18n/locales/ja-jp.json b/src/renderer/src/i18n/locales/ja-jp.json index 7b06e8559c..b3e9a8239a 100644 --- a/src/renderer/src/i18n/locales/ja-jp.json +++ b/src/renderer/src/i18n/locales/ja-jp.json @@ -81,6 +81,17 @@ "settings.reasoning_effort.medium": "普通の思考", "settings.reasoning_effort.default": "デフォルト", "settings.more": "アシスタント設定", + "settings.regular_phrases": { + "title": "定型プロンプト", + "add": "プロンプトを追加", + "edit": "プロンプトを編集", + "delete": "プロンプトを削除", + "deleteConfirm": "このプロンプトを削除してもよろしいですか?", + "titleLabel": "タイトル", + "titlePlaceholder": "タイトルを入力", + "contentLabel": "内容", + "contentPlaceholder": "フレーズの内容を入力してください。変数を使用することもできます。変数を使用する場合は、Tabキーを押して変数を選択し、変数を変更してください。例:\n私の名前は${name}です。" + }, "settings.knowledge_base.recognition.tip": "アシスタントは大規模言語モデルの意図認識能力を使用して、ナレッジベースを参照する必要があるかどうかを判断します。この機能はモデルの能力に依存します", "settings.knowledge_base.recognition": "ナレッジベースの呼び出し", "settings.knowledge_base.recognition.off": "強制検索", @@ -1585,7 +1596,10 @@ "titlePlaceholder": "フレーズのタイトルを入力してください", "contentPlaceholder": "フレーズの内容を入力してください。変数を使用することもできます。変数を使用する場合は、Tabキーを押して変数を選択し、変数を変更してください。例:\n私の名前は${name}です。", "delete": "フレーズを削除", - "deleteConfirm": "削除後は復元できません。続行しますか?" + "deleteConfirm": "削除後は復元できません。続行しますか?", + "locationLabel": "追加場所", + "global": "グローバルクイックフレーズ", + "assistant": "アシスタントプロンプト" }, "quickPanel": { "title": "クイックメニュー", diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json index 3f69284988..5047e20e6a 100644 --- a/src/renderer/src/i18n/locales/ru-ru.json +++ b/src/renderer/src/i18n/locales/ru-ru.json @@ -84,7 +84,18 @@ "settings.knowledge_base.recognition.tip": "Ассистент будет использовать возможности большой модели для распознавания намерений, чтобы определить, нужно ли обращаться к базе знаний для ответа. Эта функция будет зависеть от возможностей модели", "settings.knowledge_base.recognition": "Использование базы знаний", "settings.knowledge_base.recognition.off": "Принудительный поиск", - "settings.knowledge_base.recognition.on": "Распознавание намерений" + "settings.knowledge_base.recognition.on": "Распознавание намерений", + "settings.regular_phrases": { + "title": "Регулярные подсказки", + "add": "Добавить подсказку", + "edit": "Редактировать подсказку", + "delete": "Удалить подсказку", + "deleteConfirm": "Вы уверены, что хотите удалить эту подсказку?", + "titleLabel": "Заголовок", + "titlePlaceholder": "Введите заголовок", + "contentLabel": "Содержание", + "contentPlaceholder": "Введите содержание фразы, поддерживает использование переменных, и нажмите Tab для быстрого перехода к переменной для изменения. Например: \nПомоги мне спланировать маршрут от ${from} до ${to} и отправить его на ${email}." + } }, "auth": { "error": "Автоматический получение ключа API не удалось, пожалуйста, получите ключ вручную", @@ -1585,7 +1596,10 @@ "titlePlaceholder": "Введите заголовок фразы", "contentPlaceholder": "Введите содержание фразы, поддерживает использование переменных, и нажмите Tab для быстрого перехода к переменной для изменения. Например: \nПомоги мне спланировать маршрут от ${from} до ${to} и отправить его на ${email}.", "delete": "Удалить фразу", - "deleteConfirm": "После удаления фраза не может быть восстановлена, продолжить?" + "deleteConfirm": "После удаления фраза не может быть восстановлена, продолжить?", + "locationLabel": "Место добавления", + "global": "Глобальные быстрые фразы", + "assistant": "Подсказки ассистента" }, "quickPanel": { "title": "Быстрое меню", diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index e777f533d9..ab0855fb06 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -84,7 +84,18 @@ "settings.reasoning_effort.medium": "斟酌", "settings.reasoning_effort.high": "沉思", "settings.reasoning_effort.default": "默认", - "settings.more": "助手设置" + "settings.more": "助手设置", + "settings.regular_phrases": { + "title": "常用短语", + "add": "添加短语", + "edit": "编辑短语", + "delete": "删除短语", + "deleteConfirm": "确定要删除这个短语吗?", + "titleLabel": "标题", + "titlePlaceholder": "输入标题", + "contentLabel": "内容", + "contentPlaceholder": "请输入短语内容,支持使用变量,然后按Tab键可以快速定位到变量进行修改。比如:\n帮我规划从${from}到${to}的路线,然后发送到${email}。" + } }, "auth": { "error": "自动获取密钥失败,请手动获取", @@ -1586,7 +1597,10 @@ "titlePlaceholder": "请输入短语标题", "contentPlaceholder": "请输入短语内容,支持使用变量,然后按Tab键可以快速定位到变量进行修改。比如:\n帮我规划从${from}到${to}的路线,然后发送到${email}。", "delete": "删除短语", - "deleteConfirm": "删除短语后将无法恢复,是否继续?" + "deleteConfirm": "删除短语后将无法恢复,是否继续?", + "locationLabel": "添加位置", + "global": "全局短语", + "assistant": "助手短语" }, "quickPanel": { "title": "快捷菜单", diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index cc8d898fa4..6065ab925d 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -81,6 +81,17 @@ "settings.reasoning_effort.medium": "正常思考", "settings.reasoning_effort.default": "預設", "settings.more": "助手設定", + "settings.regular_phrases": { + "title": "常用短语", + "add": "添加短语", + "edit": "編輯短语", + "delete": "刪除短语", + "deleteConfirm": "確定要刪除這個短语嗎?", + "titleLabel": "標題", + "titlePlaceholder": "輸入標題", + "contentLabel": "內容", + "contentPlaceholder": "請輸入短語內容,支持使用變量,然後按Tab鍵可以快速定位到變量進行修改。比如:\n幫我規劃從${from}到${to}的行程,然後發送到${email}。" + }, "settings.knowledge_base.recognition.tip": "智慧代理人將調用大語言模型的意圖識別能力,判斷是否需要調用知識庫進行回答,該功能將依賴模型的能力", "settings.knowledge_base.recognition": "調用知識庫", "settings.knowledge_base.recognition.off": "強制檢索", @@ -1586,7 +1597,10 @@ "titlePlaceholder": "請輸入短語標題", "contentPlaceholder": "請輸入短語內容,支持使用變量,然後按Tab鍵可以快速定位到變量進行修改。比如:\n幫我規劃從${from}到${to}的行程,然後發送到${email}。", "delete": "刪除短語", - "deleteConfirm": "刪除後無法復原,是否繼續?" + "deleteConfirm": "刪除後無法復原,是否繼續?", + "locationLabel": "添加位置", + "global": "全局快速短語", + "assistant": "助手提示詞" }, "quickPanel": { "title": "快捷選單", diff --git a/src/renderer/src/pages/agents/components/ImportAgentPopup.tsx b/src/renderer/src/pages/agents/components/ImportAgentPopup.tsx index f9ed41f0fb..070ed9e51c 100644 --- a/src/renderer/src/pages/agents/components/ImportAgentPopup.tsx +++ b/src/renderer/src/pages/agents/components/ImportAgentPopup.tsx @@ -64,7 +64,8 @@ const PopupContainer: React.FC = ({ resolve }) => { type: 'agent', topics: [], messages: [], - defaultModel: getDefaultModel() + defaultModel: getDefaultModel(), + regularPhrases: agent.regularPhrases || [] } addAgent(newAgent) } diff --git a/src/renderer/src/pages/home/Inputbar/Inputbar.tsx b/src/renderer/src/pages/home/Inputbar/Inputbar.tsx index ace06a98d6..b5fafc827e 100644 --- a/src/renderer/src/pages/home/Inputbar/Inputbar.tsx +++ b/src/renderer/src/pages/home/Inputbar/Inputbar.tsx @@ -970,6 +970,7 @@ const Inputbar: FC = ({ assistant: _assistant, setActiveTopic, topic }) = setInputValue={setText} resizeTextArea={resizeTextArea} ToolbarButton={ToolbarButton} + assistantObj={assistant} /> diff --git a/src/renderer/src/pages/home/Inputbar/QuickPhrasesButton.tsx b/src/renderer/src/pages/home/Inputbar/QuickPhrasesButton.tsx index dd6221d09d..b1f80a0dc5 100644 --- a/src/renderer/src/pages/home/Inputbar/QuickPhrasesButton.tsx +++ b/src/renderer/src/pages/home/Inputbar/QuickPhrasesButton.tsx @@ -1,12 +1,15 @@ import { useQuickPanel } from '@renderer/components/QuickPanel' import { QuickPanelListItem, QuickPanelOpenOptions } from '@renderer/components/QuickPanel/types' +import { useAssistant } from '@renderer/hooks/useAssistant' import QuickPhraseService from '@renderer/services/QuickPhraseService' +import { useAppSelector } from '@renderer/store' import { QuickPhrase } from '@renderer/types' -import { Tooltip } from 'antd' -import { Plus, Zap } from 'lucide-react' +import { Assistant } from '@renderer/types' +import { Input, Modal, Radio, Space, Tooltip } from 'antd' +import { BotMessageSquare, Plus, Zap } from 'lucide-react' import { memo, useCallback, useEffect, useImperativeHandle, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' -import { useNavigate } from 'react-router' +import styled from 'styled-components' export interface QuickPhrasesButtonRef { openQuickPanel: () => void @@ -17,22 +20,37 @@ interface Props { setInputValue: React.Dispatch> resizeTextArea: () => void ToolbarButton: any + assistantObj: Assistant } -const QuickPhrasesButton = ({ ref, setInputValue, resizeTextArea, ToolbarButton }: Props) => { +const QuickPhrasesButton = ({ ref, setInputValue, resizeTextArea, ToolbarButton, assistantObj }: Props) => { const [quickPhrasesList, setQuickPhrasesList] = useState([]) + const [isModalOpen, setIsModalOpen] = useState(false) + const [formData, setFormData] = useState({ title: '', content: '', location: 'global' }) const { t } = useTranslation() const quickPanel = useQuickPanel() + const activeAssistantId = useAppSelector( + (state) => + state.assistants.assistants.find((a) => a.id === assistantObj.id)?.id || state.assistants.defaultAssistant.id + ) + const { assistant, updateAssistant } = useAssistant(activeAssistantId) - const navigate = useNavigate() + const loadQuickListPhrases = useCallback( + async (regularPhrases: QuickPhrase[] = []) => { + const phrases = await QuickPhraseService.getAll() + if (regularPhrases.length) { + setQuickPhrasesList([...regularPhrases, ...phrases]) + return + } + const assistantPrompts = assistant.regularPhrases || [] + setQuickPhrasesList([...assistantPrompts, ...phrases]) + }, + [assistant] + ) useEffect(() => { - const loadQuickListPhrases = async () => { - const phrases = await QuickPhraseService.getAll() - setQuickPhrasesList(phrases.reverse()) - } loadQuickListPhrases() - }, []) + }, [loadQuickListPhrases]) const handlePhraseSelect = useCallback( (phrase: QuickPhrase) => { @@ -56,20 +74,52 @@ const QuickPhrasesButton = ({ ref, setInputValue, resizeTextArea, ToolbarButton [setInputValue, resizeTextArea] ) + const handleModalOk = async () => { + if (!formData.title.trim() || !formData.content.trim()) { + return + } + + const updatedPrompts = [ + ...(assistant.regularPhrases || []), + { + id: crypto.randomUUID(), + title: formData.title, + content: formData.content, + createdAt: Date.now(), + updatedAt: Date.now() + } + ] + if (formData.location === 'assistant') { + // 添加到助手的 regularPhrases + await updateAssistant({ ...assistant, regularPhrases: updatedPrompts }) + } else { + // 添加到全局 Quick Phrases + await QuickPhraseService.add(formData) + } + setIsModalOpen(false) + setFormData({ title: '', content: '', location: 'global' }) + if (formData.location === 'assistant') { + await loadQuickListPhrases(updatedPrompts) + return + } + await loadQuickListPhrases() + } + const phraseItems = useMemo(() => { - const newList: QuickPanelListItem[] = quickPhrasesList.map((phrase) => ({ + const newList: QuickPanelListItem[] = quickPhrasesList.map((phrase, index) => ({ label: phrase.title, description: phrase.content, - icon: , + icon: index < (assistant.regularPhrases?.length || 0) ? : , action: () => handlePhraseSelect(phrase) })) + newList.push({ label: t('settings.quickPhrase.add') + '...', icon: , - action: () => navigate('/settings/quickPhrase') + action: () => setIsModalOpen(true) }) return newList - }, [quickPhrasesList, t, handlePhraseSelect, navigate]) + }, [quickPhrasesList, t, handlePhraseSelect, assistant]) const quickPanelOpenOptions = useMemo( () => ({ @@ -97,12 +147,69 @@ const QuickPhrasesButton = ({ ref, setInputValue, resizeTextArea, ToolbarButton })) return ( - - - - - + <> + + + + + + + { + setIsModalOpen(false) + setFormData({ title: '', content: '', location: 'global' }) + }} + width={520}> + +
+ + setFormData({ ...formData, title: e.target.value })} + /> +
+
+ + setFormData({ ...formData, content: e.target.value })} + rows={6} + style={{ resize: 'none' }} + /> +
+
+ + setFormData({ ...formData, location: e.target.value })}> + + + {t('settings.quickPhrase.global', '全局快速短语')} + + + + {t('settings.quickPhrase.assistant', '助手提示词')} + + +
+
+
+ ) } +const Label = styled.div` + font-size: 14px; + color: var(--color-text); + margin-bottom: 8px; +` + export default memo(QuickPhrasesButton) diff --git a/src/renderer/src/pages/settings/AssistantSettings/AssistantRegularPromptsSettings.tsx b/src/renderer/src/pages/settings/AssistantSettings/AssistantRegularPromptsSettings.tsx new file mode 100644 index 0000000000..b370a638b6 --- /dev/null +++ b/src/renderer/src/pages/settings/AssistantSettings/AssistantRegularPromptsSettings.tsx @@ -0,0 +1,179 @@ +import { DeleteOutlined, EditOutlined, ExclamationCircleOutlined, PlusOutlined } from '@ant-design/icons' +import DragableList from '@renderer/components/DragableList' +import FileItem from '@renderer/pages/files/FileItem' +import { Assistant, QuickPhrase } from '@renderer/types' +import { Button, Flex, Input, Modal, Popconfirm, Space } from 'antd' +import { FC, useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' +import styled from 'styled-components' +import { v4 as uuidv4 } from 'uuid' + +import { SettingContainer, SettingDivider, SettingGroup, SettingRow, SettingTitle } from '..' + +const { TextArea } = Input + +interface AssistantRegularPromptsSettingsProps { + assistant: Assistant + updateAssistant: (assistant: Assistant) => void +} + +const AssistantRegularPromptsSettings: FC = ({ assistant, updateAssistant }) => { + const { t } = useTranslation() + const [promptsList, setPromptsList] = useState([]) + const [isModalOpen, setIsModalOpen] = useState(false) + const [editingPrompt, setEditingPrompt] = useState(null) + const [formData, setFormData] = useState({ title: '', content: '' }) + const [dragging, setDragging] = useState(false) + + useEffect(() => { + setPromptsList(assistant.regularPhrases || []) + }, [assistant.regularPhrases]) + + const handleAdd = () => { + setEditingPrompt(null) + setFormData({ title: '', content: '' }) + setIsModalOpen(true) + } + + const handleEdit = (prompt: QuickPhrase) => { + setEditingPrompt(prompt) + setFormData({ title: prompt.title, content: prompt.content }) + setIsModalOpen(true) + } + + const handleDelete = async (id: string) => { + const updatedPrompts = promptsList.filter((prompt) => prompt.id !== id) + setPromptsList(updatedPrompts) + updateAssistant({ ...assistant, regularPhrases: updatedPrompts }) + } + + const handleModalOk = async () => { + if (!formData.title.trim() || !formData.content.trim()) { + return + } + + let updatedPrompts: QuickPhrase[] + if (editingPrompt) { + updatedPrompts = promptsList.map((prompt) => + prompt.id === editingPrompt.id ? { ...prompt, ...formData } : prompt + ) + } else { + const newPrompt: QuickPhrase = { + id: uuidv4(), + createdAt: Date.now(), + updatedAt: Date.now(), + ...formData + } + updatedPrompts = [...promptsList, newPrompt] + } + setPromptsList(updatedPrompts) + updateAssistant({ ...assistant, regularPhrases: updatedPrompts }) + setIsModalOpen(false) + } + + const handleUpdateOrder = async (newPrompts: QuickPhrase[]) => { + setPromptsList(newPrompts) + updateAssistant({ ...assistant, regularPhrases: newPrompts }) + } + + const reversedPrompts = [...promptsList].reverse() + + return ( + + + + {t('assistants.settings.regular_phrases.title', 'Regular Prompts')} +