refactor(assistants): rename agents to assistants and update related components

- Updated references from 'agents' to 'assistants' across various components and hooks.
- Changed i18n keys to reflect the new terminology for better clarity.
- Removed the deprecated agents slice and integrated its functionality into the assistants slice.
- Adjusted UI components to align with the new naming conventions for assistant presets.
This commit is contained in:
kangfenmao 2025-09-26 13:45:02 +08:00
parent 4b1f7db506
commit 0648a1f567
34 changed files with 395 additions and 387 deletions

View File

@ -8,7 +8,6 @@ import { ErrorBoundary } from './components/ErrorBoundary'
import TabsContainer from './components/Tab/TabContainer'
import NavigationHandler from './handler/NavigationHandler'
import { useNavbarPosition } from './hooks/useSettings'
import AssistantPresetsPage from './pages/assistantPresets/AssistantPresetsPage'
import CodeToolsPage from './pages/code/CodeToolsPage'
import FilesPage from './pages/files/FilesPage'
import HomePage from './pages/home/HomePage'
@ -19,6 +18,7 @@ import MinAppsPage from './pages/minapps/MinAppsPage'
import NotesPage from './pages/notes/NotesPage'
import PaintingsRoutePage from './pages/paintings/PaintingsRoutePage'
import SettingsPage from './pages/settings/SettingsPage'
import AssistantPresetsPage from './pages/store/assistants/presets/AssistantPresetsPage'
import TranslatePage from './pages/translate/TranslatePage'
const Router: FC = () => {
@ -29,7 +29,7 @@ const Router: FC = () => {
<ErrorBoundary>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/assistantPresets" element={<AssistantPresetsPage />} />
<Route path="/store" element={<AssistantPresetsPage />} />
<Route path="/paintings/*" element={<PaintingsRoutePage />} />
<Route path="/translate" element={<TranslatePage />} />
<Route path="/files" element={<FilesPage />} />

View File

@ -2,7 +2,7 @@ import { TopView } from '@renderer/components/TopView'
import { useAssistants, useDefaultAssistant } from '@renderer/hooks/useAssistant'
import { useAssistantPresets } from '@renderer/hooks/useAssistantPresets'
import { useTimer } from '@renderer/hooks/useTimer'
import { useSystemAssistantPresets } from '@renderer/pages/assistantPresets'
import { useSystemAssistantPresets } from '@renderer/pages/store/assistants/presets'
import { createAssistantFromAgent } from '@renderer/services/AssistantService'
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
import { Assistant, AssistantPreset } from '@renderer/types'
@ -203,9 +203,9 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
<EmojiIcon emoji={preset.emoji || ''} />
<span className="text-nowrap">{preset.name}</span>
</HStack>
{preset.id === 'default' && <Tag color="green">{t('agents.tag.system')}</Tag>}
{preset.type === 'agent' && <Tag color="orange">{t('agents.tag.agent')}</Tag>}
{preset.id === 'new' && <Tag color="green">{t('agents.tag.new')}</Tag>}
{preset.id === 'default' && <Tag color="green">{t('assistants.presets.tag.system')}</Tag>}
{preset.type === 'agent' && <Tag color="orange">{t('assistants.presets.tag.agent')}</Tag>}
{preset.id === 'new' && <Tag color="green">{t('assistants.presets.tag.new')}</Tag>}
</AgentItem>
))}
</Container>

View File

@ -86,7 +86,7 @@ const getTabIcon = (
switch (tabId) {
case 'home':
return <Home size={14} />
case 'assistantPresets':
case 'store':
return <Sparkle size={14} />
case 'translate':
return <Languages size={14} />

View File

@ -132,7 +132,7 @@ const MainMenus: FC = () => {
const iconMap = {
assistants: <MessageSquare size={18} className="icon" />,
agents: <Sparkle size={18} className="icon" />,
store: <Sparkle size={18} className="icon" />,
paintings: <Palette size={18} className="icon" />,
translate: <Languages size={18} className="icon" />,
minapp: <LayoutGrid size={18} className="icon" />,
@ -144,7 +144,7 @@ const MainMenus: FC = () => {
const pathMap = {
assistants: '/',
agents: '/agents',
store: '/store',
paintings: `/paintings/${defaultPaintingProvider}`,
translate: '/translate',
minapp: '/apps',

View File

@ -6,7 +6,7 @@ import { SidebarIcon } from '@renderer/types'
*/
export const DEFAULT_SIDEBAR_ICONS: SidebarIcon[] = [
'assistants',
'agents',
'store',
'paintings',
'translate',
'minapp',

View File

@ -5,11 +5,11 @@ import {
setAssistantPresets,
updateAssistantPreset,
updateAssistantPresetSettings
} from '@renderer/store/agents'
} from '@renderer/store/assistants'
import { AssistantPreset, AssistantSettings } from '@renderer/types'
export function useAssistantPresets() {
const presets = useAppSelector((state) => state.agents.agents)
const presets = useAppSelector((state) => state.assistants.presets)
const dispatch = useAppDispatch()
return {
@ -22,7 +22,7 @@ export function useAssistantPresets() {
export function useAssistantPreset(id: string) {
// FIXME: undefined is not handled
const preset = useAppSelector((state) => state.agents.agents.find((a) => a.id === id) as AssistantPreset)
const preset = useAppSelector((state) => state.assistants.presets.find((a) => a.id === id) as AssistantPreset)
const dispatch = useAppDispatch()
return {

View File

@ -133,7 +133,7 @@ export const getRestoreProgressLabel = (key: string): string => {
const titleKeyMap = {
// TODO: update i18n key
assistantPresets: 'title.agents',
store: 'title.store',
apps: 'title.apps',
code: 'title.code',
files: 'title.files',

View File

@ -194,83 +194,6 @@
"enable_server": "Enable API Server to use agents."
}
},
"agents": {
"add": {
"button": "Add to Assistant",
"knowledge_base": {
"label": "Knowledge Base",
"placeholder": "Select Knowledge Base"
},
"name": {
"label": "Name",
"placeholder": "Enter name"
},
"prompt": {
"label": "Prompt",
"placeholder": "Enter prompt",
"variables": {
"tip": {
"content": "{{date}}:\tDate\n{{time}}:\tTime\n{{datetime}}:\tDate and time\n{{system}}:\tOperating system\n{{arch}}:\tCPU architecture\n{{language}}:\tLanguage\n{{model_name}}:\tModel name\n{{username}}:\tUsername",
"title": "Available variables"
}
}
},
"title": "Create Agent",
"unsaved_changes_warning": "You have unsaved changes. Are you sure you want to close?"
},
"delete": {
"popup": {
"content": "Are you sure you want to delete this agent?"
}
},
"edit": {
"model": {
"select": {
"title": "Select Model"
}
},
"title": "Edit Agent"
},
"export": {
"agent": "Export Agent"
},
"import": {
"button": "Import",
"error": {
"fetch_failed": "Failed to fetch from URL",
"invalid_format": "Invalid agent format: missing required fields",
"url_required": "Please enter a URL"
},
"file_filter": "JSON Files",
"select_file": "Select File",
"title": "Import from External",
"type": {
"file": "File",
"url": "URL"
},
"url_placeholder": "Enter JSON URL"
},
"manage": {
"title": "Manage Agents"
},
"my_agents": "My Agents",
"search": {
"no_results": "No results found"
},
"settings": {
"title": "Agent Setting"
},
"sorting": {
"title": "Sorting"
},
"tag": {
"agent": "Agent",
"default": "Default",
"new": "New",
"system": "System"
},
"title": "Agents"
},
"apiServer": {
"actions": {
"copy": "Copy",
@ -350,9 +273,86 @@
"showByList": "List View",
"showByTags": "Tag View"
},
"presets": {
"add": {
"button": "Add to Assistant",
"knowledge_base": {
"label": "Knowledge Base",
"placeholder": "Select Knowledge Base"
},
"name": {
"label": "Name",
"placeholder": "Enter name"
},
"prompt": {
"label": "Prompt",
"placeholder": "Enter prompt",
"variables": {
"tip": {
"content": "{{date}}:\tDate\n{{time}}:\tTime\n{{datetime}}:\tDate and time\n{{system}}:\tOperating system\n{{arch}}:\tCPU architecture\n{{language}}:\tLanguage\n{{model_name}}:\tModel name\n{{username}}:\tUsername",
"title": "Available variables"
}
}
},
"title": "Create Assistant",
"unsaved_changes_warning": "You have unsaved changes. Are you sure you want to close?"
},
"delete": {
"popup": {
"content": "Are you sure you want to delete this assistant?"
}
},
"edit": {
"model": {
"select": {
"title": "Select Model"
}
},
"title": "Edit Assistant"
},
"export": {
"agent": "Export Assistant"
},
"import": {
"button": "Import",
"error": {
"fetch_failed": "Failed to fetch from URL",
"invalid_format": "Invalid assistant format: missing required fields",
"url_required": "Please enter a URL"
},
"file_filter": "JSON Files",
"select_file": "Select File",
"title": "Import from External",
"type": {
"file": "File",
"url": "URL"
},
"url_placeholder": "Enter JSON URL"
},
"manage": {
"title": "Manage Assistants"
},
"my_agents": "My Assistants",
"search": {
"no_results": "No results found"
},
"settings": {
"title": "Assistant Setting"
},
"sorting": {
"title": "Sorting"
},
"tag": {
"agent": "Assistant",
"default": "Default",
"new": "New",
"system": "System"
},
"title": "Assistants Library"
},
"save": {
"success": "Saved successfully",
"title": "Save to agent"
"title": "Save to assistant library"
},
"search": "Search assistants...",
"settings": {
@ -4398,7 +4398,6 @@
}
},
"title": {
"agents": "Agents",
"apps": "Apps",
"code": "Code",
"files": "Files",
@ -4410,6 +4409,7 @@
"notes": "Notes",
"paintings": "Paintings",
"settings": "Settings",
"store": "Assistant Library",
"translate": "Translate"
},
"trace": {

View File

@ -194,83 +194,6 @@
"enable_server": "请启用 API 服务器以使用智能体功能"
}
},
"agents": {
"add": {
"button": "添加到助手",
"knowledge_base": {
"label": "知识库",
"placeholder": "选择知识库"
},
"name": {
"label": "名称",
"placeholder": "输入名称"
},
"prompt": {
"label": "提示词",
"placeholder": "输入提示词",
"variables": {
"tip": {
"content": "{{date}}:\t日期\n{{time}}:\t时间\n{{datetime}}:\t日期和时间\n{{system}}:\t操作系统\n{{arch}}:\tCPU 架构\n{{language}}:\t语言\n{{model_name}}:\t模型名称\n{{username}}:\t用户名",
"title": "可用的变量"
}
}
},
"title": "创建智能体",
"unsaved_changes_warning": "你有未保存的内容,确定要关闭吗?"
},
"delete": {
"popup": {
"content": "确定要删除此智能体吗?"
}
},
"edit": {
"model": {
"select": {
"title": "选择模型"
}
},
"title": "编辑智能体"
},
"export": {
"agent": "导出智能体"
},
"import": {
"button": "导入",
"error": {
"fetch_failed": "从 URL 获取数据失败",
"invalid_format": "无效的代理格式:缺少必填字段",
"url_required": "请输入 URL"
},
"file_filter": "JSON 文件",
"select_file": "选择文件",
"title": "从外部导入",
"type": {
"file": "文件",
"url": "URL"
},
"url_placeholder": "输入 JSON URL"
},
"manage": {
"title": "管理智能体"
},
"my_agents": "我的智能体",
"search": {
"no_results": "没有找到相关智能体"
},
"settings": {
"title": "智能体配置"
},
"sorting": {
"title": "排序"
},
"tag": {
"agent": "智能体",
"default": "默认",
"new": "新建",
"system": "系统"
},
"title": "智能体"
},
"apiServer": {
"actions": {
"copy": "复制",
@ -350,9 +273,86 @@
"showByList": "列表展示",
"showByTags": "标签展示"
},
"presets": {
"add": {
"button": "添加到助手",
"knowledge_base": {
"label": "知识库",
"placeholder": "选择知识库"
},
"name": {
"label": "名称",
"placeholder": "输入名称"
},
"prompt": {
"label": "提示词",
"placeholder": "输入提示词",
"variables": {
"tip": {
"content": "{{date}}:\t日期\n{{time}}:\t时间\n{{datetime}}:\t日期和时间\n{{system}}:\t操作系统\n{{arch}}:\tCPU 架构\n{{language}}:\t语言\n{{model_name}}:\t模型名称\n{{username}}:\t用户名",
"title": "可用的变量"
}
}
},
"title": "创建助手",
"unsaved_changes_warning": "你有未保存的内容,确定要关闭吗?"
},
"delete": {
"popup": {
"content": "确定要删除此助手吗?"
}
},
"edit": {
"model": {
"select": {
"title": "选择模型"
}
},
"title": "编辑助手"
},
"export": {
"agent": "导出助手"
},
"import": {
"button": "导入",
"error": {
"fetch_failed": "从 URL 获取数据失败",
"invalid_format": "无效的助手格式:缺少必填字段",
"url_required": "请输入 URL"
},
"file_filter": "JSON 文件",
"select_file": "选择文件",
"title": "从外部导入",
"type": {
"file": "文件",
"url": "URL"
},
"url_placeholder": "输入 JSON URL"
},
"manage": {
"title": "管理助手"
},
"my_agents": "我的助手",
"search": {
"no_results": "没有找到相关助手"
},
"settings": {
"title": "助手配置"
},
"sorting": {
"title": "排序"
},
"tag": {
"agent": "助手",
"default": "默认",
"new": "新建",
"system": "系统"
},
"title": "助手库"
},
"save": {
"success": "保存成功",
"title": "保存到智能体"
"title": "保存到助手库"
},
"search": "搜索助手",
"settings": {
@ -363,7 +363,7 @@
"label": "调用知识库",
"off": "强制检索",
"on": "意图识别",
"tip": "智能体将调用大模型的意图识别能力,判断是否需要调用知识库进行回答,该功能将依赖模型的能力"
"tip": "助手将调用大模型的意图识别能力,判断是否需要调用知识库进行回答,该功能将依赖模型的能力"
}
},
"mcp": {
@ -4398,7 +4398,6 @@
}
},
"title": {
"agents": "智能体",
"apps": "小程序",
"code": "Code",
"files": "文件",
@ -4410,6 +4409,7 @@
"notes": "笔记",
"paintings": "绘画",
"settings": "设置",
"store": "助手库",
"translate": "翻译"
},
"trace": {

View File

@ -194,83 +194,6 @@
"enable_server": "啟用 API 伺服器以使用代理程式。"
}
},
"agents": {
"add": {
"button": "新增到助手",
"knowledge_base": {
"label": "知識庫",
"placeholder": "選擇知識庫"
},
"name": {
"label": "名稱",
"placeholder": "輸入名稱"
},
"prompt": {
"label": "提示詞",
"placeholder": "輸入提示詞",
"variables": {
"tip": {
"content": "{{date}}:\t日期\n{{time}}:\t時間\n{{datetime}}:\t日期和時間\n{{system}}:\t作業系統\n{{arch}}:\tCPU 架構\n{{language}}:\t語言\n{{model_name}}:\t模型名稱\n{{username}}:\t使用者名稱",
"title": "可用的變數"
}
}
},
"title": "建立智慧代理人",
"unsaved_changes_warning": "有未保存的變更,確定要關閉嗎?"
},
"delete": {
"popup": {
"content": "確定要刪除此智慧代理人嗎?"
}
},
"edit": {
"model": {
"select": {
"title": "選擇模型"
}
},
"title": "編輯智慧代理人"
},
"export": {
"agent": "匯出智慧代理人"
},
"import": {
"button": "導入",
"error": {
"fetch_failed": "從 URL 獲取資料失敗",
"invalid_format": "無效的代理人格式:缺少必填欄位",
"url_required": "請輸入 URL"
},
"file_filter": "JSON 檔案",
"select_file": "選擇檔案",
"title": "從外部導入",
"type": {
"file": "檔案",
"url": "URL"
},
"url_placeholder": "輸入 JSON URL"
},
"manage": {
"title": "管理智慧代理人"
},
"my_agents": "我的智慧代理人",
"search": {
"no_results": "沒有找到相關智慧代理人"
},
"settings": {
"title": "智慧代理人設定"
},
"sorting": {
"title": "排序"
},
"tag": {
"agent": "智慧代理人",
"default": "預設",
"new": "新增",
"system": "系統"
},
"title": "智慧代理人"
},
"apiServer": {
"actions": {
"copy": "複製",
@ -350,9 +273,86 @@
"showByList": "列表展示",
"showByTags": "標籤展示"
},
"presets": {
"add": {
"button": "新增到助手",
"knowledge_base": {
"label": "知識庫",
"placeholder": "選擇知識庫"
},
"name": {
"label": "名稱",
"placeholder": "輸入名稱"
},
"prompt": {
"label": "提示詞",
"placeholder": "輸入提示詞",
"variables": {
"tip": {
"content": "{{date}}:\t日期\n{{time}}:\t時間\n{{datetime}}:\t日期和時間\n{{system}}:\t作業系統\n{{arch}}:\tCPU 架構\n{{language}}:\t語言\n{{model_name}}:\t模型名稱\n{{username}}:\t使用者名稱",
"title": "可用的變數"
}
}
},
"title": "建立助手",
"unsaved_changes_warning": "有未保存的變更,確定要關閉嗎?"
},
"delete": {
"popup": {
"content": "確定要刪除此助手嗎?"
}
},
"edit": {
"model": {
"select": {
"title": "選擇模型"
}
},
"title": "編輯助手"
},
"export": {
"agent": "匯出助手"
},
"import": {
"button": "導入",
"error": {
"fetch_failed": "從 URL 獲取資料失敗",
"invalid_format": "無效的助手格式:缺少必填欄位",
"url_required": "請輸入 URL"
},
"file_filter": "JSON 檔案",
"select_file": "選擇檔案",
"title": "從外部導入",
"type": {
"file": "檔案",
"url": "URL"
},
"url_placeholder": "輸入 JSON URL"
},
"manage": {
"title": "管理助手"
},
"my_agents": "我的助手",
"search": {
"no_results": "沒有找到相關助手"
},
"settings": {
"title": "助手配置"
},
"sorting": {
"title": "排序"
},
"tag": {
"agent": "助手",
"default": "預設",
"new": "新增",
"system": "系統"
},
"title": "助手庫"
},
"save": {
"success": "儲存成功",
"title": "儲存到智慧代理人"
"title": "儲存到助手庫"
},
"search": "搜尋助手...",
"settings": {
@ -363,7 +363,7 @@
"label": "調用知識庫",
"off": "強制檢索",
"on": "意圖識別",
"tip": "智慧代理人將調用大語言模型的意圖識別能力,判斷是否需要調用知識庫進行回答,該功能將依賴模型的能力"
"tip": "助手將調用大語言模型的意圖識別能力,判斷是否需要調用知識庫進行回答,該功能將依賴模型的能力"
}
},
"mcp": {
@ -4398,7 +4398,6 @@
}
},
"title": {
"agents": "智能體",
"apps": "小程序",
"code": "Code",
"files": "文件",
@ -4410,6 +4409,7 @@
"notes": "筆記",
"paintings": "繪畫",
"settings": "設定",
"store": "助手庫",
"translate": "翻譯"
},
"trace": {

View File

@ -218,7 +218,7 @@ const InputbarTools = ({
}
},
{
label: t('agents.edit.model.select.title'),
label: t('assistants.presets.edit.model.select.title'),
description: '',
icon: <AtSign />,
isMenu: true,
@ -421,7 +421,7 @@ const InputbarTools = ({
},
{
key: 'mention_models',
label: t('agents.edit.model.select.title'),
label: t('assistants.presets.edit.model.select.title'),
component: (
<MentionModelsButton
ref={mentionModelsButtonRef}

View File

@ -238,7 +238,7 @@ const MentionModelsButton: FC<Props> = ({
triggerInfoRef.current = triggerInfo
quickPanel.open({
title: t('agents.edit.model.select.title'),
title: t('assistants.presets.edit.model.select.title'),
list: modelItems,
symbol: QuickPanelReservedSymbol.MentionModels,
multiple: true,
@ -304,7 +304,7 @@ const MentionModelsButton: FC<Props> = ({
}))
return (
<Tooltip placement="top" title={t('agents.edit.model.select.title')} mouseLeaveDelay={0} arrow>
<Tooltip placement="top" title={t('assistants.presets.edit.model.select.title')} mouseLeaveDelay={0} arrow>
<ActionIconButton onClick={handleOpenQuickPanel} active={mentionedModels.length > 0}>
<AtSign size={18} />
</ActionIconButton>

View File

@ -36,8 +36,8 @@ const LaunchpadPage: FC = () => {
},
{
icon: <Sparkle size={32} className="icon" />,
text: t('title.agents'),
path: '/assistantPresets',
text: t('title.store'),
path: '/store',
bgColor: 'linear-gradient(135deg, #6366F1, #4F46E5)' // AI助手靛蓝渐变代表智能和科技
},
{

View File

@ -87,7 +87,7 @@ const NotesSidebarHeader: FC<NotesSidebarHeaderProps> = ({
onClick: handleSortMenuClick
}}
trigger={['click']}>
<Tooltip title={t('agents.sorting.title')} mouseEnterDelay={0.8}>
<Tooltip title={t('assistants.presets.sorting.title')} mouseEnterDelay={0.8}>
<ActionButton>
<ArrowUpNarrowWide size={18} />
</ActionButton>

View File

@ -51,7 +51,7 @@ const PromptSettings: FC<AgentPromptSettingsProps> = ({ agentBase, update }) =>
update({ id: agentBase.id, instructions } satisfies UpdateAgentBaseForm)
}
const promptVarsContent = <pre>{t('agents.add.prompt.variables.tip.content')}</pre>
const promptVarsContent = <pre>{t('assistants.presets.add.prompt.variables.tip.content')}</pre>
if (!agentBase) return null
@ -60,7 +60,7 @@ const PromptSettings: FC<AgentPromptSettingsProps> = ({ agentBase, update }) =>
<SettingsItem divider={false} className="flex-1">
<SettingsTitle>
{t('common.prompt')}
<Popover title={t('agents.add.prompt.variables.tip.title')} content={promptVarsContent}>
<Popover title={t('assistants.presets.add.prompt.variables.tip.title')} content={promptVarsContent}>
<HelpCircle size={14} color="var(--color-text-2)" />
</Popover>
</SettingsTitle>

View File

@ -37,7 +37,7 @@ const AssistantKnowledgeBaseSettings: React.FC<Props> = ({ assistant, updateAssi
mode="multiple"
allowClear
value={assistant.knowledge_bases?.map((b) => b.id)}
placeholder={t('agents.add.knowledge_base.placeholder')}
placeholder={t('assistants.presets.add.knowledge_base.placeholder')}
menuItemSelectedIcon={<CheckOutlined />}
options={knowledgeOptions}
onChange={(value) => onUpdate(value)}

View File

@ -221,7 +221,7 @@ const AssistantModelSettings: FC<Props> = ({ assistant, updateAssistant, updateA
<ModelSelectButton
icon={defaultModel ? <ModelAvatar model={defaultModel} size={20} /> : <PlusIcon size={18} />}
onClick={onSelectModel}>
<ModelName>{defaultModel ? defaultModel.name : t('agents.edit.model.select.title')}</ModelName>
<ModelName>{defaultModel ? defaultModel.name : t('assistants.presets.edit.model.select.title')}</ModelName>
</ModelSelectButton>
{defaultModel && (
<Button

View File

@ -65,7 +65,7 @@ const AssistantPromptSettings: React.FC<Props> = ({ assistant, updateAssistant }
updateAssistant(_assistant)
}
const promptVarsContent = <pre>{t('agents.add.prompt.variables.tip.content')}</pre>
const promptVarsContent = <pre>{t('assistants.presets.add.prompt.variables.tip.content')}</pre>
return (
<Container>
@ -115,7 +115,7 @@ const AssistantPromptSettings: React.FC<Props> = ({ assistant, updateAssistant }
<SettingDivider />
<HStack mb={8} alignItems="center" gap={4}>
<Box style={{ fontWeight: 'bold' }}>{t('common.prompt')}</Box>
<Popover title={t('agents.add.prompt.variables.tip.title')} content={promptVarsContent}>
<Popover title={t('assistants.presets.add.prompt.variables.tip.title')} content={promptVarsContent}>
<HelpCircle size={14} color="var(--color-text-2)" />
</Popover>
</HStack>

View File

@ -23,7 +23,7 @@ const AgentsSubscribeUrlSettings: FC = () => {
return (
<SettingGroup theme={theme}>
<SettingTitle>
{t('agents.tag.agent')}
{t('assistants.presets.tag.agent')}
{t('settings.tool.websearch.subscribe_add')}
</SettingTitle>
<SettingDivider />

View File

@ -129,7 +129,7 @@ const DataSettings: FC = () => {
},
{
key: 'agentssubscribe_url',
title: t('agents.settings.title'),
title: t('assistants.presets.settings.title'),
icon: <Sparkle size={16} className="icon" />
}
]

View File

@ -39,7 +39,7 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
TopicNamingModalPopup.hide = onCancel
const promptVarsContent = useMemo(() => <pre>{t('agents.add.prompt.variables.tip.content')}</pre>, [t])
const promptVarsContent = useMemo(() => <pre>{t('assistants.presets.add.prompt.variables.tip.content')}</pre>, [t])
return (
<Modal
@ -64,7 +64,7 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
<div>
<Flex align="center" gap={4} style={{ marginBottom: 4, height: 30 }}>
<div>{t('settings.models.topic_naming.prompt')}</div>
<Popover title={t('agents.add.prompt.variables.tip.title')} content={promptVarsContent}>
<Popover title={t('assistants.presets.add.prompt.variables.tip.title')} content={promptVarsContent}>
<QuestionCircleOutlined size={14} style={{ color: 'var(--color-text-2)' }} />
</Popover>
{topicNamingPrompt && <Button icon={<ResetIcon size={14} />} onClick={handleReset} type="text" />}

View File

@ -86,7 +86,7 @@ const AssistantPresetsPage: FC = () => {
maskClosable: true,
centered: true,
okButtonProps: { type: 'primary' },
okText: t('agents.add.button'),
okText: t('assistants.presets.add.button'),
onOk: () => createAssistantFromAgent(preset)
})
},
@ -178,7 +178,7 @@ const AssistantPresetsPage: FC = () => {
<Container>
<Navbar>
<NavbarCenter style={{ borderRight: 'none', justifyContent: 'space-between' }}>
{t('agents.title')}
{t('assistants.presets.title')}
<Input
placeholder={t('common.search')}
className="nodrag"
@ -274,10 +274,10 @@ const AssistantPresetsPage: FC = () => {
)
)}
<Button type="text" onClick={handleImportAgent} icon={<ImportOutlined />}>
{t('agents.import.title')}
{t('assistants.presets.import.title')}
</Button>
<Button type="text" onClick={handleAddAgent} icon={<PlusOutlined />}>
{t('agents.add.title')}
{t('assistants.presets.add.title')}
</Button>
</Flex>
</AgentsListHeader>
@ -296,7 +296,7 @@ const AssistantPresetsPage: FC = () => {
</AgentsList>
) : (
<EmptyView>
<Empty description={t('agents.search.no_results')} />
<Empty description={t('assistants.presets.search.no_results')} />
</EmptyView>
)}
</AgentsListContainer>

View File

@ -100,7 +100,7 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
if (hasUnsavedChanges) {
window.modal.confirm({
title: t('common.confirm'),
content: t('agents.add.unsaved_changes_warning'),
content: t('assistants.presets.add.unsaved_changes_warning'),
okText: t('common.confirm'),
cancelText: t('common.cancel'),
centered: true,
@ -155,19 +155,23 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
}
// Compute label width based on the longest label
const labelWidth = [t('agents.add.name.label'), t('agents.add.prompt.label'), t('agents.add.knowledge_base.label')]
const labelWidth = [
t('assistants.presets.add.name.label'),
t('assistants.presets.add.prompt.label'),
t('assistants.presets.add.knowledge_base.label')
]
.map((labelText) => stringWidth(labelText) * 8)
.reduce((maxWidth, currentWidth) => Math.max(maxWidth, currentWidth), 80)
return (
<Modal
title={t('agents.add.title')}
title={t('assistants.presets.add.title')}
open={open}
onOk={() => formRef.current?.submit()}
onCancel={handleCancel}
maskClosable={false}
afterClose={onClose}
okText={t('agents.add.title')}
okText={t('assistants.presets.add.title')}
width={600}
transitionName="animation-move-down"
centered>
@ -204,16 +208,16 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
<Button icon={emoji && <span style={{ fontSize: 20 }}>{emoji}</span>}>{t('common.select')}</Button>
</Popover>
</Form.Item>
<Form.Item name="name" label={t('agents.add.name.label')} rules={[{ required: true }]}>
<Input placeholder={t('agents.add.name.placeholder')} spellCheck={false} allowClear />
<Form.Item name="name" label={t('assistants.presets.add.name.label')} rules={[{ required: true }]}>
<Input placeholder={t('assistants.presets.add.name.placeholder')} spellCheck={false} allowClear />
</Form.Item>
<div style={{ position: 'relative' }}>
<Form.Item
name="prompt"
label={t('agents.add.prompt.label')}
label={t('assistants.presets.add.prompt.label')}
rules={[{ required: true }]}
style={{ position: 'relative' }}>
<TextArea placeholder={t('agents.add.prompt.placeholder')} spellCheck={false} rows={10} />
<TextArea placeholder={t('assistants.presets.add.prompt.placeholder')} spellCheck={false} rows={10} />
</Form.Item>
<TokenCount>Tokens: {tokenCount}</TokenCount>
<Button
@ -233,12 +237,12 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
{showKnowledgeIcon && (
<Form.Item
name="knowledge_base_ids"
label={t('agents.add.knowledge_base.label')}
label={t('assistants.presets.add.knowledge_base.label')}
rules={[{ required: false }]}>
<Select
mode="multiple"
allowClear
placeholder={t('agents.add.knowledge_base.placeholder')}
placeholder={t('assistants.presets.add.knowledge_base.placeholder')}
menuItemSelectedIcon={<CheckOutlined />}
options={knowledgeOptions}
filterOption={(input, option) =>

View File

@ -29,7 +29,7 @@ const AssistantPresetCard: FC<Props> = ({ preset, onClick, activegroup, getLocal
(preset: AssistantPreset) => {
window.modal.confirm({
centered: true,
content: t('agents.delete.popup.content'),
content: t('assistants.presets.delete.popup.content'),
onOk: () => removeAssistantPreset(preset.id)
})
},
@ -52,14 +52,14 @@ const AssistantPresetCard: FC<Props> = ({ preset, onClick, activegroup, getLocal
const resultStr = JSON.stringify(result, null, 2)
await window.api.file.save(`${preset.name}.json`, new TextEncoder().encode(resultStr), {
filters: [{ name: t('agents.import.file_filter'), extensions: ['json'] }]
filters: [{ name: t('assistants.presets.import.file_filter'), extensions: ['json'] }]
})
}, [preset])
const menuItems = [
{
key: 'edit',
label: t('agents.edit.title'),
label: t('assistants.presets.edit.title'),
icon: <EditIcon size={14} />,
onClick: (e: any) => {
e.domEvent.stopPropagation()
@ -68,7 +68,7 @@ const AssistantPresetCard: FC<Props> = ({ preset, onClick, activegroup, getLocal
},
{
key: 'create',
label: t('agents.add.button'),
label: t('assistants.presets.add.button'),
icon: <PlusIcon size={14} />,
onClick: (e: any) => {
e.domEvent.stopPropagation()
@ -77,7 +77,7 @@ const AssistantPresetCard: FC<Props> = ({ preset, onClick, activegroup, getLocal
},
{
key: 'sort',
label: t('agents.sorting.title'),
label: t('assistants.presets.sorting.title'),
icon: <ArrowDownAZ size={14} />,
onClick: (e: any) => {
e.domEvent.stopPropagation()
@ -86,7 +86,7 @@ const AssistantPresetCard: FC<Props> = ({ preset, onClick, activegroup, getLocal
},
{
key: 'export',
label: t('agents.export.agent'),
label: t('assistants.presets.export.agent'),
icon: <SquareArrowOutUpRight size={14} />,
onClick: (e: any) => {
e.domEvent.stopPropagation()

View File

@ -1,4 +1,4 @@
import { groupTranslations } from '@renderer/pages/assistantPresets/assistantPresetGroupTranslations'
import { groupTranslations } from '@renderer/pages/store/assistants/presets/assistantPresetGroupTranslations'
import { DynamicIcon, IconName } from 'lucide-react/dynamic'
import { FC } from 'react'
import { useTranslation } from 'react-i18next'

View File

@ -29,17 +29,17 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
if (importType === 'url') {
if (!values.url) {
throw new Error(t('agents.import.error.url_required'))
throw new Error(t('assistants.presets.import.error.url_required'))
}
const response = await fetch(values.url)
if (!response.ok) {
throw new Error(t('agents.import.error.fetch_failed'))
throw new Error(t('assistants.presets.import.error.fetch_failed'))
}
const data = await response.json()
presets = Array.isArray(data) ? data : [data]
} else {
const result = await window.api.file.open({
filters: [{ name: t('agents.import.file_filter'), extensions: ['json'] }]
filters: [{ name: t('assistants.presets.import.file_filter'), extensions: ['json'] }]
})
if (result) {
@ -55,7 +55,7 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
// Validate and process agents
for (const preset of presets) {
if (!preset.name || !preset.prompt) {
throw new Error(t('agents.import.error.invalid_format'))
throw new Error(t('assistants.presets.import.error.invalid_format'))
}
const newPreset: AssistantPreset = {
@ -93,7 +93,7 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
return (
<Modal
title={t('agents.import.title')}
title={t('assistants.presets.import.title')}
open={open}
onCancel={onCancel}
maskClosable={false}
@ -101,7 +101,7 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
<Flex justify="end" gap={8}>
<Button onClick={onCancel}>{t('common.cancel')}</Button>
<Button type="primary" onClick={() => form.submit()} loading={loading}>
{t('agents.import.button')}
{t('assistants.presets.import.button')}
</Button>
</Flex>
}
@ -110,20 +110,22 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
<Form form={form} onFinish={onFinish} layout="vertical">
<Form.Item>
<Radio.Group value={importType} onChange={(e) => setImportType(e.target.value)}>
<Radio.Button value="url">{t('agents.import.type.url')}</Radio.Button>
<Radio.Button value="file">{t('agents.import.type.file')}</Radio.Button>
<Radio.Button value="url">{t('assistants.presets.import.type.url')}</Radio.Button>
<Radio.Button value="file">{t('assistants.presets.import.type.file')}</Radio.Button>
</Radio.Group>
</Form.Item>
{importType === 'url' && (
<Form.Item name="url" rules={[{ required: true, message: t('agents.import.error.url_required') }]}>
<Input placeholder={t('agents.import.url_placeholder')} />
<Form.Item
name="url"
rules={[{ required: true, message: t('assistants.presets.import.error.url_required') }]}>
<Input placeholder={t('assistants.presets.import.url_placeholder')} />
</Form.Item>
)}
{importType === 'file' && (
<Form.Item>
<Button onClick={() => form.submit()}>{t('agents.import.select_file')}</Button>
<Button onClick={() => form.submit()}>{t('assistants.presets.import.select_file')}</Button>
</Form.Item>
)}
</Form>

View File

@ -33,7 +33,7 @@ const PopupContainer: React.FC = () => {
return (
<Modal
title={t('agents.manage.title')}
title={t('assistants.presets.manage.title')}
open={open}
onOk={onOk}
onCancel={onCancel}

View File

@ -1,76 +0,0 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
import { DEFAULT_CONTEXTCOUNT, DEFAULT_TEMPERATURE } from '@renderer/config/constant'
import { AssistantPreset, AssistantSettings } from '@renderer/types'
// const logger = loggerService.withContext('Agents')
export interface AgentsState {
/** They are actually assistant presets.
* They should not be in this slice. However, since redux will be removed
* in the future, I just don't care where should they are. */
agents: AssistantPreset[]
}
const initialState: AgentsState = {
agents: []
}
const assistantsSlice = createSlice({
name: 'agents',
initialState,
reducers: {
setAssistantPresets: (state, action: PayloadAction<AssistantPreset[]>) => {
const presets = action.payload
state.agents = []
presets.forEach((p) => {
state.agents.push(p)
})
},
addAssistantPreset: (state, action: PayloadAction<AssistantPreset>) => {
// @ts-ignore ts-2589 false positive
state.agents.push(action.payload)
},
removeAssistantPreset: (state, action: PayloadAction<{ id: string }>) => {
state.agents = state.agents.filter((c) => c.id !== action.payload.id)
},
updateAssistantPreset: (state, action: PayloadAction<AssistantPreset>) => {
const preset = action.payload
state.agents.forEach((a) => {
if (a.id === preset.id) {
a = preset
}
})
},
updateAssistantPresetSettings: (
state,
action: PayloadAction<{ assistantId: string; settings: Partial<AssistantSettings> }>
) => {
for (const agent of state.agents) {
const settings = action.payload.settings
if (agent.id === action.payload.assistantId) {
for (const key in settings) {
if (!agent.settings) {
agent.settings = {
temperature: DEFAULT_TEMPERATURE,
contextCount: DEFAULT_CONTEXTCOUNT,
enableMaxTokens: false,
maxTokens: 0,
streamOutput: true
}
}
agent.settings[key] = settings[key]
}
}
}
}
}
})
export const {
setAssistantPresets,
addAssistantPreset,
removeAssistantPreset,
updateAssistantPreset,
updateAssistantPresetSettings
} = assistantsSlice.actions
export default assistantsSlice.reducer

View File

@ -2,7 +2,7 @@ import { createSelector, createSlice, PayloadAction } from '@reduxjs/toolkit'
import { DEFAULT_CONTEXTCOUNT, DEFAULT_TEMPERATURE } from '@renderer/config/constant'
import { TopicManager } from '@renderer/hooks/useTopic'
import { getDefaultAssistant, getDefaultTopic } from '@renderer/services/AssistantService'
import { Assistant, AssistantSettings, Model, Topic } from '@renderer/types'
import { Assistant, AssistantPreset, AssistantSettings, Model, Topic } from '@renderer/types'
import { isEmpty, uniqBy } from 'lodash'
import { RootState } from '.'
@ -12,13 +12,15 @@ export interface AssistantsState {
assistants: Assistant[]
tagsOrder: string[]
collapsedTags: Record<string, boolean>
presets: AssistantPreset[]
}
const initialState: AssistantsState = {
defaultAssistant: getDefaultAssistant(),
assistants: [getDefaultAssistant()],
tagsOrder: [],
collapsedTags: {}
collapsedTags: {},
presets: []
}
const assistantsSlice = createSlice({
@ -174,6 +176,51 @@ const assistantsSlice = createSlice({
}
: assistant
)
},
// Assistant Presets
setAssistantPresets: (state, action: PayloadAction<AssistantPreset[]>) => {
const presets = action.payload
state.presets = []
presets.forEach((p) => {
state.presets.push(p)
})
},
addAssistantPreset: (state, action: PayloadAction<AssistantPreset>) => {
// @ts-ignore ts-2589 false positive
state.agents.push(action.payload)
},
removeAssistantPreset: (state, action: PayloadAction<{ id: string }>) => {
state.presets = state.presets.filter((c) => c.id !== action.payload.id)
},
updateAssistantPreset: (state, action: PayloadAction<AssistantPreset>) => {
const preset = action.payload
state.presets.forEach((a) => {
if (a.id === preset.id) {
a = preset
}
})
},
updateAssistantPresetSettings: (
state,
action: PayloadAction<{ assistantId: string; settings: Partial<AssistantSettings> }>
) => {
for (const agent of state.presets) {
const settings = action.payload.settings
if (agent.id === action.payload.assistantId) {
for (const key in settings) {
if (!agent.settings) {
agent.settings = {
temperature: DEFAULT_TEMPERATURE,
contextCount: DEFAULT_CONTEXTCOUNT,
enableMaxTokens: false,
maxTokens: 0,
streamOutput: true
}
}
agent.settings[key] = settings[key]
}
}
}
}
}
})
@ -194,7 +241,12 @@ export const {
setModel,
setTagsOrder,
updateAssistantSettings,
updateTagCollapse
updateTagCollapse,
setAssistantPresets,
addAssistantPreset,
removeAssistantPreset,
updateAssistantPreset,
updateAssistantPresetSettings
} = assistantsSlice.actions
export const selectAllTopics = createSelector([(state: RootState) => state.assistants.assistants], (assistants) =>

View File

@ -5,7 +5,6 @@ import { FLUSH, PAUSE, PERSIST, persistReducer, persistStore, PURGE, REGISTER, R
import storage from 'redux-persist/lib/storage'
import storeSyncService from '../services/StoreSyncService'
import agents from './agents'
import assistants from './assistants'
import backup from './backup'
import codeTools from './codeTools'
@ -37,7 +36,6 @@ const logger = loggerService.withContext('Store')
const rootReducer = combineReducers({
assistants,
agents,
backup,
codeTools,
nutstore,
@ -67,7 +65,7 @@ const persistedReducer = persistReducer(
{
key: 'cherry-studio',
storage,
version: 157,
version: 158,
blacklist: ['runtime', 'messages', 'messageBlocks', 'tabs'],
migrate
},

View File

@ -566,6 +566,7 @@ const migrateConfig = {
try {
state.assistants.defaultAssistant.type = 'assistant'
// @ts-ignore
state.agents.agents.forEach((agent) => {
agent.type = 'agent'
// @ts-ignore eslint-disable-next-line
@ -1067,6 +1068,7 @@ const migrateConfig = {
}
})
// @ts-ignore
state.agents.agents.forEach((agent) => {
const leadingEmoji = getLeadingEmoji(agent.name)
if (leadingEmoji) {
@ -2524,6 +2526,7 @@ const migrateConfig = {
}
})
// @ts-ignore
state.agents.agents.forEach((agent) => {
// @ts-ignore model is not defined in Agent
if (agent.model?.provider === 'cherryin') {
@ -2539,6 +2542,31 @@ const migrateConfig = {
logger.error('migrate 157 error', error as Error)
return state
}
},
'158': (state: RootState) => {
try {
// @ts-ignore
if (state?.agents?.agents) {
// @ts-ignore
state.assistants.presets = [...state.agents.agents]
// @ts-ignore
delete state.agents.agents
state.settings.sidebarIcons.visible = state.settings.sidebarIcons.visible.map((icon) => {
// @ts-ignore
return icon === 'agents' ? 'store' : icon
})
state.settings.sidebarIcons.disabled = state.settings.sidebarIcons.disabled.map((icon) => {
// @ts-ignore
return icon === 'agents' ? 'store' : icon
})
}
return state
} catch (error) {
logger.error('migrate 158 error', error as Error)
return state
}
}
}

View File

@ -525,7 +525,7 @@ export const isAutoDetectionMethod = (method: string): method is AutoDetectionMe
export type SidebarIcon =
| 'assistants'
| 'agents'
| 'store'
| 'paintings'
| 'translate'
| 'minapp'