@lobehub/chat 0.122.8 → 0.123.0
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 +22 -2
- package/.eslintrc.js +1 -0
- package/CHANGELOG.md +50 -0
- package/README.md +11 -9
- package/README.zh-CN.md +11 -9
- package/docs/Deployment/Environment-Variable.md +65 -23
- package/docs/Deployment/Environment-Variable.zh-CN.md +64 -22
- package/locales/ar/chat.json +12 -1
- package/locales/ar/common.json +16 -0
- package/locales/ar/error.json +35 -6
- package/locales/ar/setting.json +53 -6
- package/locales/de-DE/chat.json +12 -1
- package/locales/de-DE/common.json +16 -0
- package/locales/de-DE/error.json +35 -6
- package/locales/de-DE/setting.json +53 -6
- package/locales/en-US/chat.json +13 -2
- package/locales/en-US/common.json +16 -0
- package/locales/en-US/error.json +35 -6
- package/locales/en-US/setting.json +53 -6
- package/locales/es-ES/chat.json +12 -1
- package/locales/es-ES/common.json +16 -0
- package/locales/es-ES/error.json +35 -6
- package/locales/es-ES/setting.json +53 -6
- package/locales/fr-FR/chat.json +13 -2
- package/locales/fr-FR/common.json +16 -0
- package/locales/fr-FR/error.json +35 -6
- package/locales/fr-FR/setting.json +53 -6
- package/locales/it-IT/chat.json +12 -1
- package/locales/it-IT/common.json +16 -0
- package/locales/it-IT/error.json +35 -6
- package/locales/it-IT/setting.json +53 -6
- package/locales/ja-JP/chat.json +12 -1
- package/locales/ja-JP/common.json +16 -0
- package/locales/ja-JP/error.json +35 -6
- package/locales/ja-JP/setting.json +53 -6
- package/locales/ko-KR/chat.json +12 -1
- package/locales/ko-KR/common.json +16 -0
- package/locales/ko-KR/error.json +35 -6
- package/locales/ko-KR/setting.json +53 -6
- package/locales/nl-NL/chat.json +12 -1
- package/locales/nl-NL/common.json +16 -0
- package/locales/nl-NL/error.json +35 -6
- package/locales/nl-NL/setting.json +53 -6
- package/locales/pl-PL/chat.json +12 -1
- package/locales/pl-PL/common.json +16 -0
- package/locales/pl-PL/error.json +35 -6
- package/locales/pl-PL/setting.json +53 -6
- package/locales/pt-BR/chat.json +12 -1
- package/locales/pt-BR/common.json +16 -0
- package/locales/pt-BR/error.json +35 -6
- package/locales/pt-BR/setting.json +53 -6
- package/locales/ru-RU/chat.json +12 -1
- package/locales/ru-RU/common.json +16 -0
- package/locales/ru-RU/error.json +35 -6
- package/locales/ru-RU/setting.json +53 -6
- package/locales/tr-TR/chat.json +12 -1
- package/locales/tr-TR/common.json +16 -0
- package/locales/tr-TR/error.json +35 -6
- package/locales/tr-TR/setting.json +53 -6
- package/locales/vi-VN/chat.json +12 -1
- package/locales/vi-VN/common.json +16 -0
- package/locales/vi-VN/error.json +35 -6
- package/locales/vi-VN/setting.json +53 -6
- package/locales/zh-CN/chat.json +12 -1
- package/locales/zh-CN/common.json +16 -0
- package/locales/zh-CN/error.json +34 -5
- package/locales/zh-CN/setting.json +59 -12
- package/locales/zh-TW/chat.json +12 -1
- package/locales/zh-TW/common.json +16 -0
- package/locales/zh-TW/error.json +35 -6
- package/locales/zh-TW/setting.json +53 -6
- package/next.config.mjs +8 -2
- package/package.json +8 -1
- package/src/app/api/chat/[provider]/agentRuntime.ts +129 -0
- package/src/app/api/chat/[provider]/route.ts +64 -0
- package/src/app/api/chat/auth.ts +42 -0
- package/src/app/api/chat/google/route.ts +86 -0
- package/src/app/api/config/route.ts +6 -1
- package/src/app/api/config.ts +3 -3
- package/src/app/api/errorResponse.test.ts +8 -8
- package/src/app/api/errorResponse.ts +43 -6
- package/src/app/api/openai/createBizOpenAI/createAzureOpenai.ts +1 -1
- package/src/app/api/openai/createBizOpenAI/createOpenai.ts +1 -1
- package/src/app/api/openai/createBizOpenAI/index.ts +2 -2
- package/src/app/chat/(desktop)/features/ChatHeader.tsx +6 -4
- package/src/app/chat/(desktop)/features/ChatInput/Footer/index.tsx +13 -3
- package/src/app/chat/(desktop)/features/ChatInput/Header/index.tsx +16 -24
- package/src/app/chat/(desktop)/features/SideBar/index.tsx +1 -0
- package/src/app/chat/features/ChatHeader/ShareButton/Preview.tsx +3 -3
- package/src/app/chat/features/SessionListContent/List/Item/index.tsx +2 -3
- package/src/app/settings/llm/Azure/index.tsx +145 -0
- package/src/app/settings/llm/Bedrock/index.tsx +109 -0
- package/src/app/settings/llm/{LLM/Checker.tsx → Checker.tsx} +20 -22
- package/src/app/settings/llm/Google/index.tsx +74 -0
- package/src/app/settings/llm/{LLM → OpenAI}/index.tsx +27 -45
- package/src/app/settings/llm/Zhipu/index.tsx +78 -0
- package/src/app/settings/llm/const.ts +13 -0
- package/src/app/settings/llm/page.tsx +42 -2
- package/src/app/settings/llm/useSyncSettings.ts +23 -0
- package/src/chains/__tests__/langDetect.test.ts +2 -2
- package/src/chains/__tests__/pickEmoji.test.ts +2 -2
- package/src/chains/langDetect.ts +2 -2
- package/src/chains/pickEmoji.ts +2 -2
- package/src/chains/summaryAgentName.ts +2 -2
- package/src/chains/summaryDescription.ts +2 -2
- package/src/chains/summaryTags.ts +2 -2
- package/src/chains/summaryTitle.ts +2 -2
- package/src/chains/translate.ts +2 -2
- package/src/components/ModelIcon/index.tsx +37 -0
- package/src/components/ModelProviderIcon/index.tsx +44 -0
- package/src/components/ModelSelect/index.tsx +133 -0
- package/src/components/ModelTag/ModelIcon.tsx +35 -0
- package/src/components/ModelTag/index.tsx +13 -0
- package/src/config/modelProviders/bedrock.ts +43 -0
- package/src/config/modelProviders/google.ts +20 -0
- package/src/config/modelProviders/index.ts +18 -0
- package/src/config/modelProviders/openai.ts +102 -0
- package/src/config/modelProviders/zhipu.ts +34 -0
- package/src/config/{server.ts → server/app.ts} +1 -24
- package/src/config/server/index.ts +13 -0
- package/src/config/server/provider.ts +78 -0
- package/src/const/auth.ts +30 -0
- package/src/const/fetch.ts +3 -0
- package/src/const/settings.ts +24 -4
- package/src/database/core/migrations/migrateSettingsToUser/index.test.ts +2 -1
- package/src/database/core/migrations/migrateSettingsToUser/index.ts +1 -18
- package/src/database/core/migrations/migrateSettingsToUser/type.ts +100 -0
- package/src/database/models/message.ts +3 -1
- package/src/database/schemas/message.ts +1 -0
- package/src/database/schemas/session.ts +1 -0
- package/src/features/AgentSetting/AgentConfig/ModelSelect.tsx +71 -0
- package/src/features/AgentSetting/AgentConfig/index.tsx +171 -161
- package/src/features/AgentSetting/AgentConfig/useSyncConfig.ts +23 -0
- package/src/features/AgentSetting/AgentPrompt/TokenTag.tsx +39 -0
- package/src/features/AgentSetting/AgentPrompt/index.tsx +4 -26
- package/src/features/AgentSetting/store/index.ts +10 -2
- package/src/features/ChatInput/ActionBar/FileUpload.tsx +11 -4
- package/src/features/ChatInput/ActionBar/ModelSwitch.tsx +56 -14
- package/src/features/ChatInput/ActionBar/Token/TokenTag.tsx +42 -7
- package/src/features/ChatInput/ActionBar/Token/index.tsx +5 -2
- package/src/features/ChatInput/ActionBar/Tools/index.tsx +11 -1
- package/src/features/ChatInput/useChatInput.ts +5 -1
- package/src/features/Conversation/Error/APIKeyForm/Bedrock.tsx +80 -0
- package/src/features/Conversation/Error/APIKeyForm/Google.tsx +61 -0
- package/src/features/Conversation/Error/APIKeyForm/OpenAI.tsx +63 -0
- package/src/features/Conversation/Error/APIKeyForm/Zhipu.tsx +62 -0
- package/src/features/Conversation/Error/APIKeyForm/index.tsx +72 -0
- package/src/features/Conversation/Error/AccessCodeForm.tsx +63 -0
- package/src/features/Conversation/Error/ErrorJsonViewer.tsx +26 -0
- package/src/features/Conversation/Error/InvalidAPIKey.tsx +16 -0
- package/src/features/Conversation/Error/InvalidAccessCode.tsx +50 -0
- package/src/features/Conversation/Error/OpenAiBizError.tsx +9 -17
- package/src/features/Conversation/Error/{Plugin/PluginSettings.tsx → PluginSettings.tsx} +8 -3
- package/src/features/Conversation/Error/index.tsx +75 -70
- package/src/features/Conversation/Error/style.tsx +9 -3
- package/src/features/Conversation/Extras/Assistant.tsx +2 -3
- package/src/features/Conversation/Plugins/Inspector/index.tsx +8 -2
- package/src/features/Conversation/components/ChatItem/index.tsx +9 -24
- package/src/features/Conversation/types/index.tsx +2 -5
- package/src/hooks/_header.ts +35 -0
- package/src/libs/agent-runtime/BaseAI.ts +9 -0
- package/src/libs/agent-runtime/azureOpenai/index.ts +72 -0
- package/src/libs/agent-runtime/bedrock/index.ts +134 -0
- package/src/libs/agent-runtime/error.ts +26 -0
- package/src/libs/agent-runtime/google/index.ts +160 -0
- package/src/libs/agent-runtime/index.ts +9 -0
- package/src/libs/agent-runtime/openai/index.test.ts +176 -0
- package/src/libs/agent-runtime/openai/index.ts +107 -0
- package/src/libs/agent-runtime/types/chat.ts +119 -0
- package/src/libs/agent-runtime/types/index.ts +2 -0
- package/src/libs/agent-runtime/types/type.ts +34 -0
- package/src/libs/agent-runtime/utils/createError.ts +10 -0
- package/src/libs/agent-runtime/utils/debugStream.ts +18 -0
- package/src/libs/agent-runtime/utils/env.ts +1 -0
- package/src/libs/agent-runtime/utils/handleOpenAIError.ts +39 -0
- package/src/libs/agent-runtime/utils/uriParser.ts +16 -0
- package/src/libs/agent-runtime/zhipu/authToken.ts +22 -0
- package/src/libs/agent-runtime/zhipu/index.ts +125 -0
- package/src/locales/default/chat.ts +12 -2
- package/src/locales/default/common.ts +16 -0
- package/src/locales/default/error.ts +42 -5
- package/src/locales/default/setting.ts +61 -12
- package/src/services/__tests__/chat.test.ts +8 -2
- package/src/services/__tests__/message.test.ts +1 -1
- package/src/services/_auth.ts +106 -0
- package/src/services/_header.ts +1 -0
- package/src/services/_url.ts +0 -2
- package/src/services/chat.ts +32 -14
- package/src/store/chat/slices/message/action.ts +9 -6
- package/src/store/global/slices/common/action.ts +8 -2
- package/src/store/global/slices/settings/action.test.ts +2 -2
- package/src/store/global/slices/settings/action.ts +11 -4
- package/src/store/global/slices/settings/selectors/__snapshots__/modelProvider.test.ts.snap +242 -78
- package/src/store/global/slices/settings/selectors/__snapshots__/selectors.test.ts.snap +2 -0
- package/src/store/global/slices/settings/selectors/modelProvider.test.ts +171 -47
- package/src/store/global/slices/settings/selectors/modelProvider.ts +136 -43
- package/src/store/session/slices/agent/selectors.test.ts +0 -48
- package/src/store/session/slices/agent/selectors.ts +7 -12
- package/src/types/agent/index.ts +6 -2
- package/src/types/fetch.ts +4 -2
- package/src/types/files.ts +3 -3
- package/src/types/llm.ts +27 -0
- package/src/types/message/index.ts +7 -3
- package/src/types/openai/chat.ts +6 -11
- package/src/types/settings/index.ts +1 -0
- package/src/types/settings/modelProvider.ts +37 -1
- package/src/utils/__snapshots__/parseModels.test.ts.snap +63 -0
- package/src/utils/parseModels.test.ts +24 -0
- package/src/utils/parseModels.ts +37 -0
- package/src/app/api/openai/chat/createChatCompletion.test.ts +0 -166
- package/src/app/api/openai/chat/createChatCompletion.ts +0 -76
- package/src/app/api/openai/chat/route.ts +0 -19
- package/src/app/api/openai/models/route.ts +0 -17
- package/src/app/settings/llm/LLM/ModelList.tsx +0 -51
- package/src/app/settings/llm/LLM/getModelList.ts +0 -9
- package/src/app/settings/llm/index.tsx +0 -36
- package/src/const/llm.ts +0 -32
- package/src/features/Conversation/Error/ApiKeyForm.tsx +0 -87
- package/src/features/Conversation/Error/ErrorActionContainer.tsx +0 -23
- package/src/features/Conversation/Error/InvalidAccess.tsx +0 -90
- package/src/features/Conversation/Error/OpenAPIKey.tsx +0 -13
- package/src/features/Conversation/Error/Plugin/PluginError.tsx +0 -30
- package/src/services/modelList.ts +0 -15
- /package/src/app/chat/(desktop)/features/ChatInput/{Header → Footer}/DragUpload.tsx +0 -0
- /package/src/{app/api/openai/chat → libs/agent-runtime/utils}/desensitizeUrl.test.ts +0 -0
- /package/src/{app/api/openai/chat → libs/agent-runtime/utils}/desensitizeUrl.ts +0 -0
- /package/src/types/{translate.ts → message/translate.ts} +0 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { getPreferredRegion } from '@/app/api/config';
|
|
2
|
+
import { createErrorResponse } from '@/app/api/errorResponse';
|
|
3
|
+
import { LOBE_CHAT_AUTH_HEADER } from '@/const/auth';
|
|
4
|
+
import {
|
|
5
|
+
AgentInitErrorPayload,
|
|
6
|
+
AgentRuntimeError,
|
|
7
|
+
ChatCompletionErrorPayload,
|
|
8
|
+
ILobeAgentRuntimeErrorType,
|
|
9
|
+
} from '@/libs/agent-runtime';
|
|
10
|
+
import { ChatErrorType } from '@/types/fetch';
|
|
11
|
+
import { ChatStreamPayload } from '@/types/openai/chat';
|
|
12
|
+
|
|
13
|
+
import { checkPasswordOrUseUserApiKey, getJWTPayload } from '../auth';
|
|
14
|
+
import AgentRuntime from './agentRuntime';
|
|
15
|
+
|
|
16
|
+
export const runtime = 'edge';
|
|
17
|
+
|
|
18
|
+
export const preferredRegion = getPreferredRegion();
|
|
19
|
+
|
|
20
|
+
export const POST = async (req: Request, { params }: { params: { provider: string } }) => {
|
|
21
|
+
let agentRuntime: AgentRuntime;
|
|
22
|
+
|
|
23
|
+
// ============ 1. init chat model ============ //
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
// get Authorization from header
|
|
27
|
+
const authorization = req.headers.get(LOBE_CHAT_AUTH_HEADER);
|
|
28
|
+
if (!authorization) throw AgentRuntimeError.createError(ChatErrorType.Unauthorized);
|
|
29
|
+
|
|
30
|
+
// check the Auth With payload
|
|
31
|
+
const payload = await getJWTPayload(authorization);
|
|
32
|
+
checkPasswordOrUseUserApiKey(payload.accessCode, payload.apiKey);
|
|
33
|
+
|
|
34
|
+
const body = await req.clone().json();
|
|
35
|
+
agentRuntime = await AgentRuntime.initializeWithUserPayload(params.provider, payload, {
|
|
36
|
+
apiVersion: payload.azureApiVersion,
|
|
37
|
+
model: body.model,
|
|
38
|
+
useAzure: payload.useAzure,
|
|
39
|
+
});
|
|
40
|
+
} catch (e) {
|
|
41
|
+
// if catch the error, just return it
|
|
42
|
+
const err = e as AgentInitErrorPayload;
|
|
43
|
+
|
|
44
|
+
return createErrorResponse(err.type as ILobeAgentRuntimeErrorType, {
|
|
45
|
+
error: err.error,
|
|
46
|
+
provider: params.provider,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ============ 2. create chat completion ============ //
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
const payload = (await req.json()) as ChatStreamPayload;
|
|
54
|
+
|
|
55
|
+
return await agentRuntime.chat(payload);
|
|
56
|
+
} catch (e) {
|
|
57
|
+
const { errorType, provider, error: errorContent, ...res } = e as ChatCompletionErrorPayload;
|
|
58
|
+
|
|
59
|
+
// track the error at server side
|
|
60
|
+
console.error(`Route: [${provider}] ${errorType}:`, errorContent);
|
|
61
|
+
|
|
62
|
+
return createErrorResponse(errorType, { error: errorContent, provider, ...res });
|
|
63
|
+
}
|
|
64
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { importJWK, jwtVerify } from 'jose';
|
|
2
|
+
|
|
3
|
+
import { getServerConfig } from '@/config/server';
|
|
4
|
+
import { JWTPayload, JWT_SECRET_KEY } from '@/const/auth';
|
|
5
|
+
import { AgentRuntimeError } from '@/libs/agent-runtime';
|
|
6
|
+
import { ChatErrorType } from '@/types/fetch';
|
|
7
|
+
|
|
8
|
+
export const getJWTPayload = async (token: string) => {
|
|
9
|
+
const encoder = new TextEncoder();
|
|
10
|
+
const secretKey = await crypto.subtle.digest('SHA-256', encoder.encode(JWT_SECRET_KEY));
|
|
11
|
+
|
|
12
|
+
const jwkSecretKey = await importJWK(
|
|
13
|
+
{ k: Buffer.from(secretKey).toString('base64'), kty: 'oct' },
|
|
14
|
+
'HS256',
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
const { payload } = await jwtVerify(token, jwkSecretKey);
|
|
18
|
+
|
|
19
|
+
return payload as JWTPayload;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Check if the provided access code is valid or if a user API key should be used.
|
|
24
|
+
*
|
|
25
|
+
* @param {string} accessCode - The access code to check.
|
|
26
|
+
* @param {string} apiKey - The user API key.
|
|
27
|
+
* @throws {AgentRuntimeError} If the access code is invalid and no user API key is provided.
|
|
28
|
+
*/
|
|
29
|
+
export const checkPasswordOrUseUserApiKey = (accessCode?: string, apiKey?: string) => {
|
|
30
|
+
const { ACCESS_CODES } = getServerConfig();
|
|
31
|
+
|
|
32
|
+
// if apiKey exist
|
|
33
|
+
if (apiKey) return;
|
|
34
|
+
|
|
35
|
+
// if accessCode doesn't exist
|
|
36
|
+
if (!ACCESS_CODES.length) return;
|
|
37
|
+
|
|
38
|
+
if (!accessCode || !ACCESS_CODES.includes(accessCode)) {
|
|
39
|
+
console.warn('tracked an invalid access code, 检查到输入的错误密码:', accessCode);
|
|
40
|
+
throw AgentRuntimeError.createError(ChatErrorType.InvalidAccessCode);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { createErrorResponse } from '@/app/api/errorResponse';
|
|
2
|
+
import { getServerConfig } from '@/config/server';
|
|
3
|
+
import { LOBE_CHAT_AUTH_HEADER } from '@/const/auth';
|
|
4
|
+
import {
|
|
5
|
+
AgentInitErrorPayload,
|
|
6
|
+
AgentRuntimeError,
|
|
7
|
+
ChatCompletionErrorPayload,
|
|
8
|
+
ILobeAgentRuntimeErrorType,
|
|
9
|
+
LobeGoogleAI,
|
|
10
|
+
} from '@/libs/agent-runtime';
|
|
11
|
+
import { ChatErrorType } from '@/types/fetch';
|
|
12
|
+
import { ChatStreamPayload } from '@/types/openai/chat';
|
|
13
|
+
|
|
14
|
+
import { checkPasswordOrUseUserApiKey, getJWTPayload } from '../auth';
|
|
15
|
+
|
|
16
|
+
// due to the Chinese region does not support accessing Google
|
|
17
|
+
// we need to use proxy to access it
|
|
18
|
+
// refs: https://github.com/google/generative-ai-js/issues/29#issuecomment-1866246513
|
|
19
|
+
// if (process.env.HTTP_PROXY_URL) {
|
|
20
|
+
// const { setGlobalDispatcher, ProxyAgent } = require('undici');
|
|
21
|
+
//
|
|
22
|
+
// setGlobalDispatcher(new ProxyAgent({ uri: process.env.HTTP_PROXY_URL }));
|
|
23
|
+
// }
|
|
24
|
+
// undici only can be used in NodeJS
|
|
25
|
+
// export const runtime = 'nodejs';
|
|
26
|
+
|
|
27
|
+
export const runtime = 'edge';
|
|
28
|
+
|
|
29
|
+
export const preferredRegion = [
|
|
30
|
+
'bom1',
|
|
31
|
+
'cle1',
|
|
32
|
+
'cpt1',
|
|
33
|
+
'gru1',
|
|
34
|
+
'hnd1',
|
|
35
|
+
'iad1',
|
|
36
|
+
'icn1',
|
|
37
|
+
'kix1',
|
|
38
|
+
'pdx1',
|
|
39
|
+
'sfo1',
|
|
40
|
+
'sin1',
|
|
41
|
+
'syd1',
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
export const POST = async (req: Request) => {
|
|
45
|
+
let agentRuntime: LobeGoogleAI;
|
|
46
|
+
|
|
47
|
+
// ============ 1. init chat model ============ //
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
// get Authorization from header
|
|
51
|
+
const authorization = req.headers.get(LOBE_CHAT_AUTH_HEADER);
|
|
52
|
+
if (!authorization) throw AgentRuntimeError.createError(ChatErrorType.Unauthorized);
|
|
53
|
+
|
|
54
|
+
// check the Auth With payload
|
|
55
|
+
const payload = await getJWTPayload(authorization);
|
|
56
|
+
checkPasswordOrUseUserApiKey(payload.accessCode, payload.apiKey);
|
|
57
|
+
|
|
58
|
+
const { GOOGLE_API_KEY } = getServerConfig();
|
|
59
|
+
const apiKey = payload?.apiKey || GOOGLE_API_KEY;
|
|
60
|
+
|
|
61
|
+
agentRuntime = new LobeGoogleAI(apiKey);
|
|
62
|
+
} catch (e) {
|
|
63
|
+
// if catch the error, just return it
|
|
64
|
+
const err = e as AgentInitErrorPayload;
|
|
65
|
+
|
|
66
|
+
return createErrorResponse(err.type as ILobeAgentRuntimeErrorType, {
|
|
67
|
+
error: err.error,
|
|
68
|
+
provider: 'google',
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ============ 2. create chat completion ============ //
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
const payload = (await req.json()) as ChatStreamPayload;
|
|
76
|
+
|
|
77
|
+
return await agentRuntime.chat(payload);
|
|
78
|
+
} catch (e) {
|
|
79
|
+
const { errorType, provider, error: errorContent, ...res } = e as ChatCompletionErrorPayload;
|
|
80
|
+
|
|
81
|
+
// track the error at server side
|
|
82
|
+
console.error(`Route: [${provider}] ${errorType}:`, errorContent);
|
|
83
|
+
|
|
84
|
+
return createErrorResponse(errorType, { error: errorContent, provider, ...res });
|
|
85
|
+
}
|
|
86
|
+
};
|
|
@@ -7,10 +7,15 @@ export const runtime = 'edge';
|
|
|
7
7
|
* get Server config to client
|
|
8
8
|
*/
|
|
9
9
|
export const GET = async () => {
|
|
10
|
-
const { CUSTOM_MODELS } = getServerConfig();
|
|
10
|
+
const { CUSTOM_MODELS, ENABLED_ZHIPU, ENABLED_AWS_BEDROCK, ENABLED_GOOGLE } = getServerConfig();
|
|
11
11
|
|
|
12
12
|
const config: GlobalServerConfig = {
|
|
13
13
|
customModelName: CUSTOM_MODELS,
|
|
14
|
+
languageModel: {
|
|
15
|
+
bedrock: { enabled: ENABLED_AWS_BEDROCK },
|
|
16
|
+
google: { enabled: ENABLED_GOOGLE },
|
|
17
|
+
zhipu: { enabled: ENABLED_ZHIPU },
|
|
18
|
+
},
|
|
14
19
|
};
|
|
15
20
|
|
|
16
21
|
return new Response(JSON.stringify(config));
|
package/src/app/api/config.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { getServerConfig } from '@/config/server';
|
|
2
2
|
|
|
3
|
-
export const getPreferredRegion = () => {
|
|
3
|
+
export const getPreferredRegion = (region: string | string[] = 'auto') => {
|
|
4
4
|
try {
|
|
5
5
|
const cfg = getServerConfig();
|
|
6
6
|
if (cfg.OPENAI_FUNCTION_REGIONS.length <= 0) {
|
|
7
|
-
return
|
|
7
|
+
return region;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
return cfg.OPENAI_FUNCTION_REGIONS;
|
|
11
11
|
} catch (error) {
|
|
12
12
|
console.error('get server config failed, error:', error);
|
|
13
|
-
return
|
|
13
|
+
return region;
|
|
14
14
|
}
|
|
15
15
|
};
|
|
@@ -6,34 +6,34 @@ import { createErrorResponse } from './errorResponse';
|
|
|
6
6
|
|
|
7
7
|
describe('createErrorResponse', () => {
|
|
8
8
|
// 测试各种错误类型的状态码
|
|
9
|
-
it('returns a 401 status for
|
|
10
|
-
const errorType = ChatErrorType.
|
|
9
|
+
it('returns a 401 status for NoOpenAIAPIKey error type', () => {
|
|
10
|
+
const errorType = ChatErrorType.NoOpenAIAPIKey;
|
|
11
11
|
const response = createErrorResponse(errorType);
|
|
12
12
|
expect(response.status).toBe(401);
|
|
13
13
|
});
|
|
14
14
|
|
|
15
15
|
it('returns a 401 status for InvalidAccessCode error type', () => {
|
|
16
16
|
const errorType = ChatErrorType.InvalidAccessCode;
|
|
17
|
-
const response = createErrorResponse(errorType);
|
|
17
|
+
const response = createErrorResponse(errorType as any);
|
|
18
18
|
expect(response.status).toBe(401);
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
-
it('returns a
|
|
21
|
+
it('returns a 471 status for OpenAIBizError error type', () => {
|
|
22
22
|
const errorType = ChatErrorType.OpenAIBizError;
|
|
23
23
|
const response = createErrorResponse(errorType);
|
|
24
|
-
expect(response.status).toBe(
|
|
24
|
+
expect(response.status).toBe(471);
|
|
25
25
|
});
|
|
26
26
|
|
|
27
27
|
// 测试默认情况
|
|
28
28
|
it('returns the same error type as status for unknown error types', () => {
|
|
29
29
|
const errorType = 500; // 假设500是一个未知的错误类型
|
|
30
|
-
const response = createErrorResponse(errorType);
|
|
30
|
+
const response = createErrorResponse(errorType as any);
|
|
31
31
|
expect(response.status).toBe(errorType);
|
|
32
32
|
});
|
|
33
33
|
|
|
34
34
|
// 测试返回的Response对象是否包含正确的body和errorType
|
|
35
35
|
it('returns a Response object with the correct body and errorType', () => {
|
|
36
|
-
const errorType = ChatErrorType.
|
|
36
|
+
const errorType = ChatErrorType.NoOpenAIAPIKey;
|
|
37
37
|
const body = { message: 'No API key provided' };
|
|
38
38
|
const response = createErrorResponse(errorType, body);
|
|
39
39
|
return response.json().then((data) => {
|
|
@@ -46,7 +46,7 @@ describe('createErrorResponse', () => {
|
|
|
46
46
|
|
|
47
47
|
// 测试没有提供body时,返回的Response对象的body是否为undefined
|
|
48
48
|
it('returns a Response object with an undefined body when no body is provided', () => {
|
|
49
|
-
const errorType = ChatErrorType.
|
|
49
|
+
const errorType = ChatErrorType.NoOpenAIAPIKey;
|
|
50
50
|
const response = createErrorResponse(errorType);
|
|
51
51
|
return response.json().then((data) => {
|
|
52
52
|
expect(data.body).toBeUndefined();
|
|
@@ -1,23 +1,60 @@
|
|
|
1
|
+
import { consola } from 'consola';
|
|
2
|
+
|
|
3
|
+
import { AgentRuntimeErrorType, ILobeAgentRuntimeErrorType } from '@/libs/agent-runtime';
|
|
1
4
|
import { ChatErrorType, ErrorResponse, ErrorType } from '@/types/fetch';
|
|
2
5
|
|
|
3
|
-
const getStatus = (errorType: ErrorType) => {
|
|
6
|
+
const getStatus = (errorType: ILobeAgentRuntimeErrorType | ErrorType) => {
|
|
4
7
|
switch (errorType) {
|
|
5
|
-
case ChatErrorType.
|
|
6
|
-
case
|
|
8
|
+
case ChatErrorType.InvalidAccessCode:
|
|
9
|
+
case AgentRuntimeErrorType.NoOpenAIAPIKey:
|
|
10
|
+
case AgentRuntimeErrorType.InvalidAzureAPIKey:
|
|
11
|
+
case AgentRuntimeErrorType.InvalidBedrockCredentials:
|
|
12
|
+
case AgentRuntimeErrorType.InvalidZhipuAPIKey:
|
|
13
|
+
case AgentRuntimeErrorType.InvalidGoogleAPIKey: {
|
|
7
14
|
return 401;
|
|
8
15
|
}
|
|
9
16
|
|
|
10
|
-
case
|
|
11
|
-
return
|
|
17
|
+
case AgentRuntimeErrorType.LocationNotSupportError: {
|
|
18
|
+
return 403;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// define the 471~480 as provider error
|
|
22
|
+
case AgentRuntimeErrorType.AgentRuntimeError: {
|
|
23
|
+
return 470;
|
|
24
|
+
}
|
|
25
|
+
case AgentRuntimeErrorType.OpenAIBizError: {
|
|
26
|
+
return 471;
|
|
27
|
+
}
|
|
28
|
+
case AgentRuntimeErrorType.AzureBizError: {
|
|
29
|
+
return 472;
|
|
30
|
+
}
|
|
31
|
+
case AgentRuntimeErrorType.ZhipuBizError: {
|
|
32
|
+
return 473;
|
|
33
|
+
}
|
|
34
|
+
case AgentRuntimeErrorType.BedrockBizError: {
|
|
35
|
+
return 474;
|
|
36
|
+
}
|
|
37
|
+
case AgentRuntimeErrorType.GoogleBizError: {
|
|
38
|
+
return 475;
|
|
12
39
|
}
|
|
13
40
|
}
|
|
14
41
|
return errorType;
|
|
15
42
|
};
|
|
16
43
|
|
|
17
|
-
export const createErrorResponse = (
|
|
44
|
+
export const createErrorResponse = (
|
|
45
|
+
errorType: ErrorType | ILobeAgentRuntimeErrorType,
|
|
46
|
+
body?: any,
|
|
47
|
+
) => {
|
|
18
48
|
const statusCode = getStatus(errorType);
|
|
19
49
|
|
|
20
50
|
const data: ErrorResponse = { body, errorType };
|
|
21
51
|
|
|
52
|
+
if (typeof statusCode !== 'number' || statusCode < 200 || statusCode > 599) {
|
|
53
|
+
consola.error(
|
|
54
|
+
`current StatusCode: \`${statusCode}\` .`,
|
|
55
|
+
'Please go to `./src/app/api/errorResponse.ts` to defined the statusCode.',
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
22
59
|
return new Response(JSON.stringify(data), { status: statusCode });
|
|
23
60
|
};
|
|
@@ -20,7 +20,7 @@ export const createAzureOpenai = (params: {
|
|
|
20
20
|
const apiVersion = !params.apiVersion ? defaultApiVersion : params.apiVersion;
|
|
21
21
|
const apiKey = !params.userApiKey ? AZURE_API_KEY : params.userApiKey;
|
|
22
22
|
|
|
23
|
-
if (!apiKey) throw new Error('AZURE_API_KEY is empty', { cause: ChatErrorType.
|
|
23
|
+
if (!apiKey) throw new Error('AZURE_API_KEY is empty', { cause: ChatErrorType.NoOpenAIAPIKey });
|
|
24
24
|
|
|
25
25
|
const config: ClientOptions = {
|
|
26
26
|
apiKey,
|
|
@@ -11,7 +11,7 @@ export const createOpenai = (userApiKey: string | null, endpoint?: string | null
|
|
|
11
11
|
|
|
12
12
|
const apiKey = !userApiKey ? OPENAI_API_KEY : userApiKey;
|
|
13
13
|
|
|
14
|
-
if (!apiKey) throw new Error('OPENAI_API_KEY is empty', { cause: ChatErrorType.
|
|
14
|
+
if (!apiKey) throw new Error('OPENAI_API_KEY is empty', { cause: ChatErrorType.NoOpenAIAPIKey });
|
|
15
15
|
|
|
16
16
|
return new OpenAI({ apiKey, baseURL });
|
|
17
17
|
};
|
|
@@ -34,8 +34,8 @@ export const createBizOpenAI = (req: Request, model: string): Response | OpenAI
|
|
|
34
34
|
openai = createOpenai(apiKey, endpoint);
|
|
35
35
|
}
|
|
36
36
|
} catch (error) {
|
|
37
|
-
if ((error as Error).cause === ChatErrorType.
|
|
38
|
-
return createErrorResponse(ChatErrorType.
|
|
37
|
+
if ((error as Error).cause === ChatErrorType.NoOpenAIAPIKey) {
|
|
38
|
+
return createErrorResponse(ChatErrorType.NoOpenAIAPIKey);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
console.error(error); // log error to trace it
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { ActionIcon, Avatar, ChatHeader, ChatHeaderTitle, Tag } from '@lobehub/ui';
|
|
1
|
+
import { ActionIcon, Avatar, ChatHeader, ChatHeaderTitle } from '@lobehub/ui';
|
|
3
2
|
import { Skeleton } from 'antd';
|
|
4
3
|
import { PanelRightClose, PanelRightOpen } from 'lucide-react';
|
|
5
4
|
import { useRouter } from 'next/navigation';
|
|
@@ -7,8 +6,10 @@ import { memo } from 'react';
|
|
|
7
6
|
import { useTranslation } from 'react-i18next';
|
|
8
7
|
import { Flexbox } from 'react-layout-kit';
|
|
9
8
|
|
|
9
|
+
import ModelTag from '@/components/ModelTag';
|
|
10
10
|
import { DESKTOP_HEADER_ICON_SIZE } from '@/const/layoutTokens';
|
|
11
11
|
import { useGlobalStore } from '@/store/global';
|
|
12
|
+
import { modelProviderSelectors } from '@/store/global/slices/settings/selectors';
|
|
12
13
|
import { useSessionStore } from '@/store/session';
|
|
13
14
|
import { agentSelectors, sessionSelectors } from '@/store/session/selectors';
|
|
14
15
|
import { pathString } from '@/utils/url';
|
|
@@ -34,6 +35,7 @@ const Left = memo(() => {
|
|
|
34
35
|
agentSelectors.currentAgentPlugins(s),
|
|
35
36
|
]);
|
|
36
37
|
|
|
38
|
+
const showPlugin = useGlobalStore(modelProviderSelectors.modelEnabledFunctionCall(model));
|
|
37
39
|
const displayTitle = isInbox ? t('inbox.title') : title;
|
|
38
40
|
const displayDesc = isInbox ? t('inbox.desc') : description;
|
|
39
41
|
|
|
@@ -63,8 +65,8 @@ const Left = memo(() => {
|
|
|
63
65
|
desc={displayDesc}
|
|
64
66
|
tag={
|
|
65
67
|
<>
|
|
66
|
-
<
|
|
67
|
-
{plugins?.length > 0 && <PluginTag plugins={plugins} />}
|
|
68
|
+
<ModelTag model={model} />
|
|
69
|
+
{showPlugin && plugins?.length > 0 && <PluginTag plugins={plugins} />}
|
|
68
70
|
</>
|
|
69
71
|
}
|
|
70
72
|
title={displayTitle}
|
|
@@ -19,11 +19,12 @@ import SaveTopic from '@/features/ChatInput/Topic';
|
|
|
19
19
|
import { useSendMessage } from '@/features/ChatInput/useSend';
|
|
20
20
|
import { useChatStore } from '@/store/chat';
|
|
21
21
|
import { useGlobalStore } from '@/store/global';
|
|
22
|
-
import { preferenceSelectors } from '@/store/global/selectors';
|
|
22
|
+
import { modelProviderSelectors, preferenceSelectors } from '@/store/global/selectors';
|
|
23
23
|
import { useSessionStore } from '@/store/session';
|
|
24
24
|
import { agentSelectors } from '@/store/session/selectors';
|
|
25
25
|
import { isMacOS } from '@/utils/platform';
|
|
26
26
|
|
|
27
|
+
import DragUpload from './DragUpload';
|
|
27
28
|
import { LocalFiles } from './LocalFiles';
|
|
28
29
|
|
|
29
30
|
const useStyles = createStyles(({ css, prefixCls, token }) => {
|
|
@@ -59,7 +60,7 @@ const Footer = memo<{ setExpand?: (expand: boolean) => void }>(({ setExpand }) =
|
|
|
59
60
|
const { t } = useTranslation('chat');
|
|
60
61
|
|
|
61
62
|
const { theme, styles } = useStyles();
|
|
62
|
-
|
|
63
|
+
|
|
63
64
|
const [loading, stopGenerateMessage] = useChatStore((s) => [
|
|
64
65
|
!!s.chatLoadingId,
|
|
65
66
|
s.stopGenerateMessage,
|
|
@@ -68,6 +69,10 @@ const Footer = memo<{ setExpand?: (expand: boolean) => void }>(({ setExpand }) =
|
|
|
68
69
|
preferenceSelectors.useCmdEnterToSend(s),
|
|
69
70
|
s.updatePreference,
|
|
70
71
|
]);
|
|
72
|
+
|
|
73
|
+
const model = useSessionStore(agentSelectors.currentAgentModel);
|
|
74
|
+
const canUpload = useGlobalStore(modelProviderSelectors.modelEnabledVision(model));
|
|
75
|
+
|
|
71
76
|
const sendMessage = useSendMessage();
|
|
72
77
|
|
|
73
78
|
const cmdEnter = (
|
|
@@ -98,7 +103,12 @@ const Footer = memo<{ setExpand?: (expand: boolean) => void }>(({ setExpand }) =
|
|
|
98
103
|
padding={'0 24px'}
|
|
99
104
|
>
|
|
100
105
|
<Flexbox align={'center'} gap={8} horizontal>
|
|
101
|
-
{canUpload &&
|
|
106
|
+
{canUpload && (
|
|
107
|
+
<>
|
|
108
|
+
<DragUpload />
|
|
109
|
+
<LocalFiles />
|
|
110
|
+
</>
|
|
111
|
+
)}
|
|
102
112
|
</Flexbox>
|
|
103
113
|
<Flexbox align={'center'} gap={8} horizontal>
|
|
104
114
|
<Flexbox
|
|
@@ -3,31 +3,23 @@ import { Maximize2, Minimize2 } from 'lucide-react';
|
|
|
3
3
|
import { memo } from 'react';
|
|
4
4
|
|
|
5
5
|
import ActionBar from '@/features/ChatInput/ActionBar';
|
|
6
|
-
import { useSessionStore } from '@/store/session';
|
|
7
|
-
import { agentSelectors } from '@/store/session/selectors';
|
|
8
6
|
|
|
9
|
-
|
|
7
|
+
interface HeaderProps {
|
|
8
|
+
expand: boolean;
|
|
9
|
+
setExpand: (expand: boolean) => void;
|
|
10
|
+
}
|
|
10
11
|
|
|
11
|
-
const Header = memo<{ expand
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
setExpand(!expand);
|
|
24
|
-
}}
|
|
25
|
-
/>
|
|
26
|
-
}
|
|
27
|
-
/>
|
|
28
|
-
</>
|
|
29
|
-
);
|
|
30
|
-
},
|
|
31
|
-
);
|
|
12
|
+
const Header = memo<HeaderProps>(({ expand, setExpand }) => (
|
|
13
|
+
<ActionBar
|
|
14
|
+
rightAreaEndRender={
|
|
15
|
+
<ActionIcon
|
|
16
|
+
icon={expand ? Minimize2 : Maximize2}
|
|
17
|
+
onClick={() => {
|
|
18
|
+
setExpand(!expand);
|
|
19
|
+
}}
|
|
20
|
+
/>
|
|
21
|
+
}
|
|
22
|
+
/>
|
|
23
|
+
));
|
|
32
24
|
|
|
33
25
|
export default Header;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Avatar, ChatHeaderTitle, Logo, Markdown, Tag } from '@lobehub/ui';
|
|
1
|
+
import { Avatar, ChatHeaderTitle, Logo, Markdown } from '@lobehub/ui';
|
|
3
2
|
import { memo } from 'react';
|
|
4
3
|
import { useTranslation } from 'react-i18next';
|
|
5
4
|
import { Flexbox } from 'react-layout-kit';
|
|
6
5
|
|
|
7
6
|
import pkg from '@/../package.json';
|
|
7
|
+
import ModelTag from '@/components/ModelTag';
|
|
8
8
|
import ChatList from '@/features/Conversation/components/ChatList';
|
|
9
9
|
import { useSessionStore } from '@/store/session';
|
|
10
10
|
import { agentSelectors, sessionSelectors } from '@/store/session/selectors';
|
|
@@ -42,7 +42,7 @@ const Preview = memo<FieldType & { title?: string }>(
|
|
|
42
42
|
desc={displayDesc}
|
|
43
43
|
tag={
|
|
44
44
|
<>
|
|
45
|
-
<
|
|
45
|
+
<ModelTag model={model} />
|
|
46
46
|
{plugins?.length > 0 && <PluginTag plugins={plugins} />}
|
|
47
47
|
</>
|
|
48
48
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { SiOpenai } from '@icons-pack/react-simple-icons';
|
|
2
|
-
import { Tag } from '@lobehub/ui';
|
|
3
1
|
import { memo, useMemo, useState } from 'react';
|
|
4
2
|
import { Flexbox } from 'react-layout-kit';
|
|
5
3
|
import { shallow } from 'zustand/shallow';
|
|
6
4
|
|
|
5
|
+
import ModelTag from '@/components/ModelTag';
|
|
7
6
|
import { useChatStore } from '@/store/chat';
|
|
8
7
|
import { useGlobalStore } from '@/store/global';
|
|
9
8
|
import { settingsSelectors } from '@/store/global/selectors';
|
|
@@ -64,7 +63,7 @@ const SessionItem = memo<SessionItemProps>(({ id }) => {
|
|
|
64
63
|
() =>
|
|
65
64
|
!showModel ? undefined : (
|
|
66
65
|
<Flexbox gap={4} horizontal style={{ flexWrap: 'wrap' }}>
|
|
67
|
-
{showModel && <
|
|
66
|
+
{showModel && <ModelTag model={model} />}
|
|
68
67
|
</Flexbox>
|
|
69
68
|
),
|
|
70
69
|
[showModel, model],
|