@lobehub/lobehub 2.0.0-next.15 → 2.0.0-next.17
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/CHANGELOG.md +50 -0
- package/README.md +2 -45
- package/README.zh-CN.md +2 -45
- package/changelog/v1.json +18 -0
- package/docs/self-hosting/advanced/feature-flags.mdx +0 -1
- package/docs/self-hosting/advanced/feature-flags.zh-CN.mdx +0 -1
- package/e2e/src/features/discover/smoke.feature +34 -1
- package/e2e/src/steps/discover/smoke.steps.ts +116 -4
- package/package.json +1 -1
- package/packages/model-runtime/src/utils/googleErrorParser.test.ts +125 -0
- package/packages/model-runtime/src/utils/googleErrorParser.ts +103 -77
- package/packages/types/src/serverConfig.ts +2 -6
- package/src/app/[variants]/(auth)/signup/[[...signup]]/page.tsx +1 -8
- package/src/app/[variants]/(main)/(mobile)/me/(home)/features/UserBanner.tsx +3 -6
- package/src/app/[variants]/(main)/discover/(list)/features/Pagination.tsx +1 -0
- package/src/app/[variants]/(main)/discover/(list)/features/SortButton/index.tsx +1 -1
- package/src/app/[variants]/(main)/discover/(list)/mcp/features/List/Item.tsx +1 -0
- package/src/app/[variants]/(main)/discover/(list)/model/features/List/Item.tsx +1 -0
- package/src/app/[variants]/(main)/discover/(list)/provider/features/List/Item.tsx +1 -0
- package/src/app/[variants]/(main)/discover/components/CategoryMenu.tsx +9 -1
- package/src/app/[variants]/(main)/labs/components/LabCard.tsx +3 -1
- package/src/app/[variants]/(main)/settings/provider/detail/azure/index.tsx +5 -7
- package/src/components/InvalidAPIKey/APIKeyForm/Bedrock.tsx +8 -13
- package/src/config/featureFlags/schema.test.ts +0 -2
- package/src/config/featureFlags/schema.ts +0 -6
- package/src/config/modelProviders/ai21.ts +1 -16
- package/src/config/modelProviders/ai302.ts +1 -128
- package/src/config/modelProviders/ai360.ts +1 -32
- package/src/config/modelProviders/anthropic.ts +1 -71
- package/src/config/modelProviders/azure.ts +1 -51
- package/src/config/modelProviders/baichuan.ts +1 -57
- package/src/config/modelProviders/bedrock.ts +1 -276
- package/src/config/modelProviders/cloudflare.ts +1 -64
- package/src/config/modelProviders/deepseek.ts +1 -19
- package/src/config/modelProviders/fireworksai.ts +1 -174
- package/src/config/modelProviders/giteeai.ts +1 -135
- package/src/config/modelProviders/github.ts +1 -254
- package/src/config/modelProviders/google.ts +1 -130
- package/src/config/modelProviders/groq.ts +1 -119
- package/src/config/modelProviders/higress.ts +1 -1713
- package/src/config/modelProviders/huggingface.ts +1 -54
- package/src/config/modelProviders/hunyuan.ts +1 -83
- package/src/config/modelProviders/infiniai.ts +1 -74
- package/src/config/modelProviders/internlm.ts +1 -20
- package/src/config/modelProviders/mistral.ts +1 -95
- package/src/config/modelProviders/modelscope.ts +1 -27
- package/src/config/modelProviders/moonshot.ts +1 -29
- package/src/config/modelProviders/novita.ts +1 -105
- package/src/config/modelProviders/ollama.ts +1 -325
- package/src/config/modelProviders/openai.ts +1 -242
- package/src/config/modelProviders/openrouter.ts +1 -240
- package/src/config/modelProviders/perplexity.ts +1 -45
- package/src/config/modelProviders/ppio.ts +1 -152
- package/src/config/modelProviders/qiniu.ts +1 -18
- package/src/config/modelProviders/qwen.ts +1 -245
- package/src/config/modelProviders/search1api.ts +1 -34
- package/src/config/modelProviders/sensenova.ts +1 -69
- package/src/config/modelProviders/siliconcloud.ts +1 -417
- package/src/config/modelProviders/spark.ts +1 -59
- package/src/config/modelProviders/stepfun.ts +1 -98
- package/src/config/modelProviders/taichu.ts +1 -18
- package/src/config/modelProviders/togetherai.ts +1 -274
- package/src/config/modelProviders/upstage.ts +1 -28
- package/src/config/modelProviders/wenxin.ts +1 -140
- package/src/config/modelProviders/xai.ts +1 -38
- package/src/config/modelProviders/zeroone.ts +1 -81
- package/src/config/modelProviders/zhipu.ts +1 -108
- package/src/helpers/isCanUseFC.ts +0 -8
- package/src/hooks/useEnabledChatModels.ts +0 -8
- package/src/hooks/useModelContextWindowTokens.ts +0 -8
- package/src/hooks/useModelHasContextWindowToken.ts +1 -10
- package/src/hooks/useModelSupportFiles.ts +1 -11
- package/src/hooks/useModelSupportReasoning.ts +1 -11
- package/src/hooks/useModelSupportToolUse.ts +1 -11
- package/src/hooks/useModelSupportVision.ts +1 -11
- package/src/layout/AuthProvider/Clerk/index.tsx +2 -16
- package/src/server/globalConfig/index.ts +0 -23
- package/src/server/routers/lambda/config/__snapshots__/index.test.ts.snap +175 -12
- package/src/server/routers/lambda/config/index.test.ts +36 -28
- package/src/services/chat/chat.test.ts +12 -0
- package/src/services/chat/helper.ts +7 -31
- package/src/services/models.ts +2 -11
- package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +41 -14
- package/src/store/global/store.ts +1 -7
- package/src/store/user/initialState.ts +1 -7
- package/src/store/user/selectors.ts +1 -5
- package/src/store/user/slices/common/action.ts +5 -4
- package/src/store/user/slices/settings/selectors/index.ts +1 -0
- package/src/store/user/slices/settings/selectors/keyVaults.ts +21 -0
- package/src/store/user/store.ts +0 -3
- package/src/tools/web-browsing/Render/Search/ConfigForm/Form.tsx +1 -1
- package/packages/utils/src/_deprecated/__snapshots__/parseModels.test.ts.snap +0 -104
- package/packages/utils/src/_deprecated/parseModels.test.ts +0 -287
- package/packages/utils/src/_deprecated/parseModels.ts +0 -165
- package/src/hooks/_header.ts +0 -23
- package/src/server/globalConfig/_deprecated.test.ts +0 -92
- package/src/server/globalConfig/_deprecated.ts +0 -41
- package/src/store/global/actions/clientDb.ts +0 -67
- package/src/store/user/slices/modelList/__snapshots__/action.test.ts.snap +0 -12
- package/src/store/user/slices/modelList/action.test.ts +0 -359
- package/src/store/user/slices/modelList/action.ts +0 -223
- package/src/store/user/slices/modelList/initialState.ts +0 -15
- package/src/store/user/slices/modelList/reducers/customModelCard.test.ts +0 -204
- package/src/store/user/slices/modelList/reducers/customModelCard.ts +0 -64
- package/src/store/user/slices/modelList/selectors/index.ts +0 -3
- package/src/store/user/slices/modelList/selectors/keyVaults.test.ts +0 -201
- package/src/store/user/slices/modelList/selectors/keyVaults.ts +0 -50
- package/src/store/user/slices/modelList/selectors/modelConfig.test.ts +0 -219
- package/src/store/user/slices/modelList/selectors/modelConfig.ts +0 -95
- package/src/store/user/slices/modelList/selectors/modelProvider.test.ts +0 -138
- package/src/store/user/slices/modelList/selectors/modelProvider.ts +0 -170
|
@@ -1,287 +0,0 @@
|
|
|
1
|
-
import { ChatModelCard } from '@lobechat/types';
|
|
2
|
-
import { describe, expect, it } from 'vitest';
|
|
3
|
-
|
|
4
|
-
import { LOBE_DEFAULT_MODEL_LIST, OpenAIProviderCard } from '@/config/modelProviders';
|
|
5
|
-
|
|
6
|
-
import { parseModelString, transformToChatModelCards } from './parseModels';
|
|
7
|
-
|
|
8
|
-
describe('parseModelString', () => {
|
|
9
|
-
it('custom deletion, addition, and renaming of models', () => {
|
|
10
|
-
const result = parseModelString(
|
|
11
|
-
'-all,+llama,+claude-2,-gpt-3.5-turbo,gpt-4-1106-preview=gpt-4-turbo,gpt-4-1106-preview=gpt-4-32k',
|
|
12
|
-
);
|
|
13
|
-
|
|
14
|
-
expect(result).toMatchSnapshot();
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it('duplicate naming model', () => {
|
|
18
|
-
const result = parseModelString('gpt-4-1106-preview=gpt-4-turbo,gpt-4-1106-preview=gpt-4-32k');
|
|
19
|
-
expect(result).toMatchSnapshot();
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('only add the model', () => {
|
|
23
|
-
const result = parseModelString('model1,model2,model3,model4');
|
|
24
|
-
|
|
25
|
-
expect(result).toMatchSnapshot();
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it('empty string model', () => {
|
|
29
|
-
const result = parseModelString('gpt-4-1106-preview=gpt-4-turbo,, ,\n ,+claude-2');
|
|
30
|
-
expect(result).toMatchSnapshot();
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
describe('extension capabilities', () => {
|
|
34
|
-
it('with token', () => {
|
|
35
|
-
const result = parseModelString('chatglm-6b=ChatGLM 6B<4096>');
|
|
36
|
-
|
|
37
|
-
expect(result.add[0]).toEqual({
|
|
38
|
-
displayName: 'ChatGLM 6B',
|
|
39
|
-
id: 'chatglm-6b',
|
|
40
|
-
contextWindowTokens: 4096,
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('token and function calling', () => {
|
|
45
|
-
const result = parseModelString('spark-v3.5=讯飞星火 v3.5<8192:fc>');
|
|
46
|
-
|
|
47
|
-
expect(result.add[0]).toEqual({
|
|
48
|
-
displayName: '讯飞星火 v3.5',
|
|
49
|
-
functionCall: true,
|
|
50
|
-
id: 'spark-v3.5',
|
|
51
|
-
contextWindowTokens: 8192,
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('token and reasoning', () => {
|
|
56
|
-
const result = parseModelString('deepseek-r1=Deepseek R1<65536:reasoning>');
|
|
57
|
-
|
|
58
|
-
expect(result.add[0]).toEqual({
|
|
59
|
-
displayName: 'Deepseek R1',
|
|
60
|
-
reasoning: true,
|
|
61
|
-
id: 'deepseek-r1',
|
|
62
|
-
contextWindowTokens: 65_536,
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('multi models', () => {
|
|
67
|
-
const result = parseModelString(
|
|
68
|
-
'gemini-1.5-flash-latest=Gemini 1.5 Flash<16000:vision>,gpt-4-all=ChatGPT Plus<128000:fc:vision:file>',
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
expect(result.add).toEqual([
|
|
72
|
-
{
|
|
73
|
-
displayName: 'Gemini 1.5 Flash',
|
|
74
|
-
vision: true,
|
|
75
|
-
id: 'gemini-1.5-flash-latest',
|
|
76
|
-
contextWindowTokens: 16000,
|
|
77
|
-
},
|
|
78
|
-
{
|
|
79
|
-
displayName: 'ChatGPT Plus',
|
|
80
|
-
vision: true,
|
|
81
|
-
functionCall: true,
|
|
82
|
-
files: true,
|
|
83
|
-
id: 'gpt-4-all',
|
|
84
|
-
contextWindowTokens: 128000,
|
|
85
|
-
},
|
|
86
|
-
]);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it('should have file with builtin models like gpt-4-0125-preview', () => {
|
|
90
|
-
const result = parseModelString(
|
|
91
|
-
'-all,+gpt-4-0125-preview=ChatGPT-4<128000:fc:file>,+gpt-4-turbo-2024-04-09=ChatGPT-4 Vision<128000:fc:vision:file>',
|
|
92
|
-
);
|
|
93
|
-
expect(result.add).toEqual([
|
|
94
|
-
{
|
|
95
|
-
displayName: 'ChatGPT-4',
|
|
96
|
-
files: true,
|
|
97
|
-
functionCall: true,
|
|
98
|
-
id: 'gpt-4-0125-preview',
|
|
99
|
-
contextWindowTokens: 128000,
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
displayName: 'ChatGPT-4 Vision',
|
|
103
|
-
files: true,
|
|
104
|
-
functionCall: true,
|
|
105
|
-
id: 'gpt-4-turbo-2024-04-09',
|
|
106
|
-
contextWindowTokens: 128000,
|
|
107
|
-
vision: true,
|
|
108
|
-
},
|
|
109
|
-
]);
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it('should handle empty extension capability value', () => {
|
|
113
|
-
const result = parseModelString('model1<1024:>');
|
|
114
|
-
expect(result.add[0]).toEqual({ id: 'model1', contextWindowTokens: 1024 });
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
it('should handle empty extension capability name', () => {
|
|
118
|
-
const result = parseModelString('model1<1024::file>');
|
|
119
|
-
expect(result.add[0]).toEqual({ id: 'model1', contextWindowTokens: 1024, files: true });
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it('should handle duplicate extension capabilities', () => {
|
|
123
|
-
const result = parseModelString('model1<1024:vision:vision>');
|
|
124
|
-
expect(result.add[0]).toEqual({ id: 'model1', contextWindowTokens: 1024, vision: true });
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
it('should handle case-sensitive extension capability names', () => {
|
|
128
|
-
const result = parseModelString('model1<1024:VISION:FC:file>');
|
|
129
|
-
expect(result.add[0]).toEqual({ id: 'model1', contextWindowTokens: 1024, files: true });
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it('should handle case-sensitive extension capability values', () => {
|
|
133
|
-
const result = parseModelString('model1<1024:vision:Fc:File>');
|
|
134
|
-
expect(result.add[0]).toEqual({ id: 'model1', contextWindowTokens: 1024, vision: true });
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
it('should handle empty angle brackets', () => {
|
|
138
|
-
const result = parseModelString('model1<>');
|
|
139
|
-
expect(result.add[0]).toEqual({ id: 'model1' });
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
it('should handle not close angle brackets', () => {
|
|
143
|
-
const result = parseModelString('model1<,model2');
|
|
144
|
-
expect(result.add).toEqual([{ id: 'model1' }, { id: 'model2' }]);
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
it('should handle multi close angle brackets', () => {
|
|
148
|
-
const result = parseModelString('model1<>>,model2');
|
|
149
|
-
expect(result.add).toEqual([{ id: 'model1' }, { id: 'model2' }]);
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
it('should handle only colon inside angle brackets', () => {
|
|
153
|
-
const result = parseModelString('model1<:>');
|
|
154
|
-
expect(result.add[0]).toEqual({ id: 'model1' });
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
it('should handle only non-digit characters inside angle brackets', () => {
|
|
158
|
-
const result = parseModelString('model1<abc>');
|
|
159
|
-
expect(result.add[0]).toEqual({ id: 'model1' });
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
it('should handle non-digit characters followed by digits inside angle brackets', () => {
|
|
163
|
-
const result = parseModelString('model1<abc123>');
|
|
164
|
-
expect(result.add[0]).toEqual({ id: 'model1' });
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
it('should handle digits followed by non-colon characters inside angle brackets', () => {
|
|
168
|
-
const result = parseModelString('model1<1024abc>');
|
|
169
|
-
expect(result.add[0]).toEqual({ id: 'model1', contextWindowTokens: 1024 });
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
it('should handle digits followed by multiple colons inside angle brackets', () => {
|
|
173
|
-
const result = parseModelString('model1<1024::>');
|
|
174
|
-
expect(result.add[0]).toEqual({ id: 'model1', contextWindowTokens: 1024 });
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
it('should handle digits followed by a colon and non-letter characters inside angle brackets', () => {
|
|
178
|
-
const result = parseModelString('model1<1024:123>');
|
|
179
|
-
expect(result.add[0]).toEqual({ id: 'model1', contextWindowTokens: 1024 });
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
it('should handle digits followed by a colon and spaces inside angle brackets', () => {
|
|
183
|
-
const result = parseModelString('model1<1024: vision>');
|
|
184
|
-
expect(result.add[0]).toEqual({ id: 'model1', contextWindowTokens: 1024 });
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
it('should handle digits followed by multiple colons and spaces inside angle brackets', () => {
|
|
188
|
-
const result = parseModelString('model1<1024: : vision>');
|
|
189
|
-
expect(result.add[0]).toEqual({ id: 'model1', contextWindowTokens: 1024 });
|
|
190
|
-
});
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
describe('deployment name', () => {
|
|
194
|
-
it('should have same deployment name as id', () => {
|
|
195
|
-
const result = parseModelString('model1=Model 1', true);
|
|
196
|
-
expect(result.add[0]).toEqual({
|
|
197
|
-
id: 'model1',
|
|
198
|
-
displayName: 'Model 1',
|
|
199
|
-
deploymentName: 'model1',
|
|
200
|
-
});
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
it('should have diff deployment name as id', () => {
|
|
204
|
-
const result = parseModelString('gpt-35-turbo->my-deploy=GPT 3.5 Turbo', true);
|
|
205
|
-
expect(result.add[0]).toEqual({
|
|
206
|
-
id: 'gpt-35-turbo',
|
|
207
|
-
displayName: 'GPT 3.5 Turbo',
|
|
208
|
-
deploymentName: 'my-deploy',
|
|
209
|
-
});
|
|
210
|
-
});
|
|
211
|
-
});
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
describe('transformToChatModelCards', () => {
|
|
215
|
-
const defaultChatModels: ChatModelCard[] = [
|
|
216
|
-
{ id: 'model1', displayName: 'Model 1', enabled: true },
|
|
217
|
-
{ id: 'model2', displayName: 'Model 2', enabled: false },
|
|
218
|
-
];
|
|
219
|
-
|
|
220
|
-
it('should return undefined when modelString is empty', () => {
|
|
221
|
-
const result = transformToChatModelCards({
|
|
222
|
-
modelString: '',
|
|
223
|
-
defaultChatModels,
|
|
224
|
-
});
|
|
225
|
-
expect(result).toBeUndefined();
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
it('should remove all models when removeAll is true', () => {
|
|
229
|
-
const result = transformToChatModelCards({
|
|
230
|
-
modelString: '-all',
|
|
231
|
-
defaultChatModels,
|
|
232
|
-
});
|
|
233
|
-
expect(result).toEqual([]);
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
it('should remove specified models', () => {
|
|
237
|
-
const result = transformToChatModelCards({
|
|
238
|
-
modelString: '-model1',
|
|
239
|
-
defaultChatModels,
|
|
240
|
-
});
|
|
241
|
-
expect(result).toEqual([{ id: 'model2', displayName: 'Model 2', enabled: false }]);
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
it('should add a new known model', () => {
|
|
245
|
-
const knownModel = LOBE_DEFAULT_MODEL_LIST[0];
|
|
246
|
-
const result = transformToChatModelCards({
|
|
247
|
-
modelString: `${knownModel.id}`,
|
|
248
|
-
defaultChatModels,
|
|
249
|
-
});
|
|
250
|
-
expect(result).toContainEqual({
|
|
251
|
-
...knownModel,
|
|
252
|
-
displayName: knownModel.displayName || knownModel.id,
|
|
253
|
-
enabled: true,
|
|
254
|
-
});
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
it('should update an existing known model', () => {
|
|
258
|
-
const knownModel = LOBE_DEFAULT_MODEL_LIST[0];
|
|
259
|
-
const result = transformToChatModelCards({
|
|
260
|
-
modelString: `+${knownModel.id}=Updated Model`,
|
|
261
|
-
defaultChatModels: [knownModel],
|
|
262
|
-
});
|
|
263
|
-
expect(result![0]).toEqual({ ...knownModel, displayName: 'Updated Model', enabled: true });
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
it('should add a new custom model', () => {
|
|
267
|
-
const result = transformToChatModelCards({
|
|
268
|
-
modelString: '+custom_model=Custom Model',
|
|
269
|
-
defaultChatModels,
|
|
270
|
-
});
|
|
271
|
-
expect(result).toContainEqual({
|
|
272
|
-
id: 'custom_model',
|
|
273
|
-
displayName: 'Custom Model',
|
|
274
|
-
enabled: true,
|
|
275
|
-
});
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
it('should have file with builtin models like gpt-4-0125-preview', () => {
|
|
279
|
-
const result = transformToChatModelCards({
|
|
280
|
-
modelString:
|
|
281
|
-
'-all,+gpt-4-0125-preview=ChatGPT-4<128000:fc:file>,+gpt-4-turbo-2024-04-09=ChatGPT-4 Vision<128000:fc:vision:file>',
|
|
282
|
-
defaultChatModels: OpenAIProviderCard.chatModels,
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
expect(result).toMatchSnapshot();
|
|
286
|
-
});
|
|
287
|
-
});
|
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
import { ChatModelCard } from '@lobechat/types';
|
|
2
|
-
import { produce } from 'immer';
|
|
3
|
-
|
|
4
|
-
import { LOBE_DEFAULT_MODEL_LIST } from '@/config/modelProviders';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Parse model string to add or remove models.
|
|
8
|
-
*/
|
|
9
|
-
export const parseModelString = (modelString: string = '', withDeploymentName = false) => {
|
|
10
|
-
let models: ChatModelCard[] = [];
|
|
11
|
-
let removeAll = false;
|
|
12
|
-
const removedModels: string[] = [];
|
|
13
|
-
const modelNames = modelString.split(/[,,]/).filter(Boolean);
|
|
14
|
-
|
|
15
|
-
for (const item of modelNames) {
|
|
16
|
-
const disable = item.startsWith('-');
|
|
17
|
-
const nameConfig = item.startsWith('+') || item.startsWith('-') ? item.slice(1) : item;
|
|
18
|
-
const [idAndDisplayName, ...capabilities] = nameConfig.split('<');
|
|
19
|
-
let [id, displayName] = idAndDisplayName.split('=');
|
|
20
|
-
|
|
21
|
-
let deploymentName: string | undefined;
|
|
22
|
-
|
|
23
|
-
if (withDeploymentName) {
|
|
24
|
-
[id, deploymentName] = id.split('->');
|
|
25
|
-
if (!deploymentName) deploymentName = id;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (disable) {
|
|
29
|
-
// Disable all models.
|
|
30
|
-
if (id === 'all') {
|
|
31
|
-
removeAll = true;
|
|
32
|
-
}
|
|
33
|
-
removedModels.push(id);
|
|
34
|
-
continue;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// remove empty model name
|
|
38
|
-
if (!item.trim().length) {
|
|
39
|
-
continue;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Remove duplicate model entries.
|
|
43
|
-
const existingIndex = models.findIndex(({ id: n }) => n === id);
|
|
44
|
-
if (existingIndex !== -1) {
|
|
45
|
-
models.splice(existingIndex, 1);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const model: ChatModelCard = {
|
|
49
|
-
displayName: displayName || undefined,
|
|
50
|
-
id,
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
if (deploymentName) {
|
|
54
|
-
model.deploymentName = deploymentName;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (capabilities.length > 0) {
|
|
58
|
-
const [maxTokenStr, ...capabilityList] = capabilities[0].replace('>', '').split(':');
|
|
59
|
-
model.contextWindowTokens = parseInt(maxTokenStr, 10) || undefined;
|
|
60
|
-
|
|
61
|
-
for (const capability of capabilityList) {
|
|
62
|
-
switch (capability) {
|
|
63
|
-
case 'reasoning': {
|
|
64
|
-
model.reasoning = true;
|
|
65
|
-
break;
|
|
66
|
-
}
|
|
67
|
-
case 'vision': {
|
|
68
|
-
model.vision = true;
|
|
69
|
-
break;
|
|
70
|
-
}
|
|
71
|
-
case 'fc': {
|
|
72
|
-
model.functionCall = true;
|
|
73
|
-
break;
|
|
74
|
-
}
|
|
75
|
-
case 'file': {
|
|
76
|
-
model.files = true;
|
|
77
|
-
break;
|
|
78
|
-
}
|
|
79
|
-
default: {
|
|
80
|
-
console.warn(`Unknown capability: ${capability}`);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
models.push(model);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return {
|
|
90
|
-
add: models,
|
|
91
|
-
removeAll,
|
|
92
|
-
removed: removedModels,
|
|
93
|
-
};
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Extract a special method to process chatModels
|
|
98
|
-
*/
|
|
99
|
-
export const transformToChatModelCards = ({
|
|
100
|
-
modelString = '',
|
|
101
|
-
defaultChatModels,
|
|
102
|
-
withDeploymentName = false,
|
|
103
|
-
}: {
|
|
104
|
-
defaultChatModels: ChatModelCard[];
|
|
105
|
-
modelString?: string;
|
|
106
|
-
withDeploymentName?: boolean;
|
|
107
|
-
}): ChatModelCard[] | undefined => {
|
|
108
|
-
if (!modelString) return undefined;
|
|
109
|
-
|
|
110
|
-
const modelConfig = parseModelString(modelString, withDeploymentName);
|
|
111
|
-
let chatModels = modelConfig.removeAll ? [] : defaultChatModels;
|
|
112
|
-
|
|
113
|
-
// 处理移除逻辑
|
|
114
|
-
if (!modelConfig.removeAll) {
|
|
115
|
-
chatModels = chatModels.filter((m) => !modelConfig.removed.includes(m.id));
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
return produce(chatModels, (draft) => {
|
|
119
|
-
// 处理添加或替换逻辑
|
|
120
|
-
for (const toAddModel of modelConfig.add) {
|
|
121
|
-
// first try to find the model in LOBE_DEFAULT_MODEL_LIST to confirm if it is a known model
|
|
122
|
-
const knownModel = LOBE_DEFAULT_MODEL_LIST.find((model) => model.id === toAddModel.id);
|
|
123
|
-
|
|
124
|
-
// if the model is known, update it based on the known model
|
|
125
|
-
if (knownModel) {
|
|
126
|
-
const index = draft.findIndex((model) => model.id === toAddModel.id);
|
|
127
|
-
const modelInList = draft[index];
|
|
128
|
-
|
|
129
|
-
// if the model is already in chatModels, update it
|
|
130
|
-
if (modelInList) {
|
|
131
|
-
draft[index] = {
|
|
132
|
-
...modelInList,
|
|
133
|
-
...toAddModel,
|
|
134
|
-
displayName: toAddModel.displayName || modelInList.displayName || modelInList.id,
|
|
135
|
-
enabled: true,
|
|
136
|
-
};
|
|
137
|
-
} else {
|
|
138
|
-
// if the model is not in chatModels, add it
|
|
139
|
-
draft.push({
|
|
140
|
-
...knownModel,
|
|
141
|
-
...toAddModel,
|
|
142
|
-
displayName: toAddModel.displayName || knownModel.displayName || knownModel.id,
|
|
143
|
-
enabled: true,
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
} else {
|
|
147
|
-
// if the model is not in LOBE_DEFAULT_MODEL_LIST, add it as a new custom model
|
|
148
|
-
draft.push({
|
|
149
|
-
...toAddModel,
|
|
150
|
-
displayName: toAddModel.displayName || toAddModel.id,
|
|
151
|
-
enabled: true,
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
});
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
export const extractEnabledModels = (modelString: string = '', withDeploymentName = false) => {
|
|
159
|
-
const modelConfig = parseModelString(modelString, withDeploymentName);
|
|
160
|
-
const list = modelConfig.add.map((m) => m.id);
|
|
161
|
-
|
|
162
|
-
if (list.length === 0) return;
|
|
163
|
-
|
|
164
|
-
return list;
|
|
165
|
-
};
|
package/src/hooks/_header.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { LOBE_CHAT_ACCESS_CODE, OPENAI_API_KEY_HEADER_KEY, OPENAI_END_POINT } from '@/const/fetch';
|
|
2
|
-
import { useUserStore } from '@/store/user';
|
|
3
|
-
import { keyVaultsConfigSelectors } from '@/store/user/selectors';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* TODO: Need to be removed after tts refactor
|
|
7
|
-
* @deprecated
|
|
8
|
-
*/
|
|
9
|
-
// eslint-disable-next-line no-undef
|
|
10
|
-
export const createHeaderWithOpenAI = (header?: HeadersInit): HeadersInit => {
|
|
11
|
-
const openai = keyVaultsConfigSelectors.openAIConfig(useUserStore.getState());
|
|
12
|
-
|
|
13
|
-
const apiKey = openai.apiKey || '';
|
|
14
|
-
const endpoint = openai.baseURL || '';
|
|
15
|
-
|
|
16
|
-
// eslint-disable-next-line no-undef
|
|
17
|
-
return {
|
|
18
|
-
...header,
|
|
19
|
-
[LOBE_CHAT_ACCESS_CODE]: keyVaultsConfigSelectors.password(useUserStore.getState()),
|
|
20
|
-
[OPENAI_API_KEY_HEADER_KEY]: apiKey,
|
|
21
|
-
[OPENAI_END_POINT]: endpoint,
|
|
22
|
-
};
|
|
23
|
-
};
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
-
|
|
3
|
-
import { genServerLLMConfig } from './_deprecated';
|
|
4
|
-
|
|
5
|
-
// Mock ModelProvider enum
|
|
6
|
-
vi.mock('model-bank', () => ({
|
|
7
|
-
ModelProvider: {
|
|
8
|
-
Azure: 'azure',
|
|
9
|
-
Bedrock: 'bedrock',
|
|
10
|
-
Ollama: 'ollama',
|
|
11
|
-
},
|
|
12
|
-
}));
|
|
13
|
-
|
|
14
|
-
// Mock ProviderCards
|
|
15
|
-
vi.mock('@/config/modelProviders', () => ({
|
|
16
|
-
azureProviderCard: {
|
|
17
|
-
chatModels: [],
|
|
18
|
-
},
|
|
19
|
-
bedrockProviderCard: {
|
|
20
|
-
chatModels: ['bedrockModel1', 'bedrockModel2'],
|
|
21
|
-
},
|
|
22
|
-
ollamaProviderCard: {
|
|
23
|
-
chatModels: ['ollamaModel1', 'ollamaModel2'],
|
|
24
|
-
},
|
|
25
|
-
}));
|
|
26
|
-
|
|
27
|
-
// Mock LLM config
|
|
28
|
-
vi.mock('@/envs/llm', () => ({
|
|
29
|
-
getLLMConfig: () => ({
|
|
30
|
-
ENABLED_AZURE_OPENAI: true,
|
|
31
|
-
ENABLED_AWS_BEDROCK: true,
|
|
32
|
-
ENABLED_OLLAMA: true,
|
|
33
|
-
AZURE_MODEL_LIST: 'azureModels',
|
|
34
|
-
AWS_BEDROCK_MODEL_LIST: 'bedrockModels',
|
|
35
|
-
OLLAMA_MODEL_LIST: 'ollamaModels',
|
|
36
|
-
OLLAMA_PROXY_URL: '',
|
|
37
|
-
}),
|
|
38
|
-
}));
|
|
39
|
-
|
|
40
|
-
// Mock parse models utils
|
|
41
|
-
vi.mock('@/utils/_deprecated/parseModels', () => ({
|
|
42
|
-
extractEnabledModels: (modelString: string, withDeploymentName?: boolean) => {
|
|
43
|
-
// Returns different format if withDeploymentName is true
|
|
44
|
-
return withDeploymentName ? [`${modelString}_withDeployment`] : [modelString];
|
|
45
|
-
},
|
|
46
|
-
transformToChatModelCards: ({ defaultChatModels, modelString, withDeploymentName }: any) => {
|
|
47
|
-
// Simulate transformation based on withDeploymentName
|
|
48
|
-
return withDeploymentName ? [`${modelString}_transformed`] : defaultChatModels;
|
|
49
|
-
},
|
|
50
|
-
}));
|
|
51
|
-
|
|
52
|
-
describe('genServerLLMConfig', () => {
|
|
53
|
-
it('should generate correct LLM config for Azure, Bedrock, and Ollama', () => {
|
|
54
|
-
vi.stubEnv('AZURE_MODEL_LIST', 'azureModels');
|
|
55
|
-
vi.stubEnv('AWS_BEDROCK_MODEL_LIST', 'bedrockModels');
|
|
56
|
-
vi.stubEnv('OLLAMA_MODEL_LIST', 'ollamaModels');
|
|
57
|
-
|
|
58
|
-
const specificConfig = {
|
|
59
|
-
azure: {
|
|
60
|
-
enabledKey: 'ENABLED_AZURE_OPENAI',
|
|
61
|
-
withDeploymentName: true,
|
|
62
|
-
},
|
|
63
|
-
bedrock: {
|
|
64
|
-
enabledKey: 'ENABLED_AWS_BEDROCK',
|
|
65
|
-
modelListKey: 'AWS_BEDROCK_MODEL_LIST',
|
|
66
|
-
},
|
|
67
|
-
ollama: {
|
|
68
|
-
fetchOnClient: !process.env.OLLAMA_PROXY_URL,
|
|
69
|
-
},
|
|
70
|
-
};
|
|
71
|
-
const config = genServerLLMConfig(specificConfig);
|
|
72
|
-
|
|
73
|
-
expect(config.azure).toEqual({
|
|
74
|
-
enabled: true,
|
|
75
|
-
enabledModels: ['azureModels_withDeployment'],
|
|
76
|
-
serverModelCards: ['azureModels_transformed'],
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
expect(config.bedrock).toEqual({
|
|
80
|
-
enabled: true,
|
|
81
|
-
enabledModels: ['bedrockModels'],
|
|
82
|
-
serverModelCards: ['bedrockModel1', 'bedrockModel2'],
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
expect(config.ollama).toEqual({
|
|
86
|
-
enabled: true,
|
|
87
|
-
enabledModels: ['ollamaModels'],
|
|
88
|
-
fetchOnClient: true,
|
|
89
|
-
serverModelCards: ['ollamaModel1', 'ollamaModel2'],
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
});
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { ModelProvider } from 'model-bank';
|
|
2
|
-
|
|
3
|
-
import * as ProviderCards from '@/config/modelProviders';
|
|
4
|
-
import { getLLMConfig } from '@/envs/llm';
|
|
5
|
-
import { ModelProviderCard } from '@/types/llm';
|
|
6
|
-
import { extractEnabledModels, transformToChatModelCards } from '@/utils/_deprecated/parseModels';
|
|
7
|
-
|
|
8
|
-
export const genServerLLMConfig = (specificConfig: Record<any, any>) => {
|
|
9
|
-
const llmConfig = getLLMConfig() as Record<string, any>;
|
|
10
|
-
|
|
11
|
-
return Object.values(ModelProvider).reduce(
|
|
12
|
-
(config, provider) => {
|
|
13
|
-
const providerUpperCase = provider.toUpperCase();
|
|
14
|
-
const providerCard = ProviderCards[
|
|
15
|
-
`${provider}ProviderCard` as keyof typeof ProviderCards
|
|
16
|
-
] as ModelProviderCard;
|
|
17
|
-
const providerConfig = specificConfig[provider as keyof typeof specificConfig] || {};
|
|
18
|
-
const providerModelList =
|
|
19
|
-
process.env[providerConfig.modelListKey ?? `${providerUpperCase}_MODEL_LIST`];
|
|
20
|
-
|
|
21
|
-
config[provider] = {
|
|
22
|
-
enabled: llmConfig[providerConfig.enabledKey || `ENABLED_${providerUpperCase}`],
|
|
23
|
-
enabledModels: extractEnabledModels(
|
|
24
|
-
providerModelList,
|
|
25
|
-
providerConfig.withDeploymentName || false,
|
|
26
|
-
),
|
|
27
|
-
serverModelCards: transformToChatModelCards({
|
|
28
|
-
defaultChatModels: (providerCard as ModelProviderCard)?.chatModels || [],
|
|
29
|
-
modelString: providerModelList,
|
|
30
|
-
withDeploymentName: providerConfig.withDeploymentName || false,
|
|
31
|
-
}),
|
|
32
|
-
...(providerConfig.fetchOnClient !== undefined && {
|
|
33
|
-
fetchOnClient: providerConfig.fetchOnClient,
|
|
34
|
-
}),
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
return config;
|
|
38
|
-
},
|
|
39
|
-
{} as Record<ModelProvider, any>,
|
|
40
|
-
);
|
|
41
|
-
};
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { SWRResponse } from 'swr';
|
|
2
|
-
import type { StateCreator } from 'zustand/vanilla';
|
|
3
|
-
|
|
4
|
-
import { useOnlyFetchOnceSWR } from '@/libs/swr';
|
|
5
|
-
import type { GlobalStore } from '@/store/global';
|
|
6
|
-
import { DatabaseLoadingState, OnStageChange } from '@/types/clientDB';
|
|
7
|
-
|
|
8
|
-
type InitClientDBParams = { onStateChange: OnStageChange };
|
|
9
|
-
/**
|
|
10
|
-
* 设置操作
|
|
11
|
-
*/
|
|
12
|
-
export interface GlobalClientDBAction {
|
|
13
|
-
initializeClientDB: (params?: InitClientDBParams) => Promise<void>;
|
|
14
|
-
markPgliteEnabled: () => void;
|
|
15
|
-
useInitClientDB: (params?: InitClientDBParams) => SWRResponse;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export const clientDBSlice: StateCreator<
|
|
19
|
-
GlobalStore,
|
|
20
|
-
[['zustand/devtools', never]],
|
|
21
|
-
[],
|
|
22
|
-
GlobalClientDBAction
|
|
23
|
-
> = (set, get) => ({
|
|
24
|
-
initializeClientDB: async (params) => {
|
|
25
|
-
// if the db has started initialized or not error, just skip.
|
|
26
|
-
if (
|
|
27
|
-
get().initClientDBStage !== DatabaseLoadingState.Idle &&
|
|
28
|
-
get().initClientDBStage !== DatabaseLoadingState.Error
|
|
29
|
-
)
|
|
30
|
-
return;
|
|
31
|
-
|
|
32
|
-
const { initializeDB } = await import('@/database/client/db');
|
|
33
|
-
await initializeDB({
|
|
34
|
-
onError: ({ error, migrationsSQL, migrationTableItems }) => {
|
|
35
|
-
set({
|
|
36
|
-
initClientDBError: error,
|
|
37
|
-
initClientDBMigrations: {
|
|
38
|
-
sqls: migrationsSQL,
|
|
39
|
-
tableRecords: migrationTableItems,
|
|
40
|
-
},
|
|
41
|
-
});
|
|
42
|
-
},
|
|
43
|
-
onProgress: (data) => {
|
|
44
|
-
set({ initClientDBProcess: data });
|
|
45
|
-
},
|
|
46
|
-
onStateChange: (state) => {
|
|
47
|
-
set({ initClientDBStage: state });
|
|
48
|
-
params?.onStateChange?.(state);
|
|
49
|
-
},
|
|
50
|
-
});
|
|
51
|
-
},
|
|
52
|
-
markPgliteEnabled: async () => {
|
|
53
|
-
get().updateSystemStatus({ isEnablePglite: true });
|
|
54
|
-
|
|
55
|
-
if (navigator.storage && !!navigator.storage.persist) {
|
|
56
|
-
// 1. Check if persistent permission has been obtained
|
|
57
|
-
const isPersisted = await navigator.storage.persisted();
|
|
58
|
-
|
|
59
|
-
// 2. If the persistent permission has not been obtained, request permission
|
|
60
|
-
if (!isPersisted) {
|
|
61
|
-
await navigator.storage.persist();
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
},
|
|
65
|
-
useInitClientDB: (params) =>
|
|
66
|
-
useOnlyFetchOnceSWR('initClientDB', () => get().initializeClientDB(params)),
|
|
67
|
-
});
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
-
|
|
3
|
-
exports[`LLMSettingsSliceAction > refreshModelProviderList > visible 1`] = `
|
|
4
|
-
{
|
|
5
|
-
"contextWindowTokens": 4096,
|
|
6
|
-
"description": "LLaVA 是结合视觉编码器和 Vicuna 的多模态模型,用于强大的视觉和语言理解。",
|
|
7
|
-
"displayName": "LLaVA 7B",
|
|
8
|
-
"enabled": true,
|
|
9
|
-
"id": "llava",
|
|
10
|
-
"vision": true,
|
|
11
|
-
}
|
|
12
|
-
`;
|