@lobehub/chat 1.46.7 → 1.47.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 (50) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/changelog/v1.json +9 -0
  3. package/package.json +2 -1
  4. package/src/app/(main)/discover/(detail)/provider/[slug]/features/ProviderConfig.tsx +2 -2
  5. package/src/app/(main)/settings/hooks/useCategory.tsx +3 -3
  6. package/src/app/(main)/settings/provider/(detail)/[id]/ClientMode.tsx +25 -0
  7. package/src/app/(main)/settings/provider/(detail)/[id]/page.tsx +2 -1
  8. package/src/app/(main)/settings/provider/ProviderMenu/SortProviderModal/index.tsx +0 -1
  9. package/src/database/client/migrations.json +11 -0
  10. package/src/database/repositories/tableViewer/index.test.ts +256 -0
  11. package/src/database/repositories/tableViewer/index.ts +251 -0
  12. package/src/database/server/models/aiProvider.ts +2 -2
  13. package/src/features/DevPanel/FloatPanel.tsx +136 -0
  14. package/src/features/DevPanel/PostgresViewer/DataTable/Table.tsx +157 -0
  15. package/src/features/DevPanel/PostgresViewer/DataTable/TableCell.tsx +34 -0
  16. package/src/features/DevPanel/PostgresViewer/DataTable/index.tsx +67 -0
  17. package/src/features/DevPanel/PostgresViewer/Schema.tsx +196 -0
  18. package/src/features/DevPanel/PostgresViewer/TableColumns.tsx +67 -0
  19. package/src/features/DevPanel/PostgresViewer/index.tsx +19 -0
  20. package/src/features/DevPanel/PostgresViewer/useTableColumns.ts +13 -0
  21. package/src/features/DevPanel/index.tsx +12 -0
  22. package/src/features/ModelSwitchPanel/index.tsx +4 -2
  23. package/src/hooks/useEnabledChatModels.ts +2 -2
  24. package/src/hooks/useModelContextWindowTokens.ts +2 -2
  25. package/src/hooks/useModelHasContextWindowToken.ts +2 -2
  26. package/src/hooks/useModelSupportToolUse.ts +2 -2
  27. package/src/hooks/useModelSupportVision.ts +2 -2
  28. package/src/layout/GlobalProvider/index.tsx +2 -2
  29. package/src/services/_auth.ts +2 -2
  30. package/src/services/aiModel/client.ts +60 -0
  31. package/src/services/aiModel/index.test.ts +10 -0
  32. package/src/services/aiModel/index.ts +5 -0
  33. package/src/services/aiModel/server.ts +47 -0
  34. package/src/services/aiModel/type.ts +30 -0
  35. package/src/services/aiProvider/client.ts +64 -0
  36. package/src/services/aiProvider/index.test.ts +10 -0
  37. package/src/services/aiProvider/index.ts +5 -0
  38. package/src/services/aiProvider/server.ts +43 -0
  39. package/src/services/aiProvider/type.ts +26 -0
  40. package/src/services/chat.ts +5 -5
  41. package/src/services/tableViewer/client.ts +16 -0
  42. package/src/services/tableViewer/index.ts +3 -0
  43. package/src/store/aiInfra/slices/aiProvider/action.ts +2 -2
  44. package/src/types/serverConfig.ts +6 -0
  45. package/src/types/tableViewer.ts +30 -0
  46. package/tests/utils.tsx +46 -0
  47. package/src/features/DebugUI/Content.tsx +0 -34
  48. package/src/features/DebugUI/index.tsx +0 -20
  49. package/src/services/aiModel.ts +0 -52
  50. package/src/services/aiProvider.ts +0 -47
@@ -0,0 +1,12 @@
1
+ 'use client';
2
+
3
+ import FloatPanel from './FloatPanel';
4
+ import PostgresViewer from './PostgresViewer';
5
+
6
+ const DevPanel = () => (
7
+ <FloatPanel>
8
+ <PostgresViewer />
9
+ </FloatPanel>
10
+ );
11
+
12
+ export default DevPanel;
@@ -9,7 +9,7 @@ import { useTranslation } from 'react-i18next';
9
9
  import { Flexbox } from 'react-layout-kit';
10
10
 
11
11
  import { ModelItemRender, ProviderItemRender } from '@/components/ModelSelect';
12
- import { isServerMode } from '@/const/version';
12
+ import { isDeprecatedEdition } from '@/const/version';
13
13
  import { useEnabledChatModels } from '@/hooks/useEnabledChatModels';
14
14
  import { useIsMobile } from '@/hooks/useIsMobile';
15
15
  import { useAgentStore } from '@/store/agent';
@@ -76,7 +76,9 @@ const ModelSwitchPanel = memo<PropsWithChildren>(({ children }) => {
76
76
  </Flexbox>
77
77
  ),
78
78
  onClick: () => {
79
- router.push(!isServerMode ? '/settings/llm' : `/settings/provider/${provider.id}`);
79
+ router.push(
80
+ isDeprecatedEdition ? '/settings/llm' : `/settings/provider/${provider.id}`,
81
+ );
80
82
  },
81
83
  },
82
84
  ];
@@ -1,6 +1,6 @@
1
1
  import isEqual from 'fast-deep-equal';
2
2
 
3
- import { isServerMode } from '@/const/version';
3
+ import { isDeprecatedEdition } from '@/const/version';
4
4
  import { useAiInfraStore } from '@/store/aiInfra';
5
5
  import { useUserStore } from '@/store/user';
6
6
  import { modelProviderSelectors } from '@/store/user/selectors';
@@ -10,7 +10,7 @@ export const useEnabledChatModels = (): EnabledProviderWithModels[] => {
10
10
  const enabledList = useUserStore(modelProviderSelectors.modelProviderListForModelSelect, isEqual);
11
11
  const enabledChatModelList = useAiInfraStore((s) => s.enabledChatModelList, isEqual);
12
12
 
13
- if (!isServerMode) {
13
+ if (isDeprecatedEdition) {
14
14
  return enabledList;
15
15
  }
16
16
 
@@ -1,4 +1,4 @@
1
- import { isServerMode } from '@/const/version';
1
+ import { isDeprecatedEdition } from '@/const/version';
2
2
  import { aiModelSelectors, useAiInfraStore } from '@/store/aiInfra';
3
3
  import { useUserStore } from '@/store/user';
4
4
  import { modelProviderSelectors } from '@/store/user/selectors';
@@ -8,7 +8,7 @@ export const useModelContextWindowTokens = (model: string, provider: string) =>
8
8
 
9
9
  // TODO: remove this in V2.0
10
10
  const oldValue = useUserStore(modelProviderSelectors.modelMaxToken(model));
11
- if (!isServerMode) return oldValue;
11
+ if (isDeprecatedEdition) return oldValue;
12
12
  //
13
13
 
14
14
  return newValue as number;
@@ -1,4 +1,4 @@
1
- import { isServerMode } from '@/const/version';
1
+ import { isDeprecatedEdition } from '@/const/version';
2
2
  import { useAgentStore } from '@/store/agent';
3
3
  import { agentSelectors } from '@/store/agent/slices/chat';
4
4
  import { aiModelSelectors, useAiInfraStore } from '@/store/aiInfra';
@@ -12,7 +12,7 @@ export const useModelHasContextWindowToken = () => {
12
12
 
13
13
  // TODO: remove this in V2.0
14
14
  const oldValue = useUserStore(modelProviderSelectors.isModelHasMaxToken(model));
15
- if (!isServerMode) return oldValue;
15
+ if (isDeprecatedEdition) return oldValue;
16
16
  //
17
17
 
18
18
  return newValue;
@@ -1,4 +1,4 @@
1
- import { isServerMode } from '@/const/version';
1
+ import { isDeprecatedEdition } from '@/const/version';
2
2
  import { aiModelSelectors, useAiInfraStore } from '@/store/aiInfra';
3
3
  import { useUserStore } from '@/store/user';
4
4
  import { modelProviderSelectors } from '@/store/user/selectors';
@@ -8,7 +8,7 @@ export const useModelSupportToolUse = (model: string, provider: string) => {
8
8
 
9
9
  // TODO: remove this in V2.0
10
10
  const oldValue = useUserStore(modelProviderSelectors.isModelEnabledFunctionCall(model));
11
- if (!isServerMode) return oldValue;
11
+ if (isDeprecatedEdition) return oldValue;
12
12
  //
13
13
 
14
14
  return newValue;
@@ -1,4 +1,4 @@
1
- import { isServerMode } from '@/const/version';
1
+ import { isDeprecatedEdition } from '@/const/version';
2
2
  import { aiModelSelectors, useAiInfraStore } from '@/store/aiInfra';
3
3
  import { useUserStore } from '@/store/user';
4
4
  import { modelProviderSelectors } from '@/store/user/selectors';
@@ -8,7 +8,7 @@ export const useModelSupportVision = (model: string, provider: string) => {
8
8
 
9
9
  // TODO: remove this in V2.0
10
10
  const oldValue = useUserStore(modelProviderSelectors.isModelEnabledVision(model));
11
- if (!isServerMode) return oldValue;
11
+ if (isDeprecatedEdition) return oldValue;
12
12
  //
13
13
 
14
14
  return newValue;
@@ -9,7 +9,7 @@ import {
9
9
  LOBE_THEME_NEUTRAL_COLOR,
10
10
  LOBE_THEME_PRIMARY_COLOR,
11
11
  } from '@/const/theme';
12
- import DebugUI from '@/features/DebugUI';
12
+ import DevPanel from '@/features/DevPanel';
13
13
  import { getServerGlobalConfig } from '@/server/globalConfig';
14
14
  import { ServerConfigStoreProvider } from '@/store/serverConfig';
15
15
  import { getAntdLocale, parseBrowserLanguage } from '@/utils/locale';
@@ -65,9 +65,9 @@ const GlobalLayout = async ({ children }: PropsWithChildren) => {
65
65
  <Suspense>
66
66
  <StoreInitialization />
67
67
  <ReactScan />
68
+ {process.env.NODE_ENV === 'development' && <DevPanel />}
68
69
  </Suspense>
69
70
  </ServerConfigStoreProvider>
70
- <DebugUI />
71
71
  </AppTheme>
72
72
  <AntdV5MonkeyPatch />
73
73
  </Locale>
@@ -1,5 +1,5 @@
1
1
  import { JWTPayload, LOBE_CHAT_AUTH_HEADER } from '@/const/auth';
2
- import { isServerMode } from '@/const/version';
2
+ import { isDeprecatedEdition } from '@/const/version';
3
3
  import { ModelProvider } from '@/libs/agent-runtime';
4
4
  import { aiProviderSelectors, useAiInfraStore } from '@/store/aiInfra';
5
5
  import { useUserStore } from '@/store/user';
@@ -94,7 +94,7 @@ export const createPayloadWithKeyVaults = (provider: string) => {
94
94
  let keyVaults = {};
95
95
 
96
96
  // TODO: remove this condition in V2.0
97
- if (!isServerMode) {
97
+ if (isDeprecatedEdition) {
98
98
  keyVaults = keyVaultsConfigSelectors.getVaultByProvider(provider as any)(
99
99
  useUserStore.getState(),
100
100
  );
@@ -0,0 +1,60 @@
1
+ import { clientDB } from '@/database/client/db';
2
+ import { AiInfraRepos } from '@/database/repositories/aiInfra';
3
+ import { AiModelModel } from '@/database/server/models/aiModel';
4
+ import { BaseClientService } from '@/services/baseClientService';
5
+
6
+ import { IAiModelService } from './type';
7
+
8
+ export class ClientService extends BaseClientService implements IAiModelService {
9
+ private get aiModel(): AiModelModel {
10
+ return new AiModelModel(clientDB as any, this.userId);
11
+ }
12
+ private get aiInfraRepos(): AiInfraRepos {
13
+ return new AiInfraRepos(clientDB as any, this.userId, {});
14
+ }
15
+
16
+ createAiModel: IAiModelService['createAiModel'] = async (params) => {
17
+ const data = await this.aiModel.create(params);
18
+
19
+ return data?.id;
20
+ };
21
+
22
+ getAiProviderModelList: IAiModelService['getAiProviderModelList'] = async (id) => {
23
+ return this.aiInfraRepos.getAiProviderModelList(id);
24
+ };
25
+
26
+ getAiModelById: IAiModelService['getAiModelById'] = async (id) => {
27
+ return this.aiModel.findById(id);
28
+ };
29
+
30
+ toggleModelEnabled: IAiModelService['toggleModelEnabled'] = async (params) => {
31
+ return this.aiModel.toggleModelEnabled(params);
32
+ };
33
+
34
+ updateAiModel: IAiModelService['updateAiModel'] = async (id, providerId, value) => {
35
+ return this.aiModel.update(id, providerId, value);
36
+ };
37
+
38
+ batchUpdateAiModels: IAiModelService['batchUpdateAiModels'] = async (id, models) => {
39
+ return this.aiModel.batchUpdateAiModels(id, models);
40
+ };
41
+
42
+ batchToggleAiModels: IAiModelService['batchToggleAiModels'] = async (id, models, enabled) => {
43
+ return this.aiModel.batchToggleAiModels(id, models, enabled);
44
+ };
45
+
46
+ clearRemoteModels: IAiModelService['clearRemoteModels'] = async (providerId) => {
47
+ return this.aiModel.clearRemoteModels(providerId);
48
+ };
49
+
50
+ updateAiModelOrder: IAiModelService['updateAiModelOrder'] = async (providerId, items) => {
51
+ return this.aiModel.updateModelsOrder(providerId, items);
52
+ };
53
+
54
+ deleteAiModel: IAiModelService['deleteAiModel'] = async (params: {
55
+ id: string;
56
+ providerId: string;
57
+ }) => {
58
+ return this.aiModel.delete(params.id, params.providerId);
59
+ };
60
+ }
@@ -0,0 +1,10 @@
1
+ import { testService } from '~test-utils';
2
+
3
+ import { ClientService } from './client';
4
+ import { ServerService } from './server';
5
+
6
+ describe('aiModelService', () => {
7
+ testService(ServerService);
8
+
9
+ testService(ClientService);
10
+ });
@@ -0,0 +1,5 @@
1
+ import { ClientService } from './client';
2
+ import { ServerService } from './server';
3
+
4
+ export const aiModelService =
5
+ process.env.NEXT_PUBLIC_SERVICE_MODE === 'server' ? new ServerService() : new ClientService();
@@ -0,0 +1,47 @@
1
+ import { lambdaClient } from '@/libs/trpc/client';
2
+ import { IAiModelService } from '@/services/aiModel/type';
3
+
4
+ export class ServerService implements IAiModelService {
5
+ createAiModel: IAiModelService['createAiModel'] = async (params) => {
6
+ return lambdaClient.aiModel.createAiModel.mutate(params);
7
+ };
8
+
9
+ getAiProviderModelList: IAiModelService['getAiProviderModelList'] = async (id) => {
10
+ return lambdaClient.aiModel.getAiProviderModelList.query({ id });
11
+ };
12
+
13
+ getAiModelById: IAiModelService['getAiModelById'] = async (id) => {
14
+ return lambdaClient.aiModel.getAiModelById.query({ id });
15
+ };
16
+
17
+ toggleModelEnabled: IAiModelService['toggleModelEnabled'] = async (params) => {
18
+ return lambdaClient.aiModel.toggleModelEnabled.mutate(params);
19
+ };
20
+
21
+ updateAiModel: IAiModelService['updateAiModel'] = async (id, providerId, value) => {
22
+ return lambdaClient.aiModel.updateAiModel.mutate({ id, providerId, value });
23
+ };
24
+
25
+ batchUpdateAiModels: IAiModelService['batchUpdateAiModels'] = async (id, models) => {
26
+ return lambdaClient.aiModel.batchUpdateAiModels.mutate({ id, models });
27
+ };
28
+
29
+ batchToggleAiModels: IAiModelService['batchToggleAiModels'] = async (id, models, enabled) => {
30
+ return lambdaClient.aiModel.batchToggleAiModels.mutate({ enabled, id, models });
31
+ };
32
+
33
+ clearRemoteModels: IAiModelService['clearRemoteModels'] = async (providerId) => {
34
+ return lambdaClient.aiModel.clearRemoteModels.mutate({ providerId });
35
+ };
36
+
37
+ updateAiModelOrder: IAiModelService['updateAiModelOrder'] = async (providerId, items) => {
38
+ return lambdaClient.aiModel.updateAiModelOrder.mutate({ providerId, sortMap: items });
39
+ };
40
+
41
+ deleteAiModel: IAiModelService['deleteAiModel'] = async (params: {
42
+ id: string;
43
+ providerId: string;
44
+ }) => {
45
+ return lambdaClient.aiModel.removeAiModel.mutate(params);
46
+ };
47
+ }
@@ -0,0 +1,30 @@
1
+ /* eslint-disable typescript-sort-keys/interface */
2
+ import {
3
+ AiModelSortMap,
4
+ AiProviderModelListItem,
5
+ CreateAiModelParams,
6
+ ToggleAiModelEnableParams,
7
+ UpdateAiModelParams,
8
+ } from '@/types/aiModel';
9
+
10
+ export interface IAiModelService {
11
+ createAiModel: (params: CreateAiModelParams) => Promise<any>;
12
+
13
+ getAiProviderModelList: (id: string) => Promise<AiProviderModelListItem[]>;
14
+
15
+ getAiModelById: (id: string) => Promise<any>;
16
+
17
+ toggleModelEnabled: (params: ToggleAiModelEnableParams) => Promise<any>;
18
+
19
+ updateAiModel: (id: string, providerId: string, value: UpdateAiModelParams) => Promise<any>;
20
+
21
+ batchUpdateAiModels: (id: string, models: AiProviderModelListItem[]) => Promise<any>;
22
+
23
+ batchToggleAiModels: (id: string, models: string[], enabled: boolean) => Promise<any>;
24
+
25
+ clearRemoteModels: (providerId: string) => Promise<any>;
26
+
27
+ updateAiModelOrder: (providerId: string, items: AiModelSortMap[]) => Promise<any>;
28
+
29
+ deleteAiModel: (params: { id: string; providerId: string }) => Promise<any>;
30
+ }
@@ -0,0 +1,64 @@
1
+ import { clientDB } from '@/database/client/db';
2
+ import { AiInfraRepos } from '@/database/repositories/aiInfra';
3
+ import { AiProviderModel } from '@/database/server/models/aiProvider';
4
+ import { BaseClientService } from '@/services/baseClientService';
5
+
6
+ import { IAiProviderService } from './type';
7
+
8
+ export class ClientService extends BaseClientService implements IAiProviderService {
9
+ private get aiProviderModel(): AiProviderModel {
10
+ return new AiProviderModel(clientDB as any, this.userId);
11
+ }
12
+ private get aiInfraRepos(): AiInfraRepos {
13
+ let config = {};
14
+ if (typeof window !== 'undefined') {
15
+ config = window.global_serverConfigStore.getState().serverConfig.aiProvider || {};
16
+ }
17
+
18
+ return new AiInfraRepos(clientDB as any, this.userId, config);
19
+ }
20
+
21
+ createAiProvider: IAiProviderService['createAiProvider'] = async (params) => {
22
+ const data = await this.aiProviderModel.create(params);
23
+
24
+ return data?.id;
25
+ };
26
+
27
+ getAiProviderById: IAiProviderService['getAiProviderById'] = async (id) => {
28
+ return this.aiProviderModel.getAiProviderById(id);
29
+ };
30
+
31
+ getAiProviderList: IAiProviderService['getAiProviderList'] = async () => {
32
+ return await this.aiInfraRepos.getAiProviderList();
33
+ };
34
+
35
+ getAiProviderRuntimeState: IAiProviderService['getAiProviderRuntimeState'] = async () => {
36
+ const runtimeConfig = await this.aiProviderModel.getAiProviderRuntimeConfig();
37
+
38
+ const enabledAiProviders = await this.aiInfraRepos.getUserEnabledProviderList();
39
+
40
+ const enabledAiModels = await this.aiInfraRepos.getEnabledModels();
41
+
42
+ return { enabledAiModels, enabledAiProviders, runtimeConfig };
43
+ };
44
+
45
+ toggleProviderEnabled: IAiProviderService['toggleProviderEnabled'] = async (id, enabled) => {
46
+ return this.aiProviderModel.toggleProviderEnabled(id, enabled);
47
+ };
48
+
49
+ updateAiProvider: IAiProviderService['updateAiProvider'] = async (id, value) => {
50
+ return this.aiProviderModel.update(id, value);
51
+ };
52
+
53
+ updateAiProviderConfig: IAiProviderService['updateAiProviderConfig'] = async (id, value) => {
54
+ return this.aiProviderModel.updateConfig(id, value);
55
+ };
56
+
57
+ updateAiProviderOrder: IAiProviderService['updateAiProviderOrder'] = async (items) => {
58
+ return this.aiProviderModel.updateOrder(items);
59
+ };
60
+
61
+ deleteAiProvider: IAiProviderService['deleteAiProvider'] = async (id) => {
62
+ return this.aiProviderModel.delete(id);
63
+ };
64
+ }
@@ -0,0 +1,10 @@
1
+ import { testService } from '~test-utils';
2
+
3
+ import { ClientService } from './client';
4
+ import { ServerService } from './server';
5
+
6
+ describe('aiProviderService', () => {
7
+ testService(ServerService);
8
+
9
+ testService(ClientService);
10
+ });
@@ -0,0 +1,5 @@
1
+ import { ClientService } from './client';
2
+ import { ServerService } from './server';
3
+
4
+ export const aiProviderService =
5
+ process.env.NEXT_PUBLIC_SERVICE_MODE === 'server' ? new ServerService() : new ClientService();
@@ -0,0 +1,43 @@
1
+ import { lambdaClient } from '@/libs/trpc/client';
2
+
3
+ import { IAiProviderService } from './type';
4
+
5
+ export class ServerService implements IAiProviderService {
6
+ createAiProvider: IAiProviderService['createAiProvider'] = async (params) => {
7
+ return lambdaClient.aiProvider.createAiProvider.mutate(params);
8
+ };
9
+
10
+ getAiProviderList: IAiProviderService['getAiProviderList'] = async () => {
11
+ return lambdaClient.aiProvider.getAiProviderList.query();
12
+ };
13
+
14
+ getAiProviderById: IAiProviderService['getAiProviderById'] = async (id) => {
15
+ return lambdaClient.aiProvider.getAiProviderById.query({ id });
16
+ };
17
+
18
+ toggleProviderEnabled: IAiProviderService['toggleProviderEnabled'] = async (id, enabled) => {
19
+ return lambdaClient.aiProvider.toggleProviderEnabled.mutate({ enabled, id });
20
+ };
21
+
22
+ updateAiProvider: IAiProviderService['updateAiProvider'] = async (id, value) => {
23
+ return lambdaClient.aiProvider.updateAiProvider.mutate({ id, value });
24
+ };
25
+
26
+ updateAiProviderConfig: IAiProviderService['updateAiProviderConfig'] = async (id, value) => {
27
+ return lambdaClient.aiProvider.updateAiProviderConfig.mutate({ id, value });
28
+ };
29
+
30
+ updateAiProviderOrder: IAiProviderService['updateAiProviderOrder'] = async (items) => {
31
+ return lambdaClient.aiProvider.updateAiProviderOrder.mutate({ sortMap: items });
32
+ };
33
+
34
+ deleteAiProvider: IAiProviderService['deleteAiProvider'] = async (id) => {
35
+ return lambdaClient.aiProvider.removeAiProvider.mutate({ id });
36
+ };
37
+
38
+ getAiProviderRuntimeState: IAiProviderService['getAiProviderRuntimeState'] = async (
39
+ isLogin?: boolean,
40
+ ) => {
41
+ return lambdaClient.aiProvider.getAiProviderRuntimeState.query({ isLogin });
42
+ };
43
+ }
@@ -0,0 +1,26 @@
1
+ import {
2
+ AiProviderRuntimeState,
3
+ AiProviderSortMap,
4
+ CreateAiProviderParams,
5
+ UpdateAiProviderConfigParams,
6
+ } from '@/types/aiProvider';
7
+
8
+ export interface IAiProviderService {
9
+ createAiProvider: (params: CreateAiProviderParams) => Promise<any>;
10
+
11
+ deleteAiProvider: (id: string) => Promise<any>;
12
+
13
+ getAiProviderById: (id: string) => Promise<any>;
14
+
15
+ getAiProviderList: () => Promise<any>;
16
+
17
+ getAiProviderRuntimeState: (isLogin?: boolean) => Promise<AiProviderRuntimeState>;
18
+
19
+ toggleProviderEnabled: (id: string, enabled: boolean) => Promise<any>;
20
+
21
+ updateAiProvider: (id: string, value: any) => Promise<any>;
22
+
23
+ updateAiProviderConfig: (id: string, value: UpdateAiProviderConfigParams) => Promise<any>;
24
+
25
+ updateAiProviderOrder: (items: AiProviderSortMap[]) => Promise<any>;
26
+ }
@@ -7,7 +7,7 @@ import { INBOX_GUIDE_SYSTEMROLE } from '@/const/guide';
7
7
  import { INBOX_SESSION_ID } from '@/const/session';
8
8
  import { DEFAULT_AGENT_CONFIG } from '@/const/settings';
9
9
  import { TracePayload, TraceTagMap } from '@/const/trace';
10
- import { isServerMode } from '@/const/version';
10
+ import { isDeprecatedEdition, isServerMode } from '@/const/version';
11
11
  import {
12
12
  AgentRuntime,
13
13
  AgentRuntimeError,
@@ -42,7 +42,7 @@ import { API_ENDPOINTS } from './_url';
42
42
 
43
43
  const isCanUseFC = (model: string, provider: string) => {
44
44
  // TODO: remove isDeprecatedEdition condition in V2.0
45
- if (!isServerMode) {
45
+ if (isDeprecatedEdition) {
46
46
  return modelProviderSelectors.isModelEnabledFunctionCall(model)(useUserStore.getState());
47
47
  }
48
48
 
@@ -53,7 +53,7 @@ const findAzureDeploymentName = (model: string) => {
53
53
  let deploymentId = model;
54
54
 
55
55
  // TODO: remove isDeprecatedEdition condition in V2.0
56
- if (!isServerMode) {
56
+ if (isDeprecatedEdition) {
57
57
  const chatModelCards = modelProviderSelectors.getModelCardsById(ModelProvider.Azure)(
58
58
  useUserStore.getState(),
59
59
  );
@@ -74,7 +74,7 @@ const findAzureDeploymentName = (model: string) => {
74
74
 
75
75
  const isEnableFetchOnClient = (provider: string) => {
76
76
  // TODO: remove this condition in V2.0
77
- if (!isServerMode) {
77
+ if (isDeprecatedEdition) {
78
78
  return modelConfigSelectors.isProviderFetchOnClient(provider)(useUserStore.getState());
79
79
  } else {
80
80
  return aiProviderSelectors.isProviderFetchOnClient(provider)(useAiInfraStore.getState());
@@ -341,7 +341,7 @@ class ChatService {
341
341
  const isBuiltin = Object.values(ModelProvider).includes(provider as any);
342
342
 
343
343
  // TODO: remove `!isDeprecatedEdition` condition in V2.0
344
- if (isServerMode && !isBuiltin) {
344
+ if (!isDeprecatedEdition && !isBuiltin) {
345
345
  const providerConfig = aiProviderSelectors.providerConfigById(provider)(
346
346
  useAiInfraStore.getState(),
347
347
  );
@@ -0,0 +1,16 @@
1
+ import { clientDB } from '@/database/client/db';
2
+ import { TableViewerRepo } from '@/database/repositories/tableViewer';
3
+ import { BaseClientService } from '@/services/baseClientService';
4
+
5
+ export class ClientService extends BaseClientService {
6
+ private get tableViewerRepo(): TableViewerRepo {
7
+ return new TableViewerRepo(clientDB as any, this.userId);
8
+ }
9
+
10
+ getAllTables = async () => this.tableViewerRepo.getAllTables();
11
+
12
+ getTableDetails = async (tableName: string) => this.tableViewerRepo.getTableDetails(tableName);
13
+
14
+ getTableData = async (tableName: string) =>
15
+ this.tableViewerRepo.getTableData(tableName, { page: 1, pageSize: 300 });
16
+ }
@@ -0,0 +1,3 @@
1
+ import { ClientService } from './client';
2
+
3
+ export const tableViewerService = new ClientService();
@@ -3,7 +3,7 @@ import { SWRResponse, mutate } from 'swr';
3
3
  import { StateCreator } from 'zustand/vanilla';
4
4
 
5
5
  import { DEFAULT_MODEL_PROVIDER_LIST } from '@/config/modelProviders';
6
- import { isServerMode } from '@/const/version';
6
+ import { isDeprecatedEdition } from '@/const/version';
7
7
  import { useClientDataSWR } from '@/libs/swr';
8
8
  import { aiProviderService } from '@/services/aiProvider';
9
9
  import { AiInfraStore } from '@/store/aiInfra/store';
@@ -170,7 +170,7 @@ export const createAiProviderSlice: StateCreator<
170
170
 
171
171
  useFetchAiProviderRuntimeState: (isLogin) =>
172
172
  useClientDataSWR<AiProviderRuntimeState | undefined>(
173
- isServerMode ? [AiProviderSwrKey.fetchAiProviderRuntimeState, isLogin] : null,
173
+ !isDeprecatedEdition ? [AiProviderSwrKey.fetchAiProviderRuntimeState, isLogin] : null,
174
174
  async ([, isLogin]) => {
175
175
  if (isLogin) return aiProviderService.getAiProviderRuntimeState();
176
176
 
@@ -24,7 +24,13 @@ export interface GlobalServerConfig {
24
24
  defaultAgent?: DeepPartial<UserDefaultAgent>;
25
25
  enableUploadFileToServer?: boolean;
26
26
  enabledAccessCode?: boolean;
27
+ /**
28
+ * @deprecated
29
+ */
27
30
  enabledOAuthSSO?: boolean;
31
+ /**
32
+ * @deprecated
33
+ */
28
34
  languageModel?: ServerLanguageModel;
29
35
  oAuthSSOProviders?: string[];
30
36
  systemAgent?: DeepPartial<UserSystemAgentConfig>;
@@ -0,0 +1,30 @@
1
+ export interface TableBasicInfo {
2
+ count: number;
3
+ name: string;
4
+ type: 'BASE TABLE' | 'VIEW';
5
+ }
6
+
7
+ export interface TableColumnInfo {
8
+ defaultValue?: string;
9
+ foreignKey?: {
10
+ column: string;
11
+ table: string;
12
+ };
13
+ isPrimaryKey: boolean;
14
+ name: string;
15
+ nullable: boolean;
16
+ type: string;
17
+ }
18
+
19
+ export interface PaginationParams {
20
+ page: number;
21
+ pageSize: number;
22
+ sortBy?: string;
23
+ sortOrder?: 'asc' | 'desc';
24
+ }
25
+
26
+ export interface FilterCondition {
27
+ column: string;
28
+ operator: 'equals' | 'contains' | 'startsWith' | 'endsWith';
29
+ value: any;
30
+ }
package/tests/utils.tsx CHANGED
@@ -9,3 +9,49 @@ const swrConfig = {
9
9
  export const withSWR = ({ children }: PropsWithChildren) => (
10
10
  <SWRConfig value={swrConfig}>{children}</SWRConfig>
11
11
  );
12
+
13
+ interface TestServiceOptions {
14
+ /** 是否检查 async */
15
+ checkAsync?: boolean;
16
+ /** 自定义的额外检查 */
17
+ extraChecks?: (method: string, func: () => any) => void;
18
+ /** 是否跳过某些方法 */
19
+ skipMethods?: string[];
20
+ }
21
+
22
+ const builtinSkipProps = new Set(['userId']);
23
+
24
+ export const testService = (ServiceClass: new () => any, options: TestServiceOptions = {}) => {
25
+ const { checkAsync = true, skipMethods = ['userId'], extraChecks } = options;
26
+
27
+ describe(ServiceClass.name, () => {
28
+ it('should implement all methods as arrow functions', () => {
29
+ const service = new ServiceClass();
30
+
31
+ const methods = Object.getOwnPropertyNames(service).filter(
32
+ (method) => !builtinSkipProps.has(method) || !skipMethods.includes(method),
33
+ );
34
+
35
+ methods.forEach((method) => {
36
+ const func = service[method];
37
+ // 检查是否为函数
38
+ expect(typeof func).toBe('function');
39
+
40
+ const funcString = func.toString();
41
+
42
+ // 验证是否是箭头函数
43
+ expect(funcString).toContain('=>');
44
+
45
+ // 可选的 async 检查
46
+ if (checkAsync) {
47
+ expect(funcString).toMatch(/^async.*=>/);
48
+ }
49
+
50
+ // 运行额外的自定义检查
51
+ if (extraChecks) {
52
+ extraChecks(method, func);
53
+ }
54
+ });
55
+ });
56
+ });
57
+ };