@lobehub/lobehub 2.0.0-next.22 → 2.0.0-next.24

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 (54) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/apps/desktop/package.json +1 -1
  3. package/changelog/v1.json +18 -0
  4. package/locales/ar/labs.json +4 -0
  5. package/locales/bg-BG/labs.json +4 -0
  6. package/locales/de-DE/labs.json +4 -0
  7. package/locales/en-US/labs.json +4 -0
  8. package/locales/es-ES/labs.json +4 -0
  9. package/locales/fa-IR/labs.json +4 -0
  10. package/locales/fr-FR/labs.json +4 -0
  11. package/locales/it-IT/labs.json +4 -0
  12. package/locales/ja-JP/labs.json +4 -0
  13. package/locales/ko-KR/labs.json +4 -0
  14. package/locales/nl-NL/labs.json +4 -0
  15. package/locales/pl-PL/labs.json +4 -0
  16. package/locales/pt-BR/labs.json +4 -0
  17. package/locales/ru-RU/labs.json +4 -0
  18. package/locales/tr-TR/labs.json +4 -0
  19. package/locales/vi-VN/labs.json +4 -0
  20. package/locales/zh-CN/labs.json +4 -0
  21. package/locales/zh-TW/labs.json +4 -0
  22. package/package.json +1 -1
  23. package/packages/const/src/user.ts +5 -2
  24. package/packages/memory-extract/package.json +1 -1
  25. package/packages/types/src/index.ts +0 -1
  26. package/packages/types/src/message/ui/params.ts +3 -3
  27. package/packages/types/src/user/index.ts +2 -88
  28. package/packages/types/src/user/preference.ts +105 -0
  29. package/renovate.json +44 -13
  30. package/src/app/[variants]/(main)/labs/components/LabCard.tsx +5 -5
  31. package/src/app/[variants]/(main)/labs/page.tsx +18 -22
  32. package/src/app/[variants]/(main)/settings/provider/detail/azure/index.tsx +1 -1
  33. package/src/app/[variants]/(main)/settings/provider/detail/azureai/index.tsx +1 -1
  34. package/src/app/[variants]/(main)/settings/provider/detail/bedrock/index.tsx +1 -1
  35. package/src/app/[variants]/(main)/settings/provider/detail/cloudflare/index.tsx +1 -1
  36. package/src/app/[variants]/(main)/settings/provider/detail/comfyui/index.tsx +1 -1
  37. package/src/app/[variants]/(main)/settings/provider/detail/github/index.tsx +1 -1
  38. package/src/app/[variants]/(main)/settings/provider/detail/vertexai/index.tsx +1 -1
  39. package/src/app/[variants]/(main)/settings/provider/features/ProviderConfig/index.tsx +2 -4
  40. package/src/components/Skeleton/SkeletonSwitch.tsx +13 -0
  41. package/src/components/Skeleton/index.ts +2 -0
  42. package/src/features/ChatInput/ActionBar/index.tsx +2 -2
  43. package/src/features/ChatInput/InputEditor/index.tsx +2 -2
  44. package/src/locales/default/labs.ts +4 -0
  45. package/src/server/routers/lambda/message.ts +5 -20
  46. package/src/server/services/mcp/deps/MCPSystemDepsCheckService.test.ts +541 -0
  47. package/src/services/message/server.ts +0 -10
  48. package/src/services/message/type.ts +0 -2
  49. package/src/store/user/selectors.ts +1 -1
  50. package/src/store/user/slices/preference/action.ts +8 -1
  51. package/src/store/user/slices/preference/selectors/index.ts +2 -0
  52. package/src/store/user/slices/preference/selectors/labPrefer.ts +13 -0
  53. package/src/store/user/slices/preference/{selectors.ts → selectors/preference.ts} +0 -2
  54. /package/src/{app/[variants]/(main)/settings/provider/features/ProviderConfig → components/Skeleton}/SkeletonInput.tsx +0 -0
@@ -6,6 +6,8 @@ import Image from 'next/image';
6
6
  import { PropsWithChildren, memo } from 'react';
7
7
  import { Flexbox } from 'react-layout-kit';
8
8
 
9
+ import { SkeletonSwitch } from '@/components/Skeleton';
10
+
9
11
  interface LabCardProps {
10
12
  checked: boolean;
11
13
  cover?: string;
@@ -80,11 +82,9 @@ const LabCard = memo<PropsWithChildren<LabCardProps>>(
80
82
  <div className={styles.desc}>{desc}</div>
81
83
  {meta ? <div className={styles.meta}>{meta}</div> : null}
82
84
  </Flexbox>
83
- {!loading && (
84
- <Flexbox align={'flex-end'} height={'100%'} justify={'center'} paddingInline={8}>
85
- <Switch checked={checked} onChange={onChange} />
86
- </Flexbox>
87
- )}
85
+ <Flexbox align={'flex-end'} height={'100%'} justify={'center'} paddingInline={8}>
86
+ {loading ? <SkeletonSwitch /> : <Switch checked={checked} onChange={onChange} />}
87
+ </Flexbox>
88
88
  </div>
89
89
  </div>
90
90
  </div>
@@ -1,11 +1,11 @@
1
1
  'use client';
2
2
 
3
- import { memo, useCallback } from 'react';
3
+ import { memo } from 'react';
4
4
  import { useTranslation } from 'react-i18next';
5
5
  import { Flexbox } from 'react-layout-kit';
6
6
 
7
7
  import { useUserStore } from '@/store/user';
8
- import { preferenceSelectors } from '@/store/user/selectors';
8
+ import { labPreferSelectors, preferenceSelectors } from '@/store/user/selectors';
9
9
 
10
10
  import Hero from './components/Hero';
11
11
  import LabCard from './components/LabCard';
@@ -15,7 +15,6 @@ interface LabItem {
15
15
  cover?: string;
16
16
  desc: string;
17
17
  key: string;
18
- onChange: (checked: boolean) => void;
19
18
  title: string;
20
19
  }
21
20
 
@@ -24,40 +23,37 @@ const LabsPage = memo(() => {
24
23
 
25
24
  const [
26
25
  isPreferenceInit,
27
- inputMarkdownRender,
26
+ enableInputMarkdown,
27
+ // enableAssistantMessageGroup,
28
28
  // enableGroupChat,
29
- updatePreference,
29
+ updateLab,
30
30
  ] = useUserStore((s) => [
31
31
  preferenceSelectors.isPreferenceInit(s),
32
- preferenceSelectors.inputMarkdownRender(s),
33
- // preferenceSelectors.enableGroupChat(s),
34
- s.updatePreference,
32
+ labPreferSelectors.enableInputMarkdown(s),
33
+ // labPreferSelectors.enableAssistantMessageGroup(s),
34
+ // labPreferSelectors.enableGroupChat(s),
35
+ s.updateLab,
35
36
  ]);
36
37
 
37
- const onToggleMarkdown = useCallback(
38
- (checked: boolean) => updatePreference({ disableInputMarkdownRender: !checked }),
39
- [updatePreference],
40
- );
41
- // const onToggleGroupChat = useCallback(
42
- // (checked: boolean) => updatePreference({ enableGroupChat: checked }),
43
- // [updatePreference],
44
- // );
45
-
46
38
  const labItems: LabItem[] = [
47
39
  {
48
- checked: inputMarkdownRender,
40
+ checked: enableInputMarkdown,
49
41
  cover: 'https://github.com/user-attachments/assets/0527a966-3d95-46b4-b880-c0f3fca18f02',
50
42
  desc: t('features.inputMarkdown.desc'),
51
- key: 'inputMarkdown',
52
- onChange: onToggleMarkdown,
43
+ key: 'enableInputMarkdown',
53
44
  title: t('features.inputMarkdown.title'),
54
45
  },
55
46
  // {
47
+ // checked: enableAssistantMessageGroup,
48
+ // desc: t('features.assistantMessageGroup.desc'),
49
+ // key: 'enableAssistantMessageGroup',
50
+ // title: t('features.assistantMessageGroup.title'),
51
+ // },
52
+ // {
56
53
  // checked: enableGroupChat,
57
54
  // cover: 'https://github.com/user-attachments/assets/72894d24-a96a-4d7c-a823-ff9e6a1a8b6d',
58
55
  // desc: t('features.groupChat.desc'),
59
56
  // key: 'groupChat',
60
- // onChange: onToggleGroupChat,
61
57
  // title: t('features.groupChat.title'),
62
58
  // },
63
59
  ];
@@ -73,7 +69,7 @@ const LabsPage = memo(() => {
73
69
  desc={item.desc}
74
70
  key={item.key}
75
71
  loading={!isPreferenceInit}
76
- onChange={item.onChange}
72
+ onChange={(checked: boolean) => updateLab({ [item.key]: checked })}
77
73
  title={item.title}
78
74
  />
79
75
  ))}
@@ -6,11 +6,11 @@ import { ModelProvider } from 'model-bank';
6
6
  import { useTranslation } from 'react-i18next';
7
7
 
8
8
  import { FormInput, FormPassword } from '@/components/FormInput';
9
+ import { SkeletonInput } from '@/components/Skeleton';
9
10
  import { AzureProviderCard } from '@/config/modelProviders';
10
11
  import { aiModelSelectors, aiProviderSelectors, useAiInfraStore } from '@/store/aiInfra';
11
12
 
12
13
  import { KeyVaultsConfigKey, LLMProviderApiTokenKey, LLMProviderBaseUrlKey } from '../../const';
13
- import { SkeletonInput } from '../../features/ProviderConfig';
14
14
  import { ProviderItem } from '../../type';
15
15
  import ProviderDetail from '../default';
16
16
 
@@ -4,11 +4,11 @@ import { ModelProvider } from 'model-bank';
4
4
  import { useTranslation } from 'react-i18next';
5
5
 
6
6
  import { FormInput, FormPassword } from '@/components/FormInput';
7
+ import { SkeletonInput } from '@/components/Skeleton';
7
8
  import { AzureAIProviderCard } from '@/config/modelProviders';
8
9
  import { aiProviderSelectors, useAiInfraStore } from '@/store/aiInfra';
9
10
 
10
11
  import { KeyVaultsConfigKey, LLMProviderApiTokenKey, LLMProviderBaseUrlKey } from '../../const';
11
- import { SkeletonInput } from '../../features/ProviderConfig';
12
12
  import { ProviderItem } from '../../type';
13
13
  import ProviderDetail from '../default';
14
14
 
@@ -4,12 +4,12 @@ import { Select } from '@lobehub/ui';
4
4
  import { useTranslation } from 'react-i18next';
5
5
 
6
6
  import { FormPassword } from '@/components/FormInput';
7
+ import { SkeletonInput } from '@/components/Skeleton';
7
8
  import { BedrockProviderCard } from '@/config/modelProviders';
8
9
  import { aiProviderSelectors, useAiInfraStore } from '@/store/aiInfra';
9
10
  import { GlobalLLMProviderKey } from '@/types/user/settings';
10
11
 
11
12
  import { KeyVaultsConfigKey } from '../../const';
12
- import { SkeletonInput } from '../../features/ProviderConfig';
13
13
  import { ProviderItem } from '../../type';
14
14
  import ProviderDetail from '../default';
15
15
 
@@ -3,12 +3,12 @@
3
3
  import { useTranslation } from 'react-i18next';
4
4
 
5
5
  import { FormInput, FormPassword } from '@/components/FormInput';
6
+ import { SkeletonInput } from '@/components/Skeleton';
6
7
  import { CloudflareProviderCard } from '@/config/modelProviders';
7
8
  import { aiProviderSelectors, useAiInfraStore } from '@/store/aiInfra';
8
9
  import { GlobalLLMProviderKey } from '@/types/user/settings';
9
10
 
10
11
  import { KeyVaultsConfigKey } from '../../const';
11
- import { SkeletonInput } from '../../features/ProviderConfig';
12
12
  import { ProviderItem } from '../../type';
13
13
  import ProviderDetail from '../default';
14
14
 
@@ -5,12 +5,12 @@ import { useTranslation } from 'react-i18next';
5
5
 
6
6
  import { FormInput, FormPassword } from '@/components/FormInput';
7
7
  import KeyValueEditor from '@/components/KeyValueEditor';
8
+ import { SkeletonInput } from '@/components/Skeleton';
8
9
  import { ComfyUIProviderCard } from '@/config/modelProviders';
9
10
  import { aiProviderSelectors, useAiInfraStore } from '@/store/aiInfra';
10
11
  import { GlobalLLMProviderKey } from '@/types/user/settings';
11
12
 
12
13
  import { KeyVaultsConfigKey } from '../../const';
13
- import { SkeletonInput } from '../../features/ProviderConfig';
14
14
  import { ProviderItem } from '../../type';
15
15
  import ProviderDetail from '../default';
16
16
 
@@ -5,12 +5,12 @@ import { createStyles } from 'antd-style';
5
5
  import { useTranslation } from 'react-i18next';
6
6
 
7
7
  import { FormPassword } from '@/components/FormInput';
8
+ import { SkeletonInput } from '@/components/Skeleton';
8
9
  import { GithubProviderCard } from '@/config/modelProviders';
9
10
  import { aiProviderSelectors, useAiInfraStore } from '@/store/aiInfra';
10
11
  import { GlobalLLMProviderKey } from '@/types/user/settings';
11
12
 
12
13
  import { KeyVaultsConfigKey, LLMProviderApiTokenKey } from '../../const';
13
- import { SkeletonInput } from '../../features/ProviderConfig';
14
14
  import { ProviderItem } from '../../type';
15
15
  import ProviderDetail from '../default';
16
16
 
@@ -5,12 +5,12 @@ import { createStyles } from 'antd-style';
5
5
  import { useTranslation } from 'react-i18next';
6
6
 
7
7
  import { FormPassword } from '@/components/FormInput';
8
+ import { SkeletonInput } from '@/components/Skeleton';
8
9
  import { VertexAIProviderCard } from '@/config/modelProviders';
9
10
  import { aiProviderSelectors, useAiInfraStore } from '@/store/aiInfra';
10
11
  import { GlobalLLMProviderKey } from '@/types/user/settings';
11
12
 
12
13
  import { KeyVaultsConfigKey, LLMProviderApiTokenKey } from '../../const';
13
- import { SkeletonInput } from '../../features/ProviderConfig';
14
14
  import { ProviderItem } from '../../type';
15
15
  import ProviderDetail from '../default';
16
16
 
@@ -21,6 +21,7 @@ import urlJoin from 'url-join';
21
21
  import { z } from 'zod';
22
22
 
23
23
  import { FormInput, FormPassword } from '@/components/FormInput';
24
+ import { SkeletonInput, SkeletonSwitch } from '@/components/Skeleton';
24
25
  import { FORM_STYLE } from '@/const/layoutTokens';
25
26
  import { AES_GCM_URL, BASE_PROVIDER_DOC_URL } from '@/const/url';
26
27
  import { isDesktop, isServerMode } from '@/const/version';
@@ -34,7 +35,6 @@ import {
34
35
  import { KeyVaultsConfigKey, LLMProviderApiTokenKey, LLMProviderBaseUrlKey } from '../../const';
35
36
  import Checker, { CheckErrorRender } from './Checker';
36
37
  import EnableSwitch from './EnableSwitch';
37
- import { SkeletonInput } from './SkeletonInput';
38
38
  import UpdateProviderInfo from './UpdateProviderInfo';
39
39
 
40
40
  const useStyles = createStyles(({ css, prefixCls, responsive, token }) => ({
@@ -298,7 +298,7 @@ const ProviderConfig = memo<ProviderConfigProps>(
298
298
  (showApiKey && isProviderApiKeyNotEmpty));
299
299
  const clientFetchItem = showClientFetch && {
300
300
  children: isLoading ? (
301
- <Skeleton.Button active className={styles.switchLoading} />
301
+ <SkeletonSwitch />
302
302
  ) : (
303
303
  <Switch checked={isFetchOnClient} disabled={configUpdating} />
304
304
  ),
@@ -424,5 +424,3 @@ const ProviderConfig = memo<ProviderConfigProps>(
424
424
  );
425
425
 
426
426
  export default ProviderConfig;
427
-
428
- export { SkeletonInput } from './SkeletonInput';
@@ -0,0 +1,13 @@
1
+ import { Skeleton } from 'antd';
2
+ import { css, cx } from 'antd-style';
3
+
4
+ const switchLoading = cx(css`
5
+ width: 44px !important;
6
+ min-width: 44px !important;
7
+ height: 22px !important;
8
+ border-radius: 12px !important;
9
+ `);
10
+
11
+ export const SkeletonSwitch = () => {
12
+ return <Skeleton.Button active className={switchLoading} />;
13
+ };
@@ -0,0 +1,2 @@
1
+ export * from './SkeletonInput';
2
+ export * from './SkeletonSwitch';
@@ -4,7 +4,7 @@ import { memo, useMemo } from 'react';
4
4
  import { useGlobalStore } from '@/store/global';
5
5
  import { systemStatusSelectors } from '@/store/global/selectors';
6
6
  import { useUserStore } from '@/store/user';
7
- import { preferenceSelectors } from '@/store/user/slices/preference/selectors';
7
+ import { labPreferSelectors } from '@/store/user/slices/preference/selectors';
8
8
 
9
9
  import { ActionKeys, actionMap } from '../ActionBar/config';
10
10
  import { useChatInputStore } from '../store';
@@ -44,7 +44,7 @@ const ActionToolbar = memo(() => {
44
44
  systemStatusSelectors.expandInputActionbar(s),
45
45
  s.toggleExpandInputActionbar,
46
46
  ]);
47
- const enableRichRender = useUserStore(preferenceSelectors.inputMarkdownRender);
47
+ const enableRichRender = useUserStore(labPreferSelectors.enableInputMarkdown);
48
48
 
49
49
  const leftActions = useChatInputStore((s) =>
50
50
  s.leftActions.filter((item) => (enableRichRender ? true : item !== 'typo')),
@@ -21,7 +21,7 @@ import { useHotkeysContext } from 'react-hotkeys-hook';
21
21
  import { useTranslation } from 'react-i18next';
22
22
 
23
23
  import { useUserStore } from '@/store/user';
24
- import { preferenceSelectors, settingsSelectors } from '@/store/user/selectors';
24
+ import { labPreferSelectors, preferenceSelectors, settingsSelectors } from '@/store/user/selectors';
25
25
 
26
26
  import { useChatInputStore, useStoreApi } from '../store';
27
27
  import Placeholder from './Placeholder';
@@ -69,7 +69,7 @@ const InputEditor = memo<{ defaultRows?: number }>(({ defaultRows = 2 }) => {
69
69
  };
70
70
  }, [state.isEmpty]);
71
71
 
72
- const enableRichRender = useUserStore(preferenceSelectors.inputMarkdownRender);
72
+ const enableRichRender = useUserStore(labPreferSelectors.enableInputMarkdown);
73
73
 
74
74
  const richRenderProps = useMemo(
75
75
  () =>
@@ -1,6 +1,10 @@
1
1
  export default {
2
2
  desc: '这里会不定期更新我们正在探索的新功能,欢迎试用!',
3
3
  features: {
4
+ assistantMessageGroup: {
5
+ desc: '将助手消息及其工具调用结果聚合到分组里显示',
6
+ title: '助手消息聚合分组',
7
+ },
4
8
  groupChat: {
5
9
  desc: '启用多智能体群聊编排能力。',
6
10
  title: '群聊(多智能体)',
@@ -1,7 +1,6 @@
1
1
  import {
2
2
  CreateMessageParamsSchema,
3
3
  CreateNewMessageParamsSchema,
4
- UIChatMessage,
5
4
  UpdateMessageParamsSchema,
6
5
  UpdateMessageRAGParamsSchema,
7
6
  } from '@lobechat/types';
@@ -14,8 +13,6 @@ import { authedProcedure, publicProcedure, router } from '@/libs/trpc/lambda';
14
13
  import { serverDatabase } from '@/libs/trpc/lambda/middleware';
15
14
  import { FileService } from '@/server/services/file';
16
15
 
17
- type ChatMessageList = UIChatMessage[];
18
-
19
16
  const messageProcedure = authedProcedure.use(serverDatabase).use(async (opts) => {
20
17
  const { ctx } = opts;
21
18
 
@@ -72,22 +69,6 @@ export const messageRouter = router({
72
69
  });
73
70
  }),
74
71
 
75
- // TODO: it will be removed in V2
76
- getAllMessages: messageProcedure.query(async ({ ctx }): Promise<ChatMessageList> => {
77
- return ctx.messageModel.queryAll() as any;
78
- }),
79
-
80
- // TODO: it will be removed in V2
81
- getAllMessagesInSession: messageProcedure
82
- .input(
83
- z.object({
84
- sessionId: z.string().nullable().optional(),
85
- }),
86
- )
87
- .query(async ({ ctx, input }): Promise<ChatMessageList> => {
88
- return ctx.messageModel.queryBySessionId(input.sessionId) as any;
89
- }),
90
-
91
72
  getHeatmaps: messageProcedure.query(async ({ ctx }) => {
92
73
  return ctx.messageModel.getHeatmaps();
93
74
  }),
@@ -101,16 +82,20 @@ export const messageRouter = router({
101
82
  pageSize: z.number().optional(),
102
83
  sessionId: z.string().nullable().optional(),
103
84
  topicId: z.string().nullable().optional(),
85
+ useGroup: z.boolean().optional(),
104
86
  }),
105
87
  )
106
88
  .query(async ({ input, ctx }) => {
107
89
  if (!ctx.userId) return [];
108
90
  const serverDB = await getServerDB();
109
91
 
92
+ const { useGroup, ...queryParams } = input;
93
+
110
94
  const messageModel = new MessageModel(serverDB, ctx.userId);
111
95
  const fileService = new FileService(serverDB, ctx.userId);
112
96
 
113
- return messageModel.query(input, {
97
+ return messageModel.query(queryParams, {
98
+ groupAssistantMessages: useGroup ?? false,
114
99
  postProcessUrl: (path) => fileService.getFullFileUrl(path),
115
100
  });
116
101
  }),