@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.
- package/.env.example +22 -2
- package/.eslintrc.js +1 -0
- package/CHANGELOG.md +25 -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} +11 -14
- package/src/app/settings/llm/Google/index.tsx +74 -0
- package/src/app/settings/llm/{LLM → OpenAI}/index.tsx +26 -44
- 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
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import { IPluginErrorType } from '@lobehub/chat-plugin-sdk';
|
|
2
|
+
|
|
3
|
+
import { ILobeAgentRuntimeErrorType } from '@/libs/agent-runtime';
|
|
1
4
|
import { ErrorType } from '@/types/fetch';
|
|
2
|
-
import { Translate } from '@/types/translate';
|
|
3
5
|
|
|
4
6
|
import { LLMRoleType } from '../llm';
|
|
5
7
|
import { BaseDataModel } from '../meta';
|
|
6
8
|
import { ChatPluginPayload } from './tools';
|
|
9
|
+
import { Translate } from './translate';
|
|
7
10
|
|
|
8
11
|
/**
|
|
9
12
|
* 聊天消息错误对象
|
|
@@ -11,7 +14,7 @@ import { ChatPluginPayload } from './tools';
|
|
|
11
14
|
export interface ChatMessageError {
|
|
12
15
|
body?: any;
|
|
13
16
|
message: string;
|
|
14
|
-
type: ErrorType;
|
|
17
|
+
type: ErrorType | IPluginErrorType | ILobeAgentRuntimeErrorType;
|
|
15
18
|
}
|
|
16
19
|
|
|
17
20
|
export interface ChatTranslate extends Translate {
|
|
@@ -28,10 +31,11 @@ export * from './tools';
|
|
|
28
31
|
|
|
29
32
|
export interface ChatMessage extends BaseDataModel {
|
|
30
33
|
content: string;
|
|
31
|
-
error?:
|
|
34
|
+
error?: ChatMessageError;
|
|
32
35
|
// 扩展字段
|
|
33
36
|
extra?: {
|
|
34
37
|
fromModel?: string;
|
|
38
|
+
fromProvider?: string;
|
|
35
39
|
// 翻译
|
|
36
40
|
translate?: ChatTranslate | false;
|
|
37
41
|
// TTS
|
package/src/types/openai/chat.ts
CHANGED
|
@@ -32,24 +32,15 @@ export interface OpenAIChatMessage {
|
|
|
32
32
|
role: LLMRoleType;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
export interface OpenAIChatStringMessage {
|
|
36
|
-
content: string;
|
|
37
|
-
role: LLMRoleType;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
35
|
/**
|
|
41
|
-
* @title
|
|
36
|
+
* @title Chat Stream Payload
|
|
42
37
|
*/
|
|
43
|
-
export interface
|
|
38
|
+
export interface ChatStreamPayload {
|
|
44
39
|
/**
|
|
45
40
|
* @title 控制生成文本中的惩罚系数,用于减少重复性
|
|
46
41
|
* @default 0
|
|
47
42
|
*/
|
|
48
43
|
frequency_penalty?: number;
|
|
49
|
-
/**
|
|
50
|
-
* @deprecated
|
|
51
|
-
*/
|
|
52
|
-
functions?: ChatCompletionFunctions[];
|
|
53
44
|
/**
|
|
54
45
|
* @title 生成文本的最大长度
|
|
55
46
|
*/
|
|
@@ -75,6 +66,10 @@ export interface OpenAIChatStreamPayload {
|
|
|
75
66
|
* @default 0
|
|
76
67
|
*/
|
|
77
68
|
presence_penalty?: number;
|
|
69
|
+
/**
|
|
70
|
+
* @default openai
|
|
71
|
+
*/
|
|
72
|
+
provider?: string;
|
|
78
73
|
/**
|
|
79
74
|
* @title 是否开启流式请求
|
|
80
75
|
* @default true
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type CustomModels = { displayName: string;
|
|
1
|
+
export type CustomModels = { displayName: string; id: string }[];
|
|
2
2
|
|
|
3
3
|
export interface OpenAIConfig {
|
|
4
4
|
OPENAI_API_KEY: string;
|
|
@@ -8,10 +8,46 @@ export interface OpenAIConfig {
|
|
|
8
8
|
*/
|
|
9
9
|
customModelName?: string;
|
|
10
10
|
endpoint?: string;
|
|
11
|
+
/**
|
|
12
|
+
* @deprecated
|
|
13
|
+
*/
|
|
11
14
|
models?: string[];
|
|
12
15
|
useAzure?: boolean;
|
|
13
16
|
}
|
|
14
17
|
|
|
18
|
+
export interface AzureOpenAIConfig {
|
|
19
|
+
apiKey: string;
|
|
20
|
+
apiVersion?: string;
|
|
21
|
+
deployments: string;
|
|
22
|
+
enabled: boolean;
|
|
23
|
+
endpoint?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface ZhiPuConfig {
|
|
27
|
+
apiKey?: string;
|
|
28
|
+
enabled: boolean;
|
|
29
|
+
endpoint?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface GoogleConfig {
|
|
33
|
+
apiKey?: string;
|
|
34
|
+
enabled: boolean;
|
|
35
|
+
endpoint?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface AWSBedrockConfig {
|
|
39
|
+
accessKeyId?: string;
|
|
40
|
+
enabled: boolean;
|
|
41
|
+
region?: string;
|
|
42
|
+
secretAccessKey?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
15
45
|
export interface GlobalLLMConfig {
|
|
46
|
+
azure: AzureOpenAIConfig;
|
|
47
|
+
bedrock: AWSBedrockConfig;
|
|
48
|
+
google: GoogleConfig;
|
|
16
49
|
openAI: OpenAIConfig;
|
|
50
|
+
zhipu: ZhiPuConfig;
|
|
17
51
|
}
|
|
52
|
+
|
|
53
|
+
export type GlobalLLMProviderKey = keyof GlobalLLMConfig;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`parseModelString > custom deletion, addition, and renaming of models 1`] = `
|
|
4
|
+
{
|
|
5
|
+
"add": [
|
|
6
|
+
{
|
|
7
|
+
"displayName": undefined,
|
|
8
|
+
"id": "llama",
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"displayName": undefined,
|
|
12
|
+
"id": "claude-2",
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"displayName": "gpt-4-32k",
|
|
16
|
+
"id": "gpt-4-1106-preview",
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
"removeAll": true,
|
|
20
|
+
"removed": [
|
|
21
|
+
"all",
|
|
22
|
+
"gpt-3.5-turbo",
|
|
23
|
+
],
|
|
24
|
+
}
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
exports[`parseModelString > duplicate naming model 1`] = `
|
|
28
|
+
{
|
|
29
|
+
"add": [
|
|
30
|
+
{
|
|
31
|
+
"displayName": "gpt-4-32k",
|
|
32
|
+
"id": "gpt-4-1106-preview",
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
"removeAll": false,
|
|
36
|
+
"removed": [],
|
|
37
|
+
}
|
|
38
|
+
`;
|
|
39
|
+
|
|
40
|
+
exports[`parseModelString > only add the model 1`] = `
|
|
41
|
+
{
|
|
42
|
+
"add": [
|
|
43
|
+
{
|
|
44
|
+
"displayName": undefined,
|
|
45
|
+
"id": "model1",
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"displayName": undefined,
|
|
49
|
+
"id": "model2",
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"displayName": undefined,
|
|
53
|
+
"id": "model3",
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"displayName": undefined,
|
|
57
|
+
"id": "model4",
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
"removeAll": false,
|
|
61
|
+
"removed": [],
|
|
62
|
+
}
|
|
63
|
+
`;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { parseModelString } from './parseModels';
|
|
4
|
+
|
|
5
|
+
describe('parseModelString', () => {
|
|
6
|
+
it('custom deletion, addition, and renaming of models', () => {
|
|
7
|
+
const result = parseModelString(
|
|
8
|
+
'-all,+llama,+claude-2,-gpt-3.5-turbo,gpt-4-1106-preview=gpt-4-turbo,gpt-4-1106-preview=gpt-4-32k',
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
expect(result).toMatchSnapshot();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('duplicate naming model', () => {
|
|
15
|
+
const result = parseModelString('gpt-4-1106-preview=gpt-4-turbo,gpt-4-1106-preview=gpt-4-32k');
|
|
16
|
+
expect(result).toMatchSnapshot();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('only add the model', () => {
|
|
20
|
+
const result = parseModelString('model1,model2,model3,model4');
|
|
21
|
+
|
|
22
|
+
expect(result).toMatchSnapshot();
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { CustomModels } from '@/types/settings';
|
|
2
|
+
|
|
3
|
+
export const parseModelString = (modelString: string = '') => {
|
|
4
|
+
let models: CustomModels = [];
|
|
5
|
+
let removeAll = false;
|
|
6
|
+
const removedModels: string[] = [];
|
|
7
|
+
const modelNames = modelString.split(/[,,]/).filter(Boolean);
|
|
8
|
+
|
|
9
|
+
for (const item of modelNames) {
|
|
10
|
+
const disable = item.startsWith('-');
|
|
11
|
+
const nameConfig = item.startsWith('+') || item.startsWith('-') ? item.slice(1) : item;
|
|
12
|
+
const [id, displayName] = nameConfig.split('=');
|
|
13
|
+
|
|
14
|
+
if (disable) {
|
|
15
|
+
// Disable all models.
|
|
16
|
+
if (id === 'all') {
|
|
17
|
+
removeAll = true;
|
|
18
|
+
}
|
|
19
|
+
removedModels.push(id);
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Remove duplicate model entries.
|
|
24
|
+
const existingIndex = models.findIndex(({ id: n }) => n === id);
|
|
25
|
+
if (existingIndex !== -1) {
|
|
26
|
+
models.splice(existingIndex, 1);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
models.push({ displayName, id: id });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
add: models,
|
|
34
|
+
removeAll,
|
|
35
|
+
removed: removedModels,
|
|
36
|
+
};
|
|
37
|
+
};
|
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
import OpenAI, { APIError } from 'openai';
|
|
2
|
-
import { Mock, afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
-
|
|
4
|
-
import { createChatCompletion } from './createChatCompletion';
|
|
5
|
-
|
|
6
|
-
// Mock the console.error to avoid polluting test output
|
|
7
|
-
vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
8
|
-
|
|
9
|
-
describe('createChatCompletion', () => {
|
|
10
|
-
let openaiInstance: OpenAI;
|
|
11
|
-
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
openaiInstance = new OpenAI({ apiKey: 'test', dangerouslyAllowBrowser: true });
|
|
14
|
-
|
|
15
|
-
// 使用 vi.spyOn 来模拟 chat.completions.create 方法
|
|
16
|
-
vi.spyOn(openaiInstance.chat.completions, 'create').mockResolvedValue(
|
|
17
|
-
new ReadableStream() as any,
|
|
18
|
-
);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
afterEach(() => {
|
|
22
|
-
vi.clearAllMocks();
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('should return a StreamingTextResponse on successful API call', async () => {
|
|
26
|
-
// Arrange
|
|
27
|
-
const mockStream = new ReadableStream();
|
|
28
|
-
const mockResponse = Promise.resolve(mockStream);
|
|
29
|
-
|
|
30
|
-
(openaiInstance.chat.completions.create as Mock).mockResolvedValue(mockResponse);
|
|
31
|
-
|
|
32
|
-
// Act
|
|
33
|
-
const result = await createChatCompletion({
|
|
34
|
-
openai: openaiInstance,
|
|
35
|
-
payload: {
|
|
36
|
-
messages: [{ content: 'Hello', role: 'user' }],
|
|
37
|
-
model: 'text-davinci-003',
|
|
38
|
-
temperature: 0,
|
|
39
|
-
},
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
// Assert
|
|
43
|
-
expect(result).toBeInstanceOf(Response); // 注意这里的改动
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('should return an openai error response when OpenAI.APIError is thrown', async () => {
|
|
47
|
-
// Arrange
|
|
48
|
-
const apiError = new OpenAI.APIError(
|
|
49
|
-
400,
|
|
50
|
-
{
|
|
51
|
-
status: 400,
|
|
52
|
-
error: {
|
|
53
|
-
message: 'Bad Request',
|
|
54
|
-
},
|
|
55
|
-
},
|
|
56
|
-
'Error message',
|
|
57
|
-
{},
|
|
58
|
-
);
|
|
59
|
-
|
|
60
|
-
vi.spyOn(openaiInstance.chat.completions, 'create').mockRejectedValue(apiError);
|
|
61
|
-
|
|
62
|
-
// Act
|
|
63
|
-
const result = await createChatCompletion({
|
|
64
|
-
openai: openaiInstance,
|
|
65
|
-
payload: {
|
|
66
|
-
messages: [{ content: 'Hello', role: 'user' }],
|
|
67
|
-
model: 'text-davinci-003',
|
|
68
|
-
temperature: 0,
|
|
69
|
-
},
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
// Assert
|
|
73
|
-
expect(result).toBeInstanceOf(Response);
|
|
74
|
-
expect(result.status).toBe(577); // Your custom error status code
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it('should return an cause response when OpenAI.APIError is thrown with cause', async () => {
|
|
78
|
-
// Arrange
|
|
79
|
-
const errorInfo = {
|
|
80
|
-
stack: 'abc',
|
|
81
|
-
cause: {
|
|
82
|
-
message: 'api is undefined',
|
|
83
|
-
},
|
|
84
|
-
};
|
|
85
|
-
const apiError = new OpenAI.APIError(400, errorInfo, 'module error', {});
|
|
86
|
-
|
|
87
|
-
vi.spyOn(openaiInstance.chat.completions, 'create').mockRejectedValue(apiError);
|
|
88
|
-
|
|
89
|
-
// Act
|
|
90
|
-
const result = await createChatCompletion({
|
|
91
|
-
openai: openaiInstance,
|
|
92
|
-
payload: {
|
|
93
|
-
messages: [{ content: 'Hello', role: 'user' }],
|
|
94
|
-
model: 'text-davinci-003',
|
|
95
|
-
temperature: 0,
|
|
96
|
-
},
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
// Assert
|
|
100
|
-
expect(result).toBeInstanceOf(Response);
|
|
101
|
-
expect(result.status).toBe(577); // Your custom error status code
|
|
102
|
-
|
|
103
|
-
const content = await result.json();
|
|
104
|
-
expect(content.body).toHaveProperty('endpoint');
|
|
105
|
-
expect(content.body.endpoint).toEqual('https://api.openai.com/v1');
|
|
106
|
-
expect(content.body.error).toEqual(errorInfo);
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
it('should return an cause response with desensitize Url', async () => {
|
|
110
|
-
// Arrange
|
|
111
|
-
const errorInfo = {
|
|
112
|
-
stack: 'abc',
|
|
113
|
-
cause: { message: 'api is undefined' },
|
|
114
|
-
};
|
|
115
|
-
const apiError = new OpenAI.APIError(400, errorInfo, 'module error', {});
|
|
116
|
-
|
|
117
|
-
openaiInstance = new OpenAI({
|
|
118
|
-
apiKey: 'test',
|
|
119
|
-
dangerouslyAllowBrowser: true,
|
|
120
|
-
baseURL: 'https://api.abc.com/v1',
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
vi.spyOn(openaiInstance.chat.completions, 'create').mockRejectedValue(apiError);
|
|
124
|
-
|
|
125
|
-
// Act
|
|
126
|
-
const result = await createChatCompletion({
|
|
127
|
-
openai: openaiInstance,
|
|
128
|
-
payload: {
|
|
129
|
-
messages: [{ content: 'Hello', role: 'user' }],
|
|
130
|
-
model: 'gpt-3.5-turbo',
|
|
131
|
-
temperature: 0,
|
|
132
|
-
},
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
// Assert
|
|
136
|
-
expect(result).toBeInstanceOf(Response);
|
|
137
|
-
expect(result.status).toBe(577); // Your custom error status code
|
|
138
|
-
|
|
139
|
-
const content = await result.json();
|
|
140
|
-
expect(content.body.endpoint).toEqual('https://api.***.com/v1');
|
|
141
|
-
expect(content.body.error).toEqual(errorInfo);
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
it('should return a 500 error response for non-OpenAI errors', async () => {
|
|
145
|
-
// Arrange
|
|
146
|
-
const genericError = new Error('Generic Error');
|
|
147
|
-
|
|
148
|
-
vi.spyOn(openaiInstance.chat.completions, 'create').mockRejectedValue(genericError);
|
|
149
|
-
|
|
150
|
-
// Act
|
|
151
|
-
const result = await createChatCompletion({
|
|
152
|
-
openai: openaiInstance,
|
|
153
|
-
payload: {
|
|
154
|
-
messages: [{ content: 'Hello', role: 'user' }],
|
|
155
|
-
model: 'text-davinci-003',
|
|
156
|
-
temperature: 0,
|
|
157
|
-
},
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
// Assert
|
|
161
|
-
expect(result.status).toBe(500);
|
|
162
|
-
const content = await result.json();
|
|
163
|
-
expect(content.body).toHaveProperty('endpoint');
|
|
164
|
-
expect(content.body).toHaveProperty('error');
|
|
165
|
-
});
|
|
166
|
-
});
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import { OpenAIStream, StreamingTextResponse } from 'ai';
|
|
2
|
-
import OpenAI from 'openai';
|
|
3
|
-
|
|
4
|
-
import { ChatErrorType } from '@/types/fetch';
|
|
5
|
-
import { OpenAIChatStreamPayload } from '@/types/openai/chat';
|
|
6
|
-
|
|
7
|
-
import { createErrorResponse } from '../../errorResponse';
|
|
8
|
-
import { desensitizeUrl } from './desensitizeUrl';
|
|
9
|
-
|
|
10
|
-
interface CreateChatCompletionOptions {
|
|
11
|
-
openai: OpenAI;
|
|
12
|
-
payload: OpenAIChatStreamPayload;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export const createChatCompletion = async ({ payload, openai }: CreateChatCompletionOptions) => {
|
|
16
|
-
// ============ 1. preprocess messages ============ //
|
|
17
|
-
const { messages, ...params } = payload;
|
|
18
|
-
|
|
19
|
-
// ============ 2. send api ============ //
|
|
20
|
-
|
|
21
|
-
try {
|
|
22
|
-
const response = await openai.chat.completions.create(
|
|
23
|
-
{
|
|
24
|
-
messages,
|
|
25
|
-
...params,
|
|
26
|
-
stream: true,
|
|
27
|
-
} as unknown as OpenAI.ChatCompletionCreateParamsStreaming,
|
|
28
|
-
{ headers: { Accept: '*/*' } },
|
|
29
|
-
);
|
|
30
|
-
const stream = OpenAIStream(response);
|
|
31
|
-
return new StreamingTextResponse(stream);
|
|
32
|
-
} catch (error) {
|
|
33
|
-
let desensitizedEndpoint = openai.baseURL;
|
|
34
|
-
|
|
35
|
-
// refs: https://github.com/lobehub/lobe-chat/issues/842
|
|
36
|
-
if (openai.baseURL !== 'https://api.openai.com/v1') {
|
|
37
|
-
desensitizedEndpoint = desensitizeUrl(openai.baseURL);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Check if the error is an OpenAI APIError
|
|
41
|
-
if (error instanceof OpenAI.APIError) {
|
|
42
|
-
let errorResult: any;
|
|
43
|
-
|
|
44
|
-
// if error is definitely OpenAI APIError, there will be an error object
|
|
45
|
-
if (error.error) {
|
|
46
|
-
errorResult = error.error;
|
|
47
|
-
}
|
|
48
|
-
// Or if there is a cause, we use error cause
|
|
49
|
-
// This often happened when there is a bug of the `openai` package.
|
|
50
|
-
else if (error.cause) {
|
|
51
|
-
errorResult = error.cause;
|
|
52
|
-
}
|
|
53
|
-
// if there is no other request error, the error object is a Response like object
|
|
54
|
-
else {
|
|
55
|
-
errorResult = { headers: error.headers, stack: error.stack, status: error.status };
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// track the error at server side
|
|
59
|
-
console.error(errorResult);
|
|
60
|
-
|
|
61
|
-
return createErrorResponse(ChatErrorType.OpenAIBizError, {
|
|
62
|
-
endpoint: desensitizedEndpoint,
|
|
63
|
-
error: errorResult,
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// track the non-openai error
|
|
68
|
-
console.error(error);
|
|
69
|
-
|
|
70
|
-
// return as a GatewayTimeout error
|
|
71
|
-
return createErrorResponse(ChatErrorType.InternalServerError, {
|
|
72
|
-
endpoint: desensitizedEndpoint,
|
|
73
|
-
error: JSON.stringify(error),
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
};
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { OpenAIChatStreamPayload } from '@/types/openai/chat';
|
|
2
|
-
|
|
3
|
-
import { getPreferredRegion } from '../../config';
|
|
4
|
-
import { createBizOpenAI } from '../createBizOpenAI';
|
|
5
|
-
import { createChatCompletion } from './createChatCompletion';
|
|
6
|
-
|
|
7
|
-
export const runtime = 'edge';
|
|
8
|
-
export const preferredRegion = getPreferredRegion();
|
|
9
|
-
|
|
10
|
-
export const POST = async (req: Request) => {
|
|
11
|
-
const payload = (await req.json()) as OpenAIChatStreamPayload;
|
|
12
|
-
|
|
13
|
-
const openaiOrErrResponse = createBizOpenAI(req, payload.model);
|
|
14
|
-
|
|
15
|
-
// if resOrOpenAI is a Response, it means there is an error,just return it
|
|
16
|
-
if (openaiOrErrResponse instanceof Response) return openaiOrErrResponse;
|
|
17
|
-
|
|
18
|
-
return createChatCompletion({ openai: openaiOrErrResponse, payload });
|
|
19
|
-
};
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { getOpenAIAuthFromRequest } from '@/const/fetch';
|
|
2
|
-
|
|
3
|
-
import { createOpenai } from '../createBizOpenAI/createOpenai';
|
|
4
|
-
|
|
5
|
-
export const runtime = 'edge';
|
|
6
|
-
|
|
7
|
-
export const POST = async (req: Request) => {
|
|
8
|
-
const { apiKey, endpoint } = getOpenAIAuthFromRequest(req);
|
|
9
|
-
|
|
10
|
-
const openAI = createOpenai(apiKey, endpoint);
|
|
11
|
-
|
|
12
|
-
const res = await openAI.models.list();
|
|
13
|
-
|
|
14
|
-
const modelList = res.data.map((i) => i.id);
|
|
15
|
-
|
|
16
|
-
return new Response(JSON.stringify(modelList));
|
|
17
|
-
};
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { ActionIcon } from '@lobehub/ui';
|
|
2
|
-
import { Button, Dropdown, Skeleton } from 'antd';
|
|
3
|
-
import { MenuItemType } from 'antd/lib/menu/hooks/useItems';
|
|
4
|
-
import { RotateCwIcon } from 'lucide-react';
|
|
5
|
-
import { memo } from 'react';
|
|
6
|
-
import { useTranslation } from 'react-i18next';
|
|
7
|
-
import { Flexbox } from 'react-layout-kit';
|
|
8
|
-
|
|
9
|
-
import { getModelList } from './getModelList';
|
|
10
|
-
|
|
11
|
-
interface ModelListProps {
|
|
12
|
-
value?: string[];
|
|
13
|
-
}
|
|
14
|
-
const ModelList = memo<ModelListProps>(({ value }) => {
|
|
15
|
-
const { t } = useTranslation('setting');
|
|
16
|
-
|
|
17
|
-
const isLoading = !value;
|
|
18
|
-
|
|
19
|
-
if (isLoading)
|
|
20
|
-
return (
|
|
21
|
-
<Skeleton active paragraph={false} title={{ style: { marginBottom: 0 }, width: '50%' }} />
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
const isEmpty = value?.length === 0;
|
|
25
|
-
|
|
26
|
-
return isEmpty ? (
|
|
27
|
-
<Button
|
|
28
|
-
onClick={() => {
|
|
29
|
-
getModelList('openAI');
|
|
30
|
-
}}
|
|
31
|
-
>
|
|
32
|
-
{t('llm.OpenAI.models.fetch')}
|
|
33
|
-
</Button>
|
|
34
|
-
) : (
|
|
35
|
-
<Dropdown
|
|
36
|
-
menu={{
|
|
37
|
-
items: (value || []).map<MenuItemType>((v) => ({
|
|
38
|
-
key: v,
|
|
39
|
-
label: v,
|
|
40
|
-
})),
|
|
41
|
-
}}
|
|
42
|
-
>
|
|
43
|
-
<Flexbox align={'center'} gap={8} horizontal>
|
|
44
|
-
{t('llm.OpenAI.models.count', { count: value.length })}
|
|
45
|
-
<ActionIcon icon={RotateCwIcon} size={'small'} title={t('llm.OpenAI.models.refetch')} />
|
|
46
|
-
</Flexbox>
|
|
47
|
-
</Dropdown>
|
|
48
|
-
);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
export default ModelList;
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { fetchModelList } from '@/services/modelList';
|
|
2
|
-
import { useGlobalStore } from '@/store/global';
|
|
3
|
-
|
|
4
|
-
export const getModelList = async (brand = 'openAI') => {
|
|
5
|
-
const setSettings = useGlobalStore.getState().setSettings;
|
|
6
|
-
const models = await fetchModelList();
|
|
7
|
-
|
|
8
|
-
setSettings({ languageModel: { [brand]: { models } } });
|
|
9
|
-
};
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import Link from 'next/link';
|
|
4
|
-
import { memo } from 'react';
|
|
5
|
-
import { Trans, useTranslation } from 'react-i18next';
|
|
6
|
-
|
|
7
|
-
import Footer from '@/app/settings/features/Footer';
|
|
8
|
-
import PageTitle from '@/components/PageTitle';
|
|
9
|
-
import { useSwitchSideBarOnInit } from '@/store/global/hooks/useSwitchSettingsOnInit';
|
|
10
|
-
import { SettingsTabs } from '@/store/global/initialState';
|
|
11
|
-
|
|
12
|
-
import LLM from './LLM';
|
|
13
|
-
|
|
14
|
-
export default memo(() => {
|
|
15
|
-
useSwitchSideBarOnInit(SettingsTabs.LLM);
|
|
16
|
-
const { t } = useTranslation('setting');
|
|
17
|
-
return (
|
|
18
|
-
<>
|
|
19
|
-
<PageTitle title={t('tab.llm')} />
|
|
20
|
-
<LLM />
|
|
21
|
-
<Footer>
|
|
22
|
-
<Trans i18nKey="llm.waitingForMore" ns={'setting'}>
|
|
23
|
-
更多模型正在
|
|
24
|
-
<Link
|
|
25
|
-
aria-label={'todo'}
|
|
26
|
-
href="https://github.com/lobehub/lobe-chat/issues/151"
|
|
27
|
-
target="_blank"
|
|
28
|
-
>
|
|
29
|
-
计划接入
|
|
30
|
-
</Link>
|
|
31
|
-
中 ,敬请期待 ✨
|
|
32
|
-
</Trans>
|
|
33
|
-
</Footer>
|
|
34
|
-
</>
|
|
35
|
-
);
|
|
36
|
-
});
|
package/src/const/llm.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { LanguageModel } from '@/types/llm';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* A white list of language models that are allowed to display and be used in the app.
|
|
5
|
-
*/
|
|
6
|
-
export const LanguageModelWhiteList = [
|
|
7
|
-
// OpenAI
|
|
8
|
-
'gpt-3.5-turbo',
|
|
9
|
-
'gpt-3.5-turbo-16k',
|
|
10
|
-
'gpt-4',
|
|
11
|
-
'gpt-4-32k',
|
|
12
|
-
];
|
|
13
|
-
|
|
14
|
-
export const DEFAULT_OPENAI_MODEL_LIST: string[] = Object.values(LanguageModel);
|
|
15
|
-
|
|
16
|
-
// vision model white list, these models will change the content from string to array
|
|
17
|
-
// refs: https://github.com/lobehub/lobe-chat/issues/790
|
|
18
|
-
export const isVisionModel = (model?: string) => !!model && model.includes('vision');
|
|
19
|
-
|
|
20
|
-
// refs: https://github.com/lobehub/lobe-chat/issues/837
|
|
21
|
-
export const GPT4_VISION_MODEL_DEFAULT_MAX_TOKENS = 2048;
|
|
22
|
-
|
|
23
|
-
// refs to: https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo
|
|
24
|
-
export const ModelTokens: Record<LanguageModel, number> = {
|
|
25
|
-
[LanguageModel.GPT3_5]: 4096,
|
|
26
|
-
[LanguageModel.GPT3_5_1106]: 16_385,
|
|
27
|
-
[LanguageModel.GPT3_5_16K]: 16_385,
|
|
28
|
-
[LanguageModel.GPT4]: 8196,
|
|
29
|
-
[LanguageModel.GPT4_PREVIEW]: 128_000,
|
|
30
|
-
[LanguageModel.GPT4_VISION_PREVIEW]: 128_000,
|
|
31
|
-
[LanguageModel.GPT4_32K]: 32_768,
|
|
32
|
-
};
|