@lobehub/chat 0.127.0 → 0.127.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +50 -0
- package/README.md +8 -8
- package/README.zh-CN.md +8 -8
- package/package.json +1 -1
- package/src/app/chat/(desktop)/layout.desktop.tsx +1 -3
- package/src/app/chat/(mobile)/index.tsx +2 -5
- package/src/app/chat/features/ChatHeader/SettingButton.tsx +1 -2
- package/src/app/home/Redirect.tsx +0 -4
- package/src/app/market/(desktop)/layout.desktop.tsx +1 -4
- package/src/app/market/(mobile)/layout.mobile.tsx +1 -4
- package/src/app/market/features/AgentDetailContent/AgentInfo/Header.tsx +0 -4
- package/src/app/settings/(desktop)/features/Header.tsx +3 -4
- package/src/app/settings/(desktop)/features/SideBar.tsx +42 -0
- package/src/app/settings/(desktop)/index.tsx +4 -3
- package/src/app/settings/(desktop)/layout.desktop.tsx +14 -9
- package/src/app/settings/(desktop)/layout.responsive.tsx +4 -4
- package/src/app/settings/(mobile)/features/ExtraList.tsx +7 -5
- package/src/app/settings/(mobile)/features/Header/index.tsx +8 -5
- package/src/app/settings/(mobile)/index.tsx +1 -3
- package/src/app/settings/(mobile)/layout.mobile.tsx +8 -6
- package/src/app/settings/(mobile)/mobile/index.tsx +3 -5
- package/src/app/settings/agent/Agent.tsx +0 -4
- package/src/app/settings/agent/layout.tsx +9 -1
- package/src/app/settings/agent/page.tsx +17 -2
- package/src/app/settings/common/index.tsx +0 -3
- package/src/app/settings/common/layout.tsx +9 -1
- package/src/app/settings/features/SettingList/Item.tsx +50 -0
- package/src/app/settings/features/{SideBar/List.tsx → SettingList/index.tsx} +14 -15
- package/src/app/settings/layout.server.tsx +10 -3
- package/src/app/settings/llm/Azure/index.tsx +78 -106
- package/src/app/settings/llm/Bedrock/index.tsx +60 -89
- package/src/app/settings/llm/Google/index.tsx +26 -55
- package/src/app/settings/llm/Moonshot/index.tsx +29 -54
- package/src/app/settings/llm/Ollama/index.tsx +35 -61
- package/src/app/settings/llm/OpenAI/index.tsx +91 -107
- package/src/app/settings/llm/Zhipu/index.tsx +29 -54
- package/src/app/settings/llm/components/ProviderConfig/index.tsx +58 -0
- package/src/app/settings/llm/index.tsx +42 -0
- package/src/app/settings/llm/layout.tsx +9 -1
- package/src/app/settings/llm/page.tsx +6 -46
- package/src/app/settings/tts/layout.tsx +9 -1
- package/src/app/settings/tts/page.tsx +17 -2
- package/src/const/settings.ts +1 -0
- package/src/features/MobileTabBar/index.tsx +7 -14
- package/src/features/SideBar/BottomActions.tsx +3 -9
- package/src/features/SideBar/TopActions.tsx +2 -7
- package/src/features/SideBar/index.tsx +7 -5
- package/src/layout/AppLayout.desktop.tsx +8 -3
- package/src/layout/AppLayout.mobile.tsx +4 -2
- package/src/layout/ResponsiveLayout.client.tsx +7 -7
- package/src/layout/ResponsiveLayout.server.tsx +6 -6
- package/src/services/_auth.test.ts +132 -0
- package/src/services/_auth.ts +1 -1
- package/src/store/global/hooks/index.ts +1 -1
- package/src/store/global/slices/common/action.test.ts +0 -12
- package/src/store/global/slices/common/action.ts +1 -6
- package/src/store/global/slices/common/initialState.ts +0 -2
- package/src/store/global/slices/settings/action.test.ts +0 -13
- package/src/store/global/slices/settings/action.ts +0 -5
- package/src/store/global/slices/settings/selectors/modelProvider.ts +7 -0
- package/src/types/settings/modelProvider.ts +1 -0
- package/src/app/settings/agent/index.tsx +0 -21
- package/src/app/settings/features/SideBar/Item.tsx +0 -44
- package/src/app/settings/features/SideBar/index.tsx +0 -42
- package/src/app/settings/tts/index.tsx +0 -21
- package/src/store/global/hooks/useSwitchSettingsOnInit.ts +0 -12
- package/src/store/global/hooks/useSwitchSideBarOnInit.ts +0 -13
- /package/src/app/settings/llm/{Checker.tsx → components/Checker.tsx} +0 -0
- /package/src/app/settings/llm/{useSyncSettings.ts → components/ProviderConfig/useSyncSettings.ts} +0 -0
|
@@ -1,49 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
import { getServerConfig } from '@/config/server';
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import { memo } from 'react';
|
|
5
|
-
import { Trans, useTranslation } from 'react-i18next';
|
|
3
|
+
import Page from './index';
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
import { MORE_MODEL_PROVIDER_REQUEST_URL } from '@/const/url';
|
|
10
|
-
import { useGlobalStore } from '@/store/global';
|
|
11
|
-
import { useSwitchSideBarOnInit } from '@/store/global/hooks/useSwitchSettingsOnInit';
|
|
12
|
-
import { SettingsTabs } from '@/store/global/initialState';
|
|
13
|
-
import { modelProviderSelectors } from '@/store/global/selectors';
|
|
5
|
+
export default () => {
|
|
6
|
+
const { ENABLE_OLLAMA } = getServerConfig();
|
|
14
7
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
import Moonshot from './Moonshot';
|
|
18
|
-
import Ollama from './Ollama';
|
|
19
|
-
import OpenAI from './OpenAI';
|
|
20
|
-
import Zhipu from './Zhipu';
|
|
21
|
-
|
|
22
|
-
export default memo(() => {
|
|
23
|
-
useSwitchSideBarOnInit(SettingsTabs.LLM);
|
|
24
|
-
const enableOllamaFormServerConfig = useGlobalStore(
|
|
25
|
-
modelProviderSelectors.enableOllamaConfigInSettings,
|
|
26
|
-
);
|
|
27
|
-
const { t } = useTranslation('setting');
|
|
28
|
-
return (
|
|
29
|
-
<>
|
|
30
|
-
<PageTitle title={t('tab.llm')} />
|
|
31
|
-
<OpenAI />
|
|
32
|
-
{/*<AzureOpenAI />*/}
|
|
33
|
-
<Zhipu />
|
|
34
|
-
<Moonshot />
|
|
35
|
-
<Google />
|
|
36
|
-
<Bedrock />
|
|
37
|
-
{enableOllamaFormServerConfig && <Ollama />}
|
|
38
|
-
<Footer>
|
|
39
|
-
<Trans i18nKey="llm.waitingForMore" ns={'setting'}>
|
|
40
|
-
更多模型正在
|
|
41
|
-
<Link aria-label={'todo'} href={MORE_MODEL_PROVIDER_REQUEST_URL} target="_blank">
|
|
42
|
-
计划接入
|
|
43
|
-
</Link>
|
|
44
|
-
中 ,敬请期待 ✨
|
|
45
|
-
</Trans>
|
|
46
|
-
</Footer>
|
|
47
|
-
</>
|
|
48
|
-
);
|
|
49
|
-
});
|
|
8
|
+
return <Page showOllama={ENABLE_OLLAMA} />;
|
|
9
|
+
};
|
|
@@ -1 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
import { PropsWithChildren } from 'react';
|
|
2
|
+
|
|
3
|
+
import { SettingsTabs } from '@/store/global/initialState';
|
|
4
|
+
|
|
5
|
+
import SettingLayout from '../layout.server';
|
|
6
|
+
|
|
7
|
+
export default ({ children }: PropsWithChildren) => {
|
|
8
|
+
return <SettingLayout activeTab={SettingsTabs.TTS}>{children}</SettingLayout>;
|
|
9
|
+
};
|
|
@@ -1,3 +1,18 @@
|
|
|
1
|
-
|
|
1
|
+
'use client';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import { memo } from 'react';
|
|
4
|
+
import { useTranslation } from 'react-i18next';
|
|
5
|
+
|
|
6
|
+
import PageTitle from '@/components/PageTitle';
|
|
7
|
+
|
|
8
|
+
import TTS from './TTS';
|
|
9
|
+
|
|
10
|
+
export default memo(() => {
|
|
11
|
+
const { t } = useTranslation('setting');
|
|
12
|
+
return (
|
|
13
|
+
<>
|
|
14
|
+
<PageTitle title={t('tab.llm')} />
|
|
15
|
+
<TTS />
|
|
16
|
+
</>
|
|
17
|
+
);
|
|
18
|
+
});
|
package/src/const/settings.ts
CHANGED
|
@@ -6,7 +6,6 @@ import { rgba } from 'polished';
|
|
|
6
6
|
import { memo, useMemo } from 'react';
|
|
7
7
|
import { useTranslation } from 'react-i18next';
|
|
8
8
|
|
|
9
|
-
import { useGlobalStore } from '@/store/global';
|
|
10
9
|
import { SidebarTabKey } from '@/store/global/initialState';
|
|
11
10
|
|
|
12
11
|
const useStyles = createStyles(({ css, token }) => ({
|
|
@@ -17,8 +16,12 @@ const useStyles = createStyles(({ css, token }) => ({
|
|
|
17
16
|
`,
|
|
18
17
|
}));
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
interface Props {
|
|
20
|
+
className?: string;
|
|
21
|
+
tabBarKey?: SidebarTabKey;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default memo<Props>(({ className, tabBarKey }) => {
|
|
22
25
|
const { t } = useTranslation('common');
|
|
23
26
|
const { styles } = useStyles();
|
|
24
27
|
const router = useRouter();
|
|
@@ -30,7 +33,6 @@ export default memo<{ className?: string }>(({ className }) => {
|
|
|
30
33
|
),
|
|
31
34
|
key: SidebarTabKey.Chat,
|
|
32
35
|
onClick: () => {
|
|
33
|
-
setTab(SidebarTabKey.Chat);
|
|
34
36
|
router.push('/chat');
|
|
35
37
|
},
|
|
36
38
|
title: t('tab.chat'),
|
|
@@ -39,7 +41,6 @@ export default memo<{ className?: string }>(({ className }) => {
|
|
|
39
41
|
icon: (active) => <Icon className={active ? styles.active : undefined} icon={Bot} />,
|
|
40
42
|
key: SidebarTabKey.Market,
|
|
41
43
|
onClick: () => {
|
|
42
|
-
setTab(SidebarTabKey.Market);
|
|
43
44
|
router.push('/market');
|
|
44
45
|
},
|
|
45
46
|
title: t('tab.market'),
|
|
@@ -48,7 +49,6 @@ export default memo<{ className?: string }>(({ className }) => {
|
|
|
48
49
|
icon: (active) => <Icon className={active ? styles.active : undefined} icon={User} />,
|
|
49
50
|
key: SidebarTabKey.Setting,
|
|
50
51
|
onClick: () => {
|
|
51
|
-
setTab(SidebarTabKey.Setting);
|
|
52
52
|
router.push('/settings');
|
|
53
53
|
},
|
|
54
54
|
title: t('tab.setting'),
|
|
@@ -56,12 +56,5 @@ export default memo<{ className?: string }>(({ className }) => {
|
|
|
56
56
|
],
|
|
57
57
|
[t],
|
|
58
58
|
);
|
|
59
|
-
return
|
|
60
|
-
<MobileTabBar
|
|
61
|
-
activeKey={tab}
|
|
62
|
-
className={className}
|
|
63
|
-
items={items}
|
|
64
|
-
onChange={(key) => setTab(key as any)}
|
|
65
|
-
/>
|
|
66
|
-
);
|
|
59
|
+
return <MobileTabBar activeKey={tabBarKey} className={className} items={items} />;
|
|
67
60
|
});
|
|
@@ -20,14 +20,13 @@ import { ABOUT, CHANGELOG, DISCORD, FEEDBACK, GITHUB, WIKI } from '@/const/url';
|
|
|
20
20
|
import DataImporter from '@/features/DataImporter';
|
|
21
21
|
import { configService } from '@/services/config';
|
|
22
22
|
import { GlobalStore, useGlobalStore } from '@/store/global';
|
|
23
|
-
import {
|
|
23
|
+
import { SidebarTabKey } from '@/store/global/initialState';
|
|
24
24
|
|
|
25
25
|
export interface BottomActionProps {
|
|
26
|
-
|
|
27
|
-
tab: GlobalStore['sidebarKey'];
|
|
26
|
+
tab?: GlobalStore['sidebarKey'];
|
|
28
27
|
}
|
|
29
28
|
|
|
30
|
-
const BottomActions = memo<BottomActionProps>(({ tab
|
|
29
|
+
const BottomActions = memo<BottomActionProps>(({ tab }) => {
|
|
31
30
|
const router = useRouter();
|
|
32
31
|
const { t } = useTranslation('common');
|
|
33
32
|
|
|
@@ -113,11 +112,6 @@ const BottomActions = memo<BottomActionProps>(({ tab, setTab }) => {
|
|
|
113
112
|
</Flexbox>
|
|
114
113
|
),
|
|
115
114
|
onClick: () => {
|
|
116
|
-
setTab(SidebarTabKey.Setting);
|
|
117
|
-
useGlobalStore.setState({
|
|
118
|
-
settingsTab: SettingsTabs.Common,
|
|
119
|
-
sidebarKey: SidebarTabKey.Setting,
|
|
120
|
-
});
|
|
121
115
|
router.push('/settings/common');
|
|
122
116
|
},
|
|
123
117
|
},
|
|
@@ -9,11 +9,10 @@ import { SidebarTabKey } from '@/store/global/initialState';
|
|
|
9
9
|
import { useSessionStore } from '@/store/session';
|
|
10
10
|
|
|
11
11
|
export interface TopActionProps {
|
|
12
|
-
|
|
13
|
-
tab: GlobalStore['sidebarKey'];
|
|
12
|
+
tab?: GlobalStore['sidebarKey'];
|
|
14
13
|
}
|
|
15
14
|
|
|
16
|
-
const TopActions = memo<TopActionProps>(({ tab
|
|
15
|
+
const TopActions = memo<TopActionProps>(({ tab }) => {
|
|
17
16
|
const { t } = useTranslation('common');
|
|
18
17
|
const switchBackToChat = useGlobalStore((s) => s.switchBackToChat);
|
|
19
18
|
|
|
@@ -24,7 +23,6 @@ const TopActions = memo<TopActionProps>(({ tab, setTab }) => {
|
|
|
24
23
|
onClick={(e) => {
|
|
25
24
|
e.preventDefault();
|
|
26
25
|
switchBackToChat(useSessionStore.getState().activeId);
|
|
27
|
-
setTab(SidebarTabKey.Chat);
|
|
28
26
|
}}
|
|
29
27
|
>
|
|
30
28
|
<ActionIcon
|
|
@@ -39,9 +37,6 @@ const TopActions = memo<TopActionProps>(({ tab, setTab }) => {
|
|
|
39
37
|
<ActionIcon
|
|
40
38
|
active={tab === SidebarTabKey.Market}
|
|
41
39
|
icon={Compass}
|
|
42
|
-
onClick={() => {
|
|
43
|
-
setTab(SidebarTabKey.Market);
|
|
44
|
-
}}
|
|
45
40
|
placement={'right'}
|
|
46
41
|
size="large"
|
|
47
42
|
title={t('tab.market')}
|
|
@@ -2,20 +2,22 @@ import { SideNav } from '@lobehub/ui';
|
|
|
2
2
|
import { memo } from 'react';
|
|
3
3
|
|
|
4
4
|
import AvatarWithUpload from '@/features/AvatarWithUpload';
|
|
5
|
-
import {
|
|
5
|
+
import { SidebarTabKey } from '@/store/global/initialState';
|
|
6
6
|
|
|
7
7
|
import BottomActions from './BottomActions';
|
|
8
8
|
import TopActions from './TopActions';
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
interface Props {
|
|
11
|
+
sidebarKey?: SidebarTabKey;
|
|
12
|
+
}
|
|
12
13
|
|
|
14
|
+
export default memo<Props>(({ sidebarKey }) => {
|
|
13
15
|
return (
|
|
14
16
|
<SideNav
|
|
15
17
|
avatar={<AvatarWithUpload id={'avatar'} />}
|
|
16
|
-
bottomActions={<BottomActions
|
|
18
|
+
bottomActions={<BottomActions tab={sidebarKey} />}
|
|
17
19
|
style={{ height: '100%' }}
|
|
18
|
-
topActions={<TopActions
|
|
20
|
+
topActions={<TopActions tab={sidebarKey} />}
|
|
19
21
|
/>
|
|
20
22
|
);
|
|
21
23
|
});
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { useTheme } from 'antd-style';
|
|
2
|
-
import {
|
|
2
|
+
import { ReactNode, memo } from 'react';
|
|
3
3
|
import { Flexbox } from 'react-layout-kit';
|
|
4
4
|
|
|
5
5
|
import SideBar from '@/features/SideBar';
|
|
6
6
|
import { useIsPWA } from '@/hooks/useIsPWA';
|
|
7
|
+
import { SidebarTabKey } from '@/store/global/initialState';
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
interface AppLayoutDesktopProps {
|
|
10
|
+
children: ReactNode;
|
|
11
|
+
sidebarKey?: SidebarTabKey;
|
|
12
|
+
}
|
|
13
|
+
const AppLayoutDesktop = memo<AppLayoutDesktopProps>(({ children, sidebarKey }) => {
|
|
9
14
|
const isPWA = useIsPWA();
|
|
10
15
|
const theme = useTheme();
|
|
11
16
|
|
|
@@ -16,7 +21,7 @@ const AppLayoutDesktop = memo<PropsWithChildren>(({ children }) => {
|
|
|
16
21
|
style={isPWA ? { borderTop: `1px solid ${theme.colorBorder}` } : {}}
|
|
17
22
|
width={'100%'}
|
|
18
23
|
>
|
|
19
|
-
<SideBar />
|
|
24
|
+
<SideBar sidebarKey={sidebarKey} />
|
|
20
25
|
{children}
|
|
21
26
|
</Flexbox>
|
|
22
27
|
);
|
|
@@ -5,6 +5,7 @@ import { CSSProperties, PropsWithChildren, ReactNode, memo } from 'react';
|
|
|
5
5
|
import { Flexbox } from 'react-layout-kit';
|
|
6
6
|
|
|
7
7
|
import SafeSpacing from '@/components/SafeSpacing';
|
|
8
|
+
import { SidebarTabKey } from '@/store/global/slices/common/initialState';
|
|
8
9
|
|
|
9
10
|
const MobileTabBar = dynamic(() => import('@/features/MobileTabBar'));
|
|
10
11
|
|
|
@@ -39,11 +40,12 @@ interface AppMobileLayoutProps extends PropsWithChildren {
|
|
|
39
40
|
navBar?: ReactNode;
|
|
40
41
|
showTabBar?: boolean;
|
|
41
42
|
style?: CSSProperties;
|
|
43
|
+
tabBarKey?: SidebarTabKey;
|
|
42
44
|
title?: MobileNavBarTitleProps;
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
const AppLayoutMobile = memo<AppMobileLayoutProps>(
|
|
46
|
-
({ children, showTabBar, navBar, style, className }) => {
|
|
48
|
+
({ children, showTabBar, tabBarKey, navBar, style, className }) => {
|
|
47
49
|
const { styles, cx } = useStyles();
|
|
48
50
|
|
|
49
51
|
return (
|
|
@@ -58,7 +60,7 @@ const AppLayoutMobile = memo<AppMobileLayoutProps>(
|
|
|
58
60
|
{showTabBar && (
|
|
59
61
|
<>
|
|
60
62
|
<SafeSpacing mobile position={'bottom'} />
|
|
61
|
-
<MobileTabBar className={styles.mobileTabBar} />
|
|
63
|
+
<MobileTabBar className={styles.mobileTabBar} tabBarKey={tabBarKey} />
|
|
62
64
|
</>
|
|
63
65
|
)}
|
|
64
66
|
</Flexbox>
|
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { ReactNode, Suspense } from 'react';
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
5
|
|
|
6
6
|
import FullscreenLoading from '@/components/FullscreenLoading';
|
|
7
7
|
import { useIsMobile } from '@/hooks/useIsMobile';
|
|
8
8
|
|
|
9
|
-
interface ServerResponsiveLayoutProps {
|
|
10
|
-
Desktop: (props:
|
|
11
|
-
Mobile: (props:
|
|
9
|
+
interface ServerResponsiveLayoutProps extends Record<string, any> {
|
|
10
|
+
Desktop: (props: any) => ReactNode;
|
|
11
|
+
Mobile: (props: any) => ReactNode;
|
|
12
12
|
children?: ReactNode;
|
|
13
13
|
}
|
|
14
|
-
const ResponsiveLayout = ({ children, Desktop, Mobile }: ServerResponsiveLayoutProps) => {
|
|
14
|
+
const ResponsiveLayout = ({ children, Desktop, Mobile, ...res }: ServerResponsiveLayoutProps) => {
|
|
15
15
|
const { t } = useTranslation();
|
|
16
16
|
const mobile = useIsMobile();
|
|
17
17
|
|
|
18
18
|
return mobile ? (
|
|
19
19
|
<Suspense fallback={<FullscreenLoading title={t('layoutInitializing', { ns: 'common' })} />}>
|
|
20
|
-
<Mobile>{children}</Mobile>
|
|
20
|
+
<Mobile {...res}>{children}</Mobile>
|
|
21
21
|
</Suspense>
|
|
22
22
|
) : (
|
|
23
|
-
<Desktop>{children}</Desktop>
|
|
23
|
+
<Desktop {...res}>{children}</Desktop>
|
|
24
24
|
);
|
|
25
25
|
};
|
|
26
26
|
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
2
|
|
|
3
3
|
import { isMobileDevice } from '@/utils/responsive';
|
|
4
4
|
|
|
5
|
-
interface ServerResponsiveLayoutProps {
|
|
6
|
-
Desktop: (props:
|
|
7
|
-
Mobile: (props:
|
|
5
|
+
interface ServerResponsiveLayoutProps extends Record<string, any> {
|
|
6
|
+
Desktop: (props: any) => ReactNode;
|
|
7
|
+
Mobile: (props: any) => ReactNode;
|
|
8
8
|
children?: ReactNode;
|
|
9
9
|
}
|
|
10
|
-
const ResponsiveLayout = ({ children, Desktop, Mobile }: ServerResponsiveLayoutProps) => {
|
|
10
|
+
const ResponsiveLayout = ({ children, Desktop, Mobile, ...res }: ServerResponsiveLayoutProps) => {
|
|
11
11
|
const mobile = isMobileDevice();
|
|
12
12
|
|
|
13
|
-
return mobile ? <Mobile>{children}</Mobile> : <Desktop>{children}</Desktop>;
|
|
13
|
+
return mobile ? <Mobile {...res}>{children}</Mobile> : <Desktop {...res}>{children}</Desktop>;
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
export default ResponsiveLayout;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { act } from '@testing-library/react';
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
3
|
+
|
|
4
|
+
import { ModelProvider } from '@/libs/agent-runtime';
|
|
5
|
+
import { useGlobalStore } from '@/store/global';
|
|
6
|
+
import { GlobalLLMConfig, GlobalLLMProviderKey } from '@/types/settings';
|
|
7
|
+
|
|
8
|
+
import { getProviderAuthPayload } from './_auth';
|
|
9
|
+
|
|
10
|
+
// Mock data for different providers
|
|
11
|
+
const mockZhiPuAPIKey = 'zhipu-api-key';
|
|
12
|
+
const mockMoonshotAPIKey = 'moonshot-api-key';
|
|
13
|
+
const mockGoogleAPIKey = 'google-api-key';
|
|
14
|
+
|
|
15
|
+
// mock the traditional zustand
|
|
16
|
+
vi.mock('zustand/traditional');
|
|
17
|
+
|
|
18
|
+
const setModelProviderConfig = <T extends GlobalLLMProviderKey>(
|
|
19
|
+
provider: T,
|
|
20
|
+
config: Partial<GlobalLLMConfig[T]>,
|
|
21
|
+
) => {
|
|
22
|
+
useGlobalStore.setState({
|
|
23
|
+
settings: { languageModel: { [provider]: config } },
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
describe('getProviderAuthPayload', () => {
|
|
28
|
+
it('should return correct payload for ZhiPu provider', () => {
|
|
29
|
+
act(() => {
|
|
30
|
+
setModelProviderConfig('zhipu', { apiKey: mockZhiPuAPIKey });
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const payload = getProviderAuthPayload(ModelProvider.ZhiPu);
|
|
34
|
+
expect(payload).toEqual({ apiKey: mockZhiPuAPIKey });
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should return correct payload for Moonshot provider', () => {
|
|
38
|
+
act(() => {
|
|
39
|
+
setModelProviderConfig('moonshot', { apiKey: mockMoonshotAPIKey });
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const payload = getProviderAuthPayload(ModelProvider.Moonshot);
|
|
43
|
+
expect(payload).toEqual({ apiKey: mockMoonshotAPIKey });
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should return correct payload for Google provider', () => {
|
|
47
|
+
act(() => {
|
|
48
|
+
setModelProviderConfig('google', { apiKey: mockGoogleAPIKey });
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const payload = getProviderAuthPayload(ModelProvider.Google);
|
|
52
|
+
expect(payload).toEqual({ apiKey: mockGoogleAPIKey });
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should return correct payload for Bedrock provider', () => {
|
|
56
|
+
// 假设的 Bedrock 配置
|
|
57
|
+
const mockBedrockConfig = {
|
|
58
|
+
accessKeyId: 'bedrock-access-key-id',
|
|
59
|
+
region: 'bedrock-region',
|
|
60
|
+
secretAccessKey: 'bedrock-secret-access-key',
|
|
61
|
+
};
|
|
62
|
+
act(() => {
|
|
63
|
+
setModelProviderConfig('bedrock', mockBedrockConfig);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const payload = getProviderAuthPayload(ModelProvider.Bedrock);
|
|
67
|
+
expect(payload).toEqual({
|
|
68
|
+
apiKey: mockBedrockConfig.secretAccessKey + mockBedrockConfig.accessKeyId,
|
|
69
|
+
awsAccessKeyId: mockBedrockConfig.accessKeyId,
|
|
70
|
+
awsRegion: mockBedrockConfig.region,
|
|
71
|
+
awsSecretAccessKey: mockBedrockConfig.secretAccessKey,
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should return correct payload for Azure provider', () => {
|
|
76
|
+
// 假设的 Azure 配置
|
|
77
|
+
const mockAzureConfig = {
|
|
78
|
+
apiKey: 'azure-api-key',
|
|
79
|
+
apiVersion: 'azure-api-version',
|
|
80
|
+
endpoint: 'azure-endpoint',
|
|
81
|
+
};
|
|
82
|
+
act(() => {
|
|
83
|
+
setModelProviderConfig('azure', mockAzureConfig);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const payload = getProviderAuthPayload(ModelProvider.Azure);
|
|
87
|
+
expect(payload).toEqual({
|
|
88
|
+
apiKey: mockAzureConfig.apiKey,
|
|
89
|
+
azureApiVersion: mockAzureConfig.apiVersion,
|
|
90
|
+
endpoint: mockAzureConfig.endpoint,
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should return correct payload for Ollama provider', () => {
|
|
95
|
+
// 假设的 Ollama 配置
|
|
96
|
+
const mockOllamaProxyUrl = 'ollama-proxy-url';
|
|
97
|
+
act(() => {
|
|
98
|
+
setModelProviderConfig('ollama', { endpoint: mockOllamaProxyUrl });
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const payload = getProviderAuthPayload(ModelProvider.Ollama);
|
|
102
|
+
expect(payload).toEqual({
|
|
103
|
+
endpoint: mockOllamaProxyUrl,
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should return correct payload for OpenAI provider', () => {
|
|
108
|
+
// 假设的 OpenAI 配置
|
|
109
|
+
const mockOpenAIConfig = {
|
|
110
|
+
OPENAI_API_KEY: 'openai-api-key',
|
|
111
|
+
endpoint: 'openai-endpoint',
|
|
112
|
+
useAzure: true,
|
|
113
|
+
azureApiVersion: 'openai-azure-api-version',
|
|
114
|
+
};
|
|
115
|
+
act(() => {
|
|
116
|
+
setModelProviderConfig('openAI', mockOpenAIConfig);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const payload = getProviderAuthPayload(ModelProvider.OpenAI);
|
|
120
|
+
expect(payload).toEqual({
|
|
121
|
+
apiKey: mockOpenAIConfig.OPENAI_API_KEY,
|
|
122
|
+
azureApiVersion: mockOpenAIConfig.azureApiVersion,
|
|
123
|
+
endpoint: mockOpenAIConfig.endpoint,
|
|
124
|
+
useAzure: mockOpenAIConfig.useAzure,
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('should return an empty object or throw an error for an unknown provider', () => {
|
|
129
|
+
const payload = getProviderAuthPayload('UnknownProvider');
|
|
130
|
+
expect(payload).toEqual({ apiKey: '', endpoint: '' });
|
|
131
|
+
});
|
|
132
|
+
});
|
package/src/services/_auth.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { useGlobalStore } from '@/store/global';
|
|
|
4
4
|
import { modelProviderSelectors, settingsSelectors } from '@/store/global/selectors';
|
|
5
5
|
import { createJWT } from '@/utils/jwt';
|
|
6
6
|
|
|
7
|
-
const getProviderAuthPayload = (provider: string) => {
|
|
7
|
+
export const getProviderAuthPayload = (provider: string) => {
|
|
8
8
|
switch (provider) {
|
|
9
9
|
case ModelProvider.ZhiPu: {
|
|
10
10
|
return { apiKey: modelProviderSelectors.zhipuAPIKey(useGlobalStore.getState()) };
|
|
@@ -38,16 +38,4 @@ describe('createCommonSlice', () => {
|
|
|
38
38
|
expect(router.push).toHaveBeenCalledWith('/chat?session=session-id');
|
|
39
39
|
});
|
|
40
40
|
});
|
|
41
|
-
|
|
42
|
-
describe('switchSideBar', () => {
|
|
43
|
-
it('should switch sidebar', () => {
|
|
44
|
-
const { result } = renderHook(() => useGlobalStore());
|
|
45
|
-
|
|
46
|
-
act(() => {
|
|
47
|
-
result.current.switchSideBar(SidebarTabKey.Market);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
expect(result.current.sidebarKey).toEqual(SidebarTabKey.Market);
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
41
|
});
|
|
@@ -13,8 +13,6 @@ import type { GlobalServerConfig, GlobalSettings } from '@/types/settings';
|
|
|
13
13
|
import { merge } from '@/utils/merge';
|
|
14
14
|
import { setNamespace } from '@/utils/storeDebug';
|
|
15
15
|
|
|
16
|
-
import type { SidebarTabKey } from './initialState';
|
|
17
|
-
|
|
18
16
|
const n = setNamespace('common');
|
|
19
17
|
|
|
20
18
|
/**
|
|
@@ -23,7 +21,6 @@ const n = setNamespace('common');
|
|
|
23
21
|
export interface CommonAction {
|
|
24
22
|
refreshUserConfig: () => Promise<void>;
|
|
25
23
|
switchBackToChat: (sessionId?: string) => void;
|
|
26
|
-
switchSideBar: (key: SidebarTabKey) => void;
|
|
27
24
|
updateAvatar: (avatar: string) => Promise<void>;
|
|
28
25
|
useCheckLatestVersion: () => SWRResponse<string>;
|
|
29
26
|
useFetchServerConfig: () => SWRResponse;
|
|
@@ -44,9 +41,7 @@ export const createCommonSlice: StateCreator<
|
|
|
44
41
|
switchBackToChat: (sessionId) => {
|
|
45
42
|
get().router?.push(SESSION_CHAT_URL(sessionId || INBOX_SESSION_ID, get().isMobile));
|
|
46
43
|
},
|
|
47
|
-
|
|
48
|
-
set({ sidebarKey: key }, false, n('switchSideBar', key));
|
|
49
|
-
},
|
|
44
|
+
|
|
50
45
|
updateAvatar: async (avatar) => {
|
|
51
46
|
await userService.updateAvatar(avatar);
|
|
52
47
|
await get().refreshUserConfig();
|
|
@@ -23,12 +23,10 @@ export interface GlobalCommonState {
|
|
|
23
23
|
isMobile?: boolean;
|
|
24
24
|
latestVersion?: string;
|
|
25
25
|
router?: AppRouterInstance;
|
|
26
|
-
settingsTab: SettingsTabs;
|
|
27
26
|
sidebarKey: SidebarTabKey;
|
|
28
27
|
}
|
|
29
28
|
|
|
30
29
|
export const initialCommonState: GlobalCommonState = {
|
|
31
30
|
isMobile: false,
|
|
32
|
-
settingsTab: SettingsTabs.Common,
|
|
33
31
|
sidebarKey: SidebarTabKey.Chat,
|
|
34
32
|
};
|
|
@@ -6,7 +6,6 @@ import { withSWR } from '~test-utils';
|
|
|
6
6
|
import { DEFAULT_AGENT, DEFAULT_SETTINGS } from '@/const/settings';
|
|
7
7
|
import { userService } from '@/services/user';
|
|
8
8
|
import { useGlobalStore } from '@/store/global';
|
|
9
|
-
import { SettingsTabs } from '@/store/global/initialState';
|
|
10
9
|
import { LobeAgentSettings } from '@/types/session';
|
|
11
10
|
import { GlobalSettings, OpenAIConfig } from '@/types/settings';
|
|
12
11
|
|
|
@@ -100,18 +99,6 @@ describe('SettingsAction', () => {
|
|
|
100
99
|
});
|
|
101
100
|
});
|
|
102
101
|
|
|
103
|
-
describe('switchSettingTabs', () => {
|
|
104
|
-
it('should switch settings tabs', () => {
|
|
105
|
-
const { result } = renderHook(() => useGlobalStore());
|
|
106
|
-
|
|
107
|
-
act(() => {
|
|
108
|
-
result.current.switchSettingTabs(SettingsTabs.Agent);
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
expect(result.current.settingsTab).toEqual(SettingsTabs.Agent);
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
|
|
115
102
|
describe('switchThemeMode', () => {
|
|
116
103
|
it('should switch theme mode', async () => {
|
|
117
104
|
const { result } = renderHook(() => useGlobalStore());
|
|
@@ -5,7 +5,6 @@ import type { StateCreator } from 'zustand/vanilla';
|
|
|
5
5
|
|
|
6
6
|
import { userService } from '@/services/user';
|
|
7
7
|
import type { GlobalStore } from '@/store/global';
|
|
8
|
-
import { SettingsTabs } from '@/store/global/initialState';
|
|
9
8
|
import { LobeAgentSettings } from '@/types/session';
|
|
10
9
|
import { GlobalLLMConfig, GlobalLLMProviderKey, GlobalSettings } from '@/types/settings';
|
|
11
10
|
import { difference } from '@/utils/difference';
|
|
@@ -22,7 +21,6 @@ export interface SettingsAction {
|
|
|
22
21
|
config: Partial<GlobalLLMConfig[T]>,
|
|
23
22
|
) => Promise<void>;
|
|
24
23
|
setSettings: (settings: DeepPartial<GlobalSettings>) => Promise<void>;
|
|
25
|
-
switchSettingTabs: (tab: SettingsTabs) => void;
|
|
26
24
|
switchThemeMode: (themeMode: ThemeMode) => Promise<void>;
|
|
27
25
|
toggleProviderEnabled: (provider: GlobalLLMProviderKey, enabled: boolean) => Promise<void>;
|
|
28
26
|
updateDefaultAgent: (agent: DeepPartial<LobeAgentSettings>) => Promise<void>;
|
|
@@ -61,9 +59,6 @@ export const createSettingsSlice: StateCreator<
|
|
|
61
59
|
await get().refreshUserConfig();
|
|
62
60
|
},
|
|
63
61
|
|
|
64
|
-
switchSettingTabs: (tab) => {
|
|
65
|
-
set({ settingsTab: tab });
|
|
66
|
-
},
|
|
67
62
|
switchThemeMode: async (themeMode) => {
|
|
68
63
|
await get().setSettings({ themeMode });
|
|
69
64
|
},
|
|
@@ -10,12 +10,16 @@ import {
|
|
|
10
10
|
ZhiPuProvider,
|
|
11
11
|
} from '@/config/modelProviders';
|
|
12
12
|
import { ChatModelCard, ModelProviderCard } from '@/types/llm';
|
|
13
|
+
import { GlobalLLMProviderKey } from '@/types/settings';
|
|
13
14
|
import { parseModelString } from '@/utils/parseModels';
|
|
14
15
|
|
|
15
16
|
import { GlobalStore } from '../../../store';
|
|
16
17
|
import { currentSettings } from './settings';
|
|
17
18
|
|
|
18
19
|
const modelProvider = (s: GlobalStore) => currentSettings(s).languageModel;
|
|
20
|
+
const providerEnabled = (provider: GlobalLLMProviderKey) => (s: GlobalStore) =>
|
|
21
|
+
currentSettings(s).languageModel[provider]?.enabled || false;
|
|
22
|
+
|
|
19
23
|
const openAIConfig = (s: GlobalStore) => modelProvider(s).openAI;
|
|
20
24
|
|
|
21
25
|
const openAIAPIKey = (s: GlobalStore) => openAIConfig(s).OPENAI_API_KEY;
|
|
@@ -169,6 +173,9 @@ export const modelProviderSelectors = {
|
|
|
169
173
|
modelEnabledFiles,
|
|
170
174
|
modelEnabledUpload,
|
|
171
175
|
|
|
176
|
+
modelProviderConfig: modelProvider,
|
|
177
|
+
providerEnabled,
|
|
178
|
+
|
|
172
179
|
// OpenAI
|
|
173
180
|
openAIConfig,
|
|
174
181
|
openAIAPIKey,
|