@lobehub/chat 0.162.16 → 0.162.18
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/.env.example +4 -0
- package/CHANGELOG.md +50 -0
- package/Dockerfile +3 -0
- package/README.md +1 -0
- package/README.zh-CN.md +1 -0
- package/docs/self-hosting/advanced/settings-url-share.mdx +89 -57
- package/docs/self-hosting/advanced/settings-url-share.zh-CN.mdx +87 -56
- package/docs/self-hosting/advanced/upstream-sync.mdx +1 -1
- package/docs/self-hosting/environment-variables/model-provider.mdx +10 -1
- package/docs/self-hosting/environment-variables/model-provider.zh-CN.mdx +9 -0
- package/docs/usage/features/multi-ai-providers.mdx +1 -0
- package/docs/usage/features/multi-ai-providers.zh-CN.mdx +1 -0
- package/locales/ar/error.json +7 -37
- package/locales/ar/modelProvider.json +15 -171
- package/locales/ar/setting.json +5 -8
- package/locales/bg-BG/error.json +6 -36
- package/locales/bg-BG/modelProvider.json +15 -171
- package/locales/bg-BG/setting.json +5 -8
- package/locales/de-DE/error.json +6 -36
- package/locales/de-DE/modelProvider.json +15 -171
- package/locales/de-DE/setting.json +5 -8
- package/locales/en-US/error.json +6 -36
- package/locales/en-US/modelProvider.json +15 -171
- package/locales/en-US/setting.json +6 -9
- package/locales/es-ES/error.json +6 -36
- package/locales/es-ES/modelProvider.json +15 -171
- package/locales/es-ES/setting.json +5 -8
- package/locales/fr-FR/error.json +6 -36
- package/locales/fr-FR/modelProvider.json +15 -171
- package/locales/fr-FR/setting.json +9 -12
- package/locales/it-IT/error.json +6 -36
- package/locales/it-IT/modelProvider.json +15 -171
- package/locales/it-IT/setting.json +5 -8
- package/locales/ja-JP/error.json +6 -36
- package/locales/ja-JP/modelProvider.json +15 -171
- package/locales/ja-JP/setting.json +5 -8
- package/locales/ko-KR/error.json +6 -36
- package/locales/ko-KR/modelProvider.json +15 -171
- package/locales/ko-KR/setting.json +5 -8
- package/locales/nl-NL/error.json +6 -36
- package/locales/nl-NL/modelProvider.json +15 -171
- package/locales/nl-NL/setting.json +5 -8
- package/locales/pl-PL/error.json +6 -36
- package/locales/pl-PL/modelProvider.json +15 -171
- package/locales/pl-PL/setting.json +6 -9
- package/locales/pt-BR/error.json +6 -36
- package/locales/pt-BR/modelProvider.json +15 -171
- package/locales/pt-BR/setting.json +5 -8
- package/locales/ru-RU/error.json +6 -36
- package/locales/ru-RU/modelProvider.json +15 -171
- package/locales/ru-RU/setting.json +5 -8
- package/locales/tr-TR/error.json +6 -36
- package/locales/tr-TR/modelProvider.json +15 -171
- package/locales/tr-TR/setting.json +5 -8
- package/locales/vi-VN/error.json +6 -36
- package/locales/vi-VN/modelProvider.json +15 -171
- package/locales/vi-VN/setting.json +5 -8
- package/locales/zh-CN/error.json +7 -37
- package/locales/zh-CN/modelProvider.json +15 -171
- package/locales/zh-CN/setting.json +5 -8
- package/locales/zh-TW/error.json +7 -37
- package/locales/zh-TW/modelProvider.json +15 -171
- package/locales/zh-TW/setting.json +5 -8
- package/package.json +1 -2
- package/src/app/(main)/settings/llm/ProviderList/Azure/index.tsx +107 -0
- package/src/app/(main)/settings/llm/ProviderList/Bedrock/index.tsx +68 -0
- package/src/app/(main)/settings/llm/ProviderList/Ollama/index.tsx +29 -0
- package/src/app/(main)/settings/llm/ProviderList/OpenAI/index.tsx +20 -0
- package/src/app/(main)/settings/llm/ProviderList/providers.tsx +141 -0
- package/src/app/(main)/settings/llm/components/ProviderConfig/index.tsx +18 -27
- package/src/app/(main)/settings/llm/index.tsx +7 -34
- package/src/app/(main)/settings/llm/type.ts +5 -0
- package/src/app/api/chat/[provider]/route.ts +8 -3
- package/src/app/api/chat/agentRuntime.test.ts +17 -0
- package/src/app/api/chat/agentRuntime.ts +7 -0
- package/src/app/api/errorResponse.test.ts +7 -89
- package/src/app/api/errorResponse.ts +6 -43
- package/src/app/api/middleware/auth/index.ts +7 -3
- package/src/components/ModelProviderIcon/index.tsx +5 -0
- package/src/components/ModelSelect/index.tsx +7 -10
- package/src/config/llm.ts +6 -0
- package/src/config/modelProviders/anthropic.ts +5 -0
- package/src/config/modelProviders/azure.ts +2 -0
- package/src/config/modelProviders/bedrock.ts +2 -0
- package/src/config/modelProviders/deepseek.ts +4 -1
- package/src/config/modelProviders/google.ts +5 -0
- package/src/config/modelProviders/groq.ts +5 -0
- package/src/config/modelProviders/index.ts +4 -0
- package/src/config/modelProviders/minimax.ts +2 -0
- package/src/config/modelProviders/mistral.ts +2 -0
- package/src/config/modelProviders/moonshot.ts +2 -0
- package/src/config/modelProviders/ollama.ts +4 -0
- package/src/config/modelProviders/openai.ts +3 -0
- package/src/config/modelProviders/openrouter.ts +3 -0
- package/src/config/modelProviders/perplexity.ts +5 -0
- package/src/config/modelProviders/qwen.ts +35 -0
- package/src/config/modelProviders/togetherai.ts +3 -0
- package/src/config/modelProviders/zeroone.ts +4 -2
- package/src/config/modelProviders/zhipu.ts +2 -0
- package/src/const/settings/llm.ts +5 -0
- package/src/features/Conversation/Error/APIKeyForm/ProviderApiKeyForm.tsx +6 -3
- package/src/features/Conversation/Error/APIKeyForm/ProviderAvatar.tsx +5 -0
- package/src/features/Conversation/Error/APIKeyForm/index.tsx +4 -0
- package/src/features/Conversation/Error/OllamaBizError/InvalidOllamaModel/index.tsx +11 -14
- package/src/features/Conversation/Error/index.tsx +29 -19
- package/src/features/Conversation/components/ChatItem/index.tsx +4 -13
- package/src/features/ModelSelect/index.tsx +1 -1
- package/src/features/ModelSwitchPanel/index.tsx +1 -1
- package/src/hooks/useProviderName.ts +8 -0
- package/src/libs/agent-runtime/AgentRuntime.ts +8 -1
- package/src/libs/agent-runtime/anthropic/index.test.ts +8 -5
- package/src/libs/agent-runtime/anthropic/index.ts +3 -3
- package/src/libs/agent-runtime/azureOpenai/index.test.ts +5 -2
- package/src/libs/agent-runtime/azureOpenai/index.ts +2 -2
- package/src/libs/agent-runtime/bedrock/index.test.ts +2 -2
- package/src/libs/agent-runtime/bedrock/index.ts +2 -2
- package/src/libs/agent-runtime/deepseek/index.test.ts +3 -2
- package/src/libs/agent-runtime/deepseek/index.ts +0 -5
- package/src/libs/agent-runtime/error.ts +11 -43
- package/src/libs/agent-runtime/google/index.test.ts +3 -4
- package/src/libs/agent-runtime/google/index.ts +4 -4
- package/src/libs/agent-runtime/groq/index.test.ts +3 -2
- package/src/libs/agent-runtime/groq/index.ts +0 -4
- package/src/libs/agent-runtime/index.ts +1 -0
- package/src/libs/agent-runtime/minimax/index.test.ts +3 -2
- package/src/libs/agent-runtime/minimax/index.ts +5 -5
- package/src/libs/agent-runtime/mistral/index.test.ts +2 -2
- package/src/libs/agent-runtime/mistral/index.ts +0 -5
- package/src/libs/agent-runtime/moonshot/index.test.ts +3 -2
- package/src/libs/agent-runtime/moonshot/index.ts +0 -5
- package/src/libs/agent-runtime/openrouter/index.test.ts +3 -2
- package/src/libs/agent-runtime/openrouter/index.ts +0 -6
- package/src/libs/agent-runtime/perplexity/index.test.ts +2 -2
- package/src/libs/agent-runtime/perplexity/index.ts +0 -5
- package/src/libs/agent-runtime/qwen/index.test.ts +251 -0
- package/src/libs/agent-runtime/qwen/index.ts +28 -0
- package/src/libs/agent-runtime/togetherai/index.test.ts +3 -2
- package/src/libs/agent-runtime/togetherai/index.ts +0 -5
- package/src/libs/agent-runtime/types/type.ts +1 -1
- package/src/libs/agent-runtime/utils/openaiCompatibleFactory/index.test.ts +123 -6
- package/src/libs/agent-runtime/utils/openaiCompatibleFactory/index.ts +12 -6
- package/src/libs/agent-runtime/utils/streams/openai.ts +1 -1
- package/src/libs/agent-runtime/zeroone/index.test.ts +3 -2
- package/src/libs/agent-runtime/zeroone/index.ts +1 -5
- package/src/libs/agent-runtime/zhipu/index.test.ts +8 -5
- package/src/libs/agent-runtime/zhipu/index.ts +2 -2
- package/src/locales/default/error.ts +10 -51
- package/src/locales/default/modelProvider.ts +12 -169
- package/src/locales/default/setting.ts +5 -8
- package/src/server/globalConfig/index.ts +2 -0
- package/src/services/__tests__/chat.test.ts +16 -0
- package/src/services/chat.ts +3 -0
- package/src/services/message/client.test.ts +4 -1
- package/src/types/fetch.ts +3 -1
- package/src/types/llm.ts +22 -0
- package/src/types/user/settings/keyVaults.ts +1 -0
- package/src/app/(main)/settings/llm/Anthropic/index.tsx +0 -25
- package/src/app/(main)/settings/llm/Azure/index.tsx +0 -110
- package/src/app/(main)/settings/llm/Bedrock/index.tsx +0 -74
- package/src/app/(main)/settings/llm/DeepSeek/index.tsx +0 -21
- package/src/app/(main)/settings/llm/Google/index.tsx +0 -31
- package/src/app/(main)/settings/llm/Groq/index.tsx +0 -26
- package/src/app/(main)/settings/llm/Minimax/index.tsx +0 -20
- package/src/app/(main)/settings/llm/Mistral/index.tsx +0 -20
- package/src/app/(main)/settings/llm/Moonshot/index.tsx +0 -28
- package/src/app/(main)/settings/llm/Ollama/index.tsx +0 -37
- package/src/app/(main)/settings/llm/OpenAI/index.tsx +0 -28
- package/src/app/(main)/settings/llm/OpenRouter/index.tsx +0 -21
- package/src/app/(main)/settings/llm/Perplexity/index.tsx +0 -23
- package/src/app/(main)/settings/llm/TogetherAI/index.tsx +0 -19
- package/src/app/(main)/settings/llm/ZeroOne/index.tsx +0 -20
- package/src/app/(main)/settings/llm/Zhipu/index.tsx +0 -18
- /package/src/app/(main)/settings/llm/{Ollama → ProviderList/Ollama}/Checker.tsx +0 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Anthropic,
|
|
3
|
+
Claude,
|
|
4
|
+
DeepSeek,
|
|
5
|
+
Gemini,
|
|
6
|
+
Google,
|
|
7
|
+
Groq,
|
|
8
|
+
Minimax,
|
|
9
|
+
Mistral,
|
|
10
|
+
Moonshot,
|
|
11
|
+
OpenRouter,
|
|
12
|
+
Perplexity,
|
|
13
|
+
Together,
|
|
14
|
+
Tongyi,
|
|
15
|
+
ZeroOne,
|
|
16
|
+
Zhipu,
|
|
17
|
+
} from '@lobehub/icons';
|
|
18
|
+
import { Divider } from 'antd';
|
|
19
|
+
import { useTheme } from 'antd-style';
|
|
20
|
+
import { useMemo } from 'react';
|
|
21
|
+
import { Flexbox } from 'react-layout-kit';
|
|
22
|
+
|
|
23
|
+
import {
|
|
24
|
+
AnthropicProviderCard,
|
|
25
|
+
DeepSeekProviderCard,
|
|
26
|
+
GoogleProviderCard,
|
|
27
|
+
GroqProviderCard,
|
|
28
|
+
MinimaxProviderCard,
|
|
29
|
+
MistralProviderCard,
|
|
30
|
+
MoonshotProviderCard,
|
|
31
|
+
OpenRouterProviderCard,
|
|
32
|
+
PerplexityProviderCard,
|
|
33
|
+
QwenProviderCard,
|
|
34
|
+
TogetherAIProviderCard,
|
|
35
|
+
ZeroOneProviderCard,
|
|
36
|
+
ZhiPuProviderCard,
|
|
37
|
+
} from '@/config/modelProviders';
|
|
38
|
+
|
|
39
|
+
import { ProviderItem } from '../type';
|
|
40
|
+
import { useAzureProvider } from './Azure';
|
|
41
|
+
import { useBedrockProvider } from './Bedrock';
|
|
42
|
+
import { useOllamaProvider } from './Ollama';
|
|
43
|
+
import { useOpenAIProvider } from './OpenAI';
|
|
44
|
+
|
|
45
|
+
const AnthropicBrand = () => {
|
|
46
|
+
const { isDarkMode } = useTheme();
|
|
47
|
+
return <Anthropic.Text color={isDarkMode ? undefined : Claude.colorPrimary} size={15} />;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const MoonshotBrand = () => {
|
|
51
|
+
const theme = useTheme();
|
|
52
|
+
return (
|
|
53
|
+
<Moonshot.Combine
|
|
54
|
+
color={theme.isDarkMode ? theme.colorText : Moonshot.colorPrimary}
|
|
55
|
+
size={22}
|
|
56
|
+
/>
|
|
57
|
+
);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const GroqBrand = () => {
|
|
61
|
+
const theme = useTheme();
|
|
62
|
+
|
|
63
|
+
return <Groq.Text color={theme.isDarkMode ? theme.colorText : Groq.colorPrimary} size={20} />;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const GoogleBrand = () => (
|
|
67
|
+
<Flexbox align={'center'} gap={8} horizontal>
|
|
68
|
+
<Google.BrandColor size={22} />
|
|
69
|
+
<Divider style={{ margin: '0 4px' }} type={'vertical'} />
|
|
70
|
+
<Gemini.Combine size={22} type={'color'} />
|
|
71
|
+
</Flexbox>
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
export const useProviderList = (): ProviderItem[] => {
|
|
75
|
+
const azureProvider = useAzureProvider();
|
|
76
|
+
const ollamaProvider = useOllamaProvider();
|
|
77
|
+
const openAIProvider = useOpenAIProvider();
|
|
78
|
+
const bedrockProvider = useBedrockProvider();
|
|
79
|
+
|
|
80
|
+
return useMemo(
|
|
81
|
+
() => [
|
|
82
|
+
openAIProvider,
|
|
83
|
+
ollamaProvider,
|
|
84
|
+
azureProvider,
|
|
85
|
+
{
|
|
86
|
+
...GoogleProviderCard,
|
|
87
|
+
title: <GoogleBrand />,
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
...AnthropicProviderCard,
|
|
91
|
+
title: <AnthropicBrand />,
|
|
92
|
+
},
|
|
93
|
+
bedrockProvider,
|
|
94
|
+
{
|
|
95
|
+
...GroqProviderCard,
|
|
96
|
+
title: <GroqBrand />,
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
...OpenRouterProviderCard,
|
|
100
|
+
title: <OpenRouter.Combine iconProps={{ color: OpenRouter.colorPrimary }} size={20} />,
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
...TogetherAIProviderCard,
|
|
104
|
+
title: <Together.Combine size={26} type={'color'} />,
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
...QwenProviderCard,
|
|
108
|
+
title: <Tongyi.Combine extra={'千问'} size={26} type={'color'} />,
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
...DeepSeekProviderCard,
|
|
112
|
+
title: <DeepSeek.Combine size={28} type={'color'} />,
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
...MinimaxProviderCard,
|
|
116
|
+
title: <Minimax.Combine size={32} type={'color'} />,
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
...MistralProviderCard,
|
|
120
|
+
title: <Mistral.Combine size={26} type={'color'} />,
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
...MoonshotProviderCard,
|
|
124
|
+
title: <MoonshotBrand />,
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
...PerplexityProviderCard,
|
|
128
|
+
title: <Perplexity.Combine size={24} type={'color'} />,
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
...ZhiPuProviderCard,
|
|
132
|
+
title: <Zhipu.Combine size={32} type={'color'} />,
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
...ZeroOneProviderCard,
|
|
136
|
+
title: <ZeroOne.Text size={20} />,
|
|
137
|
+
},
|
|
138
|
+
],
|
|
139
|
+
[azureProvider, ollamaProvider, ollamaProvider, bedrockProvider],
|
|
140
|
+
);
|
|
141
|
+
};
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
import { FORM_STYLE } from '@/const/layoutTokens';
|
|
20
20
|
import { useUserStore } from '@/store/user';
|
|
21
21
|
import { keyVaultsConfigSelectors, modelConfigSelectors } from '@/store/user/selectors';
|
|
22
|
+
import { ModelProviderCard } from '@/types/llm';
|
|
22
23
|
import { GlobalLLMProviderKey } from '@/types/user/settings';
|
|
23
24
|
|
|
24
25
|
import Checker from '../Checker';
|
|
@@ -46,36 +47,26 @@ const useStyles = createStyles(({ css, prefixCls, responsive }) => ({
|
|
|
46
47
|
`,
|
|
47
48
|
}));
|
|
48
49
|
|
|
49
|
-
interface ProviderConfigProps {
|
|
50
|
+
export interface ProviderConfigProps extends Omit<ModelProviderCard, 'id'> {
|
|
50
51
|
apiKeyItems?: FormItemProps[];
|
|
51
52
|
canDeactivate?: boolean;
|
|
52
|
-
checkModel?: string;
|
|
53
53
|
checkerItem?: FormItemProps;
|
|
54
54
|
className?: string;
|
|
55
55
|
hideSwitch?: boolean;
|
|
56
|
+
id: GlobalLLMProviderKey;
|
|
56
57
|
modelList?: {
|
|
57
58
|
azureDeployName?: boolean;
|
|
58
59
|
notFoundContent?: ReactNode;
|
|
59
60
|
placeholder?: string;
|
|
60
61
|
showModelFetcher?: boolean;
|
|
61
62
|
};
|
|
62
|
-
provider: GlobalLLMProviderKey;
|
|
63
|
-
proxyUrl?:
|
|
64
|
-
| {
|
|
65
|
-
desc?: string;
|
|
66
|
-
placeholder: string;
|
|
67
|
-
title?: string;
|
|
68
|
-
}
|
|
69
|
-
| false;
|
|
70
|
-
showApiKey?: boolean;
|
|
71
|
-
showBrowserRequest?: boolean;
|
|
72
63
|
title: ReactNode;
|
|
73
64
|
}
|
|
74
65
|
|
|
75
66
|
const ProviderConfig = memo<ProviderConfigProps>(
|
|
76
67
|
({
|
|
77
68
|
apiKeyItems,
|
|
78
|
-
|
|
69
|
+
id,
|
|
79
70
|
proxyUrl,
|
|
80
71
|
showApiKey = true,
|
|
81
72
|
checkModel,
|
|
@@ -85,9 +76,9 @@ const ProviderConfig = memo<ProviderConfigProps>(
|
|
|
85
76
|
modelList,
|
|
86
77
|
showBrowserRequest,
|
|
87
78
|
className,
|
|
79
|
+
name,
|
|
88
80
|
}) => {
|
|
89
81
|
const { t } = useTranslation('setting');
|
|
90
|
-
const { t: modelT } = useTranslation('modelProvider');
|
|
91
82
|
const [form] = Form.useForm();
|
|
92
83
|
const { cx, styles } = useStyles();
|
|
93
84
|
const [
|
|
@@ -99,9 +90,9 @@ const ProviderConfig = memo<ProviderConfigProps>(
|
|
|
99
90
|
] = useUserStore((s) => [
|
|
100
91
|
s.toggleProviderEnabled,
|
|
101
92
|
s.setSettings,
|
|
102
|
-
modelConfigSelectors.isProviderEnabled(
|
|
103
|
-
modelConfigSelectors.isProviderFetchOnClient(
|
|
104
|
-
keyVaultsConfigSelectors.isProviderEndpointNotEmpty(
|
|
93
|
+
modelConfigSelectors.isProviderEnabled(id)(s),
|
|
94
|
+
modelConfigSelectors.isProviderFetchOnClient(id)(s),
|
|
95
|
+
keyVaultsConfigSelectors.isProviderEndpointNotEmpty(id)(s),
|
|
105
96
|
]);
|
|
106
97
|
|
|
107
98
|
useSyncSettings(form);
|
|
@@ -113,12 +104,12 @@ const ProviderConfig = memo<ProviderConfigProps>(
|
|
|
113
104
|
children: (
|
|
114
105
|
<Input.Password
|
|
115
106
|
autoComplete={'new-password'}
|
|
116
|
-
placeholder={
|
|
107
|
+
placeholder={t(`llm.apiKey.placeholder`, { name })}
|
|
117
108
|
/>
|
|
118
109
|
),
|
|
119
|
-
desc:
|
|
120
|
-
label:
|
|
121
|
-
name: [KeyVaultsConfigKey,
|
|
110
|
+
desc: t(`llm.apiKey.desc`, { name }),
|
|
111
|
+
label: t(`llm.apiKey.title`),
|
|
112
|
+
name: [KeyVaultsConfigKey, id, LLMProviderApiTokenKey],
|
|
122
113
|
},
|
|
123
114
|
];
|
|
124
115
|
|
|
@@ -129,13 +120,13 @@ const ProviderConfig = memo<ProviderConfigProps>(
|
|
|
129
120
|
children: <Input allowClear placeholder={proxyUrl?.placeholder} />,
|
|
130
121
|
desc: proxyUrl?.desc || t('llm.proxyUrl.desc'),
|
|
131
122
|
label: proxyUrl?.title || t('llm.proxyUrl.title'),
|
|
132
|
-
name: [KeyVaultsConfigKey,
|
|
123
|
+
name: [KeyVaultsConfigKey, id, LLMProviderBaseUrlKey],
|
|
133
124
|
},
|
|
134
125
|
(showBrowserRequest || (showEndpoint && isProviderEndpointNotEmpty)) && {
|
|
135
126
|
children: (
|
|
136
127
|
<Switch
|
|
137
128
|
onChange={(enabled) => {
|
|
138
|
-
setSettings({ [LLMProviderConfigKey]: { [
|
|
129
|
+
setSettings({ [LLMProviderConfigKey]: { [id]: { fetchOnClient: enabled } } });
|
|
139
130
|
}}
|
|
140
131
|
value={isFetchOnClient}
|
|
141
132
|
/>
|
|
@@ -149,17 +140,17 @@ const ProviderConfig = memo<ProviderConfigProps>(
|
|
|
149
140
|
<ProviderModelListSelect
|
|
150
141
|
notFoundContent={modelList?.notFoundContent}
|
|
151
142
|
placeholder={modelList?.placeholder ?? t('llm.modelList.placeholder')}
|
|
152
|
-
provider={
|
|
143
|
+
provider={id}
|
|
153
144
|
showAzureDeployName={modelList?.azureDeployName}
|
|
154
145
|
showModelFetcher={modelList?.showModelFetcher}
|
|
155
146
|
/>
|
|
156
147
|
),
|
|
157
148
|
desc: t('llm.modelList.desc'),
|
|
158
149
|
label: t('llm.modelList.title'),
|
|
159
|
-
name: [LLMProviderConfigKey,
|
|
150
|
+
name: [LLMProviderConfigKey, id, LLMProviderModelListKey],
|
|
160
151
|
},
|
|
161
152
|
checkerItem ?? {
|
|
162
|
-
children: <Checker model={checkModel!} provider={
|
|
153
|
+
children: <Checker model={checkModel!} provider={id} />,
|
|
163
154
|
desc: t('llm.checker.desc'),
|
|
164
155
|
label: t('llm.checker.title'),
|
|
165
156
|
minWidth: undefined,
|
|
@@ -173,7 +164,7 @@ const ProviderConfig = memo<ProviderConfigProps>(
|
|
|
173
164
|
extra: canDeactivate ? (
|
|
174
165
|
<Switch
|
|
175
166
|
onChange={(enabled) => {
|
|
176
|
-
toggleProviderEnabled(
|
|
167
|
+
toggleProviderEnabled(id, enabled);
|
|
177
168
|
}}
|
|
178
169
|
value={enabled}
|
|
179
170
|
/>
|
|
@@ -2,44 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
import { Flexbox } from 'react-layout-kit';
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import Bedrock from './Bedrock';
|
|
8
|
-
import DeepSeek from './DeepSeek';
|
|
9
|
-
import Google from './Google';
|
|
10
|
-
import Groq from './Groq';
|
|
11
|
-
import Minimax from './Minimax';
|
|
12
|
-
import Mistral from './Mistral';
|
|
13
|
-
import Moonshot from './Moonshot';
|
|
14
|
-
import Ollama from './Ollama';
|
|
15
|
-
import OpenAI from './OpenAI';
|
|
16
|
-
import OpenRouter from './OpenRouter';
|
|
17
|
-
import Perplexity from './Perplexity';
|
|
18
|
-
import TogetherAI from './TogetherAI';
|
|
19
|
-
import ZeroOne from './ZeroOne';
|
|
20
|
-
import Zhipu from './Zhipu';
|
|
21
|
-
import Footer from './components/Footer';
|
|
5
|
+
import { useProviderList } from './ProviderList/providers';
|
|
6
|
+
import ProviderConfig from './components/ProviderConfig';
|
|
22
7
|
|
|
23
8
|
const Page = () => {
|
|
9
|
+
const list = useProviderList();
|
|
10
|
+
|
|
24
11
|
return (
|
|
25
12
|
<Flexbox gap={24} width={'100%'}>
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
<Google />
|
|
30
|
-
<Anthropic />
|
|
31
|
-
<Bedrock />
|
|
32
|
-
<DeepSeek />
|
|
33
|
-
<OpenRouter />
|
|
34
|
-
<TogetherAI />
|
|
35
|
-
<Groq />
|
|
36
|
-
<Perplexity />
|
|
37
|
-
<Minimax />
|
|
38
|
-
<Mistral />
|
|
39
|
-
<Moonshot />
|
|
40
|
-
<Zhipu />
|
|
41
|
-
<ZeroOne />
|
|
42
|
-
<Footer />
|
|
13
|
+
{list.map(({ id, ...res }) => (
|
|
14
|
+
<ProviderConfig id={id as any} key={id} {...res} />
|
|
15
|
+
))}
|
|
43
16
|
</Flexbox>
|
|
44
17
|
);
|
|
45
18
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getPreferredRegion } from '@/app/api/config';
|
|
2
2
|
import { createErrorResponse } from '@/app/api/errorResponse';
|
|
3
|
-
import { ChatCompletionErrorPayload } from '@/libs/agent-runtime';
|
|
3
|
+
import { AgentRuntime, ChatCompletionErrorPayload } from '@/libs/agent-runtime';
|
|
4
4
|
import { ChatErrorType } from '@/types/fetch';
|
|
5
5
|
import { ChatStreamPayload } from '@/types/openai/chat';
|
|
6
6
|
import { getTracePayload } from '@/utils/trace';
|
|
@@ -12,12 +12,17 @@ export const runtime = 'edge';
|
|
|
12
12
|
|
|
13
13
|
export const preferredRegion = getPreferredRegion();
|
|
14
14
|
|
|
15
|
-
export const POST = checkAuth(async (req: Request, { params, jwtPayload }) => {
|
|
15
|
+
export const POST = checkAuth(async (req: Request, { params, jwtPayload, createRuntime }) => {
|
|
16
16
|
const { provider } = params;
|
|
17
17
|
|
|
18
18
|
try {
|
|
19
19
|
// ============ 1. init chat model ============ //
|
|
20
|
-
|
|
20
|
+
let agentRuntime: AgentRuntime;
|
|
21
|
+
if (createRuntime) {
|
|
22
|
+
agentRuntime = createRuntime(jwtPayload);
|
|
23
|
+
} else {
|
|
24
|
+
agentRuntime = await initAgentRuntimeWithUserPayload(provider, jwtPayload);
|
|
25
|
+
}
|
|
21
26
|
|
|
22
27
|
// ============ 2. create chat completion ============ //
|
|
23
28
|
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
LobeOpenAI,
|
|
17
17
|
LobeOpenRouterAI,
|
|
18
18
|
LobePerplexityAI,
|
|
19
|
+
LobeQwenAI,
|
|
19
20
|
LobeRuntimeAI,
|
|
20
21
|
LobeTogetherAI,
|
|
21
22
|
LobeZeroOneAI,
|
|
@@ -49,6 +50,7 @@ vi.mock('@/config/llm', () => ({
|
|
|
49
50
|
MISTRAL_API_KEY: 'test-mistral-key',
|
|
50
51
|
OPENROUTER_API_KEY: 'test-openrouter-key',
|
|
51
52
|
TOGETHERAI_API_KEY: 'test-togetherai-key',
|
|
53
|
+
QWEN_API_KEY: 'test-qwen-key',
|
|
52
54
|
})),
|
|
53
55
|
}));
|
|
54
56
|
|
|
@@ -101,6 +103,13 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
|
101
103
|
expect(runtime['_runtime']).toBeInstanceOf(LobeMoonshotAI);
|
|
102
104
|
});
|
|
103
105
|
|
|
106
|
+
it('Qwen AI provider: with apikey', async () => {
|
|
107
|
+
const jwtPayload: JWTPayload = { apiKey: 'user-qwen-key' };
|
|
108
|
+
const runtime = await initAgentRuntimeWithUserPayload(ModelProvider.Qwen, jwtPayload);
|
|
109
|
+
expect(runtime).toBeInstanceOf(AgentRuntime);
|
|
110
|
+
expect(runtime['_runtime']).toBeInstanceOf(LobeQwenAI);
|
|
111
|
+
});
|
|
112
|
+
|
|
104
113
|
it('Bedrock AI provider: with apikey, awsAccessKeyId, awsSecretAccessKey, awsRegion', async () => {
|
|
105
114
|
const jwtPayload: JWTPayload = {
|
|
106
115
|
apiKey: 'user-bedrock-key',
|
|
@@ -232,6 +241,14 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
|
232
241
|
expect(runtime['_runtime']).toBeInstanceOf(LobeMoonshotAI);
|
|
233
242
|
});
|
|
234
243
|
|
|
244
|
+
it('Qwen AI provider: without apikey', async () => {
|
|
245
|
+
const jwtPayload: JWTPayload = {};
|
|
246
|
+
const runtime = await initAgentRuntimeWithUserPayload(ModelProvider.Qwen, jwtPayload);
|
|
247
|
+
|
|
248
|
+
// 假设 LobeQwenAI 是 Qwen 提供者的实现类
|
|
249
|
+
expect(runtime['_runtime']).toBeInstanceOf(LobeQwenAI);
|
|
250
|
+
});
|
|
251
|
+
|
|
235
252
|
it('Bedrock AI provider: without apikey', async () => {
|
|
236
253
|
const jwtPayload = {};
|
|
237
254
|
const runtime = await initAgentRuntimeWithUserPayload(ModelProvider.Bedrock, jwtPayload);
|
|
@@ -156,6 +156,13 @@ const getLlmOptionsFromPayload = (provider: string, payload: JWTPayload) => {
|
|
|
156
156
|
|
|
157
157
|
const apiKey = apiKeyManager.pick(payload?.apiKey || ZEROONE_API_KEY);
|
|
158
158
|
|
|
159
|
+
return { apiKey };
|
|
160
|
+
}
|
|
161
|
+
case ModelProvider.Qwen: {
|
|
162
|
+
const { QWEN_API_KEY } = getLLMConfig();
|
|
163
|
+
|
|
164
|
+
const apiKey = apiKeyManager.pick(payload?.apiKey || QWEN_API_KEY);
|
|
165
|
+
|
|
159
166
|
return { apiKey };
|
|
160
167
|
}
|
|
161
168
|
}
|
|
@@ -34,7 +34,13 @@ describe('createErrorResponse', () => {
|
|
|
34
34
|
|
|
35
35
|
describe('Provider Biz Error', () => {
|
|
36
36
|
it('returns a 471 status for OpenAIBizError error type', () => {
|
|
37
|
-
const errorType =
|
|
37
|
+
const errorType = AgentRuntimeErrorType.OpenAIBizError;
|
|
38
|
+
const response = createErrorResponse(errorType);
|
|
39
|
+
expect(response.status).toBe(471);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('returns a 471 status for ProviderBizError error type', () => {
|
|
43
|
+
const errorType = AgentRuntimeErrorType.ProviderBizError;
|
|
38
44
|
const response = createErrorResponse(errorType);
|
|
39
45
|
expect(response.status).toBe(471);
|
|
40
46
|
});
|
|
@@ -50,94 +56,6 @@ describe('createErrorResponse', () => {
|
|
|
50
56
|
const response = createErrorResponse(errorType as any);
|
|
51
57
|
expect(response.status).toBe(471);
|
|
52
58
|
});
|
|
53
|
-
|
|
54
|
-
// 测试 AzureBizError 错误类型返回472状态码
|
|
55
|
-
it('returns a 472 status for AzureBizError error type', () => {
|
|
56
|
-
const errorType = AgentRuntimeErrorType.AzureBizError;
|
|
57
|
-
const response = createErrorResponse(errorType);
|
|
58
|
-
expect(response.status).toBe(472);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
// 测试 ZhipuBizError 错误类型返回473状态码
|
|
62
|
-
it('returns a 473 status for ZhipuBizError error type', () => {
|
|
63
|
-
const errorType = AgentRuntimeErrorType.ZhipuBizError;
|
|
64
|
-
const response = createErrorResponse(errorType);
|
|
65
|
-
expect(response.status).toBe(473);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
// 测试 BedrockBizError 错误类型返回474状态码
|
|
69
|
-
it('returns a 474 status for BedrockBizError error type', () => {
|
|
70
|
-
const errorType = AgentRuntimeErrorType.BedrockBizError;
|
|
71
|
-
const response = createErrorResponse(errorType);
|
|
72
|
-
expect(response.status).toBe(474);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
// 测试 GoogleBizError 错误类型返回475状态码
|
|
76
|
-
it('returns a 475 status for GoogleBizError error type', () => {
|
|
77
|
-
const errorType = AgentRuntimeErrorType.GoogleBizError;
|
|
78
|
-
const response = createErrorResponse(errorType);
|
|
79
|
-
expect(response.status).toBe(475);
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
// 测试 MoonshotBizError 错误类型返回476状态码
|
|
83
|
-
it('returns a 476 status for MoonshotBizError error type', () => {
|
|
84
|
-
const errorType = AgentRuntimeErrorType.MoonshotBizError;
|
|
85
|
-
const response = createErrorResponse(errorType);
|
|
86
|
-
expect(response.status).toBe(476);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
// 测试 OpenRouterBizError 错误类型返回477状态码
|
|
90
|
-
it('returns a 477 status for OpenRouterBizError error type', () => {
|
|
91
|
-
const errorType = AgentRuntimeErrorType.OpenRouterBizError;
|
|
92
|
-
const response = createErrorResponse(errorType);
|
|
93
|
-
expect(response.status).toBe(477);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
// 测试 OllamaBizError 错误类型返回478状态码
|
|
97
|
-
it('returns a 478 status for OllamaBizError error type', () => {
|
|
98
|
-
const errorType = AgentRuntimeErrorType.OllamaBizError;
|
|
99
|
-
const response = createErrorResponse(errorType);
|
|
100
|
-
expect(response.status).toBe(478);
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
// 测试 PerplexityBizError 错误类型返回479状态码
|
|
104
|
-
it('returns a 479 status for PerplexityBizError error type', () => {
|
|
105
|
-
const errorType = AgentRuntimeErrorType.PerplexityBizError;
|
|
106
|
-
const response = createErrorResponse(errorType);
|
|
107
|
-
expect(response.status).toBe(479);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
// 测试 AnthropicBizError 错误类型返回480状态码
|
|
111
|
-
it('returns a 480 status for AnthropicBizError error type', () => {
|
|
112
|
-
const errorType = AgentRuntimeErrorType.AnthropicBizError;
|
|
113
|
-
const response = createErrorResponse(errorType);
|
|
114
|
-
expect(response.status).toBe(480);
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
// 测试 MistralBizError 错误类型返回481状态码
|
|
118
|
-
it('returns a 481 status for MistralBizError error type', () => {
|
|
119
|
-
const errorType = AgentRuntimeErrorType.MistralBizError;
|
|
120
|
-
const response = createErrorResponse(errorType);
|
|
121
|
-
expect(response.status).toBe(481);
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
it('returns a 484 status for TogetherAIBizError error type', () => {
|
|
125
|
-
const errorType = AgentRuntimeErrorType.TogetherAIBizError;
|
|
126
|
-
const response = createErrorResponse(errorType);
|
|
127
|
-
expect(response.status).toBe(484);
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
it('returns a 485 status for MinimaxBizError error type', () => {
|
|
131
|
-
const errorType = AgentRuntimeErrorType.MinimaxBizError;
|
|
132
|
-
const response = createErrorResponse(errorType);
|
|
133
|
-
expect(response.status).toBe(485);
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
it('returns a 486 status for DeepSeekBizError error type', () => {
|
|
137
|
-
const errorType = AgentRuntimeErrorType.DeepSeekBizError;
|
|
138
|
-
const response = createErrorResponse(errorType);
|
|
139
|
-
expect(response.status).toBe(486);
|
|
140
|
-
});
|
|
141
59
|
});
|
|
142
60
|
|
|
143
61
|
// 测试状态码不在200-599范围内的情况
|
|
@@ -7,6 +7,7 @@ const getStatus = (errorType: ILobeAgentRuntimeErrorType | ErrorType) => {
|
|
|
7
7
|
|
|
8
8
|
switch (errorType) {
|
|
9
9
|
// TODO: Need to refactor to Invalid OpenAI API Key
|
|
10
|
+
case AgentRuntimeErrorType.InvalidProviderAPIKey:
|
|
10
11
|
case AgentRuntimeErrorType.NoOpenAIAPIKey: {
|
|
11
12
|
return 401;
|
|
12
13
|
}
|
|
@@ -19,56 +20,18 @@ const getStatus = (errorType: ILobeAgentRuntimeErrorType | ErrorType) => {
|
|
|
19
20
|
case AgentRuntimeErrorType.AgentRuntimeError: {
|
|
20
21
|
return 470;
|
|
21
22
|
}
|
|
23
|
+
|
|
24
|
+
case AgentRuntimeErrorType.ProviderBizError:
|
|
22
25
|
case AgentRuntimeErrorType.OpenAIBizError: {
|
|
23
26
|
return 471;
|
|
24
27
|
}
|
|
25
|
-
|
|
26
|
-
return 472;
|
|
27
|
-
}
|
|
28
|
-
case AgentRuntimeErrorType.ZhipuBizError: {
|
|
29
|
-
return 473;
|
|
30
|
-
}
|
|
31
|
-
case AgentRuntimeErrorType.BedrockBizError: {
|
|
32
|
-
return 474;
|
|
33
|
-
}
|
|
34
|
-
case AgentRuntimeErrorType.GoogleBizError: {
|
|
35
|
-
return 475;
|
|
36
|
-
}
|
|
37
|
-
case AgentRuntimeErrorType.MoonshotBizError: {
|
|
38
|
-
return 476;
|
|
39
|
-
}
|
|
40
|
-
case AgentRuntimeErrorType.OpenRouterBizError: {
|
|
41
|
-
return 477;
|
|
42
|
-
}
|
|
28
|
+
|
|
43
29
|
case ChatErrorType.OllamaServiceUnavailable:
|
|
44
30
|
case AgentRuntimeErrorType.OllamaBizError: {
|
|
45
|
-
return
|
|
46
|
-
}
|
|
47
|
-
case AgentRuntimeErrorType.PerplexityBizError: {
|
|
48
|
-
return 479;
|
|
49
|
-
}
|
|
50
|
-
case AgentRuntimeErrorType.AnthropicBizError: {
|
|
51
|
-
return 480;
|
|
52
|
-
}
|
|
53
|
-
case AgentRuntimeErrorType.MistralBizError: {
|
|
54
|
-
return 481;
|
|
55
|
-
}
|
|
56
|
-
case AgentRuntimeErrorType.GroqBizError: {
|
|
57
|
-
return 482;
|
|
58
|
-
}
|
|
59
|
-
case AgentRuntimeErrorType.ZeroOneBizError: {
|
|
60
|
-
return 483;
|
|
61
|
-
}
|
|
62
|
-
case AgentRuntimeErrorType.TogetherAIBizError: {
|
|
63
|
-
return 484;
|
|
64
|
-
}
|
|
65
|
-
case AgentRuntimeErrorType.MinimaxBizError: {
|
|
66
|
-
return 485;
|
|
67
|
-
}
|
|
68
|
-
case AgentRuntimeErrorType.DeepSeekBizError: {
|
|
69
|
-
return 486;
|
|
31
|
+
return 472;
|
|
70
32
|
}
|
|
71
33
|
}
|
|
34
|
+
|
|
72
35
|
return errorType as number;
|
|
73
36
|
};
|
|
74
37
|
|
|
@@ -4,16 +4,20 @@ import { NextRequest } from 'next/server';
|
|
|
4
4
|
|
|
5
5
|
import { createErrorResponse } from '@/app/api/errorResponse';
|
|
6
6
|
import { JWTPayload, LOBE_CHAT_AUTH_HEADER, OAUTH_AUTHORIZED, enableClerk } from '@/const/auth';
|
|
7
|
-
import { AgentRuntimeError, ChatCompletionErrorPayload } from '@/libs/agent-runtime';
|
|
7
|
+
import { AgentRuntime, AgentRuntimeError, ChatCompletionErrorPayload } from '@/libs/agent-runtime';
|
|
8
8
|
import { ChatErrorType } from '@/types/fetch';
|
|
9
9
|
|
|
10
10
|
import { checkAuthMethod, getJWTPayload } from './utils';
|
|
11
11
|
|
|
12
|
-
type
|
|
12
|
+
type CreateRuntime = (jwtPayload: JWTPayload) => AgentRuntime;
|
|
13
|
+
type RequestOptions = { createRuntime?: CreateRuntime, params: { provider: string }; };
|
|
13
14
|
|
|
14
15
|
export type RequestHandler = (
|
|
15
16
|
req: Request,
|
|
16
|
-
options: RequestOptions & {
|
|
17
|
+
options: RequestOptions & {
|
|
18
|
+
createRuntime?: CreateRuntime;
|
|
19
|
+
jwtPayload: JWTPayload;
|
|
20
|
+
},
|
|
17
21
|
) => Promise<Response>;
|
|
18
22
|
|
|
19
23
|
export const checkAuth =
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
OpenRouter,
|
|
15
15
|
Perplexity,
|
|
16
16
|
Together,
|
|
17
|
+
Tongyi,
|
|
17
18
|
ZeroOne,
|
|
18
19
|
Zhipu,
|
|
19
20
|
} from '@lobehub/icons';
|
|
@@ -104,6 +105,10 @@ const ModelProviderIcon = memo<ModelProviderIconProps>(({ provider }) => {
|
|
|
104
105
|
return <Together size={20} />;
|
|
105
106
|
}
|
|
106
107
|
|
|
108
|
+
case ModelProvider.Qwen: {
|
|
109
|
+
return <Tongyi size={20} />;
|
|
110
|
+
}
|
|
111
|
+
|
|
107
112
|
default: {
|
|
108
113
|
return null;
|
|
109
114
|
}
|
|
@@ -151,16 +151,13 @@ export const ModelItemRender = memo<ModelItemRenderProps>(({ showInfoTag = true,
|
|
|
151
151
|
});
|
|
152
152
|
|
|
153
153
|
interface ProviderItemRenderProps {
|
|
154
|
+
name: string;
|
|
154
155
|
provider: string;
|
|
155
156
|
}
|
|
156
157
|
|
|
157
|
-
export const ProviderItemRender = memo<ProviderItemRenderProps>(({ provider }) =>
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
{t(`${provider}.title` as any)}
|
|
164
|
-
</Flexbox>
|
|
165
|
-
);
|
|
166
|
-
});
|
|
158
|
+
export const ProviderItemRender = memo<ProviderItemRenderProps>(({ provider, name }) => (
|
|
159
|
+
<Flexbox align={'center'} gap={4} horizontal>
|
|
160
|
+
<ModelProviderIcon provider={provider} />
|
|
161
|
+
{name}
|
|
162
|
+
</Flexbox>
|
|
163
|
+
));
|