mirror of
https://github.com/lobehub/lobehub.git
synced 2026-03-27 13:29:15 +07:00
✨ feat: Improve organization and functionality of settings and configuration features
The code changes involve renaming and reorganizing files and components related to settings and chat configuration. It also includes updates to the state, functions, and selectors related to managing settings. Additionally, new imports and types are introduced, and existing interfaces are modified to include new properties. The changes aim to improve the organization and functionality of the settings and configuration features in the codebase.
This commit is contained in:
@@ -1,3 +1,6 @@
|
||||
import { MetaData } from '@/types/meta';
|
||||
|
||||
export const DEFAULT_AVATAR = '🤖';
|
||||
export const DEFAULT_USER_AVATAR = '😀';
|
||||
export const DEFAULT_BACKGROUND_COLOR = 'rgba(0,0,0,0)';
|
||||
export const DEFAULT_AGENT_META: MetaData = {};
|
||||
|
||||
50
src/const/settings.ts
Normal file
50
src/const/settings.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { DEFAULT_AGENT_META } from '@/const/meta';
|
||||
import { LanguageModel } from '@/types/llm';
|
||||
import { LobeAgentConfig } from '@/types/session';
|
||||
import { GlobalBaseSettings, GlobalDefaultAgent, GlobalSettings } from '@/types/settings';
|
||||
|
||||
export const DEFAULT_BASE_SETTINGS: GlobalBaseSettings = {
|
||||
OPENAI_API_KEY: '',
|
||||
avatar: '',
|
||||
compressThreshold: 24,
|
||||
enableCompressThreshold: false,
|
||||
enableHistoryCount: false,
|
||||
enableMaxTokens: true,
|
||||
endpoint: '',
|
||||
fontSize: 14,
|
||||
frequencyPenalty: 0,
|
||||
historyCount: 24,
|
||||
language: 'zh-CN',
|
||||
maxTokens: 2000,
|
||||
model: LanguageModel.GPT3_5,
|
||||
neutralColor: '',
|
||||
password: '',
|
||||
presencePenalty: 0,
|
||||
primaryColor: '',
|
||||
temperature: 0.5,
|
||||
themeMode: 'auto',
|
||||
topP: 1,
|
||||
};
|
||||
|
||||
export const DEFAULT_AGENT_CONFIG: LobeAgentConfig = {
|
||||
displayMode: 'chat',
|
||||
model: LanguageModel.GPT3_5,
|
||||
params: {
|
||||
frequency_penalty: 0,
|
||||
presence_penalty: 0,
|
||||
temperature: 0.6,
|
||||
top_p: 1,
|
||||
},
|
||||
plugins: [],
|
||||
systemRole: '',
|
||||
};
|
||||
|
||||
export const DEFAULT_AGENT: GlobalDefaultAgent = {
|
||||
config: DEFAULT_AGENT_CONFIG,
|
||||
meta: DEFAULT_AGENT_META,
|
||||
};
|
||||
|
||||
export const DEFAULT_SETTINGS: GlobalSettings = {
|
||||
defaultAgent: DEFAULT_AGENT,
|
||||
...DEFAULT_BASE_SETTINGS,
|
||||
};
|
||||
@@ -13,13 +13,15 @@ import { LobeAgentConfig } from '@/types/session';
|
||||
import AutoGenerateInput from './AutoGenerateInput';
|
||||
import BackgroundSwatches from './BackgroundSwatches';
|
||||
|
||||
export interface Autocomplete {
|
||||
autocompleteMeta: AgentAction['autocompleteMeta'];
|
||||
autocompleteSessionAgentMeta: AgentAction['autocompleteSessionAgentMeta'];
|
||||
id: string | null;
|
||||
loading: SessionLoadingState;
|
||||
}
|
||||
|
||||
export interface AgentMetaProps {
|
||||
autocomplete?: {
|
||||
autocompleteMeta: AgentAction['autocompleteMeta'];
|
||||
autocompleteSessionAgentMeta: AgentAction['autocompleteSessionAgentMeta'];
|
||||
id: string | null;
|
||||
loading: SessionLoadingState;
|
||||
};
|
||||
autocomplete?: Autocomplete;
|
||||
config: LobeAgentConfig;
|
||||
meta: MetaData;
|
||||
updateMeta: AgentAction['updateAgentMeta'];
|
||||
@@ -46,22 +48,23 @@ const AgentMeta = memo<AgentMetaProps>(({ config, meta, updateMeta, autocomplete
|
||||
// { key: 'tag', label: t('agentTag'), placeholder: t('agentTagPlaceholder') },
|
||||
];
|
||||
|
||||
const autocompleteItems: FormItemProps[] = basic.map((item) => ({
|
||||
children: (
|
||||
<AutoGenerateInput
|
||||
loading={autocomplete?.loading[item.key as keyof SessionLoadingState]}
|
||||
onChange={(e) => {
|
||||
updateMeta({ [item.key]: e.target.value });
|
||||
}}
|
||||
onGenerate={() => {
|
||||
autocomplete?.autocompleteMeta(item.key as keyof typeof meta);
|
||||
}}
|
||||
placeholder={item.placeholder}
|
||||
value={meta[item.key as keyof typeof meta]}
|
||||
/>
|
||||
),
|
||||
label: item.label,
|
||||
}));
|
||||
const autocompleteItems: FormItemProps[] = basic.map((item) => {
|
||||
const handleGenerate = () => autocomplete?.autocompleteMeta(item.key as keyof typeof meta);
|
||||
return {
|
||||
children: (
|
||||
<AutoGenerateInput
|
||||
loading={autocomplete?.loading[item.key as keyof SessionLoadingState]}
|
||||
onChange={(e) => {
|
||||
updateMeta({ [item.key]: e.target.value });
|
||||
}}
|
||||
onGenerate={autocomplete ? handleGenerate : undefined}
|
||||
placeholder={item.placeholder}
|
||||
value={meta[item.key as keyof typeof meta]}
|
||||
/>
|
||||
),
|
||||
label: item.label,
|
||||
};
|
||||
});
|
||||
|
||||
if (autocomplete) {
|
||||
const { autocompleteSessionAgentMeta, loading, id } = autocomplete;
|
||||
|
||||
@@ -8,17 +8,24 @@ import {
|
||||
Github,
|
||||
Heart,
|
||||
Settings,
|
||||
Settings2,
|
||||
} from 'lucide-react';
|
||||
import Router from 'next/router';
|
||||
import { ReactNode, memo, useMemo } from 'react';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { useExportConfig } from '@/hooks/useExportConfig';
|
||||
import { useImportConfig } from '@/hooks/useImportConfig';
|
||||
import { SettingsStore } from '@/store/settings';
|
||||
|
||||
import pkg from '../../../package.json';
|
||||
|
||||
const BottomAction = memo<{ children: ReactNode }>(({ children }) => {
|
||||
export interface BottomActionProps {
|
||||
setTab: SettingsStore['switchSideBar'];
|
||||
tab: SettingsStore['sidebarKey'];
|
||||
}
|
||||
|
||||
const BottomActions = memo<BottomActionProps>(({ tab, setTab }) => {
|
||||
const { t } = useTranslation('common');
|
||||
|
||||
const { exportSessions, exportSettings, exportAll, exportAgents } = useExportConfig();
|
||||
@@ -93,7 +100,10 @@ const BottomAction = memo<{ children: ReactNode }>(({ children }) => {
|
||||
icon: <Icon icon={Settings} />,
|
||||
key: 'setting',
|
||||
label: t('setting'),
|
||||
onClick: () => Router.push('/setting'),
|
||||
onClick: () => {
|
||||
setTab('settings');
|
||||
Router.push('/settings');
|
||||
},
|
||||
},
|
||||
],
|
||||
[],
|
||||
@@ -103,10 +113,10 @@ const BottomAction = memo<{ children: ReactNode }>(({ children }) => {
|
||||
<>
|
||||
<ActionIcon icon={Github} onClick={() => window.open(pkg.homepage, '__blank')} />
|
||||
<Dropdown arrow={false} menu={{ items }} trigger={['click']}>
|
||||
{children}
|
||||
<ActionIcon active={tab === 'settings'} icon={Settings2} />
|
||||
</Dropdown>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
export default BottomAction;
|
||||
export default BottomActions;
|
||||
42
src/features/SideBar/TopActions.tsx
Normal file
42
src/features/SideBar/TopActions.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import { ActionIcon } from '@lobehub/ui';
|
||||
import { MessageSquare, Sticker } from 'lucide-react';
|
||||
import Router from 'next/router';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { SettingsStore } from '@/store/settings';
|
||||
|
||||
export interface TopActionProps {
|
||||
setTab: SettingsStore['switchSideBar'];
|
||||
tab: SettingsStore['sidebarKey'];
|
||||
}
|
||||
|
||||
const TopActions = memo<TopActionProps>(({ tab, setTab }) => {
|
||||
const { t } = useTranslation('common');
|
||||
|
||||
return (
|
||||
<>
|
||||
<ActionIcon
|
||||
active={tab === 'chat'}
|
||||
icon={MessageSquare}
|
||||
onClick={() => {
|
||||
// 如果已经在 chat 路径下了,那么就不用再跳转了
|
||||
if (Router.asPath.startsWith('/chat')) return;
|
||||
|
||||
Router.push('/');
|
||||
}}
|
||||
size="large"
|
||||
title={t('tab.chat')}
|
||||
/>
|
||||
<ActionIcon
|
||||
active={tab === 'market'}
|
||||
icon={Sticker}
|
||||
onClick={() => setTab('market')}
|
||||
size="large"
|
||||
title={t('tab.market')}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
export default TopActions;
|
||||
@@ -1,13 +1,12 @@
|
||||
import { ActionIcon, SideNav } from '@lobehub/ui';
|
||||
import { MessageSquare, Settings2, Sticker } from 'lucide-react';
|
||||
import Router from 'next/router';
|
||||
import { SideNav } from '@lobehub/ui';
|
||||
import { memo } from 'react';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import AvatarWithUpload from '@/features/AvatarWithUpload';
|
||||
import { useSettings } from '@/store/settings';
|
||||
|
||||
import BottomAction from './BottomAction';
|
||||
import BottomActions from './BottomActions';
|
||||
import TopActions from './TopActions';
|
||||
|
||||
export default memo(() => {
|
||||
const [tab, setTab] = useSettings((s) => [s.sidebarKey, s.switchSideBar], shallow);
|
||||
@@ -15,33 +14,9 @@ export default memo(() => {
|
||||
return (
|
||||
<SideNav
|
||||
avatar={<AvatarWithUpload />}
|
||||
bottomActions={
|
||||
<BottomAction>
|
||||
<ActionIcon active={tab === 'setting'} icon={Settings2} />
|
||||
</BottomAction>
|
||||
}
|
||||
bottomActions={<BottomActions setTab={setTab} tab={tab} />}
|
||||
style={{ height: '100vh' }}
|
||||
topActions={
|
||||
<>
|
||||
<ActionIcon
|
||||
active={tab === 'chat'}
|
||||
icon={MessageSquare}
|
||||
onClick={() => {
|
||||
// 如果已经在 chat 路径下了,那么就不用再跳转了
|
||||
if (Router.asPath.startsWith('/chat')) return;
|
||||
|
||||
Router.push('/');
|
||||
}}
|
||||
size="large"
|
||||
/>
|
||||
<ActionIcon
|
||||
active={tab === 'market'}
|
||||
icon={Sticker}
|
||||
onClick={() => setTab('market')}
|
||||
size="large"
|
||||
/>
|
||||
</>
|
||||
}
|
||||
topActions={<TopActions setTab={setTab} tab={tab} />}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -6,13 +6,13 @@ import { importConfigFile } from '@/utils/config';
|
||||
|
||||
export const useImportConfig = () => {
|
||||
const importSessions = useSessionStore((s) => s.importSessions);
|
||||
const importSettings = useSettings((s) => s.importSettings);
|
||||
const importAppSettings = useSettings((s) => s.importAppSettings);
|
||||
|
||||
const importConfig = (info: any) => {
|
||||
importConfigFile(info, (config) => {
|
||||
switch (config.exportType) {
|
||||
case 'settings': {
|
||||
importSettings(config.state.settings);
|
||||
importAppSettings(config.state.settings);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ export const useImportConfig = () => {
|
||||
|
||||
case 'all': {
|
||||
importSessions(config.state.sessions);
|
||||
importSettings(config.state.settings);
|
||||
importAppSettings(config.state.settings);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -49,6 +49,10 @@ export default {
|
||||
sendPlaceholder: '输入聊天内容...',
|
||||
setting: '设置',
|
||||
share: '分享',
|
||||
tab: {
|
||||
chat: '会话',
|
||||
market: '助手市场',
|
||||
},
|
||||
temp: '临时',
|
||||
tokenDetail: '系统设定: {{systemRoleToken}} 历史消息: {{chatsToken}}',
|
||||
topic: {
|
||||
|
||||
@@ -162,4 +162,8 @@ export default {
|
||||
},
|
||||
title: '主题设置',
|
||||
},
|
||||
tab: {
|
||||
agent: '默认角色',
|
||||
common: '通用设置',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -33,7 +33,7 @@ const List = () => {
|
||||
const [displayMode, chatLoadingId, deleteMessage, resendMessage, dispatchMessage] =
|
||||
useSessionStore(
|
||||
(s) => [
|
||||
agentSelectors.currentAgentConfigSafe(s).displayMode,
|
||||
agentSelectors.currentAgentConfig(s).displayMode,
|
||||
s.chatLoadingId,
|
||||
s.deleteMessage,
|
||||
s.resendMessage,
|
||||
|
||||
@@ -16,7 +16,7 @@ const InputActions = memo(() => {
|
||||
shallow,
|
||||
);
|
||||
const [model, temperature] = useSessionStore((s) => {
|
||||
const config = agentSelectors.currentAgentConfigSafe(s);
|
||||
const config = agentSelectors.currentAgentConfig(s);
|
||||
return [
|
||||
config.model,
|
||||
config.params.temperature,
|
||||
|
||||
@@ -67,7 +67,7 @@ const Header = memo(() => {
|
||||
<ActionIcon
|
||||
icon={Settings}
|
||||
onClick={() => {
|
||||
Router.push(`/chat/${id}/edit`);
|
||||
Router.push(`/chat/${id}/setting`);
|
||||
}}
|
||||
size={{ fontSize: 24 }}
|
||||
title={t('header.session', { ns: 'setting' })}
|
||||
|
||||
@@ -40,7 +40,7 @@ const useStyles = createStyles(({ css, token }) => ({
|
||||
`,
|
||||
}));
|
||||
|
||||
const SideBar = memo(() => {
|
||||
const Inner = memo(() => {
|
||||
const [openModal, setOpenModal] = useState(false);
|
||||
const { styles } = useStyles();
|
||||
const [systemRole, updateAgentConfig] = useSessionStore(
|
||||
@@ -87,4 +87,4 @@ const SideBar = memo(() => {
|
||||
);
|
||||
});
|
||||
|
||||
export default SideBar;
|
||||
export default Inner;
|
||||
@@ -7,7 +7,7 @@ import HeaderSpacing from '@/components/HeaderSpacing';
|
||||
import { CHAT_SIDEBAR_WIDTH } from '@/const/layoutTokens';
|
||||
import { useSettings } from '@/store/settings';
|
||||
|
||||
import SideBar from './SideBar';
|
||||
import Inner from './Inner';
|
||||
|
||||
const useStyles = createStyles(({ cx, css, token, stylish }) => ({
|
||||
drawer: cx(
|
||||
@@ -38,7 +38,7 @@ const Config = () => {
|
||||
>
|
||||
<HeaderSpacing />
|
||||
<DraggablePanelContainer style={{ flex: 'none', minWidth: CHAT_SIDEBAR_WIDTH }}>
|
||||
<SideBar />
|
||||
<Inner />
|
||||
</DraggablePanelContainer>
|
||||
</DraggablePanel>
|
||||
);
|
||||
@@ -7,9 +7,9 @@ import { sessionSelectors, useSessionStore } from '@/store/session';
|
||||
import { genSiteHeadTitle } from '@/utils/genSiteHeadTitle';
|
||||
|
||||
import Layout from '../layout';
|
||||
import Config from './Config';
|
||||
import Conversation from './Conversation';
|
||||
import Header from './Header';
|
||||
import Config from './Sidebar';
|
||||
|
||||
const Chat = memo(() => {
|
||||
const [title] = useSessionStore((s) => {
|
||||
|
||||
@@ -16,26 +16,14 @@ import Header from './Header';
|
||||
|
||||
const EditPage = memo(() => {
|
||||
const { t } = useTranslation('setting');
|
||||
const config = useSessionStore(agentSelectors.currentAgentConfigSafe, isEqual);
|
||||
const config = useSessionStore(agentSelectors.currentAgentConfig, isEqual);
|
||||
const meta = useSessionStore(agentSelectors.currentAgentMeta, isEqual);
|
||||
const [
|
||||
updateAgentConfig,
|
||||
toggleAgentPlugin,
|
||||
autocompleteMeta,
|
||||
autocompleteSessionAgentMeta,
|
||||
loading,
|
||||
updateAgentMeta,
|
||||
id,
|
||||
title,
|
||||
] = useSessionStore(
|
||||
const autocomplete = useSessionStore(agentSelectors.currentAutocomplete, shallow);
|
||||
const [updateAgentConfig, toggleAgentPlugin, updateAgentMeta, title] = useSessionStore(
|
||||
(s) => [
|
||||
s.updateAgentConfig,
|
||||
s.toggleAgentPlugin,
|
||||
s.autocompleteMeta,
|
||||
s.autocompleteSessionAgentMeta,
|
||||
s.autocompleteLoading,
|
||||
s.updateAgentMeta,
|
||||
s.activeId,
|
||||
agentSelectors.currentAgentTitle(s),
|
||||
],
|
||||
shallow,
|
||||
@@ -54,7 +42,7 @@ const EditPage = memo(() => {
|
||||
<HeaderSpacing height={HEADER_HEIGHT - 16} />
|
||||
<AgentPrompt config={config} updateConfig={updateAgentConfig} />
|
||||
<AgentMeta
|
||||
autocomplete={{ autocompleteMeta, autocompleteSessionAgentMeta, id, loading }}
|
||||
autocomplete={autocomplete}
|
||||
config={config}
|
||||
meta={meta}
|
||||
updateMeta={updateAgentMeta}
|
||||
25
src/pages/settings/Settings/Agent.tsx
Normal file
25
src/pages/settings/Settings/Agent.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { memo } from 'react';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import { AgentConfig, AgentMeta, AgentPlugin, AgentPrompt } from '@/features/AgentSetting';
|
||||
import { settingsSelectors, useSettings } from '@/store/settings';
|
||||
|
||||
const Agent = memo(() => {
|
||||
const config = useSettings(settingsSelectors.currentAgentConfig, isEqual);
|
||||
const meta = useSettings(settingsSelectors.currentAgentMeta, isEqual);
|
||||
const [setAgentConfig, setAgentMeta, toggleAgentPlugin] = useSettings(
|
||||
(s) => [s.setAgentConfig, s.setAgentMeta, s.toggleAgentPlugin],
|
||||
shallow,
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<AgentPrompt config={config} updateConfig={setAgentConfig} />
|
||||
<AgentMeta config={config} meta={meta} updateMeta={setAgentMeta} />
|
||||
<AgentConfig config={config} updateConfig={setAgentConfig} />
|
||||
<AgentPlugin config={config} updateConfig={toggleAgentPlugin} />
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
export default Agent;
|
||||
@@ -1,23 +1,22 @@
|
||||
import { Form, type ItemGroup, ThemeSwitch } from '@lobehub/ui';
|
||||
import { Form as AntForm, App, Button, Input, Select, Switch } from 'antd';
|
||||
import { Form as AntForm, App, Button, Input, Select } from 'antd';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { debounce } from 'lodash-es';
|
||||
import { AppWindow, BrainCog, MessagesSquare, Palette, Webhook } from 'lucide-react';
|
||||
import { AppWindow, Palette, Webhook } from 'lucide-react';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import SliderWithInput from 'src/components/SliderWithInput';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import { FORM_STYLE } from '@/const/layoutTokens';
|
||||
import { DEFAULT_SETTINGS } from '@/const/settings';
|
||||
import AvatarWithUpload from '@/features/AvatarWithUpload';
|
||||
import { options } from '@/locales/options';
|
||||
import { useSessionStore } from '@/store/session';
|
||||
import { settingsSelectors, useSettings } from '@/store/settings';
|
||||
import { DEFAULT_SETTINGS } from '@/store/settings/initialState';
|
||||
import { LanguageModel } from '@/types/llm';
|
||||
import { ConfigKeys } from '@/types/settings';
|
||||
|
||||
import { ThemeSwatchesNeutral, ThemeSwatchesPrimary } from './ThemeSwatches';
|
||||
import { ThemeSwatchesNeutral, ThemeSwatchesPrimary } from '../ThemeSwatches';
|
||||
|
||||
type SettingItemGroup = ItemGroup & {
|
||||
children: {
|
||||
@@ -25,7 +24,7 @@ type SettingItemGroup = ItemGroup & {
|
||||
}[];
|
||||
};
|
||||
|
||||
const SettingForm = memo(() => {
|
||||
const Common = memo(() => {
|
||||
const { t } = useTranslation('setting');
|
||||
const [form] = AntForm.useForm();
|
||||
const clearSessions = useSessionStore((s) => s.clearSessions);
|
||||
@@ -147,111 +146,6 @@ const SettingForm = memo(() => {
|
||||
[settings],
|
||||
);
|
||||
|
||||
const chat: SettingItemGroup = useMemo(
|
||||
() => ({
|
||||
children: [
|
||||
{
|
||||
children: <Switch />,
|
||||
label: t('settingChat.enableHistoryCount.title'),
|
||||
minWidth: undefined,
|
||||
name: 'enableHistoryCount',
|
||||
valuePropName: 'checked',
|
||||
},
|
||||
{
|
||||
children: <SliderWithInput max={32} min={0} />,
|
||||
desc: t('settingChat.historyCount.desc'),
|
||||
hidden: !settings.enableHistoryCount,
|
||||
label: t('settingChat.historyCount.title'),
|
||||
name: 'historyCount',
|
||||
},
|
||||
{
|
||||
children: <Switch />,
|
||||
label: t('settingChat.enableCompressThreshold.title'),
|
||||
minWidth: undefined,
|
||||
name: 'enableCompressThreshold',
|
||||
valuePropName: 'checked',
|
||||
},
|
||||
{
|
||||
children: <SliderWithInput max={32} min={0} />,
|
||||
desc: t('settingChat.compressThreshold.desc'),
|
||||
hidden: !settings.enableCompressThreshold,
|
||||
label: t('settingChat.compressThreshold.title'),
|
||||
name: 'compressThreshold',
|
||||
},
|
||||
],
|
||||
icon: MessagesSquare,
|
||||
title: t('settingChat.title'),
|
||||
}),
|
||||
[settings],
|
||||
);
|
||||
|
||||
const model: SettingItemGroup = useMemo(
|
||||
() => ({
|
||||
children: [
|
||||
{
|
||||
children: (
|
||||
<Select
|
||||
options={Object.values(LanguageModel).map((value) => ({
|
||||
label: value,
|
||||
value,
|
||||
}))}
|
||||
/>
|
||||
),
|
||||
desc: t('settingModel.model.desc'),
|
||||
label: t('settingModel.model.title'),
|
||||
name: 'model',
|
||||
tag: 'model',
|
||||
},
|
||||
{
|
||||
children: <SliderWithInput max={1} min={0} step={0.1} />,
|
||||
desc: t('settingModel.temperature.desc'),
|
||||
label: t('settingModel.temperature.title'),
|
||||
name: 'temperature',
|
||||
tag: 'temperature',
|
||||
},
|
||||
{
|
||||
children: <SliderWithInput max={1} min={0} step={0.1} />,
|
||||
desc: t('settingModel.topP.desc'),
|
||||
label: t('settingModel.topP.title'),
|
||||
name: 'topP',
|
||||
tag: 'top_p',
|
||||
},
|
||||
{
|
||||
children: <SliderWithInput max={2} min={-2} step={0.1} />,
|
||||
desc: t('settingModel.presencePenalty.desc'),
|
||||
label: t('settingModel.presencePenalty.title'),
|
||||
name: 'presencePenalty',
|
||||
tag: 'presence_penalty',
|
||||
},
|
||||
{
|
||||
children: <SliderWithInput max={2} min={-2} step={0.1} />,
|
||||
desc: t('settingModel.frequencyPenalty.desc'),
|
||||
label: t('settingModel.frequencyPenalty.title'),
|
||||
name: 'frequencyPenalty',
|
||||
tag: 'frequency_penalty',
|
||||
},
|
||||
{
|
||||
children: <Switch />,
|
||||
label: t('settingModel.enableMaxTokens.title'),
|
||||
minWidth: undefined,
|
||||
name: 'enableMaxTokens',
|
||||
valuePropName: 'checked',
|
||||
},
|
||||
{
|
||||
children: <SliderWithInput max={32_000} min={0} step={100} />,
|
||||
desc: t('settingModel.maxTokens.desc'),
|
||||
hidden: !settings.enableMaxTokens,
|
||||
label: t('settingModel.maxTokens.title'),
|
||||
name: 'maxTokens',
|
||||
tag: 'max_tokens',
|
||||
},
|
||||
],
|
||||
icon: BrainCog,
|
||||
title: t('settingModel.title'),
|
||||
}),
|
||||
[settings],
|
||||
);
|
||||
|
||||
const system: SettingItemGroup = useMemo(
|
||||
() => ({
|
||||
children: [
|
||||
@@ -288,7 +182,7 @@ const SettingForm = memo(() => {
|
||||
[settings],
|
||||
);
|
||||
|
||||
const items = useMemo(() => [theme, openAI, chat, model, system], [settings]);
|
||||
const items = useMemo(() => [theme, openAI, system], [settings]);
|
||||
|
||||
return (
|
||||
<Form
|
||||
@@ -301,4 +195,4 @@ const SettingForm = memo(() => {
|
||||
);
|
||||
});
|
||||
|
||||
export default SettingForm;
|
||||
export default Common;
|
||||
50
src/pages/settings/Settings/index.tsx
Normal file
50
src/pages/settings/Settings/index.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import { Segmented } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { memo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Center } from 'react-layout-kit';
|
||||
|
||||
import Agent from './Agent';
|
||||
import Common from './Common';
|
||||
|
||||
const useStyles = createStyles(({ css, token }) => ({
|
||||
tabs: css`
|
||||
padding: 4px;
|
||||
border: 1px solid ${token.colorBorderSecondary};
|
||||
border-radius: ${token.borderRadiusLG}px;
|
||||
|
||||
&:hover {
|
||||
border-color: ${token.colorBorder};
|
||||
}
|
||||
`,
|
||||
}));
|
||||
|
||||
enum Tabs {
|
||||
common,
|
||||
agent,
|
||||
}
|
||||
|
||||
const Settings = memo(() => {
|
||||
const [tab, setTab] = useState<Tabs>(Tabs.common);
|
||||
const { styles } = useStyles();
|
||||
const { t } = useTranslation('setting');
|
||||
|
||||
return (
|
||||
<Center gap={16} width={'100%'}>
|
||||
<div className={styles.tabs}>
|
||||
<Segmented
|
||||
onChange={(e) => setTab(e as Tabs)}
|
||||
options={[
|
||||
{ label: t('tab.common'), value: Tabs.common },
|
||||
{ label: t('tab.agent'), value: Tabs.agent },
|
||||
]}
|
||||
value={tab}
|
||||
/>
|
||||
</div>
|
||||
{tab === Tabs.common && <Common />}
|
||||
{tab === Tabs.agent && <Agent />}
|
||||
</Center>
|
||||
);
|
||||
});
|
||||
|
||||
export default Settings;
|
||||
@@ -4,7 +4,7 @@ import { Flexbox } from 'react-layout-kit';
|
||||
import HeaderSpacing from '@/components/HeaderSpacing';
|
||||
|
||||
import Header from './Header';
|
||||
import SettingForm from './SettingForm';
|
||||
import Settings from './Settings';
|
||||
import SettingLayout from './layout';
|
||||
|
||||
const Setting = memo(() => {
|
||||
@@ -13,7 +13,7 @@ const Setting = memo(() => {
|
||||
<Header />
|
||||
<Flexbox align={'center'} flex={1} padding={24} style={{ overflow: 'auto' }}>
|
||||
<HeaderSpacing />
|
||||
<SettingForm />
|
||||
<Settings />
|
||||
</Flexbox>
|
||||
</SettingLayout>
|
||||
);
|
||||
@@ -21,7 +21,7 @@ const SettingLayout = memo<{ children: ReactNode }>(({ children }) => {
|
||||
useEffect(() => {
|
||||
const hasRehydrated = useSettings.persist.hasHydrated();
|
||||
if (hasRehydrated) {
|
||||
useSettings.setState({ sidebarKey: 'setting' });
|
||||
useSettings.setState({ sidebarKey: 'settings' });
|
||||
}
|
||||
}, []);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { LanguageModel } from '@/types/llm';
|
||||
import { DEFAULT_AGENT_CONFIG } from '@/const/settings';
|
||||
import { MetaData } from '@/types/meta';
|
||||
import { LobeAgentConfig } from '@/types/session';
|
||||
|
||||
@@ -8,18 +8,7 @@ export interface AgentConfigState {
|
||||
autocompleteLoading: SessionLoadingState;
|
||||
}
|
||||
|
||||
export const initialLobeAgentConfig: LobeAgentConfig = {
|
||||
displayMode: 'chat',
|
||||
model: LanguageModel.GPT3_5,
|
||||
params: {
|
||||
frequency_penalty: 0,
|
||||
presence_penalty: 0,
|
||||
temperature: 0.6,
|
||||
top_p: 1,
|
||||
},
|
||||
plugins: [],
|
||||
systemRole: '',
|
||||
};
|
||||
export const initialLobeAgentConfig: LobeAgentConfig = DEFAULT_AGENT_CONFIG;
|
||||
|
||||
export const initialAgentConfigState: AgentConfigState = {
|
||||
// // loading 中间态
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { t } from 'i18next';
|
||||
import { defaults } from 'lodash-es';
|
||||
|
||||
import { DEFAULT_AVATAR, DEFAULT_BACKGROUND_COLOR } from '@/const/meta';
|
||||
import { Autocomplete } from '@/features/AgentSetting/AgentMeta';
|
||||
import { SessionStore } from '@/store/session';
|
||||
import { LanguageModel } from '@/types/llm';
|
||||
import { MetaData } from '@/types/meta';
|
||||
import { LobeAgentConfig } from '@/types/session';
|
||||
|
||||
import { sessionSelectors } from '../session';
|
||||
import { initialLobeAgentConfig } from './initialState';
|
||||
@@ -15,6 +16,13 @@ const currentAgentMeta = (s: SessionStore): MetaData => {
|
||||
return { avatar: DEFAULT_AVATAR, backgroundColor: DEFAULT_BACKGROUND_COLOR, ...session?.meta };
|
||||
};
|
||||
|
||||
const currentAutocomplete = (s: SessionStore): Autocomplete => ({
|
||||
autocompleteMeta: s.autocompleteMeta,
|
||||
autocompleteSessionAgentMeta: s.autocompleteSessionAgentMeta,
|
||||
id: s.activeId,
|
||||
loading: s.autocompleteLoading,
|
||||
});
|
||||
|
||||
const currentAgentTitle = (s: SessionStore) => currentAgentMeta(s)?.title || t('defaultSession');
|
||||
|
||||
const currentAgentDescription = (s: SessionStore) =>
|
||||
@@ -34,15 +42,11 @@ const currentAgentAvatar = (s: SessionStore) => {
|
||||
|
||||
const currentAgentConfig = (s: SessionStore) => {
|
||||
const session = sessionSelectors.currentSession(s);
|
||||
return session?.config;
|
||||
};
|
||||
|
||||
const currentAgentConfigSafe = (s: SessionStore): LobeAgentConfig => {
|
||||
return currentAgentConfig(s) || initialLobeAgentConfig;
|
||||
return defaults(session?.config, initialLobeAgentConfig);
|
||||
};
|
||||
|
||||
const currentAgentSystemRole = (s: SessionStore) => {
|
||||
return currentAgentConfigSafe(s).systemRole;
|
||||
return currentAgentConfig(s).systemRole;
|
||||
};
|
||||
|
||||
const currentAgentModel = (s: SessionStore): LanguageModel => {
|
||||
@@ -52,7 +56,7 @@ const currentAgentModel = (s: SessionStore): LanguageModel => {
|
||||
};
|
||||
|
||||
const hasSystemRole = (s: SessionStore) => {
|
||||
const config = currentAgentConfigSafe(s);
|
||||
const config = currentAgentConfig(s);
|
||||
|
||||
return !!config.systemRole;
|
||||
};
|
||||
@@ -64,12 +68,12 @@ export const agentSelectors = {
|
||||
currentAgentAvatar,
|
||||
currentAgentBackgroundColor,
|
||||
currentAgentConfig,
|
||||
currentAgentConfigSafe,
|
||||
currentAgentDescription,
|
||||
currentAgentMeta,
|
||||
currentAgentModel,
|
||||
currentAgentSystemRole,
|
||||
currentAgentTitle,
|
||||
currentAutocomplete,
|
||||
getAvatar,
|
||||
getTitle,
|
||||
hasSystemRole,
|
||||
|
||||
@@ -112,7 +112,7 @@ export const chatMessage: StateCreator<
|
||||
generateMessage: async (messages, assistantId) => {
|
||||
const { dispatchMessage } = get();
|
||||
set({ chatLoadingId: assistantId });
|
||||
const config = agentSelectors.currentAgentConfigSafe(get());
|
||||
const config = agentSelectors.currentAgentConfig(get());
|
||||
|
||||
const compiler = template(config.inputTemplate, { interpolate: /{{([\S\s]+?)}}/g });
|
||||
|
||||
@@ -137,7 +137,7 @@ export const chatMessage: StateCreator<
|
||||
// 2. TODO 按参数设定截断长度
|
||||
|
||||
// 3. 添加 systemRole
|
||||
const { systemRole } = agentSelectors.currentAgentConfigSafe(get());
|
||||
const { systemRole } = agentSelectors.currentAgentConfig(get());
|
||||
if (systemRole) {
|
||||
postMessages.unshift({ content: systemRole, role: 'system' } as ChatMessage);
|
||||
}
|
||||
@@ -188,7 +188,7 @@ export const chatMessage: StateCreator<
|
||||
realFetchAIResponse: async (messages, userMessageId) => {
|
||||
const { dispatchMessage, generateMessage, triggerFunctionCall, activeTopicId } = get();
|
||||
|
||||
const { model } = agentSelectors.currentAgentConfigSafe(get());
|
||||
const { model } = agentSelectors.currentAgentConfig(get());
|
||||
|
||||
// 添加一个空的信息用于放置 ai 响应,注意顺序不能反
|
||||
// 因为如果顺序反了,messages 中将包含新增的 ai message
|
||||
|
||||
@@ -23,7 +23,7 @@ export const currentChats = (s: SessionStore): ChatMessage[] => {
|
||||
};
|
||||
|
||||
export const systemRoleSel = (s: SessionStore): string => {
|
||||
const config = agentSelectors.currentAgentConfigSafe(s);
|
||||
const config = agentSelectors.currentAgentConfig(s);
|
||||
|
||||
return config.systemRole;
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { DEFAULT_AGENT_META } from '@/const/meta';
|
||||
import { LobeAgentSession, LobeSessionType } from '@/types/session';
|
||||
|
||||
import { initialLobeAgentConfig } from '../agentConfig';
|
||||
@@ -18,7 +19,7 @@ export const initLobeSession: LobeAgentSession = {
|
||||
config: initialLobeAgentConfig,
|
||||
createAt: Date.now(),
|
||||
id: '',
|
||||
meta: {},
|
||||
meta: DEFAULT_AGENT_META,
|
||||
type: LobeSessionType.Agent,
|
||||
updateAt: Date.now(),
|
||||
};
|
||||
|
||||
@@ -1,26 +1,37 @@
|
||||
import { ThemeMode } from 'antd-style';
|
||||
import { produce } from 'immer';
|
||||
import { merge } from 'lodash-es';
|
||||
import type { StateCreator } from 'zustand/vanilla';
|
||||
|
||||
import { DEFAULT_AGENT, DEFAULT_BASE_SETTINGS, DEFAULT_SETTINGS } from '@/const/settings';
|
||||
import type { LobeAgentSession } from '@/types/session';
|
||||
import { LobeAgentConfig } from '@/types/session';
|
||||
import type { GlobalSettings } from '@/types/settings';
|
||||
|
||||
import type { SidebarTabKey } from './initialState';
|
||||
import { DEFAULT_SETTINGS, SettingsState } from './initialState';
|
||||
import { AppSettingsState } from './initialState';
|
||||
import type { SettingsStore } from './store';
|
||||
|
||||
/**
|
||||
* 设置操作
|
||||
*/
|
||||
export interface SettingsAction {
|
||||
importSettings: (settings: GlobalSettings) => void;
|
||||
importAppSettings: (settings: GlobalSettings) => void;
|
||||
resetAgentConfig: () => void;
|
||||
resetAgentMeta: () => void;
|
||||
resetBaseSettings: () => void;
|
||||
resetDefaultAgent: () => void;
|
||||
/**
|
||||
* 重置设置
|
||||
*/
|
||||
resetSettings: () => void;
|
||||
setAgentConfig: (config: Partial<LobeAgentSession['config']>) => void;
|
||||
setAgentMeta: (meta: Partial<LobeAgentSession['meta']>) => void;
|
||||
/**
|
||||
* 设置全局设置
|
||||
* @param settings - 全局设置
|
||||
*/
|
||||
setGlobalSettings: (settings: SettingsState) => void;
|
||||
setAppSettings: (settings: AppSettingsState) => void;
|
||||
/**
|
||||
* 设置部分配置设置
|
||||
* @param settings - 部分配置设置
|
||||
@@ -37,6 +48,7 @@ export interface SettingsAction {
|
||||
*/
|
||||
switchSideBar: (key: SidebarTabKey) => void;
|
||||
toggleAgentPanel: (visible?: boolean) => void;
|
||||
toggleAgentPlugin: (pluginId: string) => void;
|
||||
}
|
||||
|
||||
export const createSettings: StateCreator<
|
||||
@@ -45,10 +57,10 @@ export const createSettings: StateCreator<
|
||||
[],
|
||||
SettingsAction
|
||||
> = (set, get) => ({
|
||||
importSettings: (importSettings) => {
|
||||
importAppSettings: (importAppSettings) => {
|
||||
const { setSettings } = get();
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { OPENAI_API_KEY: _, password: __, ...settings } = importSettings;
|
||||
const { OPENAI_API_KEY: _, password: __, ...settings } = importAppSettings;
|
||||
|
||||
setSettings({
|
||||
...settings,
|
||||
@@ -56,15 +68,49 @@ export const createSettings: StateCreator<
|
||||
avatar: !get().settings.avatar ? settings.avatar : get().settings.avatar,
|
||||
});
|
||||
},
|
||||
resetAgentConfig: () => {
|
||||
const settings = get().settings;
|
||||
settings.defaultAgent.config = DEFAULT_AGENT.config;
|
||||
set({ settings });
|
||||
},
|
||||
resetAgentMeta: () => {
|
||||
const settings = get().settings;
|
||||
settings.defaultAgent.meta = DEFAULT_AGENT.meta;
|
||||
set({ settings });
|
||||
},
|
||||
resetBaseSettings: () => {
|
||||
const settings = get().settings;
|
||||
|
||||
set({ settings: { ...settings, ...DEFAULT_BASE_SETTINGS } });
|
||||
},
|
||||
resetDefaultAgent: () => {
|
||||
const settings = get().settings;
|
||||
settings.defaultAgent = DEFAULT_AGENT;
|
||||
set({ settings });
|
||||
},
|
||||
resetSettings: () => {
|
||||
set({ settings: DEFAULT_SETTINGS });
|
||||
},
|
||||
setGlobalSettings: (settings) => {
|
||||
setAgentConfig: (config) => {
|
||||
const settings = get().settings;
|
||||
const oldConfig = settings.defaultAgent.config;
|
||||
settings.defaultAgent.config = merge(config, oldConfig);
|
||||
|
||||
set({ settings });
|
||||
},
|
||||
setAgentMeta: (meta) => {
|
||||
const settings = get().settings;
|
||||
const oldMeta = settings.defaultAgent.meta;
|
||||
settings.defaultAgent.meta = merge(meta, oldMeta);
|
||||
|
||||
set({ settings });
|
||||
},
|
||||
setAppSettings: (settings) => {
|
||||
set({ ...settings });
|
||||
},
|
||||
setSettings: (settings) => {
|
||||
const oldSetting = get().settings;
|
||||
set({ settings: { ...oldSetting, ...settings } });
|
||||
set({ settings: merge(settings, oldSetting) });
|
||||
},
|
||||
setThemeMode: (themeMode) => {
|
||||
get().setSettings({ themeMode });
|
||||
@@ -77,4 +123,25 @@ export const createSettings: StateCreator<
|
||||
|
||||
set({ showAgentConfig });
|
||||
},
|
||||
toggleAgentPlugin: (id: string) => {
|
||||
const settings = get().settings;
|
||||
|
||||
settings.defaultAgent.config = produce(
|
||||
settings.defaultAgent.config,
|
||||
(draft: LobeAgentConfig) => {
|
||||
if (draft.plugins === undefined) {
|
||||
draft.plugins = [id];
|
||||
} else {
|
||||
const plugins = draft.plugins;
|
||||
if (plugins.includes(id)) {
|
||||
plugins.splice(plugins.indexOf(id), 1);
|
||||
} else {
|
||||
plugins.push(id);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
set({ settings });
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,32 +1,9 @@
|
||||
import { LanguageModel } from '@/types/llm';
|
||||
import { DEFAULT_SETTINGS } from '@/const/settings';
|
||||
import type { GlobalSettings } from '@/types/settings';
|
||||
|
||||
export type SidebarTabKey = 'chat' | 'market' | 'setting';
|
||||
export type SidebarTabKey = 'chat' | 'market' | 'settings';
|
||||
|
||||
export const DEFAULT_SETTINGS: GlobalSettings = {
|
||||
OPENAI_API_KEY: '',
|
||||
avatar: '',
|
||||
compressThreshold: 24,
|
||||
enableCompressThreshold: false,
|
||||
enableHistoryCount: false,
|
||||
enableMaxTokens: true,
|
||||
endpoint: '',
|
||||
fontSize: 14,
|
||||
frequencyPenalty: 0,
|
||||
historyCount: 24,
|
||||
language: 'zh-CN',
|
||||
maxTokens: 2000,
|
||||
model: LanguageModel.GPT3_5,
|
||||
neutralColor: '',
|
||||
password: '',
|
||||
presencePenalty: 0,
|
||||
primaryColor: '',
|
||||
temperature: 0.5,
|
||||
themeMode: 'auto',
|
||||
topP: 1,
|
||||
};
|
||||
|
||||
export interface SettingsState {
|
||||
export interface AppSettingsState {
|
||||
inputHeight: number;
|
||||
sessionExpandable?: boolean;
|
||||
sessionsWidth: number;
|
||||
@@ -35,7 +12,7 @@ export interface SettingsState {
|
||||
sidebarKey: SidebarTabKey;
|
||||
}
|
||||
|
||||
export const initialState: SettingsState = {
|
||||
export const initialState: AppSettingsState = {
|
||||
inputHeight: 200,
|
||||
sessionExpandable: true,
|
||||
sessionsWidth: 320,
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
import { defaults } from 'lodash-es';
|
||||
|
||||
import { DEFAULT_SETTINGS } from '@/store/settings/initialState';
|
||||
import { DEFAULT_AGENT_META } from '@/const/meta';
|
||||
import { DEFAULT_AGENT, DEFAULT_AGENT_CONFIG, DEFAULT_SETTINGS } from '@/const/settings';
|
||||
import { GlobalSettings } from '@/types/settings';
|
||||
|
||||
import { SettingsStore } from './store';
|
||||
|
||||
const currentSettings = (s: SettingsStore) => defaults(s.settings, DEFAULT_SETTINGS);
|
||||
|
||||
const selecThemeMode = (s: SettingsStore) => ({
|
||||
setThemeMode: s.setThemeMode,
|
||||
themeMode: s.settings.themeMode,
|
||||
});
|
||||
const currentDefaultAgent = (s: SettingsStore) => defaults(s.settings.defaultAgent, DEFAULT_AGENT);
|
||||
|
||||
const currentAgentConfig = (s: SettingsStore) =>
|
||||
defaults(s.settings.defaultAgent.config, DEFAULT_AGENT_CONFIG);
|
||||
|
||||
const currentAgentMeta = (s: SettingsStore) =>
|
||||
defaults(s.settings.defaultAgent.meta, DEFAULT_AGENT_META);
|
||||
|
||||
export const exportSettings = (s: SettingsStore) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
@@ -20,8 +24,9 @@ export const exportSettings = (s: SettingsStore) => {
|
||||
};
|
||||
|
||||
export const settingsSelectors = {
|
||||
currentAgentConfig,
|
||||
currentAgentMeta,
|
||||
currentDefaultAgent,
|
||||
currentSettings,
|
||||
exportSettings,
|
||||
|
||||
selecThemeMode,
|
||||
};
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { StateCreator } from 'zustand/vanilla';
|
||||
|
||||
import { type SettingsAction, createSettings } from './action';
|
||||
import { type SettingsState, initialState } from './initialState';
|
||||
import { type AppSettingsState, initialState } from './initialState';
|
||||
|
||||
export type SettingsStore = SettingsAction & SettingsState;
|
||||
export type SettingsStore = SettingsAction & AppSettingsState;
|
||||
|
||||
export const createStore: StateCreator<SettingsStore, [['zustand/devtools', never]]> = (
|
||||
...parameters
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import { NeutralColors, PrimaryColors } from '@lobehub/ui';
|
||||
import { ThemeMode } from 'antd-style';
|
||||
import type { NeutralColors, PrimaryColors } from '@lobehub/ui';
|
||||
import type { ThemeMode } from 'antd-style';
|
||||
|
||||
import { Locales } from '@/locales/resources';
|
||||
import { LanguageModel } from '@/types/llm';
|
||||
import type { Locales } from '@/locales/resources';
|
||||
import type { LanguageModel } from '@/types/llm';
|
||||
import type { LobeAgentSession } from '@/types/session';
|
||||
|
||||
/**
|
||||
* 配置设置
|
||||
*/
|
||||
export interface GlobalSettings {
|
||||
export interface GlobalBaseSettings {
|
||||
OPENAI_API_KEY: string;
|
||||
avatar: string;
|
||||
compressThreshold: number;
|
||||
@@ -29,4 +27,14 @@ export interface GlobalSettings {
|
||||
themeMode: ThemeMode;
|
||||
topP: number;
|
||||
}
|
||||
|
||||
export type GlobalDefaultAgent = Partial<LobeAgentSession>;
|
||||
|
||||
/**
|
||||
* 配置设置
|
||||
*/
|
||||
export interface GlobalSettings extends GlobalBaseSettings {
|
||||
defaultAgent: GlobalDefaultAgent;
|
||||
}
|
||||
|
||||
export type ConfigKeys = keyof GlobalSettings;
|
||||
|
||||
Reference in New Issue
Block a user