@lobehub/chat 0.162.16 → 0.162.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/.env.example +4 -0
  2. package/CHANGELOG.md +50 -0
  3. package/Dockerfile +3 -0
  4. package/README.md +1 -0
  5. package/README.zh-CN.md +1 -0
  6. package/docs/self-hosting/advanced/settings-url-share.mdx +89 -57
  7. package/docs/self-hosting/advanced/settings-url-share.zh-CN.mdx +87 -56
  8. package/docs/self-hosting/advanced/upstream-sync.mdx +1 -1
  9. package/docs/self-hosting/environment-variables/model-provider.mdx +10 -1
  10. package/docs/self-hosting/environment-variables/model-provider.zh-CN.mdx +9 -0
  11. package/docs/usage/features/multi-ai-providers.mdx +1 -0
  12. package/docs/usage/features/multi-ai-providers.zh-CN.mdx +1 -0
  13. package/locales/ar/error.json +7 -37
  14. package/locales/ar/modelProvider.json +15 -171
  15. package/locales/ar/setting.json +5 -8
  16. package/locales/bg-BG/error.json +6 -36
  17. package/locales/bg-BG/modelProvider.json +15 -171
  18. package/locales/bg-BG/setting.json +5 -8
  19. package/locales/de-DE/error.json +6 -36
  20. package/locales/de-DE/modelProvider.json +15 -171
  21. package/locales/de-DE/setting.json +5 -8
  22. package/locales/en-US/error.json +6 -36
  23. package/locales/en-US/modelProvider.json +15 -171
  24. package/locales/en-US/setting.json +6 -9
  25. package/locales/es-ES/error.json +6 -36
  26. package/locales/es-ES/modelProvider.json +15 -171
  27. package/locales/es-ES/setting.json +5 -8
  28. package/locales/fr-FR/error.json +6 -36
  29. package/locales/fr-FR/modelProvider.json +15 -171
  30. package/locales/fr-FR/setting.json +9 -12
  31. package/locales/it-IT/error.json +6 -36
  32. package/locales/it-IT/modelProvider.json +15 -171
  33. package/locales/it-IT/setting.json +5 -8
  34. package/locales/ja-JP/error.json +6 -36
  35. package/locales/ja-JP/modelProvider.json +15 -171
  36. package/locales/ja-JP/setting.json +5 -8
  37. package/locales/ko-KR/error.json +6 -36
  38. package/locales/ko-KR/modelProvider.json +15 -171
  39. package/locales/ko-KR/setting.json +5 -8
  40. package/locales/nl-NL/error.json +6 -36
  41. package/locales/nl-NL/modelProvider.json +15 -171
  42. package/locales/nl-NL/setting.json +5 -8
  43. package/locales/pl-PL/error.json +6 -36
  44. package/locales/pl-PL/modelProvider.json +15 -171
  45. package/locales/pl-PL/setting.json +6 -9
  46. package/locales/pt-BR/error.json +6 -36
  47. package/locales/pt-BR/modelProvider.json +15 -171
  48. package/locales/pt-BR/setting.json +5 -8
  49. package/locales/ru-RU/error.json +6 -36
  50. package/locales/ru-RU/modelProvider.json +15 -171
  51. package/locales/ru-RU/setting.json +5 -8
  52. package/locales/tr-TR/error.json +6 -36
  53. package/locales/tr-TR/modelProvider.json +15 -171
  54. package/locales/tr-TR/setting.json +5 -8
  55. package/locales/vi-VN/error.json +6 -36
  56. package/locales/vi-VN/modelProvider.json +15 -171
  57. package/locales/vi-VN/setting.json +5 -8
  58. package/locales/zh-CN/error.json +7 -37
  59. package/locales/zh-CN/modelProvider.json +15 -171
  60. package/locales/zh-CN/setting.json +5 -8
  61. package/locales/zh-TW/error.json +7 -37
  62. package/locales/zh-TW/modelProvider.json +15 -171
  63. package/locales/zh-TW/setting.json +5 -8
  64. package/package.json +1 -2
  65. package/src/app/(main)/settings/llm/ProviderList/Azure/index.tsx +107 -0
  66. package/src/app/(main)/settings/llm/ProviderList/Bedrock/index.tsx +68 -0
  67. package/src/app/(main)/settings/llm/ProviderList/Ollama/index.tsx +29 -0
  68. package/src/app/(main)/settings/llm/ProviderList/OpenAI/index.tsx +20 -0
  69. package/src/app/(main)/settings/llm/ProviderList/providers.tsx +141 -0
  70. package/src/app/(main)/settings/llm/components/ProviderConfig/index.tsx +18 -27
  71. package/src/app/(main)/settings/llm/index.tsx +7 -34
  72. package/src/app/(main)/settings/llm/type.ts +5 -0
  73. package/src/app/api/chat/[provider]/route.ts +8 -3
  74. package/src/app/api/chat/agentRuntime.test.ts +17 -0
  75. package/src/app/api/chat/agentRuntime.ts +7 -0
  76. package/src/app/api/errorResponse.test.ts +7 -89
  77. package/src/app/api/errorResponse.ts +6 -43
  78. package/src/app/api/middleware/auth/index.ts +7 -3
  79. package/src/components/ModelProviderIcon/index.tsx +5 -0
  80. package/src/components/ModelSelect/index.tsx +7 -10
  81. package/src/config/llm.ts +6 -0
  82. package/src/config/modelProviders/anthropic.ts +5 -0
  83. package/src/config/modelProviders/azure.ts +2 -0
  84. package/src/config/modelProviders/bedrock.ts +2 -0
  85. package/src/config/modelProviders/deepseek.ts +4 -1
  86. package/src/config/modelProviders/google.ts +5 -0
  87. package/src/config/modelProviders/groq.ts +5 -0
  88. package/src/config/modelProviders/index.ts +4 -0
  89. package/src/config/modelProviders/minimax.ts +2 -0
  90. package/src/config/modelProviders/mistral.ts +2 -0
  91. package/src/config/modelProviders/moonshot.ts +2 -0
  92. package/src/config/modelProviders/ollama.ts +4 -0
  93. package/src/config/modelProviders/openai.ts +3 -0
  94. package/src/config/modelProviders/openrouter.ts +3 -0
  95. package/src/config/modelProviders/perplexity.ts +5 -0
  96. package/src/config/modelProviders/qwen.ts +35 -0
  97. package/src/config/modelProviders/togetherai.ts +3 -0
  98. package/src/config/modelProviders/zeroone.ts +4 -2
  99. package/src/config/modelProviders/zhipu.ts +2 -0
  100. package/src/const/settings/llm.ts +5 -0
  101. package/src/features/Conversation/Error/APIKeyForm/ProviderApiKeyForm.tsx +6 -3
  102. package/src/features/Conversation/Error/APIKeyForm/ProviderAvatar.tsx +5 -0
  103. package/src/features/Conversation/Error/APIKeyForm/index.tsx +4 -0
  104. package/src/features/Conversation/Error/OllamaBizError/InvalidOllamaModel/index.tsx +11 -14
  105. package/src/features/Conversation/Error/index.tsx +29 -19
  106. package/src/features/Conversation/components/ChatItem/index.tsx +4 -13
  107. package/src/features/ModelSelect/index.tsx +1 -1
  108. package/src/features/ModelSwitchPanel/index.tsx +1 -1
  109. package/src/hooks/useProviderName.ts +8 -0
  110. package/src/libs/agent-runtime/AgentRuntime.ts +8 -1
  111. package/src/libs/agent-runtime/anthropic/index.test.ts +8 -5
  112. package/src/libs/agent-runtime/anthropic/index.ts +3 -3
  113. package/src/libs/agent-runtime/azureOpenai/index.test.ts +5 -2
  114. package/src/libs/agent-runtime/azureOpenai/index.ts +2 -2
  115. package/src/libs/agent-runtime/bedrock/index.test.ts +2 -2
  116. package/src/libs/agent-runtime/bedrock/index.ts +2 -2
  117. package/src/libs/agent-runtime/deepseek/index.test.ts +3 -2
  118. package/src/libs/agent-runtime/deepseek/index.ts +0 -5
  119. package/src/libs/agent-runtime/error.ts +11 -43
  120. package/src/libs/agent-runtime/google/index.test.ts +3 -4
  121. package/src/libs/agent-runtime/google/index.ts +4 -4
  122. package/src/libs/agent-runtime/groq/index.test.ts +3 -2
  123. package/src/libs/agent-runtime/groq/index.ts +0 -4
  124. package/src/libs/agent-runtime/index.ts +1 -0
  125. package/src/libs/agent-runtime/minimax/index.test.ts +3 -2
  126. package/src/libs/agent-runtime/minimax/index.ts +5 -5
  127. package/src/libs/agent-runtime/mistral/index.test.ts +2 -2
  128. package/src/libs/agent-runtime/mistral/index.ts +0 -5
  129. package/src/libs/agent-runtime/moonshot/index.test.ts +3 -2
  130. package/src/libs/agent-runtime/moonshot/index.ts +0 -5
  131. package/src/libs/agent-runtime/openrouter/index.test.ts +3 -2
  132. package/src/libs/agent-runtime/openrouter/index.ts +0 -6
  133. package/src/libs/agent-runtime/perplexity/index.test.ts +2 -2
  134. package/src/libs/agent-runtime/perplexity/index.ts +0 -5
  135. package/src/libs/agent-runtime/qwen/index.test.ts +251 -0
  136. package/src/libs/agent-runtime/qwen/index.ts +28 -0
  137. package/src/libs/agent-runtime/togetherai/index.test.ts +3 -2
  138. package/src/libs/agent-runtime/togetherai/index.ts +0 -5
  139. package/src/libs/agent-runtime/types/type.ts +1 -1
  140. package/src/libs/agent-runtime/utils/openaiCompatibleFactory/index.test.ts +123 -6
  141. package/src/libs/agent-runtime/utils/openaiCompatibleFactory/index.ts +12 -6
  142. package/src/libs/agent-runtime/utils/streams/openai.ts +1 -1
  143. package/src/libs/agent-runtime/zeroone/index.test.ts +3 -2
  144. package/src/libs/agent-runtime/zeroone/index.ts +1 -5
  145. package/src/libs/agent-runtime/zhipu/index.test.ts +8 -5
  146. package/src/libs/agent-runtime/zhipu/index.ts +2 -2
  147. package/src/locales/default/error.ts +10 -51
  148. package/src/locales/default/modelProvider.ts +12 -169
  149. package/src/locales/default/setting.ts +5 -8
  150. package/src/server/globalConfig/index.ts +2 -0
  151. package/src/services/__tests__/chat.test.ts +16 -0
  152. package/src/services/chat.ts +3 -0
  153. package/src/services/message/client.test.ts +4 -1
  154. package/src/types/fetch.ts +3 -1
  155. package/src/types/llm.ts +22 -0
  156. package/src/types/user/settings/keyVaults.ts +1 -0
  157. package/src/app/(main)/settings/llm/Anthropic/index.tsx +0 -25
  158. package/src/app/(main)/settings/llm/Azure/index.tsx +0 -110
  159. package/src/app/(main)/settings/llm/Bedrock/index.tsx +0 -74
  160. package/src/app/(main)/settings/llm/DeepSeek/index.tsx +0 -21
  161. package/src/app/(main)/settings/llm/Google/index.tsx +0 -31
  162. package/src/app/(main)/settings/llm/Groq/index.tsx +0 -26
  163. package/src/app/(main)/settings/llm/Minimax/index.tsx +0 -20
  164. package/src/app/(main)/settings/llm/Mistral/index.tsx +0 -20
  165. package/src/app/(main)/settings/llm/Moonshot/index.tsx +0 -28
  166. package/src/app/(main)/settings/llm/Ollama/index.tsx +0 -37
  167. package/src/app/(main)/settings/llm/OpenAI/index.tsx +0 -28
  168. package/src/app/(main)/settings/llm/OpenRouter/index.tsx +0 -21
  169. package/src/app/(main)/settings/llm/Perplexity/index.tsx +0 -23
  170. package/src/app/(main)/settings/llm/TogetherAI/index.tsx +0 -19
  171. package/src/app/(main)/settings/llm/ZeroOne/index.tsx +0 -20
  172. package/src/app/(main)/settings/llm/Zhipu/index.tsx +0 -18
  173. /package/src/app/(main)/settings/llm/{Ollama → ProviderList/Ollama}/Checker.tsx +0 -0
package/src/config/llm.ts CHANGED
@@ -114,6 +114,9 @@ export const getLLMConfig = () => {
114
114
  ENABLED_OLLAMA: z.boolean(),
115
115
  OLLAMA_PROXY_URL: z.string().optional(),
116
116
  OLLAMA_MODEL_LIST: z.string().optional(),
117
+
118
+ ENABLED_QWEN: z.boolean(),
119
+ QWEN_API_KEY: z.string().optional(),
117
120
  },
118
121
  runtimeEnv: {
119
122
  API_KEY_SELECT_MODE: process.env.API_KEY_SELECT_MODE,
@@ -182,6 +185,9 @@ export const getLLMConfig = () => {
182
185
  ENABLED_OLLAMA: process.env.ENABLED_OLLAMA !== '0',
183
186
  OLLAMA_PROXY_URL: process.env.OLLAMA_PROXY_URL || '',
184
187
  OLLAMA_MODEL_LIST: process.env.OLLAMA_MODEL_LIST || process.env.OLLAMA_CUSTOM_MODELS,
188
+
189
+ ENABLED_QWEN: !!process.env.QWEN_API_KEY,
190
+ QWEN_API_KEY: process.env.QWEN_API_KEY,
185
191
  },
186
192
  });
187
193
  };
@@ -58,7 +58,12 @@ const Anthropic: ModelProviderCard = {
58
58
  tokens: 100_000,
59
59
  },
60
60
  ],
61
+ checkModel: 'claude-3-haiku-20240307',
61
62
  id: 'anthropic',
63
+ name: 'Anthropic',
64
+ proxyUrl: {
65
+ placeholder: 'https://api.anthropic.com',
66
+ },
62
67
  };
63
68
 
64
69
  export default Anthropic;
@@ -38,6 +38,8 @@ const Azure: ModelProviderCard = {
38
38
  },
39
39
  ],
40
40
  id: 'azure',
41
+ name: 'Azure',
42
+ showBrowserRequest: true,
41
43
  };
42
44
 
43
45
  export default Azure;
@@ -88,7 +88,9 @@ const Bedrock: ModelProviderCard = {
88
88
  tokens: 4096,
89
89
  },
90
90
  ],
91
+ checkModel: 'anthropic.claude-instant-v1',
91
92
  id: 'bedrock',
93
+ name: 'Bedrock',
92
94
  };
93
95
 
94
96
  export default Bedrock;
@@ -17,7 +17,10 @@ const DeepSeek: ModelProviderCard = {
17
17
  tokens: 16_384,
18
18
  },
19
19
  ],
20
+ checkModel: 'deepseek-chat',
20
21
  id: 'deepseek',
22
+ modelList: { showModelFetcher: true },
23
+ name: 'DeepSeek',
21
24
  };
22
25
 
23
- export default DeepSeek;
26
+ export default DeepSeek;
@@ -97,7 +97,12 @@ const Google: ModelProviderCard = {
97
97
  tokens: 32_768,
98
98
  },
99
99
  ],
100
+ checkModel: 'gemini-pro',
100
101
  id: 'google',
102
+ name: 'Google',
103
+ proxyUrl: {
104
+ placeholder: 'https://generativelanguage.googleapis.com',
105
+ },
101
106
  };
102
107
 
103
108
  export default Google;
@@ -37,7 +37,12 @@ const Groq: ModelProviderCard = {
37
37
  tokens: 4096,
38
38
  },
39
39
  ],
40
+ checkModel: 'gemma-7b-it',
40
41
  id: 'groq',
42
+ name: 'Groq',
43
+ proxyUrl: {
44
+ placeholder: 'https://api.groq.com/openai/v1',
45
+ },
41
46
  };
42
47
 
43
48
  export default Groq;
@@ -13,12 +13,14 @@ import OllamaProvider from './ollama';
13
13
  import OpenAIProvider from './openai';
14
14
  import OpenRouterProvider from './openrouter';
15
15
  import PerplexityProvider from './perplexity';
16
+ import QwenProvider from './qwen';
16
17
  import TogetherAIProvider from './togetherai';
17
18
  import ZeroOneProvider from './zeroone';
18
19
  import ZhiPuProvider from './zhipu';
19
20
 
20
21
  export const LOBE_DEFAULT_MODEL_LIST: ChatModelCard[] = [
21
22
  OpenAIProvider.chatModels,
23
+ QwenProvider.chatModels,
22
24
  ZhiPuProvider.chatModels,
23
25
  BedrockProvider.chatModels,
24
26
  DeepSeekProvider.chatModels,
@@ -38,6 +40,7 @@ export const LOBE_DEFAULT_MODEL_LIST: ChatModelCard[] = [
38
40
  export const DEFAULT_MODEL_PROVIDER_LIST = [
39
41
  OpenAIProvider,
40
42
  { ...AzureProvider, chatModels: [] },
43
+ QwenProvider,
41
44
  OllamaProvider,
42
45
  AnthropicProvider,
43
46
  DeepSeekProvider,
@@ -71,6 +74,7 @@ export { default as OllamaProviderCard } from './ollama';
71
74
  export { default as OpenAIProviderCard } from './openai';
72
75
  export { default as OpenRouterProviderCard } from './openrouter';
73
76
  export { default as PerplexityProviderCard } from './perplexity';
77
+ export { default as QwenProviderCard } from './qwen';
74
78
  export { default as TogetherAIProviderCard } from './togetherai';
75
79
  export { default as ZeroOneProviderCard } from './zeroone';
76
80
  export { default as ZhiPuProviderCard } from './zhipu';
@@ -42,7 +42,9 @@ const Minimax: ModelProviderCard = {
42
42
  tokens: 8192,
43
43
  },
44
44
  ],
45
+ checkModel: 'abab5.5s-chat',
45
46
  id: 'minimax',
47
+ name: 'Minimax',
46
48
  };
47
49
 
48
50
  export default Minimax;
@@ -41,7 +41,9 @@ const Mistral: ModelProviderCard = {
41
41
  tokens: 32_768,
42
42
  },
43
43
  ],
44
+ checkModel: 'open-mistral-7b',
44
45
  id: 'mistral',
46
+ name: 'Mistral',
45
47
  };
46
48
 
47
49
  export default Mistral;
@@ -25,7 +25,9 @@ const Moonshot: ModelProviderCard = {
25
25
  tokens: 128_000,
26
26
  },
27
27
  ],
28
+ checkModel: 'moonshot-v1-8k',
28
29
  id: 'moonshot',
30
+ name: 'Moonshot',
29
31
  };
30
32
 
31
33
  export default Moonshot;
@@ -155,6 +155,10 @@ const Ollama: ModelProviderCard = {
155
155
  },
156
156
  ],
157
157
  id: 'ollama',
158
+ modelList: { showModelFetcher: true },
159
+ name: 'Ollama',
160
+ showApiKey: false,
161
+ showBrowserRequest: true,
158
162
  };
159
163
 
160
164
  export default Ollama;
@@ -132,8 +132,11 @@ const OpenAI: ModelProviderCard = {
132
132
  vision: true,
133
133
  },
134
134
  ],
135
+ checkModel: 'gpt-3.5-turbo',
135
136
  enabled: true,
136
137
  id: 'openai',
138
+ modelList: { showModelFetcher: true },
139
+ name: 'OpenAI',
137
140
  };
138
141
 
139
142
  export default OpenAI;
@@ -116,7 +116,10 @@ const OpenRouter: ModelProviderCard = {
116
116
  vision: false,
117
117
  },
118
118
  ],
119
+ checkModel: 'mistralai/mistral-7b-instruct:free',
119
120
  id: 'openrouter',
121
+ modelList: { showModelFetcher: true },
122
+ name: 'OpenRouter',
120
123
  };
121
124
 
122
125
  export default OpenRouter;
@@ -41,7 +41,12 @@ const Perplexity: ModelProviderCard = {
41
41
  tokens: 16_384,
42
42
  },
43
43
  ],
44
+ checkModel: 'pplx-7b-chat',
44
45
  id: 'perplexity',
46
+ name: 'Perplexity',
47
+ proxyUrl: {
48
+ placeholder: 'https://api.perplexity.ai',
49
+ },
45
50
  };
46
51
 
47
52
  export default Perplexity;
@@ -0,0 +1,35 @@
1
+ import { ModelProviderCard } from '@/types/llm';
2
+
3
+ // ref https://help.aliyun.com/zh/dashscope/developer-reference/api-details
4
+ const Qwen: ModelProviderCard = {
5
+ chatModels: [
6
+ {
7
+ description: '通义千问超大规模语言模型,支持中文、英文等不同语言输入。',
8
+ displayName: 'Qwen Turbo',
9
+ enabled: true,
10
+ id: 'qwen-turbo',
11
+ tokens: 8192,
12
+ },
13
+ {
14
+ description: '通义千问超大规模语言模型增强版,支持中文、英文等不同语言输入。',
15
+ displayName: 'Qwen Plus',
16
+ enabled: true,
17
+ id: 'qwen-plus',
18
+ tokens: 30_720,
19
+ },
20
+ {
21
+ description:
22
+ '通义千问千亿级别超大规模语言模型,支持中文、英文等不同语言输入,当前通义千问2.5产品版本背后的API模型。',
23
+ displayName: 'Qwen Max',
24
+ enabled: true,
25
+ id: 'qwen-max',
26
+ tokens: 8192,
27
+ },
28
+ ],
29
+ checkModel: 'qwen-turbo',
30
+ id: 'qwen',
31
+ modelList: { showModelFetcher: true },
32
+ name: 'Qwen',
33
+ };
34
+
35
+ export default Qwen;
@@ -70,7 +70,10 @@ const TogetherAI: ModelProviderCard = {
70
70
  tokens: 32_768,
71
71
  },
72
72
  ],
73
+ checkModel: 'togethercomputer/alpaca-7b',
73
74
  id: 'togetherai',
75
+ modelList: { showModelFetcher: true },
76
+ name: 'Together AI',
74
77
  };
75
78
 
76
79
  export default TogetherAI;
@@ -39,7 +39,8 @@ const ZeroOne: ModelProviderCard = {
39
39
  tokens: 16_384,
40
40
  },
41
41
  {
42
- description: '基于Yi-Large超强模型的高阶服务,结合检索与生成技术提供精准答案,支持客⼾私有知识库(请联系客服申请)。',
42
+ description:
43
+ '基于Yi-Large超强模型的高阶服务,结合检索与生成技术提供精准答案,支持客⼾私有知识库(请联系客服申请)。',
43
44
  displayName: 'Yi Large RAG',
44
45
  id: 'yi-large-rag',
45
46
  tokens: 16_384,
@@ -64,9 +65,10 @@ const ZeroOne: ModelProviderCard = {
64
65
  id: 'yi-large-rag-preview',
65
66
  tokens: 16_384,
66
67
  },
67
-
68
68
  ],
69
+ checkModel: 'yi-34b-chat-0205',
69
70
  id: 'zeroone',
71
+ name: '01.AI',
70
72
  };
71
73
 
72
74
  export default ZeroOne;
@@ -29,7 +29,9 @@ const ZhiPu: ModelProviderCard = {
29
29
  tokens: 128_000,
30
30
  },
31
31
  ],
32
+ checkModel: 'glm-3-turbo',
32
33
  id: 'zhipu',
34
+ name: 'ZhiPu',
33
35
  };
34
36
 
35
37
  export default ZhiPu;
@@ -11,6 +11,7 @@ import {
11
11
  OpenAIProviderCard,
12
12
  OpenRouterProviderCard,
13
13
  PerplexityProviderCard,
14
+ QwenProviderCard,
14
15
  TogetherAIProviderCard,
15
16
  ZeroOneProviderCard,
16
17
  ZhiPuProviderCard,
@@ -72,6 +73,10 @@ export const DEFAULT_LLM_CONFIG: UserModelProviderConfig = {
72
73
  enabled: false,
73
74
  enabledModels: filterEnabledModels(PerplexityProviderCard),
74
75
  },
76
+ qwen: {
77
+ enabled: false,
78
+ enabledModels: filterEnabledModels(QwenProviderCard),
79
+ },
75
80
  togetherai: {
76
81
  enabled: false,
77
82
  enabledModels: filterEnabledModels(TogetherAIProviderCard),
@@ -4,6 +4,7 @@ import { Network } from 'lucide-react';
4
4
  import { ReactNode, memo, useState } from 'react';
5
5
  import { useTranslation } from 'react-i18next';
6
6
 
7
+ import { useProviderName } from '@/hooks/useProviderName';
7
8
  import { useUserStore } from '@/store/user';
8
9
  import { keyVaultsConfigSelectors } from '@/store/user/selectors';
9
10
  import { GlobalLLMProviderKey } from '@/types/user/settings';
@@ -19,7 +20,7 @@ interface ProviderApiKeyFormProps {
19
20
 
20
21
  const ProviderApiKeyForm = memo<ProviderApiKeyFormProps>(
21
22
  ({ provider, avatar, showEndpoint = false, apiKeyPlaceholder }) => {
22
- const { t } = useTranslation('modelProvider');
23
+ const { t } = useTranslation(['modelProvider', 'error']);
23
24
  const { t: errorT } = useTranslation('error');
24
25
  const [showProxy, setShow] = useState(false);
25
26
 
@@ -29,11 +30,13 @@ const ProviderApiKeyForm = memo<ProviderApiKeyFormProps>(
29
30
  s.updateKeyVaultConfig,
30
31
  ]);
31
32
 
33
+ const providerName = useProviderName(provider);
34
+
32
35
  return (
33
36
  <FormAction
34
37
  avatar={avatar}
35
- description={t(`${provider}.unlock.description` as any)}
36
- title={t(`${provider}.unlock.title` as any)}
38
+ description={t(`unlock.apiKey.description`, { name: providerName, ns: 'error' })}
39
+ title={t(`unlock.apiKey.title`, { name: providerName, ns: 'error' })}
37
40
  >
38
41
  <Input.Password
39
42
  autoComplete={'new-password'}
@@ -10,6 +10,7 @@ import {
10
10
  OpenRouter,
11
11
  Perplexity,
12
12
  Together,
13
+ Tongyi,
13
14
  ZeroOne,
14
15
  Zhipu,
15
16
  } from '@lobehub/icons';
@@ -66,6 +67,10 @@ const ProviderAvatar = memo<ProviderAvatarProps>(({ provider }) => {
66
67
  return <OpenRouter color={OpenRouter.colorPrimary} size={56} />;
67
68
  }
68
69
 
70
+ case ModelProvider.Qwen: {
71
+ return <Tongyi color={Tongyi.colorPrimary} size={56} />;
72
+ }
73
+
69
74
  case ModelProvider.TogetherAI: {
70
75
  return <Together color={Together.colorPrimary} size={56} />;
71
76
  }
@@ -47,6 +47,10 @@ const APIKeyForm = memo<APIKeyFormProps>(({ id, provider }) => {
47
47
  return 'sk_******************************';
48
48
  }
49
49
 
50
+ case ModelProvider.Qwen: {
51
+ return 'sk-********************************';
52
+ }
53
+
50
54
  default: {
51
55
  return '*********************************';
52
56
  }
@@ -18,8 +18,7 @@ interface OllamaModelFormProps {
18
18
  }
19
19
 
20
20
  const OllamaModelForm = memo<OllamaModelFormProps>(({ id, model }) => {
21
- const { t } = useTranslation('error');
22
- const { t: settingT } = useTranslation('setting');
21
+ const { t } = useTranslation(['modelProvider', 'error']);
23
22
 
24
23
  const [modelToPull, setModelToPull] = useState(model);
25
24
  const [completed, setCompleted] = useState(0);
@@ -60,13 +59,11 @@ const OllamaModelForm = memo<OllamaModelFormProps>(({ id, model }) => {
60
59
  <Center gap={16} style={{ maxWidth: 300, width: '100%' }}>
61
60
  <FormAction
62
61
  avatar={<Ollama color={theme.colorPrimary} size={64} />}
63
- description={
64
- isDownloading ? settingT('ollama.download.desc') : t('unlock.ollama.description')
65
- }
62
+ description={isDownloading ? t('ollama.download.desc') : t('ollama.unlock.description')}
66
63
  title={
67
64
  isDownloading
68
- ? settingT('ollama.download.title', { model: modelToPull })
69
- : t('unlock.ollama.title')
65
+ ? t('ollama.download.title', { model: modelToPull })
66
+ : t('ollama.unlock.title')
70
67
  }
71
68
  >
72
69
  {!isDownloading && (
@@ -92,10 +89,10 @@ const OllamaModelForm = memo<OllamaModelFormProps>(({ id, model }) => {
92
89
  style={{ color: theme.colorTextDescription, fontSize: 12 }}
93
90
  >
94
91
  <span>
95
- {settingT('ollama.download.remainingTime')}: {remainingTime}
92
+ {t('ollama.download.remainingTime')}: {remainingTime}
96
93
  </span>
97
94
  <span>
98
- {settingT('ollama.download.speed')}: {downloadSpeed}
95
+ {t('ollama.download.speed')}: {downloadSpeed}
99
96
  </span>
100
97
  </Flexbox>
101
98
  </Flexbox>
@@ -111,11 +108,11 @@ const OllamaModelForm = memo<OllamaModelFormProps>(({ id, model }) => {
111
108
  type={'primary'}
112
109
  >
113
110
  {!isDownloading
114
- ? t('unlock.ollama.confirm')
111
+ ? t('ollama.unlock.confirm')
115
112
  : // if total is 0, show starting, else show downloaded
116
113
  !total
117
- ? t('unlock.ollama.starting')
118
- : t('unlock.ollama.downloaded', {
114
+ ? t('ollama.unlock.starting')
115
+ : t('ollama.unlock.downloaded', {
119
116
  completed: formatSize(completed),
120
117
  total: formatSize(total),
121
118
  })}
@@ -126,7 +123,7 @@ const OllamaModelForm = memo<OllamaModelFormProps>(({ id, model }) => {
126
123
  ollamaService.abort();
127
124
  }}
128
125
  >
129
- {t('unlock.ollama.cancel')}
126
+ {t('ollama.unlock.cancel')}
130
127
  </Button>
131
128
  ) : (
132
129
  <Button
@@ -134,7 +131,7 @@ const OllamaModelForm = memo<OllamaModelFormProps>(({ id, model }) => {
134
131
  deleteMessage(id);
135
132
  }}
136
133
  >
137
- {t('unlock.closeMessage')}
134
+ {t('unlock.closeMessage', { ns: 'error' })}
138
135
  </Button>
139
136
  )}
140
137
  </Flexbox>
@@ -2,8 +2,10 @@ import { IPluginErrorType, PluginErrorType } from '@lobehub/chat-plugin-sdk';
2
2
  import type { AlertProps } from '@lobehub/ui';
3
3
  import { Skeleton } from 'antd';
4
4
  import dynamic from 'next/dynamic';
5
- import { Suspense, memo } from 'react';
5
+ import { Suspense, memo, useMemo } from 'react';
6
+ import { useTranslation } from 'react-i18next';
6
7
 
8
+ import { useProviderName } from '@/hooks/useProviderName';
7
9
  import { AgentRuntimeErrorType, ILobeAgentRuntimeErrorType } from '@/libs/agent-runtime';
8
10
  import { ChatErrorType, ErrorType } from '@/types/fetch';
9
11
  import { ChatMessage, ChatMessageError } from '@/types/message';
@@ -20,7 +22,7 @@ const OllamaBizError = dynamic(() => import('./OllamaBizError'), { loading, ssr:
20
22
  const PluginSettings = dynamic(() => import('./PluginSettings'), { loading, ssr: false });
21
23
 
22
24
  // Config for the errorMessage display
23
- export const getErrorAlertConfig = (
25
+ const getErrorAlertConfig = (
24
26
  errorType?: IPluginErrorType | ILobeAgentRuntimeErrorType | ErrorType,
25
27
  ): AlertProps | undefined => {
26
28
  // OpenAIBizError / ZhipuBizError / GoogleBizError / ...
@@ -52,6 +54,23 @@ export const getErrorAlertConfig = (
52
54
  }
53
55
  };
54
56
 
57
+ export const useErrorContent = (error: any) => {
58
+ const { t } = useTranslation('error');
59
+ const providerName = useProviderName(error?.body?.provider || '');
60
+
61
+ return useMemo<AlertProps | undefined>(() => {
62
+ if (!error) return;
63
+ const messageError = error;
64
+
65
+ const alertConfig = getErrorAlertConfig(messageError.type);
66
+
67
+ return {
68
+ message: t(`response.${messageError.type}` as any, { provider: providerName }),
69
+ ...alertConfig,
70
+ };
71
+ }, [error]);
72
+ };
73
+
55
74
  const ErrorMessageExtra = memo<{ data: ChatMessage }>(({ data }) => {
56
75
  const error = data.error as ChatMessageError;
57
76
  if (!error?.type) return;
@@ -77,27 +96,18 @@ const ErrorMessageExtra = memo<{ data: ChatMessage }>(({ data }) => {
77
96
  return <InvalidAccessCode id={data.id} provider={data.error?.body?.provider} />;
78
97
  }
79
98
 
80
- case AgentRuntimeErrorType.InvalidBedrockCredentials:
81
- case AgentRuntimeErrorType.InvalidDeepSeekAPIKey:
82
- case AgentRuntimeErrorType.InvalidZhipuAPIKey:
83
- case AgentRuntimeErrorType.InvalidMinimaxAPIKey:
84
- case AgentRuntimeErrorType.InvalidMistralAPIKey:
85
- case AgentRuntimeErrorType.InvalidMoonshotAPIKey:
86
- case AgentRuntimeErrorType.InvalidGoogleAPIKey:
87
- case AgentRuntimeErrorType.InvalidPerplexityAPIKey:
88
- case AgentRuntimeErrorType.InvalidAnthropicAPIKey:
89
- case AgentRuntimeErrorType.InvalidGroqAPIKey:
90
- case AgentRuntimeErrorType.InvalidOpenRouterAPIKey:
91
- case AgentRuntimeErrorType.InvalidTogetherAIAPIKey:
92
- case AgentRuntimeErrorType.InvalidZeroOneAPIKey:
93
99
  case AgentRuntimeErrorType.NoOpenAIAPIKey: {
94
- return <InvalidAPIKey id={data.id} provider={data.error?.body?.provider} />;
100
+ {
101
+ return <InvalidAPIKey id={data.id} provider={data.error?.body?.provider} />;
102
+ }
95
103
  }
104
+ }
96
105
 
97
- default: {
98
- return <ErrorJsonViewer error={data.error} id={data.id} />;
99
- }
106
+ if (error.type.toString().includes('Invalid')) {
107
+ return <InvalidAPIKey id={data.id} provider={data.error?.body?.provider} />;
100
108
  }
109
+
110
+ return <ErrorJsonViewer error={data.error} id={data.id} />;
101
111
  });
102
112
 
103
113
  export default memo<{ data: ChatMessage }>(({ data }) => (
@@ -1,7 +1,7 @@
1
- import { AlertProps, ChatItem } from '@lobehub/ui';
1
+ import { ChatItem } from '@lobehub/ui';
2
2
  import { createStyles } from 'antd-style';
3
3
  import isEqual from 'fast-deep-equal';
4
- import { ReactNode, memo, useCallback, useMemo } from 'react';
4
+ import { ReactNode, memo, useCallback } from 'react';
5
5
  import { useTranslation } from 'react-i18next';
6
6
 
7
7
  import { useAgentStore } from '@/store/agent';
@@ -14,7 +14,7 @@ import { useUserStore } from '@/store/user';
14
14
  import { userGeneralSettingsSelectors } from '@/store/user/selectors';
15
15
  import { ChatMessage } from '@/types/message';
16
16
 
17
- import ErrorMessageExtra, { getErrorAlertConfig } from '../../Error';
17
+ import ErrorMessageExtra, { useErrorContent } from '../../Error';
18
18
  import { renderMessagesExtra } from '../../Extras';
19
19
  import { renderMessages, useAvatarsClick } from '../../Messages';
20
20
  import ActionsBar from './ActionsBar';
@@ -91,16 +91,7 @@ const Item = memo<ChatListItemProps>(({ index, id }) => {
91
91
  },
92
92
  [item?.role],
93
93
  );
94
-
95
- const { t: errorT } = useTranslation('error');
96
- const error = useMemo<AlertProps | undefined>(() => {
97
- if (!item?.error) return;
98
- const messageError = item.error;
99
-
100
- const alertConfig = getErrorAlertConfig(messageError.type);
101
-
102
- return { message: errorT(`response.${messageError.type}` as any), ...alertConfig };
103
- }, [item?.error]);
94
+ const error = useErrorContent(item?.error);
104
95
 
105
96
  const enableHistoryDivider = useAgentStore((s) => {
106
97
  const config = agentSelectors.currentAgentChatConfig(s);
@@ -46,7 +46,7 @@ const ModelSelect = memo<ModelSelectProps>(({ value, onChange }) => {
46
46
  }
47
47
 
48
48
  return enabledList.map((provider) => ({
49
- label: <ProviderItemRender provider={provider.id} />,
49
+ label: <ProviderItemRender name={provider.name} provider={provider.id} />,
50
50
  options: getChatModels(provider),
51
51
  }));
52
52
  }, [enabledList]);
@@ -86,7 +86,7 @@ const ModelSwitchPanel = memo<PropsWithChildren>(({ children }) => {
86
86
  return enabledList.map((provider) => ({
87
87
  children: getModelItems(provider),
88
88
  key: provider.id,
89
- label: <ProviderItemRender provider={provider.id} />,
89
+ label: <ProviderItemRender name={provider.name} provider={provider.id} />,
90
90
  type: 'group',
91
91
  }));
92
92
  }, [enabledList]);
@@ -0,0 +1,8 @@
1
+ import { DEFAULT_MODEL_PROVIDER_LIST } from '@/config/modelProviders';
2
+
3
+ export const useProviderName = (provider: string) => {
4
+ // const { t } = useTranslation('modelProvider');
5
+ const providerCard = DEFAULT_MODEL_PROVIDER_LIST.find((p) => p.id === provider);
6
+
7
+ return providerCard?.name || provider;
8
+ };
@@ -16,6 +16,7 @@ import { LobeOllamaAI } from './ollama';
16
16
  import { LobeOpenAI } from './openai';
17
17
  import { LobeOpenRouterAI } from './openrouter';
18
18
  import { LobePerplexityAI } from './perplexity';
19
+ import { LobeQwenAI } from './qwen';
19
20
  import { LobeTogetherAI } from './togetherai';
20
21
  import {
21
22
  ChatCompetitionOptions,
@@ -112,6 +113,7 @@ class AgentRuntime {
112
113
  openai: Partial<ClientOptions>;
113
114
  openrouter: Partial<ClientOptions>;
114
115
  perplexity: Partial<ClientOptions>;
116
+ qwen: Partial<ClientOptions>;
115
117
  togetherai: Partial<ClientOptions>;
116
118
  zeroone: Partial<ClientOptions>;
117
119
  zhipu: Partial<ClientOptions>;
@@ -175,7 +177,7 @@ class AgentRuntime {
175
177
  runtimeModel = new LobeDeepSeekAI(params.deepseek ?? {});
176
178
  break;
177
179
  }
178
-
180
+
179
181
  case ModelProvider.Minimax: {
180
182
  runtimeModel = new LobeMinimaxAI(params.minimax ?? {});
181
183
  break;
@@ -205,6 +207,11 @@ class AgentRuntime {
205
207
  runtimeModel = new LobeZeroOneAI(params.zeroone ?? {});
206
208
  break;
207
209
  }
210
+
211
+ case ModelProvider.Qwen: {
212
+ runtimeModel = new LobeQwenAI(params.qwen ?? {});
213
+ break;
214
+ }
208
215
  }
209
216
 
210
217
  return new AgentRuntime(runtimeModel);