mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-23 18:10:26 +08:00
添加智能体订阅功能 (#5954)
* 添加智能体订阅功能 * 修改图标 * 修改hook点 修改图标 * 优雅的引用图标 * feat(i18n): add settings title for agents in multiple languages * fix(i18n): update translations for improved clarity * Merge branch 'main' into Subscribe --------- Co-authored-by: VM 96 <eov@88.com> Co-authored-by: suyao <sy20010504@gmail.com>
This commit is contained in:
parent
8bd38ccd86
commit
71cd2def2e
@ -44,6 +44,9 @@
|
|||||||
"my_agents": "My Agents",
|
"my_agents": "My Agents",
|
||||||
"search.no_results": "No results found",
|
"search.no_results": "No results found",
|
||||||
"sorting.title": "Sorting",
|
"sorting.title": "Sorting",
|
||||||
|
"settings": {
|
||||||
|
"title": "Agent Setting"
|
||||||
|
},
|
||||||
"tag.agent": "Agent",
|
"tag.agent": "Agent",
|
||||||
"tag.default": "Default",
|
"tag.default": "Default",
|
||||||
"tag.new": "New",
|
"tag.new": "New",
|
||||||
|
|||||||
@ -48,7 +48,10 @@
|
|||||||
"tag.default": "デフォルト",
|
"tag.default": "デフォルト",
|
||||||
"tag.new": "新規",
|
"tag.new": "新規",
|
||||||
"tag.system": "システム",
|
"tag.system": "システム",
|
||||||
"title": "エージェント"
|
"title": "エージェント",
|
||||||
|
"settings": {
|
||||||
|
"title": "エージェント設定"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"assistants": {
|
"assistants": {
|
||||||
"title": "アシスタント",
|
"title": "アシスタント",
|
||||||
|
|||||||
@ -48,6 +48,9 @@
|
|||||||
},
|
},
|
||||||
"export": {
|
"export": {
|
||||||
"agent": "Экспорт агента"
|
"agent": "Экспорт агента"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"title": "Настройки агента"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"assistants": {
|
"assistants": {
|
||||||
|
|||||||
@ -48,7 +48,10 @@
|
|||||||
"tag.default": "默认",
|
"tag.default": "默认",
|
||||||
"tag.new": "新建",
|
"tag.new": "新建",
|
||||||
"tag.system": "系统",
|
"tag.system": "系统",
|
||||||
"title": "智能体"
|
"title": "智能体",
|
||||||
|
"settings": {
|
||||||
|
"title": "智能体配置"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"assistants": {
|
"assistants": {
|
||||||
"title": "助手",
|
"title": "助手",
|
||||||
|
|||||||
@ -48,7 +48,10 @@
|
|||||||
"tag.default": "預設",
|
"tag.default": "預設",
|
||||||
"tag.new": "新增",
|
"tag.new": "新增",
|
||||||
"tag.system": "系統",
|
"tag.system": "系統",
|
||||||
"title": "智慧代理人"
|
"title": "智慧代理人",
|
||||||
|
"settings": {
|
||||||
|
"title": "智慧代理人設定"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"assistants": {
|
"assistants": {
|
||||||
"title": "助手",
|
"title": "助手",
|
||||||
|
|||||||
@ -2,6 +2,8 @@ import { useRuntime } from '@renderer/hooks/useRuntime'
|
|||||||
import { useSettings } from '@renderer/hooks/useSettings'
|
import { useSettings } from '@renderer/hooks/useSettings'
|
||||||
import { Agent } from '@renderer/types'
|
import { Agent } from '@renderer/types'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
|
import store from '@renderer/store'
|
||||||
|
|
||||||
let _agents: Agent[] = []
|
let _agents: Agent[] = []
|
||||||
|
|
||||||
export const getAgentsFromSystemAgents = (systemAgents: any) => {
|
export const getAgentsFromSystemAgents = (systemAgents: any) => {
|
||||||
@ -19,27 +21,44 @@ export function useSystemAgents() {
|
|||||||
const { defaultAgent } = useSettings()
|
const { defaultAgent } = useSettings()
|
||||||
const [agents, setAgents] = useState<Agent[]>([])
|
const [agents, setAgents] = useState<Agent[]>([])
|
||||||
const { resourcesPath } = useRuntime()
|
const { resourcesPath } = useRuntime()
|
||||||
|
const { agentssubscribeUrl } = store.getState().settings
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadAgents = async () => {
|
const loadAgents = async () => {
|
||||||
try {
|
try {
|
||||||
// 始终加载本地 agents
|
// 检查是否使用远程数据源
|
||||||
|
if (agentssubscribeUrl && agentssubscribeUrl.startsWith('http')) {
|
||||||
|
try {
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 500));
|
||||||
|
const response = await fetch(agentssubscribeUrl);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! Status: ${response.status}`);
|
||||||
|
}
|
||||||
|
const agentsData = await response.json() as Agent[];
|
||||||
|
setAgents(agentsData);
|
||||||
|
return;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to load remote agents:", error);
|
||||||
|
// 远程加载失败,继续尝试加载本地数据
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有远程配置或获取失败,加载本地代理
|
||||||
if (resourcesPath && _agents.length === 0) {
|
if (resourcesPath && _agents.length === 0) {
|
||||||
const localAgentsData = await window.api.fs.read(resourcesPath + '/data/agents.json')
|
const localAgentsData = await window.api.fs.read(resourcesPath + '/data/agents.json')
|
||||||
_agents = JSON.parse(localAgentsData) as Agent[]
|
_agents = JSON.parse(localAgentsData) as Agent[]
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果没有远程配置或获取失败,使用本地 agents
|
|
||||||
setAgents(_agents)
|
setAgents(_agents)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load agents:', error)
|
console.error('Failed to load agents:', error)
|
||||||
// 发生错误时使用本地 agents
|
// 发生错误时使用已加载的本地 agents
|
||||||
setAgents(_agents)
|
setAgents(_agents)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loadAgents()
|
loadAgents()
|
||||||
}, [defaultAgent, resourcesPath])
|
}, [defaultAgent, resourcesPath, agentssubscribeUrl])
|
||||||
|
|
||||||
return agents
|
return agents
|
||||||
}
|
}
|
||||||
|
|||||||
47
src/renderer/src/pages/settings/DataSettings/AgentsSubscribeUrlSettings.tsx
Executable file
47
src/renderer/src/pages/settings/DataSettings/AgentsSubscribeUrlSettings.tsx
Executable file
@ -0,0 +1,47 @@
|
|||||||
|
import { HStack } from '@renderer/components/Layout'
|
||||||
|
import { useTheme } from '@renderer/context/ThemeProvider'
|
||||||
|
import { useSettings } from '@renderer/hooks/useSettings'
|
||||||
|
import { useAppDispatch } from '@renderer/store'
|
||||||
|
import { setAgentssubscribeUrl } from '@renderer/store/settings'
|
||||||
|
import Input from 'antd/es/input/Input'
|
||||||
|
import { FC } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
|
import { SettingDivider, SettingGroup, SettingRow, SettingRowTitle, SettingTitle } from '..'
|
||||||
|
|
||||||
|
const AgentsSubscribeUrlSettings: FC = () => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const { theme } = useTheme()
|
||||||
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
|
const { agentssubscribeUrl } = useSettings()
|
||||||
|
|
||||||
|
const handleAgentChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
dispatch(setAgentssubscribeUrl(e.target.value))
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SettingGroup theme={theme}>
|
||||||
|
<SettingTitle>
|
||||||
|
{t('agents.tag.agent')}
|
||||||
|
{t('settings.websearch.subscribe_add')}
|
||||||
|
</SettingTitle>
|
||||||
|
<SettingDivider />
|
||||||
|
<SettingRow>
|
||||||
|
<SettingRowTitle>{t('settings.websearch.subscribe_url')}</SettingRowTitle>
|
||||||
|
<HStack alignItems="center" gap="5px" style={{ width: 315 }}>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
value={agentssubscribeUrl || ''}
|
||||||
|
onChange={handleAgentChange}
|
||||||
|
style={{ width: 315 }}
|
||||||
|
placeholder={t('settings.websearch.subscribe_name.placeholder')}
|
||||||
|
/>
|
||||||
|
</HStack>
|
||||||
|
</SettingRow>
|
||||||
|
<SettingDivider />
|
||||||
|
</SettingGroup>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AgentsSubscribeUrlSettings
|
||||||
@ -17,12 +17,13 @@ import { reset } from '@renderer/services/BackupService'
|
|||||||
import { AppInfo } from '@renderer/types'
|
import { AppInfo } from '@renderer/types'
|
||||||
import { formatFileSize } from '@renderer/utils'
|
import { formatFileSize } from '@renderer/utils'
|
||||||
import { Button, Typography } from 'antd'
|
import { Button, Typography } from 'antd'
|
||||||
import { FileText, FolderCog, FolderInput } from 'lucide-react'
|
import { FileText, FolderCog, FolderInput, Sparkle } from 'lucide-react'
|
||||||
import { FC, useEffect, useState } from 'react'
|
import { FC, useEffect, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
import { SettingContainer, SettingDivider, SettingGroup, SettingRow, SettingRowTitle, SettingTitle } from '..'
|
import { SettingContainer, SettingDivider, SettingGroup, SettingRow, SettingRowTitle, SettingTitle } from '..'
|
||||||
|
import AgentsSubscribeUrlSettings from './AgentsSubscribeUrlSettings'
|
||||||
import ExportMenuOptions from './ExportMenuSettings'
|
import ExportMenuOptions from './ExportMenuSettings'
|
||||||
import JoplinSettings from './JoplinSettings'
|
import JoplinSettings from './JoplinSettings'
|
||||||
import MarkdownExportSettings from './MarkdownExportSettings'
|
import MarkdownExportSettings from './MarkdownExportSettings'
|
||||||
@ -81,6 +82,7 @@ const DataSettings: FC = () => {
|
|||||||
title: 'settings.data.markdown_export.title',
|
title: 'settings.data.markdown_export.title',
|
||||||
icon: <FileText size={16} />
|
icon: <FileText size={16} />
|
||||||
},
|
},
|
||||||
|
|
||||||
{ key: 'divider_3', isDivider: true, text: t('settings.data.divider.third_party') },
|
{ key: 'divider_3', isDivider: true, text: t('settings.data.divider.third_party') },
|
||||||
{ key: 'notion', title: 'settings.data.notion.title', icon: <i className="iconfont icon-notion" /> },
|
{ key: 'notion', title: 'settings.data.notion.title', icon: <i className="iconfont icon-notion" /> },
|
||||||
{
|
{
|
||||||
@ -102,6 +104,11 @@ const DataSettings: FC = () => {
|
|||||||
key: 'siyuan',
|
key: 'siyuan',
|
||||||
title: 'settings.data.siyuan.title',
|
title: 'settings.data.siyuan.title',
|
||||||
icon: <SiyuanIcon />
|
icon: <SiyuanIcon />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'agentssubscribe_url',
|
||||||
|
title: 'agents.settings.title',
|
||||||
|
icon: <Sparkle size={16} className="icon" />
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -253,6 +260,7 @@ const DataSettings: FC = () => {
|
|||||||
{menu === 'joplin' && <JoplinSettings />}
|
{menu === 'joplin' && <JoplinSettings />}
|
||||||
{menu === 'obsidian' && <ObsidianSettings />}
|
{menu === 'obsidian' && <ObsidianSettings />}
|
||||||
{menu === 'siyuan' && <SiyuanSettings />}
|
{menu === 'siyuan' && <SiyuanSettings />}
|
||||||
|
{menu === 'agentssubscribe_url' && <AgentsSubscribeUrlSettings />}
|
||||||
</SettingContainer>
|
</SettingContainer>
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -111,6 +111,8 @@ export interface SettingsState {
|
|||||||
siyuanToken: string | null
|
siyuanToken: string | null
|
||||||
siyuanBoxId: string | null
|
siyuanBoxId: string | null
|
||||||
siyuanRootPath: string | null
|
siyuanRootPath: string | null
|
||||||
|
// 订阅的助手地址
|
||||||
|
agentssubscribeUrl: string | null
|
||||||
// MinApps
|
// MinApps
|
||||||
maxKeepAliveMinapps: number
|
maxKeepAliveMinapps: number
|
||||||
showOpenedMinappsInSidebar: boolean
|
showOpenedMinappsInSidebar: boolean
|
||||||
@ -218,6 +220,7 @@ export const initialState: SettingsState = {
|
|||||||
siyuanToken: null,
|
siyuanToken: null,
|
||||||
siyuanBoxId: null,
|
siyuanBoxId: null,
|
||||||
siyuanRootPath: null,
|
siyuanRootPath: null,
|
||||||
|
agentssubscribeUrl: '',
|
||||||
// MinApps
|
// MinApps
|
||||||
maxKeepAliveMinapps: 3,
|
maxKeepAliveMinapps: 3,
|
||||||
showOpenedMinappsInSidebar: true,
|
showOpenedMinappsInSidebar: true,
|
||||||
@ -493,6 +496,9 @@ const settingsSlice = createSlice({
|
|||||||
setSiyuanRootPath: (state, action: PayloadAction<string>) => {
|
setSiyuanRootPath: (state, action: PayloadAction<string>) => {
|
||||||
state.siyuanRootPath = action.payload
|
state.siyuanRootPath = action.payload
|
||||||
},
|
},
|
||||||
|
setAgentssubscribeUrl: (state, action: PayloadAction<string>) => {
|
||||||
|
state.agentssubscribeUrl = action.payload
|
||||||
|
},
|
||||||
setMaxKeepAliveMinapps: (state, action: PayloadAction<number>) => {
|
setMaxKeepAliveMinapps: (state, action: PayloadAction<number>) => {
|
||||||
state.maxKeepAliveMinapps = action.payload
|
state.maxKeepAliveMinapps = action.payload
|
||||||
},
|
},
|
||||||
@ -599,6 +605,7 @@ export const {
|
|||||||
setSiyuanApiUrl,
|
setSiyuanApiUrl,
|
||||||
setSiyuanToken,
|
setSiyuanToken,
|
||||||
setSiyuanBoxId,
|
setSiyuanBoxId,
|
||||||
|
setAgentssubscribeUrl,
|
||||||
setSiyuanRootPath,
|
setSiyuanRootPath,
|
||||||
setMaxKeepAliveMinapps,
|
setMaxKeepAliveMinapps,
|
||||||
setShowOpenedMinappsInSidebar,
|
setShowOpenedMinappsInSidebar,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user