@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
@@ -1,28 +1,30 @@
1
- import { GlobalStore } from '../../../store';
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { DEFAULT_SETTINGS } from '@/const/settings';
4
+ import { agentSelectors } from '@/store/session/slices/agent';
5
+ import { merge } from '@/utils/merge';
6
+
7
+ import { GlobalStore, useGlobalStore } from '../../../store';
8
+ import { initialSettingsState } from '../initialState';
2
9
  import { modelProviderSelectors } from './modelProvider';
3
10
 
4
11
  describe('modelProviderSelectors', () => {
5
12
  describe('CUSTOM_MODELS', () => {
6
13
  it('custom deletion, addition, and renaming of models', () => {
7
- const s = {
14
+ const s = merge(initialSettingsState, {
8
15
  serverConfig: {
9
16
  customModelName:
10
17
  '-all,+llama,+claude-2,-gpt-3.5-turbo,gpt-4-0125-preview=gpt-4-turbo,gpt-4-0125-preview=gpt-4-32k',
11
18
  },
12
- settings: {
13
- languageModel: {
14
- openAI: {},
15
- },
16
- },
17
- } as unknown as GlobalStore;
19
+ }) as unknown as GlobalStore;
18
20
 
19
- const result = modelProviderSelectors.modelList(s);
21
+ const result = modelProviderSelectors.modelSelectList(s).filter((r) => r.enabled);
20
22
 
21
23
  expect(result).toMatchSnapshot();
22
24
  });
23
25
 
24
26
  it('duplicate naming model', () => {
25
- const s = {
27
+ const s = merge(initialSettingsState, {
26
28
  serverConfig: {},
27
29
  settings: {
28
30
  languageModel: {
@@ -31,57 +33,138 @@ describe('modelProviderSelectors', () => {
31
33
  },
32
34
  },
33
35
  },
34
- } as unknown as GlobalStore;
36
+ }) as unknown as GlobalStore;
35
37
 
36
- const result = modelProviderSelectors.modelList(s);
38
+ const result = modelProviderSelectors.modelSelectList(s).filter((r) => r.enabled);
37
39
 
38
40
  expect(result).toMatchSnapshot();
39
41
  });
40
42
 
41
43
  it('should delete model', () => {
42
- const s = {
43
- serverConfig: {
44
- customModelName: '-gpt-4',
44
+ const s = merge(initialSettingsState, {
45
+ serverConfig: { customModelName: '-gpt-4' },
46
+ }) as unknown as GlobalStore;
47
+
48
+ const result = modelProviderSelectors.modelSelectList(s).filter((r) => r.enabled);
49
+
50
+ expect(result).toEqual([
51
+ {
52
+ chatModels: [
53
+ {
54
+ description: 'GPT 3.5 Turbo,适用于各种文本生成和理解任务',
55
+ displayName: 'GPT-3.5 Turbo',
56
+ functionCall: true,
57
+ id: 'gpt-3.5-turbo',
58
+ tokens: 4096,
59
+ },
60
+ {
61
+ displayName: 'GPT-3.5 Turbo (0125)',
62
+ functionCall: true,
63
+ id: 'gpt-3.5-turbo-0125',
64
+ tokens: 16385,
65
+ },
66
+ {
67
+ displayName: 'GPT-3.5 Turbo (1106)',
68
+ functionCall: true,
69
+ hidden: true,
70
+ id: 'gpt-3.5-turbo-1106',
71
+ tokens: 16385,
72
+ },
73
+ {
74
+ hidden: true,
75
+ id: 'gpt-3.5-turbo-instruct',
76
+ tokens: 4096,
77
+ },
78
+ {
79
+ hidden: true,
80
+ id: 'gpt-3.5-turbo-16k',
81
+ tokens: 16385,
82
+ },
83
+ {
84
+ hidden: true,
85
+ id: 'gpt-3.5-turbo-0613',
86
+ legacy: true,
87
+ tokens: 4096,
88
+ },
89
+ {
90
+ hidden: true,
91
+ id: 'gpt-3.5-turbo-16k-0613',
92
+ legacy: true,
93
+ tokens: 4096,
94
+ },
95
+ {
96
+ displayName: 'GPT-4 Turbo Preview',
97
+ functionCall: true,
98
+ id: 'gpt-4-turbo-preview',
99
+ tokens: 128000,
100
+ },
101
+ {
102
+ displayName: 'GPT-4 Turbo Preview (0125)',
103
+ functionCall: true,
104
+ id: 'gpt-4-0125-preview',
105
+ tokens: 128000,
106
+ },
107
+ {
108
+ description: 'GPT-4 视觉预览版,支持视觉任务',
109
+ displayName: 'GPT-4 Turbo Vision (Preview)',
110
+ id: 'gpt-4-vision-preview',
111
+ tokens: 128000,
112
+ vision: true,
113
+ },
114
+ {
115
+ functionCall: true,
116
+ hidden: true,
117
+ id: 'gpt-4-1106-preview',
118
+ tokens: 128000,
119
+ },
120
+ {
121
+ functionCall: true,
122
+ hidden: true,
123
+ id: 'gpt-4-0613',
124
+ tokens: 8192,
125
+ },
126
+ {
127
+ functionCall: true,
128
+ hidden: true,
129
+ id: 'gpt-4-32k',
130
+ tokens: 32768,
131
+ },
132
+ {
133
+ functionCall: true,
134
+ hidden: true,
135
+ id: 'gpt-4-32k-0613',
136
+ tokens: 32768,
137
+ },
138
+ ],
139
+ enabled: true,
140
+ id: 'openai',
45
141
  },
142
+ ]);
143
+ });
144
+
145
+ it('show the hidden model', () => {
146
+ const s = merge(initialSettingsState, {
147
+ serverConfig: {},
46
148
  settings: {
47
149
  languageModel: {
48
- openAI: {},
150
+ openAI: {
151
+ customModelName: '+gpt-4-1106-preview',
152
+ },
49
153
  },
50
154
  },
51
- } as unknown as GlobalStore;
155
+ }) as unknown as GlobalStore;
52
156
 
53
- const result = modelProviderSelectors.modelList(s);
157
+ const result = modelProviderSelectors.modelSelectList(s).filter((r) => r.enabled);
54
158
 
55
- expect(result).toEqual([
56
- {
57
- displayName: 'gpt-3.5-turbo',
58
- name: 'gpt-3.5-turbo',
59
- },
60
- {
61
- displayName: 'gpt-3.5-turbo-1106',
62
- name: 'gpt-3.5-turbo-1106',
63
- },
64
- {
65
- displayName: 'gpt-3.5-turbo-16k',
66
- name: 'gpt-3.5-turbo-16k',
67
- },
68
- {
69
- displayName: 'gpt-4-32k',
70
- name: 'gpt-4-32k',
71
- },
72
- {
73
- displayName: 'gpt-4-0125-preview',
74
- name: 'gpt-4-0125-preview',
75
- },
76
- {
77
- displayName: 'gpt-4-vision-preview',
78
- name: 'gpt-4-vision-preview',
79
- },
80
- ]);
159
+ expect(result[0].chatModels.find((o) => o.id === 'gpt-4-1106-preview')).toEqual({
160
+ functionCall: true,
161
+ id: 'gpt-4-1106-preview',
162
+ tokens: 128000,
163
+ });
81
164
  });
82
165
 
83
166
  it('only add the model', () => {
84
- const s = {
167
+ const s = merge(initialSettingsState, {
85
168
  serverConfig: {},
86
169
  settings: {
87
170
  languageModel: {
@@ -90,11 +173,52 @@ describe('modelProviderSelectors', () => {
90
173
  },
91
174
  },
92
175
  },
93
- } as unknown as GlobalStore;
176
+ }) as unknown as GlobalStore;
94
177
 
95
- const result = modelProviderSelectors.modelList(s);
178
+ const result = modelProviderSelectors.modelSelectList(s).filter((r) => r.enabled);
96
179
 
97
180
  expect(result).toMatchSnapshot();
98
181
  });
99
182
  });
183
+
184
+ describe('modelEnabledVision', () => {
185
+ it('should return true if the model has vision ability', () => {
186
+ const hasAbility = modelProviderSelectors.modelEnabledVision('gpt-4-vision-preview')(
187
+ useGlobalStore.getState(),
188
+ );
189
+ expect(hasAbility).toBeTruthy();
190
+ });
191
+
192
+ it('should return false if the model does not have vision ability', () => {
193
+ const hasAbility = modelProviderSelectors.modelEnabledVision('some-other-model')(
194
+ useGlobalStore.getState(),
195
+ );
196
+
197
+ expect(hasAbility).toBeFalsy();
198
+ });
199
+
200
+ it('should return false if the model include vision in id', () => {
201
+ const hasAbility = modelProviderSelectors.modelEnabledVision('some-other-model-vision')(
202
+ useGlobalStore.getState(),
203
+ );
204
+
205
+ expect(hasAbility).toBeTruthy();
206
+ });
207
+ });
208
+
209
+ describe('modelEnabledVision', () => {
210
+ it('should return true if the model is in the list of models that show tokens', () => {
211
+ const show = modelProviderSelectors.modelHasMaxToken('gpt-3.5-turbo')(
212
+ useGlobalStore.getState(),
213
+ );
214
+ expect(show).toBeTruthy();
215
+ });
216
+
217
+ it('should return false if the model is not in the list of models that show tokens', () => {
218
+ const show = modelProviderSelectors.modelHasMaxToken('some-other-model')(
219
+ useGlobalStore.getState(),
220
+ );
221
+ expect(show).toBe(false);
222
+ });
223
+ });
100
224
  });
@@ -1,64 +1,157 @@
1
- import { DEFAULT_OPENAI_MODEL_LIST } from '@/const/llm';
2
- import { CustomModels } from '@/types/settings';
1
+ import { produce } from 'immer';
2
+
3
+ import {
4
+ BedrockProvider,
5
+ GoogleProvider,
6
+ LOBE_DEFAULT_MODEL_LIST,
7
+ OpenAIProvider,
8
+ ZhiPuProvider,
9
+ } from '@/config/modelProviders';
10
+ import { ChatModelCard, ModelProviderCard } from '@/types/llm';
11
+ import { parseModelString } from '@/utils/parseModels';
3
12
 
4
13
  import { GlobalStore } from '../../../store';
5
14
  import { currentSettings } from './settings';
6
15
 
7
- const openAIConfig = (s: GlobalStore) => currentSettings(s).languageModel.openAI;
16
+ const modelProvider = (s: GlobalStore) => currentSettings(s).languageModel;
17
+ const openAIConfig = (s: GlobalStore) => modelProvider(s).openAI;
8
18
 
9
- const openAIAPIKeySelectors = (s: GlobalStore) =>
10
- currentSettings(s).languageModel.openAI.OPENAI_API_KEY;
19
+ const openAIAPIKey = (s: GlobalStore) => openAIConfig(s).OPENAI_API_KEY;
20
+ const openAIProxyUrl = (s: GlobalStore) => openAIConfig(s).endpoint;
11
21
 
12
- const enableAzure = (s: GlobalStore) => currentSettings(s).languageModel.openAI.useAzure;
22
+ const enableZhipu = (s: GlobalStore) => modelProvider(s).zhipu.enabled;
23
+ const zhipuAPIKey = (s: GlobalStore) => modelProvider(s).zhipu.apiKey;
24
+ const zhipuProxyUrl = (s: GlobalStore) => modelProvider(s).zhipu.endpoint;
13
25
 
14
- const openAIProxyUrlSelectors = (s: GlobalStore) =>
15
- currentSettings(s).languageModel.openAI.endpoint;
26
+ const enableBedrock = (s: GlobalStore) => modelProvider(s).bedrock.enabled;
27
+ const bedrockConfig = (s: GlobalStore) => modelProvider(s).bedrock;
16
28
 
17
- const modelListSelectors = (s: GlobalStore) => {
18
- let models: CustomModels = [];
29
+ const enableGoogle = (s: GlobalStore) => modelProvider(s).google.enabled;
30
+ const googleAPIKey = (s: GlobalStore) => modelProvider(s).google.apiKey;
31
+ const googleProxyUrl = (s: GlobalStore) => modelProvider(s).google.endpoint;
19
32
 
20
- const removedModels: string[] = [];
21
- const modelNames = [
22
- ...DEFAULT_OPENAI_MODEL_LIST,
23
- ...(s.serverConfig.customModelName || '').split(/[,,]/).filter(Boolean),
24
- ...(currentSettings(s).languageModel.openAI.customModelName || '')
25
- .split(/[,,]/)
26
- .filter(Boolean),
27
- ];
33
+ const enableAzure = (s: GlobalStore) => modelProvider(s).openAI.useAzure;
34
+ const azureConfig = (s: GlobalStore) => modelProvider(s).azure;
35
+
36
+ // const azureModelList = (s: GlobalStore): ModelProviderCard => {
37
+ // const azure = azureConfig(s);
38
+ // return {
39
+ // chatModels: parseModelString(azure.deployments),
40
+ // id: 'azure',
41
+ // };
42
+ // };
43
+
44
+ // 提取处理 chatModels 的专门方法
45
+ const processChatModels = (modelConfig: ReturnType<typeof parseModelString>): ChatModelCard[] => {
46
+ let chatModels = modelConfig.removeAll ? [] : OpenAIProvider.chatModels;
47
+
48
+ // 处理移除逻辑
49
+ if (!modelConfig.removeAll) {
50
+ chatModels = chatModels.filter((m) => !modelConfig.removed.includes(m.id));
51
+ }
28
52
 
29
- for (const item of modelNames) {
30
- const disable = item.startsWith('-');
31
- const nameConfig = item.startsWith('+') || item.startsWith('-') ? item.slice(1) : item;
32
- const [name, displayName] = nameConfig.split('=');
53
+ return produce(chatModels, (draft) => {
54
+ // 处理添加或替换逻辑
55
+ for (const customModel of modelConfig.add) {
56
+ // 首先尝试在 LOBE_DEFAULT_MODEL_LIST 中查找模型
57
+ const defaultModel = LOBE_DEFAULT_MODEL_LIST.find((model) => model.id === customModel.id);
33
58
 
34
- if (disable) {
35
- // Disable all models.
36
- if (name === 'all') {
37
- models = [];
59
+ // 如果在默认列表中找到了模型,则基于该模型进行更新
60
+ if (defaultModel) {
61
+ const model = draft.find((model) => model.id === customModel.id);
62
+ // 如果当前 chatModels 中已有该模型,更新它
63
+ if (model) {
64
+ if (model.hidden) delete model.hidden;
65
+ if (customModel.displayName) model.displayName = customModel.displayName;
66
+ } else {
67
+ // 如果当前 chatModels 中没有该模型,添加它
68
+ draft.push({
69
+ ...defaultModel,
70
+ displayName: customModel.displayName || defaultModel.displayName,
71
+ });
72
+ }
73
+ } else {
74
+ // 如果在默认列表中未找到模型,作为新的自定义模型添加
75
+ draft.push({
76
+ ...customModel,
77
+ displayName: customModel.displayName || customModel.id,
78
+ functionCall: true,
79
+ isCustom: true,
80
+ vision: true,
81
+ });
38
82
  }
39
- removedModels.push(name);
40
- continue;
41
83
  }
84
+ });
85
+ };
42
86
 
43
- // Remove duplicate model entries.
44
- const existingIndex = models.findIndex(({ name: n }) => n === name);
45
- if (existingIndex !== -1) {
46
- models.splice(existingIndex, 1);
47
- }
87
+ const modelSelectList = (s: GlobalStore): ModelProviderCard[] => {
88
+ const string = [
89
+ s.serverConfig.customModelName,
90
+ currentSettings(s).languageModel.openAI.customModelName,
91
+ ]
92
+ .filter(Boolean)
93
+ .join(',');
48
94
 
49
- models.push({
50
- displayName: displayName || name,
51
- name,
52
- });
53
- }
95
+ const modelConfig = parseModelString(string);
54
96
 
55
- return models.filter((m) => !removedModels.includes(m.name));
97
+ const chatModels = processChatModels(modelConfig);
98
+
99
+ return [
100
+ {
101
+ ...OpenAIProvider,
102
+ chatModels,
103
+ },
104
+ // { ...azureModelList(s), enabled: enableAzure(s) },
105
+ { ...ZhiPuProvider, enabled: enableZhipu(s) },
106
+ { ...GoogleProvider, enabled: enableGoogle(s) },
107
+ { ...BedrockProvider, enabled: enableBedrock(s) },
108
+ ];
56
109
  };
57
110
 
111
+ const modelCardById = (id: string) => (s: GlobalStore) => {
112
+ const list = modelSelectList(s);
113
+
114
+ return list.flatMap((i) => i.chatModels).find((m) => m.id === id);
115
+ };
116
+
117
+ const modelEnabledFunctionCall = (id: string) => (s: GlobalStore) =>
118
+ modelCardById(id)(s)?.functionCall || false;
119
+
120
+ // vision model white list, these models will change the content from string to array
121
+ // refs: https://github.com/lobehub/lobe-chat/issues/790
122
+ const modelEnabledVision = (id: string) => (s: GlobalStore) =>
123
+ modelCardById(id)(s)?.vision || id.includes('vision');
124
+
125
+ const modelHasMaxToken = (id: string) => (s: GlobalStore) =>
126
+ typeof modelCardById(id)(s)?.tokens !== 'undefined';
127
+
128
+ const modelMaxToken = (id: string) => (s: GlobalStore) => modelCardById(id)(s)?.tokens || 0;
129
+
130
+ /* eslint-disable sort-keys-fix/sort-keys-fix, */
58
131
  export const modelProviderSelectors = {
59
- enableAzure,
60
- modelList: modelListSelectors,
61
- openAIAPI: openAIAPIKeySelectors,
132
+ modelSelectList,
133
+
134
+ modelCardById,
135
+ modelMaxToken,
136
+ modelEnabledFunctionCall,
137
+ modelEnabledVision,
138
+ modelHasMaxToken,
139
+ // OpenAI
62
140
  openAIConfig,
63
- openAIProxyUrl: openAIProxyUrlSelectors,
141
+ openAIAPIKey,
142
+ openAIProxyUrl,
143
+ // Azure OpenAI
144
+ enableAzure,
145
+ azureConfig,
146
+ // Zhipu
147
+ enableZhipu,
148
+ zhipuAPIKey,
149
+ zhipuProxyUrl,
150
+ // Google
151
+ enableGoogle,
152
+ googleAPIKey,
153
+ googleProxyUrl,
154
+ // Bedrock
155
+ enableBedrock,
156
+ bedrockConfig,
64
157
  };
@@ -177,52 +177,4 @@ describe('agentSelectors', () => {
177
177
  expect(description).toBe('noDescription'); // Assuming translation returns this key
178
178
  });
179
179
  });
180
-
181
- describe('showTokenTag', () => {
182
- it('should return true if the model is in the list of models that show tokens', () => {
183
- const show = agentSelectors.showTokenTag(mockSessionStore);
184
- expect(show).toBe(true);
185
- });
186
-
187
- it('should return false if the model is not in the list of models that show tokens', () => {
188
- const modifiedSessionStore = {
189
- ...mockSessionStore,
190
- sessions: [
191
- {
192
- ...mockSessionStore.sessions[0],
193
- config: {
194
- ...mockSessionStore.sessions[0].config,
195
- model: 'some-other-model',
196
- },
197
- },
198
- ],
199
- };
200
- const show = agentSelectors.showTokenTag(modifiedSessionStore);
201
- expect(show).toBe(false);
202
- });
203
- });
204
-
205
- describe('modelHasVisionAbility', () => {
206
- it('should return true if the model has vision ability', () => {
207
- const hasAbility = agentSelectors.modelHasVisionAbility(mockSessionStore);
208
- expect(hasAbility).toBe(false);
209
- });
210
-
211
- it('should return false if the model does not have vision ability', () => {
212
- const modifiedSessionStore = {
213
- ...mockSessionStore,
214
- sessions: [
215
- {
216
- ...mockSessionStore.sessions[0],
217
- config: {
218
- ...mockSessionStore.sessions[0].config,
219
- model: 'some-other-model',
220
- },
221
- },
222
- ],
223
- };
224
- const hasAbility = agentSelectors.modelHasVisionAbility(modifiedSessionStore);
225
- expect(hasAbility).toBe(false);
226
- });
227
- });
228
180
  });
@@ -1,7 +1,6 @@
1
1
  import { VoiceList } from '@lobehub/tts';
2
2
  import { t } from 'i18next';
3
3
 
4
- import { DEFAULT_OPENAI_MODEL_LIST, isVisionModel } from '@/const/llm';
5
4
  import { DEFAULT_AVATAR, DEFAULT_BACKGROUND_COLOR, DEFAULT_INBOX_AVATAR } from '@/const/meta';
6
5
  import { DEFAULT_AGENT_CONFIG, DEFAUTT_AGENT_TTS_CONFIG } from '@/const/settings';
7
6
  import { useGlobalStore } from '@/store/global';
@@ -36,6 +35,12 @@ const currentAgentModel = (s: SessionStore): LanguageModel | string => {
36
35
  return config?.model || LanguageModel.GPT3_5;
37
36
  };
38
37
 
38
+ const currentAgentModelProvider = (s: SessionStore) => {
39
+ const config = currentAgentConfig(s);
40
+
41
+ return config?.provider;
42
+ };
43
+
39
44
  const currentAgentPlugins = (s: SessionStore) => {
40
45
  const config = currentAgentConfig(s);
41
46
 
@@ -99,20 +104,11 @@ const getTitle = (s: MetaData) => s.title || t('defaultSession', { ns: 'common'
99
104
  export const getDescription = (s: MetaData) =>
100
105
  s.description || t('noDescription', { ns: 'common' });
101
106
 
102
- const showTokenTag = (s: SessionStore) => {
103
- const model = currentAgentModel(s);
104
-
105
- return DEFAULT_OPENAI_MODEL_LIST.includes(model);
106
- };
107
107
  const hasSystemRole = (s: SessionStore) => {
108
108
  const config = currentAgentConfig(s);
109
109
 
110
110
  return !!config.systemRole;
111
111
  };
112
- const modelHasVisionAbility = (s: SessionStore): boolean => {
113
- const model = currentAgentModel(s);
114
- return isVisionModel(model);
115
- };
116
112
 
117
113
  export const agentSelectors = {
118
114
  currentAgentAvatar,
@@ -121,6 +117,7 @@ export const agentSelectors = {
121
117
  currentAgentDescription,
122
118
  currentAgentMeta,
123
119
  currentAgentModel,
120
+ currentAgentModelProvider,
124
121
  currentAgentPlugins,
125
122
  currentAgentSystemRole,
126
123
  currentAgentTTS,
@@ -130,6 +127,4 @@ export const agentSelectors = {
130
127
  getDescription,
131
128
  getTitle,
132
129
  hasSystemRole,
133
- modelHasVisionAbility,
134
- showTokenTag,
135
130
  };
@@ -1,4 +1,4 @@
1
- import { FewShots, LLMParams, LanguageModel } from '@/types/llm';
1
+ import { FewShots, LLMParams } from '@/types/llm';
2
2
 
3
3
  export type TTSServer = 'openai' | 'edge' | 'microsoft';
4
4
 
@@ -37,7 +37,7 @@ export interface LobeAgentConfig {
37
37
  * 角色所使用的语言模型
38
38
  * @default gpt-3.5-turbo
39
39
  */
40
- model: LanguageModel | string;
40
+ model: string;
41
41
  /**
42
42
  * 语言模型参数
43
43
  */
@@ -46,6 +46,10 @@ export interface LobeAgentConfig {
46
46
  * 启用的插件
47
47
  */
48
48
  plugins?: string[];
49
+ /**
50
+ * 模型供应商
51
+ */
52
+ provider?: string;
49
53
  /**
50
54
  * 系统角色
51
55
  */
@@ -1,10 +1,12 @@
1
1
  /* eslint-disable sort-keys-fix/sort-keys-fix */
2
+ import { ILobeAgentRuntimeErrorType } from '@/libs/agent-runtime';
3
+
2
4
  export const ChatErrorType = {
3
5
  // ******* 业务错误语义 ******* //
4
6
 
5
7
  InvalidAccessCode: 'InvalidAccessCode', // 密码无效
6
8
  OpenAIBizError: 'OpenAIBizError', // OpenAI 返回的业务错误
7
- NoAPIKey: 'NoAPIKey',
9
+ NoOpenAIAPIKey: 'NoOpenAIAPIKey',
8
10
 
9
11
  // ******* 客户端错误 ******* //
10
12
  BadRequest: 400,
@@ -26,5 +28,5 @@ export type ErrorType = (typeof ChatErrorType)[keyof typeof ChatErrorType];
26
28
 
27
29
  export interface ErrorResponse {
28
30
  body: any;
29
- errorType: ErrorType;
31
+ errorType: ErrorType | ILobeAgentRuntimeErrorType;
30
32
  }
@@ -1,8 +1,8 @@
1
- import { DB_File } from '../database/schemas/files';
2
-
3
- export interface FilePreview extends Pick<DB_File, 'saveMode' | 'fileType'> {
1
+ export interface FilePreview {
4
2
  base64Url?: string;
5
3
  data?: ArrayBuffer;
4
+ fileType: string;
6
5
  name: string;
6
+ saveMode: 'local' | 'url';
7
7
  url: string;
8
8
  }
package/src/types/llm.ts CHANGED
@@ -17,6 +17,33 @@ export enum LanguageModel {
17
17
  GPT4_VISION_PREVIEW = 'gpt-4-vision-preview',
18
18
  }
19
19
 
20
+ export interface ChatModelCard {
21
+ description?: string;
22
+ displayName?: string;
23
+ /**
24
+ * 是否支持 Function Call
25
+ */
26
+ functionCall?: boolean;
27
+ hidden?: boolean;
28
+ id: string;
29
+ /**
30
+ * user defined model
31
+ */
32
+ isCustom?: boolean;
33
+ legacy?: boolean;
34
+ tokens?: number;
35
+ /**
36
+ * 是否支持视觉识别
37
+ */
38
+ vision?: boolean;
39
+ }
40
+
41
+ export interface ModelProviderCard {
42
+ chatModels: ChatModelCard[];
43
+ enabled?: boolean;
44
+ id: string;
45
+ }
46
+
20
47
  // 语言模型的设置参数
21
48
  export interface LLMParams {
22
49
  /**