mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-24 18:50:56 +08:00
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 <georgezhao@SKJLAB>
This commit is contained in:
parent
cf9aaf3ecf
commit
4de06e7184
@ -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",
|
||||
|
||||
@ -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": "クイックメニュー",
|
||||
|
||||
@ -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": "Быстрое меню",
|
||||
|
||||
@ -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": "快捷菜单",
|
||||
|
||||
@ -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": "快捷選單",
|
||||
|
||||
@ -64,7 +64,8 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
|
||||
type: 'agent',
|
||||
topics: [],
|
||||
messages: [],
|
||||
defaultModel: getDefaultModel()
|
||||
defaultModel: getDefaultModel(),
|
||||
regularPhrases: agent.regularPhrases || []
|
||||
}
|
||||
addAgent(newAgent)
|
||||
}
|
||||
|
||||
@ -970,6 +970,7 @@ const Inputbar: FC<Props> = ({ assistant: _assistant, setActiveTopic, topic }) =
|
||||
setInputValue={setText}
|
||||
resizeTextArea={resizeTextArea}
|
||||
ToolbarButton={ToolbarButton}
|
||||
assistantObj={assistant}
|
||||
/>
|
||||
<Tooltip placement="top" title={t('chat.input.clear', { Command: cleanTopicShortcut })} arrow>
|
||||
<ToolbarButton type="text" onClick={clearTopic}>
|
||||
|
||||
@ -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<React.SetStateAction<string>>
|
||||
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<QuickPhrase[]>([])
|
||||
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: <Zap />,
|
||||
icon: index < (assistant.regularPhrases?.length || 0) ? <BotMessageSquare /> : <Zap />,
|
||||
action: () => handlePhraseSelect(phrase)
|
||||
}))
|
||||
|
||||
newList.push({
|
||||
label: t('settings.quickPhrase.add') + '...',
|
||||
icon: <Plus />,
|
||||
action: () => navigate('/settings/quickPhrase')
|
||||
action: () => setIsModalOpen(true)
|
||||
})
|
||||
return newList
|
||||
}, [quickPhrasesList, t, handlePhraseSelect, navigate])
|
||||
}, [quickPhrasesList, t, handlePhraseSelect, assistant])
|
||||
|
||||
const quickPanelOpenOptions = useMemo<QuickPanelOpenOptions>(
|
||||
() => ({
|
||||
@ -97,12 +147,69 @@ const QuickPhrasesButton = ({ ref, setInputValue, resizeTextArea, ToolbarButton
|
||||
}))
|
||||
|
||||
return (
|
||||
<Tooltip placement="top" title={t('settings.quickPhrase.title')} arrow>
|
||||
<ToolbarButton type="text" onClick={handleOpenQuickPanel}>
|
||||
<Zap size={18} />
|
||||
</ToolbarButton>
|
||||
</Tooltip>
|
||||
<>
|
||||
<Tooltip placement="top" title={t('settings.quickPhrase.title')} arrow>
|
||||
<ToolbarButton type="text" onClick={handleOpenQuickPanel}>
|
||||
<Zap size={18} />
|
||||
</ToolbarButton>
|
||||
</Tooltip>
|
||||
|
||||
<Modal
|
||||
title={t('settings.quickPhrase.add')}
|
||||
open={isModalOpen}
|
||||
onOk={handleModalOk}
|
||||
onCancel={() => {
|
||||
setIsModalOpen(false)
|
||||
setFormData({ title: '', content: '', location: 'global' })
|
||||
}}
|
||||
width={520}>
|
||||
<Space direction="vertical" style={{ width: '100%' }} size="middle">
|
||||
<div>
|
||||
<Label>{t('settings.quickPhrase.titleLabel')}</Label>
|
||||
<Input
|
||||
placeholder={t('settings.quickPhrase.titlePlaceholder')}
|
||||
value={formData.title}
|
||||
onChange={(e) => setFormData({ ...formData, title: e.target.value })}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label>{t('settings.quickPhrase.contentLabel')}</Label>
|
||||
<Input.TextArea
|
||||
placeholder={t('settings.quickPhrase.contentPlaceholder')}
|
||||
value={formData.content}
|
||||
onChange={(e) => setFormData({ ...formData, content: e.target.value })}
|
||||
rows={6}
|
||||
style={{ resize: 'none' }}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label>{t('settings.quickPhrase.locationLabel', '添加位置')}</Label>
|
||||
<Radio.Group
|
||||
value={formData.location}
|
||||
onChange={(e) => setFormData({ ...formData, location: e.target.value })}>
|
||||
<Radio value="global">
|
||||
<Zap size={20} style={{ paddingRight: '4px', verticalAlign: 'middle', paddingBottom: '3px' }} />
|
||||
{t('settings.quickPhrase.global', '全局快速短语')}
|
||||
</Radio>
|
||||
<Radio value="assistant">
|
||||
<BotMessageSquare
|
||||
size={20}
|
||||
style={{ paddingRight: '4px', verticalAlign: 'middle', paddingBottom: '3px' }}
|
||||
/>
|
||||
{t('settings.quickPhrase.assistant', '助手提示词')}
|
||||
</Radio>
|
||||
</Radio.Group>
|
||||
</div>
|
||||
</Space>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const Label = styled.div`
|
||||
font-size: 14px;
|
||||
color: var(--color-text);
|
||||
margin-bottom: 8px;
|
||||
`
|
||||
|
||||
export default memo(QuickPhrasesButton)
|
||||
|
||||
@ -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<AssistantRegularPromptsSettingsProps> = ({ assistant, updateAssistant }) => {
|
||||
const { t } = useTranslation()
|
||||
const [promptsList, setPromptsList] = useState<QuickPhrase[]>([])
|
||||
const [isModalOpen, setIsModalOpen] = useState(false)
|
||||
const [editingPrompt, setEditingPrompt] = useState<QuickPhrase | null>(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 (
|
||||
<SettingContainer style={{ padding: 0, background: '#0000' }}>
|
||||
<SettingGroup style={{ marginBottom: 0, padding: 0, border: 'none' }}>
|
||||
<SettingTitle>
|
||||
{t('assistants.settings.regular_phrases.title', 'Regular Prompts')}
|
||||
<Button type="text" icon={<PlusOutlined />} onClick={handleAdd} />
|
||||
</SettingTitle>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<StyledPromptList>
|
||||
<DragableList
|
||||
list={reversedPrompts}
|
||||
onUpdate={(newPrompts) => handleUpdateOrder([...newPrompts].reverse())}
|
||||
style={{ paddingBottom: dragging ? '34px' : 0 }}
|
||||
onDragStart={() => setDragging(true)}
|
||||
onDragEnd={() => setDragging(false)}>
|
||||
{(prompt) => (
|
||||
<FileItem
|
||||
key={prompt.id}
|
||||
fileInfo={{
|
||||
name: prompt.title,
|
||||
ext: '.txt',
|
||||
extra: prompt.content,
|
||||
actions: (
|
||||
<Flex gap={4} style={{ opacity: 0.6 }}>
|
||||
<Button key="edit" type="text" icon={<EditOutlined />} onClick={() => handleEdit(prompt)} />
|
||||
<Popconfirm
|
||||
title={t('assistants.settings.regular_phrases.delete', 'Delete Prompt')}
|
||||
description={t(
|
||||
'assistants.settings.regular_phrases.deleteConfirm',
|
||||
'Are you sure to delete this prompt?'
|
||||
)}
|
||||
okText={t('common.confirm')}
|
||||
cancelText={t('common.cancel')}
|
||||
onConfirm={() => handleDelete(prompt.id)}
|
||||
icon={<ExclamationCircleOutlined style={{ color: 'red' }} />}>
|
||||
<Button key="delete" type="text" danger icon={<DeleteOutlined />} />
|
||||
</Popconfirm>
|
||||
</Flex>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</DragableList>
|
||||
</StyledPromptList>
|
||||
</SettingRow>
|
||||
</SettingGroup>
|
||||
|
||||
<Modal
|
||||
title={
|
||||
editingPrompt
|
||||
? t('assistants.settings.regular_phrases.edit', 'Edit Prompt')
|
||||
: t('assistants.settings.regular_phrases.add', 'Add Prompt')
|
||||
}
|
||||
open={isModalOpen}
|
||||
onOk={handleModalOk}
|
||||
onCancel={() => setIsModalOpen(false)}
|
||||
width={520}>
|
||||
<Space direction="vertical" style={{ width: '100%' }} size="middle">
|
||||
<div>
|
||||
<Label>{t('assistants.settings.regular_phrases.titleLabel', 'Title')}</Label>
|
||||
<Input
|
||||
placeholder={t('assistants.settings.regular_phrases.titlePlaceholder', 'Enter title')}
|
||||
value={formData.title}
|
||||
onChange={(e) => setFormData({ ...formData, title: e.target.value })}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label>{t('assistants.settings.regular_phrases.contentLabel', 'Content')}</Label>
|
||||
<TextArea
|
||||
placeholder={t('assistants.settings.regular_phrases.contentPlaceholder', 'Enter content')}
|
||||
value={formData.content}
|
||||
onChange={(e) => setFormData({ ...formData, content: e.target.value })}
|
||||
rows={6}
|
||||
style={{ resize: 'none' }}
|
||||
/>
|
||||
</div>
|
||||
</Space>
|
||||
</Modal>
|
||||
</SettingContainer>
|
||||
)
|
||||
}
|
||||
|
||||
const Label = styled.div`
|
||||
font-size: 14px;
|
||||
color: var(--color-text);
|
||||
margin-bottom: 8px;
|
||||
`
|
||||
|
||||
const StyledPromptList = styled.div`
|
||||
width: 100%;
|
||||
height: calc(100vh - 162px); // Adjusted height to match other settings pages
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
`
|
||||
|
||||
export default AssistantRegularPromptsSettings
|
||||
@ -14,13 +14,14 @@ import AssistantMCPSettings from './AssistantMCPSettings'
|
||||
import AssistantMessagesSettings from './AssistantMessagesSettings'
|
||||
import AssistantModelSettings from './AssistantModelSettings'
|
||||
import AssistantPromptSettings from './AssistantPromptSettings'
|
||||
import AssistantRegularPromptsSettings from './AssistantRegularPromptsSettings'
|
||||
|
||||
interface AssistantSettingPopupShowParams {
|
||||
assistant: Assistant
|
||||
tab?: AssistantSettingPopupTab
|
||||
}
|
||||
|
||||
type AssistantSettingPopupTab = 'prompt' | 'model' | 'messages' | 'knowledge_base' | 'mcp'
|
||||
type AssistantSettingPopupTab = 'prompt' | 'model' | 'messages' | 'knowledge_base' | 'mcp' | 'regular_phrases'
|
||||
|
||||
interface Props extends AssistantSettingPopupShowParams {
|
||||
resolve: (assistant: Assistant) => void
|
||||
@ -73,6 +74,10 @@ const AssistantSettingPopupContainer: React.FC<Props> = ({ resolve, tab, ...prop
|
||||
{
|
||||
key: 'mcp',
|
||||
label: t('assistants.settings.mcp')
|
||||
},
|
||||
{
|
||||
key: 'regular_phrases',
|
||||
label: t('assistants.settings.regular_phrases.title', 'Regular Prompts')
|
||||
}
|
||||
].filter(Boolean) as { key: string; label: string }[]
|
||||
|
||||
@ -142,6 +147,9 @@ const AssistantSettingPopupContainer: React.FC<Props> = ({ resolve, tab, ...prop
|
||||
updateAssistantSettings={updateAssistantSettings}
|
||||
/>
|
||||
)}
|
||||
{menu === 'regular_phrases' && (
|
||||
<AssistantRegularPromptsSettings assistant={assistant} updateAssistant={updateAssistant} />
|
||||
)}
|
||||
</Settings>
|
||||
</HStack>
|
||||
</StyledModal>
|
||||
|
||||
@ -17,7 +17,8 @@ export function getDefaultAssistant(): Assistant {
|
||||
prompt: '',
|
||||
topics: [getDefaultTopic('default')],
|
||||
messages: [],
|
||||
type: 'assistant'
|
||||
type: 'assistant',
|
||||
regularPhrases: [] // Added regularPhrases
|
||||
}
|
||||
}
|
||||
|
||||
@ -171,7 +172,8 @@ export async function createAssistantFromAgent(agent: Agent) {
|
||||
emoji: agent.emoji,
|
||||
topics: [topic],
|
||||
model: agent.defaultModel,
|
||||
type: 'assistant'
|
||||
type: 'assistant',
|
||||
regularPhrases: agent.regularPhrases || [] // Ensured regularPhrases
|
||||
}
|
||||
|
||||
store.dispatch(addAssistant(assistant))
|
||||
|
||||
@ -25,6 +25,7 @@ export type Assistant = {
|
||||
enableGenerateImage?: boolean
|
||||
mcpServers?: MCPServer[]
|
||||
knowledgeRecognition?: 'off' | 'on'
|
||||
regularPhrases?: QuickPhrase[] // Added for regular phrase
|
||||
}
|
||||
|
||||
export type AssistantMessage = {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user