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:
George Zhao 2025-05-10 21:57:57 +08:00 committed by GitHub
parent cf9aaf3ecf
commit 4de06e7184
12 changed files with 400 additions and 31 deletions

View File

@ -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",

View File

@ -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": "クイックメニュー",

View File

@ -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": "Быстрое меню",

View File

@ -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": "快捷菜单",

View File

@ -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": "快捷選單",

View File

@ -64,7 +64,8 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
type: 'agent',
topics: [],
messages: [],
defaultModel: getDefaultModel()
defaultModel: getDefaultModel(),
regularPhrases: agent.regularPhrases || []
}
addAgent(newAgent)
}

View File

@ -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}>

View File

@ -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)

View File

@ -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

View File

@ -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>

View File

@ -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))

View File

@ -25,6 +25,7 @@ export type Assistant = {
enableGenerateImage?: boolean
mcpServers?: MCPServer[]
knowledgeRecognition?: 'off' | 'on'
regularPhrases?: QuickPhrase[] // Added for regular phrase
}
export type AssistantMessage = {