@lobehub/chat 0.122.9 → 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.
Files changed (227) hide show
  1. package/.env.example +22 -2
  2. package/.eslintrc.js +1 -0
  3. package/CHANGELOG.md +25 -0
  4. package/README.md +11 -9
  5. package/README.zh-CN.md +11 -9
  6. package/docs/Deployment/Environment-Variable.md +65 -23
  7. package/docs/Deployment/Environment-Variable.zh-CN.md +64 -22
  8. package/locales/ar/chat.json +12 -1
  9. package/locales/ar/common.json +16 -0
  10. package/locales/ar/error.json +35 -6
  11. package/locales/ar/setting.json +53 -6
  12. package/locales/de-DE/chat.json +12 -1
  13. package/locales/de-DE/common.json +16 -0
  14. package/locales/de-DE/error.json +35 -6
  15. package/locales/de-DE/setting.json +53 -6
  16. package/locales/en-US/chat.json +13 -2
  17. package/locales/en-US/common.json +16 -0
  18. package/locales/en-US/error.json +35 -6
  19. package/locales/en-US/setting.json +53 -6
  20. package/locales/es-ES/chat.json +12 -1
  21. package/locales/es-ES/common.json +16 -0
  22. package/locales/es-ES/error.json +35 -6
  23. package/locales/es-ES/setting.json +53 -6
  24. package/locales/fr-FR/chat.json +13 -2
  25. package/locales/fr-FR/common.json +16 -0
  26. package/locales/fr-FR/error.json +35 -6
  27. package/locales/fr-FR/setting.json +53 -6
  28. package/locales/it-IT/chat.json +12 -1
  29. package/locales/it-IT/common.json +16 -0
  30. package/locales/it-IT/error.json +35 -6
  31. package/locales/it-IT/setting.json +53 -6
  32. package/locales/ja-JP/chat.json +12 -1
  33. package/locales/ja-JP/common.json +16 -0
  34. package/locales/ja-JP/error.json +35 -6
  35. package/locales/ja-JP/setting.json +53 -6
  36. package/locales/ko-KR/chat.json +12 -1
  37. package/locales/ko-KR/common.json +16 -0
  38. package/locales/ko-KR/error.json +35 -6
  39. package/locales/ko-KR/setting.json +53 -6
  40. package/locales/nl-NL/chat.json +12 -1
  41. package/locales/nl-NL/common.json +16 -0
  42. package/locales/nl-NL/error.json +35 -6
  43. package/locales/nl-NL/setting.json +53 -6
  44. package/locales/pl-PL/chat.json +12 -1
  45. package/locales/pl-PL/common.json +16 -0
  46. package/locales/pl-PL/error.json +35 -6
  47. package/locales/pl-PL/setting.json +53 -6
  48. package/locales/pt-BR/chat.json +12 -1
  49. package/locales/pt-BR/common.json +16 -0
  50. package/locales/pt-BR/error.json +35 -6
  51. package/locales/pt-BR/setting.json +53 -6
  52. package/locales/ru-RU/chat.json +12 -1
  53. package/locales/ru-RU/common.json +16 -0
  54. package/locales/ru-RU/error.json +35 -6
  55. package/locales/ru-RU/setting.json +53 -6
  56. package/locales/tr-TR/chat.json +12 -1
  57. package/locales/tr-TR/common.json +16 -0
  58. package/locales/tr-TR/error.json +35 -6
  59. package/locales/tr-TR/setting.json +53 -6
  60. package/locales/vi-VN/chat.json +12 -1
  61. package/locales/vi-VN/common.json +16 -0
  62. package/locales/vi-VN/error.json +35 -6
  63. package/locales/vi-VN/setting.json +53 -6
  64. package/locales/zh-CN/chat.json +12 -1
  65. package/locales/zh-CN/common.json +16 -0
  66. package/locales/zh-CN/error.json +34 -5
  67. package/locales/zh-CN/setting.json +59 -12
  68. package/locales/zh-TW/chat.json +12 -1
  69. package/locales/zh-TW/common.json +16 -0
  70. package/locales/zh-TW/error.json +35 -6
  71. package/locales/zh-TW/setting.json +53 -6
  72. package/next.config.mjs +8 -2
  73. package/package.json +8 -1
  74. package/src/app/api/chat/[provider]/agentRuntime.ts +129 -0
  75. package/src/app/api/chat/[provider]/route.ts +64 -0
  76. package/src/app/api/chat/auth.ts +42 -0
  77. package/src/app/api/chat/google/route.ts +86 -0
  78. package/src/app/api/config/route.ts +6 -1
  79. package/src/app/api/config.ts +3 -3
  80. package/src/app/api/errorResponse.test.ts +8 -8
  81. package/src/app/api/errorResponse.ts +43 -6
  82. package/src/app/api/openai/createBizOpenAI/createAzureOpenai.ts +1 -1
  83. package/src/app/api/openai/createBizOpenAI/createOpenai.ts +1 -1
  84. package/src/app/api/openai/createBizOpenAI/index.ts +2 -2
  85. package/src/app/chat/(desktop)/features/ChatHeader.tsx +6 -4
  86. package/src/app/chat/(desktop)/features/ChatInput/Footer/index.tsx +13 -3
  87. package/src/app/chat/(desktop)/features/ChatInput/Header/index.tsx +16 -24
  88. package/src/app/chat/(desktop)/features/SideBar/index.tsx +1 -0
  89. package/src/app/chat/features/ChatHeader/ShareButton/Preview.tsx +3 -3
  90. package/src/app/chat/features/SessionListContent/List/Item/index.tsx +2 -3
  91. package/src/app/settings/llm/Azure/index.tsx +145 -0
  92. package/src/app/settings/llm/Bedrock/index.tsx +109 -0
  93. package/src/app/settings/llm/{LLM/Checker.tsx → Checker.tsx} +11 -14
  94. package/src/app/settings/llm/Google/index.tsx +74 -0
  95. package/src/app/settings/llm/{LLM → OpenAI}/index.tsx +26 -44
  96. package/src/app/settings/llm/Zhipu/index.tsx +78 -0
  97. package/src/app/settings/llm/const.ts +13 -0
  98. package/src/app/settings/llm/page.tsx +42 -2
  99. package/src/app/settings/llm/useSyncSettings.ts +23 -0
  100. package/src/chains/__tests__/langDetect.test.ts +2 -2
  101. package/src/chains/__tests__/pickEmoji.test.ts +2 -2
  102. package/src/chains/langDetect.ts +2 -2
  103. package/src/chains/pickEmoji.ts +2 -2
  104. package/src/chains/summaryAgentName.ts +2 -2
  105. package/src/chains/summaryDescription.ts +2 -2
  106. package/src/chains/summaryTags.ts +2 -2
  107. package/src/chains/summaryTitle.ts +2 -2
  108. package/src/chains/translate.ts +2 -2
  109. package/src/components/ModelIcon/index.tsx +37 -0
  110. package/src/components/ModelProviderIcon/index.tsx +44 -0
  111. package/src/components/ModelSelect/index.tsx +133 -0
  112. package/src/components/ModelTag/ModelIcon.tsx +35 -0
  113. package/src/components/ModelTag/index.tsx +13 -0
  114. package/src/config/modelProviders/bedrock.ts +43 -0
  115. package/src/config/modelProviders/google.ts +20 -0
  116. package/src/config/modelProviders/index.ts +18 -0
  117. package/src/config/modelProviders/openai.ts +102 -0
  118. package/src/config/modelProviders/zhipu.ts +34 -0
  119. package/src/config/{server.ts → server/app.ts} +1 -24
  120. package/src/config/server/index.ts +13 -0
  121. package/src/config/server/provider.ts +78 -0
  122. package/src/const/auth.ts +30 -0
  123. package/src/const/fetch.ts +3 -0
  124. package/src/const/settings.ts +24 -4
  125. package/src/database/core/migrations/migrateSettingsToUser/index.test.ts +2 -1
  126. package/src/database/core/migrations/migrateSettingsToUser/index.ts +1 -18
  127. package/src/database/core/migrations/migrateSettingsToUser/type.ts +100 -0
  128. package/src/database/models/message.ts +3 -1
  129. package/src/database/schemas/message.ts +1 -0
  130. package/src/database/schemas/session.ts +1 -0
  131. package/src/features/AgentSetting/AgentConfig/ModelSelect.tsx +71 -0
  132. package/src/features/AgentSetting/AgentConfig/index.tsx +171 -161
  133. package/src/features/AgentSetting/AgentConfig/useSyncConfig.ts +23 -0
  134. package/src/features/AgentSetting/AgentPrompt/TokenTag.tsx +39 -0
  135. package/src/features/AgentSetting/AgentPrompt/index.tsx +4 -26
  136. package/src/features/AgentSetting/store/index.ts +10 -2
  137. package/src/features/ChatInput/ActionBar/FileUpload.tsx +11 -4
  138. package/src/features/ChatInput/ActionBar/ModelSwitch.tsx +56 -14
  139. package/src/features/ChatInput/ActionBar/Token/TokenTag.tsx +42 -7
  140. package/src/features/ChatInput/ActionBar/Token/index.tsx +5 -2
  141. package/src/features/ChatInput/ActionBar/Tools/index.tsx +11 -1
  142. package/src/features/ChatInput/useChatInput.ts +5 -1
  143. package/src/features/Conversation/Error/APIKeyForm/Bedrock.tsx +80 -0
  144. package/src/features/Conversation/Error/APIKeyForm/Google.tsx +61 -0
  145. package/src/features/Conversation/Error/APIKeyForm/OpenAI.tsx +63 -0
  146. package/src/features/Conversation/Error/APIKeyForm/Zhipu.tsx +62 -0
  147. package/src/features/Conversation/Error/APIKeyForm/index.tsx +72 -0
  148. package/src/features/Conversation/Error/AccessCodeForm.tsx +63 -0
  149. package/src/features/Conversation/Error/ErrorJsonViewer.tsx +26 -0
  150. package/src/features/Conversation/Error/InvalidAPIKey.tsx +16 -0
  151. package/src/features/Conversation/Error/InvalidAccessCode.tsx +50 -0
  152. package/src/features/Conversation/Error/OpenAiBizError.tsx +9 -17
  153. package/src/features/Conversation/Error/{Plugin/PluginSettings.tsx → PluginSettings.tsx} +8 -3
  154. package/src/features/Conversation/Error/index.tsx +75 -70
  155. package/src/features/Conversation/Error/style.tsx +9 -3
  156. package/src/features/Conversation/Extras/Assistant.tsx +2 -3
  157. package/src/features/Conversation/Plugins/Inspector/index.tsx +8 -2
  158. package/src/features/Conversation/components/ChatItem/index.tsx +9 -24
  159. package/src/features/Conversation/types/index.tsx +2 -5
  160. package/src/hooks/_header.ts +35 -0
  161. package/src/libs/agent-runtime/BaseAI.ts +9 -0
  162. package/src/libs/agent-runtime/azureOpenai/index.ts +72 -0
  163. package/src/libs/agent-runtime/bedrock/index.ts +134 -0
  164. package/src/libs/agent-runtime/error.ts +26 -0
  165. package/src/libs/agent-runtime/google/index.ts +160 -0
  166. package/src/libs/agent-runtime/index.ts +9 -0
  167. package/src/libs/agent-runtime/openai/index.test.ts +176 -0
  168. package/src/libs/agent-runtime/openai/index.ts +107 -0
  169. package/src/libs/agent-runtime/types/chat.ts +119 -0
  170. package/src/libs/agent-runtime/types/index.ts +2 -0
  171. package/src/libs/agent-runtime/types/type.ts +34 -0
  172. package/src/libs/agent-runtime/utils/createError.ts +10 -0
  173. package/src/libs/agent-runtime/utils/debugStream.ts +18 -0
  174. package/src/libs/agent-runtime/utils/env.ts +1 -0
  175. package/src/libs/agent-runtime/utils/handleOpenAIError.ts +39 -0
  176. package/src/libs/agent-runtime/utils/uriParser.ts +16 -0
  177. package/src/libs/agent-runtime/zhipu/authToken.ts +22 -0
  178. package/src/libs/agent-runtime/zhipu/index.ts +125 -0
  179. package/src/locales/default/chat.ts +12 -2
  180. package/src/locales/default/common.ts +16 -0
  181. package/src/locales/default/error.ts +42 -5
  182. package/src/locales/default/setting.ts +61 -12
  183. package/src/services/__tests__/chat.test.ts +8 -2
  184. package/src/services/__tests__/message.test.ts +1 -1
  185. package/src/services/_auth.ts +106 -0
  186. package/src/services/_header.ts +1 -0
  187. package/src/services/_url.ts +0 -2
  188. package/src/services/chat.ts +32 -14
  189. package/src/store/chat/slices/message/action.ts +9 -6
  190. package/src/store/global/slices/common/action.ts +8 -2
  191. package/src/store/global/slices/settings/action.test.ts +2 -2
  192. package/src/store/global/slices/settings/action.ts +11 -4
  193. package/src/store/global/slices/settings/selectors/__snapshots__/modelProvider.test.ts.snap +242 -78
  194. package/src/store/global/slices/settings/selectors/__snapshots__/selectors.test.ts.snap +2 -0
  195. package/src/store/global/slices/settings/selectors/modelProvider.test.ts +171 -47
  196. package/src/store/global/slices/settings/selectors/modelProvider.ts +136 -43
  197. package/src/store/session/slices/agent/selectors.test.ts +0 -48
  198. package/src/store/session/slices/agent/selectors.ts +7 -12
  199. package/src/types/agent/index.ts +6 -2
  200. package/src/types/fetch.ts +4 -2
  201. package/src/types/files.ts +3 -3
  202. package/src/types/llm.ts +27 -0
  203. package/src/types/message/index.ts +7 -3
  204. package/src/types/openai/chat.ts +6 -11
  205. package/src/types/settings/index.ts +1 -0
  206. package/src/types/settings/modelProvider.ts +37 -1
  207. package/src/utils/__snapshots__/parseModels.test.ts.snap +63 -0
  208. package/src/utils/parseModels.test.ts +24 -0
  209. package/src/utils/parseModels.ts +37 -0
  210. package/src/app/api/openai/chat/createChatCompletion.test.ts +0 -166
  211. package/src/app/api/openai/chat/createChatCompletion.ts +0 -76
  212. package/src/app/api/openai/chat/route.ts +0 -19
  213. package/src/app/api/openai/models/route.ts +0 -17
  214. package/src/app/settings/llm/LLM/ModelList.tsx +0 -51
  215. package/src/app/settings/llm/LLM/getModelList.ts +0 -9
  216. package/src/app/settings/llm/index.tsx +0 -36
  217. package/src/const/llm.ts +0 -32
  218. package/src/features/Conversation/Error/ApiKeyForm.tsx +0 -87
  219. package/src/features/Conversation/Error/ErrorActionContainer.tsx +0 -23
  220. package/src/features/Conversation/Error/InvalidAccess.tsx +0 -90
  221. package/src/features/Conversation/Error/OpenAPIKey.tsx +0 -13
  222. package/src/features/Conversation/Error/Plugin/PluginError.tsx +0 -30
  223. package/src/services/modelList.ts +0 -15
  224. /package/src/app/chat/(desktop)/features/ChatInput/{Header → Footer}/DragUpload.tsx +0 -0
  225. /package/src/{app/api/openai/chat → libs/agent-runtime/utils}/desensitizeUrl.test.ts +0 -0
  226. /package/src/{app/api/openai/chat → libs/agent-runtime/utils}/desensitizeUrl.ts +0 -0
  227. /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));
@@ -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 'auto';
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 'auto';
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 NoAPIKey error type', () => {
10
- const errorType = ChatErrorType.NoAPIKey;
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 577 status for OpenAIBizError error type', () => {
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(577);
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.NoAPIKey;
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.NoAPIKey;
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.NoAPIKey:
6
- case ChatErrorType.InvalidAccessCode: {
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 ChatErrorType.OpenAIBizError: {
11
- return 577;
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 = (errorType: ErrorType, body?: any) => {
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.NoAPIKey });
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.NoAPIKey });
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.NoAPIKey) {
38
- return createErrorResponse(ChatErrorType.NoAPIKey);
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 { SiOpenai } from '@icons-pack/react-simple-icons';
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
- <Tag icon={<SiOpenai size={'1em'} />}>{model}</Tag>
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
- const canUpload = useSessionStore(agentSelectors.modelHasVisionAbility);
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 && <LocalFiles />}
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
- import DragUpload from './DragUpload';
7
+ interface HeaderProps {
8
+ expand: boolean;
9
+ setExpand: (expand: boolean) => void;
10
+ }
10
11
 
11
- const Header = memo<{ expand: boolean; setExpand: (expand: boolean) => void }>(
12
- ({ expand, setExpand }) => {
13
- const canUpload = useSessionStore(agentSelectors.modelHasVisionAbility);
14
-
15
- return (
16
- <>
17
- {canUpload && <DragUpload />}
18
- <ActionBar
19
- rightAreaEndRender={
20
- <ActionIcon
21
- icon={expand ? Minimize2 : Maximize2}
22
- onClick={() => {
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;
@@ -19,6 +19,7 @@ const useStyles = createStyles(({ css, token }) => ({
19
19
  height: 100% !important;
20
20
  `,
21
21
  drawer: css`
22
+ z-index: 0;
22
23
  background: ${token.colorBgLayout};
23
24
  `,
24
25
  header: css`
@@ -1,10 +1,10 @@
1
- import { SiOpenai } from '@icons-pack/react-simple-icons';
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
- <Tag icon={<SiOpenai size={'1em'} />}>{model}</Tag>
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 && <Tag icon={<SiOpenai size={'1em'} />}>{model}</Tag>}
66
+ {showModel && <ModelTag model={model} />}
68
67
  </Flexbox>
69
68
  ),
70
69
  [showModel, model],