@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.
Files changed (111) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +2 -45
  3. package/README.zh-CN.md +2 -45
  4. package/changelog/v1.json +18 -0
  5. package/docs/self-hosting/advanced/feature-flags.mdx +0 -1
  6. package/docs/self-hosting/advanced/feature-flags.zh-CN.mdx +0 -1
  7. package/e2e/src/features/discover/smoke.feature +34 -1
  8. package/e2e/src/steps/discover/smoke.steps.ts +116 -4
  9. package/package.json +1 -1
  10. package/packages/model-runtime/src/utils/googleErrorParser.test.ts +125 -0
  11. package/packages/model-runtime/src/utils/googleErrorParser.ts +103 -77
  12. package/packages/types/src/serverConfig.ts +2 -6
  13. package/src/app/[variants]/(auth)/signup/[[...signup]]/page.tsx +1 -8
  14. package/src/app/[variants]/(main)/(mobile)/me/(home)/features/UserBanner.tsx +3 -6
  15. package/src/app/[variants]/(main)/discover/(list)/features/Pagination.tsx +1 -0
  16. package/src/app/[variants]/(main)/discover/(list)/features/SortButton/index.tsx +1 -1
  17. package/src/app/[variants]/(main)/discover/(list)/mcp/features/List/Item.tsx +1 -0
  18. package/src/app/[variants]/(main)/discover/(list)/model/features/List/Item.tsx +1 -0
  19. package/src/app/[variants]/(main)/discover/(list)/provider/features/List/Item.tsx +1 -0
  20. package/src/app/[variants]/(main)/discover/components/CategoryMenu.tsx +9 -1
  21. package/src/app/[variants]/(main)/labs/components/LabCard.tsx +3 -1
  22. package/src/app/[variants]/(main)/settings/provider/detail/azure/index.tsx +5 -7
  23. package/src/components/InvalidAPIKey/APIKeyForm/Bedrock.tsx +8 -13
  24. package/src/config/featureFlags/schema.test.ts +0 -2
  25. package/src/config/featureFlags/schema.ts +0 -6
  26. package/src/config/modelProviders/ai21.ts +1 -16
  27. package/src/config/modelProviders/ai302.ts +1 -128
  28. package/src/config/modelProviders/ai360.ts +1 -32
  29. package/src/config/modelProviders/anthropic.ts +1 -71
  30. package/src/config/modelProviders/azure.ts +1 -51
  31. package/src/config/modelProviders/baichuan.ts +1 -57
  32. package/src/config/modelProviders/bedrock.ts +1 -276
  33. package/src/config/modelProviders/cloudflare.ts +1 -64
  34. package/src/config/modelProviders/deepseek.ts +1 -19
  35. package/src/config/modelProviders/fireworksai.ts +1 -174
  36. package/src/config/modelProviders/giteeai.ts +1 -135
  37. package/src/config/modelProviders/github.ts +1 -254
  38. package/src/config/modelProviders/google.ts +1 -130
  39. package/src/config/modelProviders/groq.ts +1 -119
  40. package/src/config/modelProviders/higress.ts +1 -1713
  41. package/src/config/modelProviders/huggingface.ts +1 -54
  42. package/src/config/modelProviders/hunyuan.ts +1 -83
  43. package/src/config/modelProviders/infiniai.ts +1 -74
  44. package/src/config/modelProviders/internlm.ts +1 -20
  45. package/src/config/modelProviders/mistral.ts +1 -95
  46. package/src/config/modelProviders/modelscope.ts +1 -27
  47. package/src/config/modelProviders/moonshot.ts +1 -29
  48. package/src/config/modelProviders/novita.ts +1 -105
  49. package/src/config/modelProviders/ollama.ts +1 -325
  50. package/src/config/modelProviders/openai.ts +1 -242
  51. package/src/config/modelProviders/openrouter.ts +1 -240
  52. package/src/config/modelProviders/perplexity.ts +1 -45
  53. package/src/config/modelProviders/ppio.ts +1 -152
  54. package/src/config/modelProviders/qiniu.ts +1 -18
  55. package/src/config/modelProviders/qwen.ts +1 -245
  56. package/src/config/modelProviders/search1api.ts +1 -34
  57. package/src/config/modelProviders/sensenova.ts +1 -69
  58. package/src/config/modelProviders/siliconcloud.ts +1 -417
  59. package/src/config/modelProviders/spark.ts +1 -59
  60. package/src/config/modelProviders/stepfun.ts +1 -98
  61. package/src/config/modelProviders/taichu.ts +1 -18
  62. package/src/config/modelProviders/togetherai.ts +1 -274
  63. package/src/config/modelProviders/upstage.ts +1 -28
  64. package/src/config/modelProviders/wenxin.ts +1 -140
  65. package/src/config/modelProviders/xai.ts +1 -38
  66. package/src/config/modelProviders/zeroone.ts +1 -81
  67. package/src/config/modelProviders/zhipu.ts +1 -108
  68. package/src/helpers/isCanUseFC.ts +0 -8
  69. package/src/hooks/useEnabledChatModels.ts +0 -8
  70. package/src/hooks/useModelContextWindowTokens.ts +0 -8
  71. package/src/hooks/useModelHasContextWindowToken.ts +1 -10
  72. package/src/hooks/useModelSupportFiles.ts +1 -11
  73. package/src/hooks/useModelSupportReasoning.ts +1 -11
  74. package/src/hooks/useModelSupportToolUse.ts +1 -11
  75. package/src/hooks/useModelSupportVision.ts +1 -11
  76. package/src/layout/AuthProvider/Clerk/index.tsx +2 -16
  77. package/src/server/globalConfig/index.ts +0 -23
  78. package/src/server/routers/lambda/config/__snapshots__/index.test.ts.snap +175 -12
  79. package/src/server/routers/lambda/config/index.test.ts +36 -28
  80. package/src/services/chat/chat.test.ts +12 -0
  81. package/src/services/chat/helper.ts +7 -31
  82. package/src/services/models.ts +2 -11
  83. package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +41 -14
  84. package/src/store/global/store.ts +1 -7
  85. package/src/store/user/initialState.ts +1 -7
  86. package/src/store/user/selectors.ts +1 -5
  87. package/src/store/user/slices/common/action.ts +5 -4
  88. package/src/store/user/slices/settings/selectors/index.ts +1 -0
  89. package/src/store/user/slices/settings/selectors/keyVaults.ts +21 -0
  90. package/src/store/user/store.ts +0 -3
  91. package/src/tools/web-browsing/Render/Search/ConfigForm/Form.tsx +1 -1
  92. package/packages/utils/src/_deprecated/__snapshots__/parseModels.test.ts.snap +0 -104
  93. package/packages/utils/src/_deprecated/parseModels.test.ts +0 -287
  94. package/packages/utils/src/_deprecated/parseModels.ts +0 -165
  95. package/src/hooks/_header.ts +0 -23
  96. package/src/server/globalConfig/_deprecated.test.ts +0 -92
  97. package/src/server/globalConfig/_deprecated.ts +0 -41
  98. package/src/store/global/actions/clientDb.ts +0 -67
  99. package/src/store/user/slices/modelList/__snapshots__/action.test.ts.snap +0 -12
  100. package/src/store/user/slices/modelList/action.test.ts +0 -359
  101. package/src/store/user/slices/modelList/action.ts +0 -223
  102. package/src/store/user/slices/modelList/initialState.ts +0 -15
  103. package/src/store/user/slices/modelList/reducers/customModelCard.test.ts +0 -204
  104. package/src/store/user/slices/modelList/reducers/customModelCard.ts +0 -64
  105. package/src/store/user/slices/modelList/selectors/index.ts +0 -3
  106. package/src/store/user/slices/modelList/selectors/keyVaults.test.ts +0 -201
  107. package/src/store/user/slices/modelList/selectors/keyVaults.ts +0 -50
  108. package/src/store/user/slices/modelList/selectors/modelConfig.test.ts +0 -219
  109. package/src/store/user/slices/modelList/selectors/modelConfig.ts +0 -95
  110. package/src/store/user/slices/modelList/selectors/modelProvider.test.ts +0 -138
  111. package/src/store/user/slices/modelList/selectors/modelProvider.ts +0 -170
@@ -36,7 +36,7 @@ describe('configRouter', () => {
36
36
  const response = await router.getGlobalConfig();
37
37
 
38
38
  // Assert
39
- const result = response.serverConfig.languageModel?.openai;
39
+ const result = response.serverConfig.aiProvider?.openai;
40
40
 
41
41
  expect(result).toMatchSnapshot();
42
42
  process.env.OPENAI_MODEL_LIST = '';
@@ -48,7 +48,7 @@ describe('configRouter', () => {
48
48
 
49
49
  const response = await router.getGlobalConfig();
50
50
 
51
- const result = response.serverConfig.languageModel?.openai?.serverModelCards;
51
+ const result = response.serverConfig.aiProvider?.openai?.serverModelLists;
52
52
 
53
53
  expect(result).toMatchSnapshot();
54
54
 
@@ -61,7 +61,7 @@ describe('configRouter', () => {
61
61
 
62
62
  const response = await router.getGlobalConfig();
63
63
 
64
- const result = response.serverConfig.languageModel?.openai?.serverModelCards;
64
+ const result = response.serverConfig.aiProvider?.openai?.serverModelLists;
65
65
 
66
66
  expect(result?.find((s) => s.id === 'gpt-4-0125-preview')?.displayName).toEqual(
67
67
  'gpt-4-32k',
@@ -75,7 +75,7 @@ describe('configRouter', () => {
75
75
 
76
76
  const response = await router.getGlobalConfig();
77
77
 
78
- const result = response.serverConfig.languageModel?.openai?.serverModelCards;
78
+ const result = response.serverConfig.aiProvider?.openai?.serverModelLists;
79
79
 
80
80
  expect(result?.find((r) => r.id === 'gpt-4')).toBeUndefined();
81
81
 
@@ -87,7 +87,7 @@ describe('configRouter', () => {
87
87
 
88
88
  const response = await router.getGlobalConfig();
89
89
 
90
- const result = response.serverConfig.languageModel?.openai?.serverModelCards;
90
+ const result = response.serverConfig.aiProvider?.openai?.serverModelLists;
91
91
 
92
92
  const model = result?.find((o) => o.id === 'gpt-4-1106-preview');
93
93
 
@@ -101,28 +101,36 @@ describe('configRouter', () => {
101
101
 
102
102
  const response = await router.getGlobalConfig();
103
103
 
104
- const result = response.serverConfig.languageModel?.openai?.serverModelCards;
105
-
106
- expect(result).toContainEqual({
107
- displayName: 'model1',
108
- id: 'model1',
109
- enabled: true,
110
- });
111
- expect(result).toContainEqual({
112
- displayName: 'model2',
113
- enabled: true,
114
- id: 'model2',
115
- });
116
- expect(result).toContainEqual({
117
- displayName: 'model3',
118
- enabled: true,
119
- id: 'model3',
120
- });
121
- expect(result).toContainEqual({
122
- displayName: 'model4',
123
- enabled: true,
124
- id: 'model4',
125
- });
104
+ const result = response.serverConfig.aiProvider?.openai?.serverModelLists;
105
+
106
+ expect(result).toContainEqual(
107
+ expect.objectContaining({
108
+ displayName: 'model1',
109
+ id: 'model1',
110
+ enabled: true,
111
+ }),
112
+ );
113
+ expect(result).toContainEqual(
114
+ expect.objectContaining({
115
+ displayName: 'model2',
116
+ enabled: true,
117
+ id: 'model2',
118
+ }),
119
+ );
120
+ expect(result).toContainEqual(
121
+ expect.objectContaining({
122
+ displayName: 'model3',
123
+ enabled: true,
124
+ id: 'model3',
125
+ }),
126
+ );
127
+ expect(result).toContainEqual(
128
+ expect.objectContaining({
129
+ displayName: 'model4',
130
+ enabled: true,
131
+ id: 'model4',
132
+ }),
133
+ );
126
134
 
127
135
  process.env.OPENAI_MODEL_LIST = '';
128
136
  });
@@ -136,7 +144,7 @@ describe('configRouter', () => {
136
144
  const response = await router.getGlobalConfig();
137
145
 
138
146
  // Assert
139
- const result = response.serverConfig.languageModel?.openrouter;
147
+ const result = response.serverConfig.aiProvider?.openrouter;
140
148
 
141
149
  expect(result).toMatchSnapshot();
142
150
 
@@ -284,6 +284,9 @@ describe('ChatService', () => {
284
284
  vi.mocked(parseDataUri).mockReturnValue({ type: 'url', base64: null, mimeType: null });
285
285
  vi.mocked(isDesktopLocalStaticServerUrl).mockReturnValue(false); // Not a local URL
286
286
 
287
+ // Mock aiModelSelectors to return true for vision support
288
+ vi.spyOn(aiModelSelectors, 'isModelSupportVision').mockReturnValue(() => true);
289
+
287
290
  const messages = [
288
291
  {
289
292
  content: 'Hello',
@@ -369,6 +372,9 @@ describe('ChatService', () => {
369
372
  mimeType: 'image/png',
370
373
  });
371
374
 
375
+ // Mock aiModelSelectors to return true for vision support
376
+ vi.spyOn(aiModelSelectors, 'isModelSupportVision').mockReturnValue(() => true);
377
+
372
378
  const messages = [
373
379
  {
374
380
  content: 'Hello',
@@ -441,6 +447,9 @@ describe('ChatService', () => {
441
447
  vi.mocked(isDesktopLocalStaticServerUrl).mockReturnValue(false); // This is NOT a local URL
442
448
  vi.mocked(imageUrlToBase64).mockClear(); // Clear to ensure it's not called
443
449
 
450
+ // Mock aiModelSelectors to return true for vision support
451
+ vi.spyOn(aiModelSelectors, 'isModelSupportVision').mockReturnValue(() => true);
452
+
444
453
  const messages = [
445
454
  {
446
455
  content: 'Hello',
@@ -518,6 +527,9 @@ describe('ChatService', () => {
518
527
  mimeType: 'image/jpeg',
519
528
  });
520
529
 
530
+ // Mock aiModelSelectors to return true for vision support
531
+ vi.spyOn(aiModelSelectors, 'isModelSupportVision').mockReturnValue(() => true);
532
+
521
533
  const messages = [
522
534
  {
523
535
  content: 'Multiple images',
@@ -1,16 +1,9 @@
1
- import { isDeprecatedEdition } from '@lobechat/const';
2
1
  import { ModelProvider } from 'model-bank';
3
2
 
4
3
  import { getAiInfraStoreState } from '@/store/aiInfra';
5
4
  import { aiModelSelectors, aiProviderSelectors } from '@/store/aiInfra/selectors';
6
- import { getUserStoreState, useUserStore } from '@/store/user';
7
- import { modelConfigSelectors, modelProviderSelectors } from '@/store/user/selectors';
8
5
 
9
6
  export const isCanUseVision = (model: string, provider: string): boolean => {
10
- // TODO: remove isDeprecatedEdition condition in V2.0
11
- if (isDeprecatedEdition) {
12
- return modelProviderSelectors.isModelEnabledVision(model)(getUserStoreState());
13
- }
14
7
  return aiModelSelectors.isModelSupportVision(model, provider)(getAiInfraStoreState());
15
8
  };
16
9
 
@@ -24,40 +17,23 @@ export const isCanUseVideo = (model: string, provider: string): boolean => {
24
17
  export const findDeploymentName = (model: string, provider: string) => {
25
18
  let deploymentId = model;
26
19
 
27
- // TODO: remove isDeprecatedEdition condition in V2.0
28
- if (isDeprecatedEdition) {
29
- const chatModelCards = modelProviderSelectors.getModelCardsById(ModelProvider.Azure)(
30
- useUserStore.getState(),
31
- );
32
-
33
- const deploymentName = chatModelCards.find((i) => i.id === model)?.deploymentName;
34
- if (deploymentName) deploymentId = deploymentName;
35
- } else {
36
- // find the model by id
37
- const modelItem = getAiInfraStoreState().enabledAiModels?.find(
38
- (i) => i.id === model && i.providerId === provider,
39
- );
20
+ // find the model by id
21
+ const modelItem = getAiInfraStoreState().enabledAiModels?.find(
22
+ (i) => i.id === model && i.providerId === provider,
23
+ );
40
24
 
41
- if (modelItem && modelItem.config?.deploymentName) {
42
- deploymentId = modelItem.config?.deploymentName;
43
- }
25
+ if (modelItem && modelItem.config?.deploymentName) {
26
+ deploymentId = modelItem.config?.deploymentName;
44
27
  }
45
28
 
46
29
  return deploymentId;
47
30
  };
48
31
 
49
32
  export const isEnableFetchOnClient = (provider: string) => {
50
- // TODO: remove this condition in V2.0
51
- if (isDeprecatedEdition) {
52
- return modelConfigSelectors.isProviderFetchOnClient(provider)(useUserStore.getState());
53
- } else {
54
- return aiProviderSelectors.isProviderFetchOnClient(provider)(getAiInfraStoreState());
55
- }
33
+ return aiProviderSelectors.isProviderFetchOnClient(provider)(getAiInfraStoreState());
56
34
  };
57
35
 
58
36
  export const resolveRuntimeProvider = (provider: string) => {
59
- if (isDeprecatedEdition) return provider;
60
-
61
37
  const isBuiltin = Object.values(ModelProvider).includes(provider as any);
62
38
  if (isBuiltin) return provider;
63
39
 
@@ -1,8 +1,5 @@
1
- import { isDeprecatedEdition } from '@/const/version';
2
1
  import { createHeaderWithAuth } from '@/services/_auth';
3
2
  import { aiProviderSelectors, getAiInfraStoreState } from '@/store/aiInfra';
4
- import { useUserStore } from '@/store/user';
5
- import { modelConfigSelectors } from '@/store/user/selectors';
6
3
  import { ChatModelCard } from '@/types/llm';
7
4
  import { getMessageError } from '@/utils/fetch';
8
5
 
@@ -10,14 +7,8 @@ import { API_ENDPOINTS } from './_url';
10
7
  import { initializeWithClientStore } from './chat/clientModelRuntime';
11
8
  import { resolveRuntimeProvider } from './chat/helper';
12
9
 
13
- const isEnableFetchOnClient = (provider: string) => {
14
- // TODO: remove this condition in V2.0
15
- if (isDeprecatedEdition) {
16
- return modelConfigSelectors.isProviderFetchOnClient(provider)(useUserStore.getState());
17
- } else {
18
- return aiProviderSelectors.isProviderFetchOnClient(provider)(getAiInfraStoreState());
19
- }
20
- };
10
+ const isEnableFetchOnClient = (provider: string) =>
11
+ aiProviderSelectors.isProviderFetchOnClient(provider)(getAiInfraStoreState());
21
12
 
22
13
  // 进度信息接口
23
14
  export interface ModelProgressInfo {
@@ -11,8 +11,10 @@ import {
11
11
  TraceNameMap,
12
12
  UIChatMessage,
13
13
  } from '@lobechat/types';
14
+ import isEqual from 'fast-deep-equal';
14
15
  import { t } from 'i18next';
15
16
  import { produce } from 'immer';
17
+ import { throttle } from 'lodash-es';
16
18
  import { StateCreator } from 'zustand/vanilla';
17
19
 
18
20
  import { chatService } from '@/services/chat';
@@ -442,6 +444,19 @@ export const generateAIChat: StateCreator<
442
444
  // to upload image
443
445
  const uploadTasks: Map<string, Promise<{ id?: string; url?: string }>> = new Map();
444
446
 
447
+ // Throttle tool_calls updates to prevent excessive re-renders (max once per 300ms)
448
+ const throttledUpdateToolCalls = throttle(
449
+ (toolCalls: any[]) => {
450
+ internal_dispatchMessage({
451
+ id: messageId,
452
+ type: 'updateMessage',
453
+ value: { tools: get().internal_transformToolCalls(toolCalls) },
454
+ });
455
+ },
456
+ 300,
457
+ { leading: true, trailing: true },
458
+ );
459
+
445
460
  const historySummary = chatConfig.enableCompressHistory
446
461
  ? topicSelectors.currentActiveTopicSummary(get())
447
462
  : undefined;
@@ -495,6 +510,8 @@ export const generateAIChat: StateCreator<
495
510
 
496
511
  let parsedToolCalls = toolCalls;
497
512
  if (parsedToolCalls && parsedToolCalls.length > 0) {
513
+ // Flush any pending throttled updates before finalizing
514
+ throttledUpdateToolCalls.flush();
498
515
  internal_toggleToolCallingStreaming(messageId, undefined);
499
516
  parsedToolCalls = parsedToolCalls.map((item) => ({
500
517
  ...item,
@@ -506,6 +523,8 @@ export const generateAIChat: StateCreator<
506
523
  isFunctionCall = true;
507
524
  }
508
525
 
526
+ internal_toggleChatReasoning(false, messageId, n('toggleChatReasoning/false') as string);
527
+
509
528
  // update the content after fetch result
510
529
  await internal_updateMessageContent(messageId, content, {
511
530
  toolCalls: parsedToolCalls,
@@ -615,12 +634,17 @@ export const generateAIChat: StateCreator<
615
634
  // is this message is just a tool call
616
635
  case 'tool_calls': {
617
636
  internal_toggleToolCallingStreaming(messageId, chunk.isAnimationActives);
618
- internal_dispatchMessage({
619
- id: messageId,
620
- type: 'updateMessage',
621
- value: { tools: get().internal_transformToolCalls(chunk.tool_calls) },
622
- });
637
+ throttledUpdateToolCalls(chunk.tool_calls);
623
638
  isFunctionCall = true;
639
+ const isInChatReasoning =
640
+ messageStateSelectors.isMessageInChatReasoning(messageId)(get());
641
+ if (isInChatReasoning) {
642
+ internal_toggleChatReasoning(
643
+ false,
644
+ messageId,
645
+ n('toggleChatReasoning/false') as string,
646
+ );
647
+ }
624
648
  }
625
649
  }
626
650
  },
@@ -690,16 +714,19 @@ export const generateAIChat: StateCreator<
690
714
  return get().internal_toggleLoadingArrays('reasoningLoadingIds', loading, id, action);
691
715
  },
692
716
  internal_toggleToolCallingStreaming: (id, streaming) => {
717
+ const previous = get().toolCallingStreamIds;
718
+ const next = produce(previous, (draft) => {
719
+ if (!!streaming) {
720
+ draft[id] = streaming;
721
+ } else {
722
+ delete draft[id];
723
+ }
724
+ });
725
+
726
+ if (isEqual(previous, next)) return;
727
+
693
728
  set(
694
- {
695
- toolCallingStreamIds: produce(get().toolCallingStreamIds, (draft) => {
696
- if (!!streaming) {
697
- draft[id] = streaming;
698
- } else {
699
- delete draft[id];
700
- }
701
- }),
702
- },
729
+ { toolCallingStreamIds: next },
703
730
 
704
731
  false,
705
732
  `toggleToolCallingStreaming/${!!streaming ? 'start' : 'end'}`,
@@ -4,25 +4,19 @@ import { createWithEqualityFn } from 'zustand/traditional';
4
4
  import { StateCreator } from 'zustand/vanilla';
5
5
 
6
6
  import { createDevtools } from '../middleware/createDevtools';
7
- import { type GlobalClientDBAction, clientDBSlice } from './actions/clientDb';
8
7
  import { type GlobalGeneralAction, generalActionSlice } from './actions/general';
9
8
  import { type GlobalWorkspacePaneAction, globalWorkspaceSlice } from './actions/workspacePane';
10
9
  import { type GlobalState, initialState } from './initialState';
11
10
 
12
11
  // =============== 聚合 createStoreFn ============ //
13
12
 
14
- export interface GlobalStore
15
- extends GlobalState,
16
- GlobalWorkspacePaneAction,
17
- GlobalClientDBAction,
18
- GlobalGeneralAction {
13
+ export interface GlobalStore extends GlobalState, GlobalWorkspacePaneAction, GlobalGeneralAction {
19
14
  /* empty */
20
15
  }
21
16
 
22
17
  const createStore: StateCreator<GlobalStore, [['zustand/devtools', never]]> = (...parameters) => ({
23
18
  ...initialState,
24
19
  ...globalWorkspaceSlice(...parameters),
25
- ...clientDBSlice(...parameters),
26
20
  ...generalActionSlice(...parameters),
27
21
  });
28
22
 
@@ -1,19 +1,13 @@
1
1
  import { UserAuthState, initialAuthState } from './slices/auth/initialState';
2
2
  import { CommonState, initialCommonState } from './slices/common/initialState';
3
- import { ModelListState, initialModelListState } from './slices/modelList/initialState';
4
3
  import { UserPreferenceState, initialPreferenceState } from './slices/preference/initialState';
5
4
  import { UserSettingsState, initialSettingsState } from './slices/settings/initialState';
6
5
 
7
- export type UserState = UserSettingsState &
8
- UserPreferenceState &
9
- UserAuthState &
10
- ModelListState &
11
- CommonState;
6
+ export type UserState = UserSettingsState & UserPreferenceState & UserAuthState & CommonState;
12
7
 
13
8
  export const initialState: UserState = {
14
9
  ...initialSettingsState,
15
10
  ...initialPreferenceState,
16
11
  ...initialAuthState,
17
12
  ...initialCommonState,
18
- ...initialModelListState,
19
13
  };
@@ -1,11 +1,7 @@
1
1
  export { authSelectors, userProfileSelectors } from './slices/auth/selectors';
2
- export {
3
- keyVaultsConfigSelectors,
4
- modelConfigSelectors,
5
- modelProviderSelectors,
6
- } from './slices/modelList/selectors';
7
2
  export { preferenceSelectors } from './slices/preference/selectors';
8
3
  export {
4
+ keyVaultsConfigSelectors,
9
5
  settingsSelectors,
10
6
  systemAgentSelectors,
11
7
  userGeneralSettingsSelectors,
@@ -24,6 +24,7 @@ const GET_USER_STATE_KEY = 'initUserState';
24
24
  export interface CommonAction {
25
25
  refreshUserState: () => Promise<void>;
26
26
  updateAvatar: (avatar: string) => Promise<void>;
27
+ updateKeyVaultConfig: (provider: string, config: any) => Promise<void>;
27
28
  useCheckTrace: (shouldFetch: boolean) => SWRResponse;
28
29
  useInitUserState: (
29
30
  isLogin: boolean | undefined,
@@ -50,6 +51,10 @@ export const createCommonSlice: StateCreator<
50
51
  await get().refreshUserState();
51
52
  },
52
53
 
54
+ updateKeyVaultConfig: async (provider, config) => {
55
+ await get().setSettings({ keyVaults: { [provider]: config } });
56
+ },
57
+
53
58
  useCheckTrace: (shouldFetch) =>
54
59
  useSWR<boolean>(
55
60
  shouldFetch ? 'checkTrace' : null,
@@ -65,7 +70,6 @@ export const createCommonSlice: StateCreator<
65
70
  revalidateOnFocus: false,
66
71
  },
67
72
  ),
68
-
69
73
  useInitUserState: (isLogin, serverConfig, options) =>
70
74
  useOnlyFetchOnceSWR<UserInitializationState>(
71
75
  !!isLogin ? GET_USER_STATE_KEY : null,
@@ -79,7 +83,6 @@ export const createCommonSlice: StateCreator<
79
83
  const serverSettings: PartialDeep<UserSettings> = {
80
84
  defaultAgent: serverConfig.defaultAgent,
81
85
  image: serverConfig.image,
82
- languageModel: serverConfig.languageModel,
83
86
  systemAgent: serverConfig.systemAgent,
84
87
  };
85
88
 
@@ -112,7 +115,6 @@ export const createCommonSlice: StateCreator<
112
115
  isUserHasConversation: data.hasConversation,
113
116
  isUserStateInit: true,
114
117
  preference,
115
- serverLanguageModel: serverConfig.languageModel,
116
118
  settings: data.settings || {},
117
119
  subscriptionPlan: data.subscriptionPlan,
118
120
  user,
@@ -128,7 +130,6 @@ export const createCommonSlice: StateCreator<
128
130
  lastName: data.lastName,
129
131
  username: data.username,
130
132
  });
131
- get().refreshDefaultModelProviderList({ trigger: 'fetchUserState' });
132
133
  }
133
134
  },
134
135
  },
@@ -1,3 +1,4 @@
1
1
  export { userGeneralSettingsSelectors } from './general';
2
+ export { keyVaultsConfigSelectors } from './keyVaults';
2
3
  export { settingsSelectors } from './settings';
3
4
  export { systemAgentSelectors } from './systemAgent';
@@ -0,0 +1,21 @@
1
+ import { UserStore } from '@/store/user';
2
+ import { UserKeyVaults } from '@/types/user/settings';
3
+
4
+ import { currentSettings } from './settings';
5
+
6
+ export const keyVaultsSettings = (s: UserStore): UserKeyVaults =>
7
+ currentSettings(s).keyVaults || {};
8
+
9
+ const getVaultByProvider = (provider: string) => (s: UserStore) =>
10
+ // @ts-ignore
11
+ (keyVaultsSettings(s)[provider] || {}) as any;
12
+
13
+ const password = (s: UserStore) => keyVaultsSettings(s).password || '';
14
+
15
+ export const keyVaultsConfigSelectors = {
16
+ getVaultByProvider,
17
+
18
+ keyVaultsSettings,
19
+
20
+ password,
21
+ };
@@ -7,7 +7,6 @@ import { createDevtools } from '../middleware/createDevtools';
7
7
  import { type UserState, initialState } from './initialState';
8
8
  import { type UserAuthAction, createAuthSlice } from './slices/auth/action';
9
9
  import { type CommonAction, createCommonSlice } from './slices/common/action';
10
- import { type ModelListAction, createModelListSlice } from './slices/modelList/action';
11
10
  import { type PreferenceAction, createPreferenceSlice } from './slices/preference/action';
12
11
  import { type UserSettingsAction, createSettingsSlice } from './slices/settings/action';
13
12
 
@@ -16,7 +15,6 @@ import { type UserSettingsAction, createSettingsSlice } from './slices/settings/
16
15
  export type UserStore = UserState &
17
16
  UserSettingsAction &
18
17
  PreferenceAction &
19
- ModelListAction &
20
18
  UserAuthAction &
21
19
  CommonAction;
22
20
 
@@ -26,7 +24,6 @@ const createStore: StateCreator<UserStore, [['zustand/devtools', never]]> = (...
26
24
  ...createPreferenceSlice(...parameters),
27
25
  ...createAuthSlice(...parameters),
28
26
  ...createCommonSlice(...parameters),
29
- ...createModelListSlice(...parameters),
30
27
  });
31
28
 
32
29
  // =============== 实装 useStore ============ //
@@ -23,7 +23,7 @@ const Form = memo<ProviderApiKeyFormProps>(({ provider, id }) => {
23
23
  const [apiKey, baseURL, setConfig] = useUserStore((s) => [
24
24
  keyVaultsConfigSelectors.getVaultByProvider(provider as any)(s)?.apiKey,
25
25
  keyVaultsConfigSelectors.getVaultByProvider(provider as any)(s)?.baseURL,
26
- s.updateKeyVaultSettings,
26
+ s.updateKeyVaultConfig,
27
27
  ]);
28
28
 
29
29
  const [showKey, setShow] = useState(false);
@@ -1,104 +0,0 @@
1
- // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
-
3
- exports[`parseModelString > custom deletion, addition, and renaming of models 1`] = `
4
- {
5
- "add": [
6
- {
7
- "displayName": undefined,
8
- "id": "llama",
9
- },
10
- {
11
- "displayName": undefined,
12
- "id": "claude-2",
13
- },
14
- {
15
- "displayName": "gpt-4-32k",
16
- "id": "gpt-4-1106-preview",
17
- },
18
- ],
19
- "removeAll": true,
20
- "removed": [
21
- "all",
22
- "gpt-3.5-turbo",
23
- ],
24
- }
25
- `;
26
-
27
- exports[`parseModelString > duplicate naming model 1`] = `
28
- {
29
- "add": [
30
- {
31
- "displayName": "gpt-4-32k",
32
- "id": "gpt-4-1106-preview",
33
- },
34
- ],
35
- "removeAll": false,
36
- "removed": [],
37
- }
38
- `;
39
-
40
- exports[`parseModelString > empty string model 1`] = `
41
- {
42
- "add": [
43
- {
44
- "displayName": "gpt-4-turbo",
45
- "id": "gpt-4-1106-preview",
46
- },
47
- {
48
- "displayName": undefined,
49
- "id": "claude-2",
50
- },
51
- ],
52
- "removeAll": false,
53
- "removed": [],
54
- }
55
- `;
56
-
57
- exports[`parseModelString > only add the model 1`] = `
58
- {
59
- "add": [
60
- {
61
- "displayName": undefined,
62
- "id": "model1",
63
- },
64
- {
65
- "displayName": undefined,
66
- "id": "model2",
67
- },
68
- {
69
- "displayName": undefined,
70
- "id": "model3",
71
- },
72
- {
73
- "displayName": undefined,
74
- "id": "model4",
75
- },
76
- ],
77
- "removeAll": false,
78
- "removed": [],
79
- }
80
- `;
81
-
82
- exports[`transformToChatModelCards > should have file with builtin models like gpt-4-0125-preview 1`] = `
83
- [
84
- {
85
- "contextWindowTokens": 128000,
86
- "description": "最新的 GPT-4 Turbo 模型具备视觉功能。现在,视觉请求可以使用 JSON 模式和函数调用。 GPT-4 Turbo 是一个增强版本,为多模态任务提供成本效益高的支持。它在准确性和效率之间找到平衡,适合需要进行实时交互的应用程序场景。",
87
- "displayName": "ChatGPT-4",
88
- "enabled": true,
89
- "files": true,
90
- "functionCall": true,
91
- "id": "gpt-4-0125-preview",
92
- },
93
- {
94
- "contextWindowTokens": 128000,
95
- "description": "最新的 GPT-4 Turbo 模型具备视觉功能。现在,视觉请求可以使用 JSON 模式和函数调用。 GPT-4 Turbo 是一个增强版本,为多模态任务提供成本效益高的支持。它在准确性和效率之间找到平衡,适合需要进行实时交互的应用程序场景。",
96
- "displayName": "ChatGPT-4 Vision",
97
- "enabled": true,
98
- "files": true,
99
- "functionCall": true,
100
- "id": "gpt-4-turbo-2024-04-09",
101
- "vision": true,
102
- },
103
- ]
104
- `;