@lobehub/chat 0.114.8 → 0.115.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 (129) hide show
  1. package/.i18nrc.js +3 -0
  2. package/CHANGELOG.md +50 -0
  3. package/locales/de-DE/common.json +20 -0
  4. package/locales/de-DE/tool.json +8 -0
  5. package/locales/en-US/common.json +20 -0
  6. package/locales/en-US/tool.json +8 -0
  7. package/locales/es-ES/common.json +20 -0
  8. package/locales/es-ES/tool.json +8 -0
  9. package/locales/fr-FR/common.json +20 -0
  10. package/locales/fr-FR/tool.json +8 -0
  11. package/locales/it-IT/chat.json +77 -0
  12. package/locales/it-IT/common.json +113 -0
  13. package/locales/it-IT/empty.json +6 -0
  14. package/locales/it-IT/error.json +53 -0
  15. package/locales/it-IT/market.json +30 -0
  16. package/locales/it-IT/migration.json +45 -0
  17. package/locales/it-IT/plugin.json +166 -0
  18. package/locales/it-IT/setting.json +306 -0
  19. package/locales/it-IT/tool.json +8 -0
  20. package/locales/it-IT/welcome.json +14 -0
  21. package/locales/ja-JP/common.json +20 -0
  22. package/locales/ja-JP/tool.json +8 -0
  23. package/locales/ko-KR/common.json +20 -0
  24. package/locales/ko-KR/tool.json +8 -0
  25. package/locales/nl-NL/chat.json +77 -0
  26. package/locales/nl-NL/common.json +113 -0
  27. package/locales/nl-NL/empty.json +6 -0
  28. package/locales/nl-NL/error.json +53 -0
  29. package/locales/nl-NL/market.json +30 -0
  30. package/locales/nl-NL/migration.json +45 -0
  31. package/locales/nl-NL/plugin.json +166 -0
  32. package/locales/nl-NL/setting.json +306 -0
  33. package/locales/nl-NL/tool.json +8 -0
  34. package/locales/nl-NL/welcome.json +14 -0
  35. package/locales/pl-PL/chat.json +77 -0
  36. package/locales/pl-PL/common.json +113 -0
  37. package/locales/pl-PL/empty.json +6 -0
  38. package/locales/pl-PL/error.json +53 -0
  39. package/locales/pl-PL/market.json +30 -0
  40. package/locales/pl-PL/migration.json +45 -0
  41. package/locales/pl-PL/plugin.json +166 -0
  42. package/locales/pl-PL/setting.json +306 -0
  43. package/locales/pl-PL/tool.json +8 -0
  44. package/locales/pl-PL/welcome.json +14 -0
  45. package/locales/pt-BR/common.json +20 -0
  46. package/locales/pt-BR/tool.json +8 -0
  47. package/locales/ru-RU/common.json +20 -0
  48. package/locales/ru-RU/tool.json +8 -0
  49. package/locales/tr-TR/common.json +20 -0
  50. package/locales/tr-TR/tool.json +8 -0
  51. package/locales/zh-CN/common.json +20 -0
  52. package/locales/zh-CN/plugin.json +2 -2
  53. package/locales/zh-CN/setting.json +1 -1
  54. package/locales/zh-CN/tool.json +8 -0
  55. package/locales/zh-TW/common.json +20 -0
  56. package/locales/zh-TW/tool.json +8 -0
  57. package/package.json +1 -1
  58. package/src/app/api/openai/images/createImageGeneration.ts +26 -0
  59. package/src/app/api/openai/images/route.ts +16 -0
  60. package/src/app/api/proxy/route.ts +3 -1
  61. package/src/app/chat/(desktop)/features/ChatHeader.tsx +56 -56
  62. package/src/app/chat/features/ChatHeader/PluginTag/PluginStatus.tsx +7 -4
  63. package/src/app/chat/features/ChatHeader/PluginTag/index.tsx +3 -3
  64. package/src/app/chat/features/SystemRole/index.tsx +0 -1
  65. package/src/components/GalleyGrid/Grid.tsx +36 -0
  66. package/src/components/GalleyGrid/index.tsx +58 -0
  67. package/src/components/GalleyGrid/style.ts +24 -0
  68. package/src/const/settings.ts +7 -0
  69. package/src/database/schemas/files.ts +1 -0
  70. package/src/database/schemas/message.ts +1 -1
  71. package/src/features/AgentSetting/AgentPlugin/index.tsx +2 -2
  72. package/src/features/ChatInput/ActionBar/ModelSwitch.tsx +2 -1
  73. package/src/features/ChatInput/ActionBar/Token/TokenTag.tsx +3 -3
  74. package/src/features/ChatInput/ActionBar/Tools/index.tsx +26 -14
  75. package/src/features/Conversation/ChatList/Messages/Function.tsx +1 -1
  76. package/src/features/Conversation/ChatList/Plugins/Inspector/index.tsx +4 -2
  77. package/src/features/Conversation/ChatList/Plugins/Render/BuiltinType/index.tsx +36 -0
  78. package/src/features/Conversation/ChatList/Plugins/Render/DefaultType/index.tsx +6 -66
  79. package/src/features/Conversation/ChatList/Plugins/Render/Loading.tsx +61 -0
  80. package/src/features/Conversation/ChatList/Plugins/Render/index.tsx +21 -12
  81. package/src/features/Conversation/ChatList/Plugins/Render/useParseContent.ts +15 -0
  82. package/src/locales/default/common.ts +20 -0
  83. package/src/locales/default/index.ts +2 -0
  84. package/src/locales/default/plugin.ts +2 -2
  85. package/src/locales/default/setting.ts +1 -1
  86. package/src/locales/default/tool.ts +9 -0
  87. package/src/locales/options.ts +14 -2
  88. package/src/services/__tests__/chat.test.ts +132 -2
  89. package/src/services/_url.ts +1 -0
  90. package/src/services/chat.ts +3 -5
  91. package/src/services/file.ts +20 -5
  92. package/src/services/imageGeneration.ts +26 -0
  93. package/src/store/chat/actions/enhance.ts +65 -1
  94. package/src/store/chat/actions/plugin.test.ts +9 -9
  95. package/src/store/chat/actions/plugin.ts +38 -6
  96. package/src/store/chat/initialState.ts +3 -0
  97. package/src/store/chat/selectors/enchance.ts +11 -0
  98. package/src/store/chat/selectors/index.ts +1 -0
  99. package/src/store/global/selectors/__snapshots__/settings.test.ts.snap +5 -0
  100. package/src/store/global/selectors/settings.ts +5 -0
  101. package/src/store/tool/helpers.ts +6 -5
  102. package/src/store/tool/initialState.ts +3 -1
  103. package/src/store/tool/selectors/index.ts +5 -0
  104. package/src/store/tool/selectors/tool.test.ts +128 -0
  105. package/src/store/tool/selectors/tool.ts +125 -0
  106. package/src/store/tool/slices/builtin/action.ts +54 -0
  107. package/src/store/tool/slices/builtin/index.ts +2 -0
  108. package/src/store/tool/slices/builtin/initialState.ts +13 -0
  109. package/src/store/tool/slices/builtin/selectors.test.ts +70 -0
  110. package/src/store/tool/slices/builtin/selectors.ts +20 -0
  111. package/src/store/tool/slices/customPlugin/action.ts +0 -3
  112. package/src/store/tool/slices/plugin/selectors.test.ts +0 -69
  113. package/src/store/tool/slices/plugin/selectors.ts +1 -84
  114. package/src/store/tool/store.ts +8 -2
  115. package/src/tools/dalle/Render/EditMode.tsx +66 -0
  116. package/src/tools/dalle/Render/Item.tsx +97 -0
  117. package/src/tools/dalle/Render/ToolBar.tsx +54 -0
  118. package/src/tools/dalle/Render/index.tsx +21 -0
  119. package/src/tools/dalle/index.ts +81 -0
  120. package/src/tools/index.ts +11 -0
  121. package/src/tools/renders.ts +8 -0
  122. package/src/types/chatMessage.ts +1 -1
  123. package/src/types/openai/image.ts +34 -0
  124. package/src/types/settings.ts +6 -0
  125. package/src/types/tool/builtin.ts +36 -0
  126. package/src/types/tool/dalle.ts +12 -0
  127. package/src/types/tool/index.ts +5 -1
  128. package/src/types/tool/tool.ts +9 -0
  129. package/src/store/tool/selectors.ts +0 -3
@@ -129,8 +129,8 @@
129
129
  },
130
130
  "pluginList": "插件列表",
131
131
  "plugins": {
132
- "loading": "插件检测中...",
133
- "unknown": "未知插件"
132
+ "loading": "工具检测中...",
133
+ "unknown": "未知工具"
134
134
  },
135
135
  "setting": "插件设置",
136
136
  "settings": {
@@ -293,7 +293,7 @@
293
293
  },
294
294
  "tools": {
295
295
  "builtins": {
296
- "groupName": "内置"
296
+ "groupName": "内置扩展"
297
297
  },
298
298
  "plugins": {
299
299
  "enabled": "已启用 {{num}}",
@@ -0,0 +1,8 @@
1
+ {
2
+ "dalle": {
3
+ "autoGenerate": "自动生成",
4
+ "generate": "生成",
5
+ "generating": "生成中...",
6
+ "images": "图片:"
7
+ }
8
+ }
@@ -50,16 +50,36 @@
50
50
  "title": "匯入資料"
51
51
  },
52
52
  "lang": {
53
+ "bn": "孟加拉語",
54
+ "cs-CZ": "捷克語",
55
+ "da-DK": "丹麥語",
53
56
  "de-DE": "德語",
57
+ "el-GR": "希臘語",
54
58
  "en": "英文",
55
59
  "en-US": "英文",
56
60
  "es-ES": "西班牙語",
61
+ "fi-FI": "芬蘭語",
57
62
  "fr-FR": "法語",
63
+ "hi-IN": "印地語",
64
+ "hu-HU": "匈牙利語",
65
+ "id-ID": "印尼語",
66
+ "it-IT": "義大利語",
58
67
  "ja-JP": "日文",
59
68
  "ko-KR": "韓文",
69
+ "nl-NL": "荷蘭語",
70
+ "no-NO": "挪威語",
71
+ "pl-PL": "波蘭語",
60
72
  "pt-BR": "葡萄牙語",
73
+ "pt-PT": "葡萄牙語",
74
+ "ro-RO": "羅馬尼亞語",
61
75
  "ru-RU": "俄文",
76
+ "sk-SK": "斯洛伐克語",
77
+ "sr-RS": "塞爾維亞語",
78
+ "sv-SE": "瑞典語",
79
+ "th-TH": "泰語",
62
80
  "tr-TR": "土耳其",
81
+ "uk-UA": "烏克蘭語",
82
+ "vi-VN": "越南語",
63
83
  "zh": "簡體中文",
64
84
  "zh-CN": "簡體中文",
65
85
  "zh-TW": "繁體中文"
@@ -0,0 +1,8 @@
1
+ {
2
+ "dalle": {
3
+ "autoGenerate": "自動生成",
4
+ "generate": "生成",
5
+ "generating": "生成中...",
6
+ "images": "圖片:"
7
+ }
8
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "0.114.8",
3
+ "version": "0.115.0",
4
4
  "description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
5
5
  "keywords": [
6
6
  "framework",
@@ -0,0 +1,26 @@
1
+ import OpenAI from 'openai';
2
+
3
+ import { OpenAIImagePayload } from '@/types/openai/image';
4
+
5
+ export const createImageGeneration = async ({
6
+ openai,
7
+ payload,
8
+ }: {
9
+ openai: OpenAI;
10
+ payload: OpenAIImagePayload;
11
+ }) => {
12
+ const res = await openai.images.generate({ ...payload, response_format: 'url' });
13
+
14
+ const urls = res.data.map((o) => o.url) as string[];
15
+
16
+ return new Response(JSON.stringify(urls));
17
+ };
18
+
19
+ // const mockImages = [
20
+ // 'https://github-production-user-asset-6210df.s3.amazonaws.com/28616219/292159272-032d5c8b-20be-48d9-8dbb-f2491f231bac.png',
21
+ // 'https://github-production-user-asset-6210df.s3.amazonaws.com/28616219/292159798-cad89421-20c5-44b0-a337-fcbb857f1f70.png',
22
+ // 'https://github-production-user-asset-6210df.s3.amazonaws.com/28616219/292160015-2263156f-d41f-48ae-9c2c-d96799b9d2b8.png',
23
+ // 'https://github-production-user-asset-6210df.s3.amazonaws.com/28616219/292160229-592d112f-5dfd-47d7-98d3-44bc09dc91f7.png',
24
+ // ];
25
+ // export const createImageGeneration = async () =>
26
+ // new Response(JSON.stringify([mockImages[Math.round(Math.random() * 3)]]));
@@ -0,0 +1,16 @@
1
+ import { OpenAIImagePayload } from '@/types/openai/image';
2
+
3
+ import { createBizOpenAI } from '../createBizOpenAI';
4
+ import { createImageGeneration } from './createImageGeneration';
5
+
6
+ export const runtime = 'edge';
7
+
8
+ export const POST = async (req: Request) => {
9
+ const payload = (await req.json()) as OpenAIImagePayload;
10
+
11
+ const openaiOrErrResponse = createBizOpenAI(req, payload.model);
12
+ // if resOrOpenAI is a Response, it means there is an error,just return it
13
+ if (openaiOrErrResponse instanceof Response) return openaiOrErrResponse;
14
+
15
+ return createImageGeneration({ openai: openaiOrErrResponse, payload });
16
+ };
@@ -6,5 +6,7 @@ export const runtime = 'edge';
6
6
  export const POST = async (req: Request) => {
7
7
  const url = await req.text();
8
8
 
9
- return fetch(url);
9
+ const res = await fetch(url);
10
+
11
+ return new Response(res.body, { headers: res.headers });
10
12
  };
@@ -17,12 +17,11 @@ import PluginTag from '../../features/ChatHeader/PluginTag';
17
17
  import SettingButton from '../../features/ChatHeader/SettingButton';
18
18
  import ShareButton from '../../features/ChatHeader/ShareButton';
19
19
 
20
- const Header = memo(() => {
20
+ const Left = memo(() => {
21
+ const { t } = useTranslation('chat');
21
22
  const init = useSessionChatInit();
22
23
  const router = useRouter();
23
24
 
24
- const { t } = useTranslation('chat');
25
-
26
25
  const [isInbox, title, description, avatar, backgroundColor, model, plugins] = useSessionStore(
27
26
  (s) => [
28
27
  sessionSelectors.isInboxSession(s),
@@ -35,66 +34,67 @@ const Header = memo(() => {
35
34
  ],
36
35
  );
37
36
 
37
+ const displayTitle = isInbox ? t('inbox.title') : title;
38
+ const displayDesc = isInbox ? t('inbox.desc') : description;
39
+
40
+ return !init ? (
41
+ <Flexbox horizontal>
42
+ <Skeleton
43
+ active
44
+ avatar={{ shape: 'circle', size: 'default' }}
45
+ paragraph={false}
46
+ title={{ style: { margin: 0, marginTop: 8 }, width: 200 }}
47
+ />
48
+ </Flexbox>
49
+ ) : (
50
+ <Flexbox align={'flex-start'} gap={12} horizontal>
51
+ <Avatar
52
+ avatar={avatar}
53
+ background={backgroundColor}
54
+ onClick={() =>
55
+ isInbox
56
+ ? router.push('/settings/agent')
57
+ : router.push(pathString('/chat/settings', { hash: location.hash }))
58
+ }
59
+ size={40}
60
+ title={title}
61
+ />
62
+ <ChatHeaderTitle
63
+ desc={displayDesc}
64
+ tag={
65
+ <>
66
+ <Tag icon={<SiOpenai size={'1em'} />}>{model}</Tag>
67
+ {plugins?.length > 0 && <PluginTag plugins={plugins} />}
68
+ </>
69
+ }
70
+ title={displayTitle}
71
+ />
72
+ </Flexbox>
73
+ );
74
+ });
75
+
76
+ const Right = memo(() => {
77
+ const { t } = useTranslation('chat');
78
+
38
79
  const [showAgentSettings, toggleConfig] = useGlobalStore((s) => [
39
80
  s.preference.showChatSideBar,
40
81
  s.toggleChatSideBar,
41
82
  ]);
42
83
 
43
- const displayTitle = isInbox ? t('inbox.title') : title;
44
- const displayDesc = isInbox ? t('inbox.desc') : description;
45
-
46
84
  return (
47
- <ChatHeader
48
- left={
49
- !init ? (
50
- <Flexbox horizontal>
51
- <Skeleton
52
- active
53
- avatar={{ shape: 'circle', size: 'default' }}
54
- paragraph={false}
55
- title={{ style: { margin: 0, marginTop: 8 }, width: 200 }}
56
- />
57
- </Flexbox>
58
- ) : (
59
- <Flexbox align={'flex-start'} gap={12} horizontal>
60
- <Avatar
61
- avatar={avatar}
62
- background={backgroundColor}
63
- onClick={() =>
64
- isInbox
65
- ? router.push('/settings/agent')
66
- : router.push(pathString('/chat/settings', { hash: location.hash }))
67
- }
68
- size={40}
69
- title={title}
70
- />
71
- <ChatHeaderTitle
72
- desc={displayDesc}
73
- tag={
74
- <>
75
- <Tag icon={<SiOpenai size={'1em'} />}>{model}</Tag>
76
- {plugins?.length > 0 && <PluginTag plugins={plugins} />}
77
- </>
78
- }
79
- title={displayTitle}
80
- />
81
- </Flexbox>
82
- )
83
- }
84
- right={
85
- <>
86
- <ShareButton />
87
- <ActionIcon
88
- icon={showAgentSettings ? PanelRightClose : PanelRightOpen}
89
- onClick={() => toggleConfig()}
90
- size={DESKTOP_HEADER_ICON_SIZE}
91
- title={t('roleAndArchive')}
92
- />
93
- <SettingButton />
94
- </>
95
- }
96
- />
85
+ <>
86
+ <ShareButton />
87
+ <ActionIcon
88
+ icon={showAgentSettings ? PanelRightClose : PanelRightOpen}
89
+ onClick={() => toggleConfig()}
90
+ size={DESKTOP_HEADER_ICON_SIZE}
91
+ title={t('roleAndArchive')}
92
+ />
93
+ <SettingButton />
94
+ </>
97
95
  );
98
96
  });
99
97
 
98
+ const Header = memo(() => <ChatHeader left={<Left />} right={<Right />} />);
99
+
100
100
  export default Header;
@@ -1,5 +1,6 @@
1
1
  import { ActionIcon } from '@lobehub/ui';
2
2
  import { Badge, Button, Tag } from 'antd';
3
+ import isEqual from 'fast-deep-equal';
3
4
  import { LucideRotateCw, LucideTrash2, RotateCwIcon } from 'lucide-react';
4
5
  import { memo, useMemo } from 'react';
5
6
  import { useTranslation } from 'react-i18next';
@@ -8,7 +9,7 @@ import { Flexbox } from 'react-layout-kit';
8
9
  import ManifestPreviewer from '@/components/ManifestPreviewer';
9
10
  import { useSessionStore } from '@/store/session';
10
11
  import { useToolStore } from '@/store/tool';
11
- import { customPluginSelectors, pluginSelectors } from '@/store/tool/selectors';
12
+ import { customPluginSelectors, toolSelectors } from '@/store/tool/selectors';
12
13
 
13
14
  interface PluginStatusProps {
14
15
  deprecated?: boolean;
@@ -18,12 +19,12 @@ interface PluginStatusProps {
18
19
  const PluginStatus = memo<PluginStatusProps>(({ title, id, deprecated }) => {
19
20
  const { t } = useTranslation('common');
20
21
  const [status, isCustom, reinstallCustomPlugin] = useToolStore((s) => [
21
- pluginSelectors.getPluginManifestLoadingStatus(id)(s),
22
+ toolSelectors.getManifestLoadingStatus(id)(s),
22
23
  customPluginSelectors.isCustomPlugin(id)(s),
23
24
  s.reinstallCustomPlugin,
24
25
  ]);
25
26
 
26
- const manifest = useToolStore(pluginSelectors.getPluginManifestById(id));
27
+ const manifest = useToolStore(toolSelectors.getManifestById(id), isEqual);
27
28
 
28
29
  const removePlugin = useSessionStore((s) => s.removePlugin);
29
30
 
@@ -44,8 +45,10 @@ const PluginStatus = memo<PluginStatusProps>(({ title, id, deprecated }) => {
44
45
  />
45
46
  );
46
47
  }
48
+
49
+ default:
47
50
  case 'success': {
48
- return <Badge status={status} />;
51
+ return <Badge status={'success'} />;
49
52
  }
50
53
  }
51
54
  }, [status]);
@@ -6,7 +6,7 @@ import { LucideToyBrick } from 'lucide-react';
6
6
  import { memo } from 'react';
7
7
 
8
8
  import { pluginHelpers, useToolStore } from '@/store/tool';
9
- import { pluginSelectors } from '@/store/tool/selectors';
9
+ import { toolSelectors } from '@/store/tool/selectors';
10
10
 
11
11
  import PluginStatus from './PluginStatus';
12
12
 
@@ -15,8 +15,8 @@ export interface PluginTagProps {
15
15
  }
16
16
 
17
17
  const PluginTag = memo<PluginTagProps>(({ plugins }) => {
18
- const list = useToolStore(pluginSelectors.installedPluginMetaList, isEqual);
19
- const displayPlugin = useToolStore(pluginSelectors.getPluginMetaById(plugins[0]), isEqual);
18
+ const list = useToolStore(toolSelectors.metaList, isEqual);
19
+ const displayPlugin = useToolStore(toolSelectors.getMetaById(plugins[0]), isEqual);
20
20
 
21
21
  if (plugins.length === 0) return null;
22
22
 
@@ -78,7 +78,6 @@ const SystemRole = memo(() => {
78
78
  <EditableMessage
79
79
  classNames={{ markdown: styles.prompt }}
80
80
  editing={editing}
81
- height={editing ? 'calc(70vh - 108px)' : 'calc(70vh - 357.43px)'}
82
81
  model={{
83
82
  extra: (
84
83
  <AgentInfo
@@ -0,0 +1,36 @@
1
+ import { CSSProperties, ReactNode, memo } from 'react';
2
+ import { Flexbox } from 'react-layout-kit';
3
+
4
+ import { MAX_SIZE_DESKTOP, MIN_IMAGE_SIZE, useStyles } from './style';
5
+
6
+ interface GridProps {
7
+ children: ReactNode;
8
+ className?: string;
9
+ col?: number;
10
+ gap?: number;
11
+ max?: number;
12
+ min?: number;
13
+ style?: CSSProperties;
14
+ }
15
+
16
+ const Grid = memo<GridProps>(
17
+ ({
18
+ gap = 4,
19
+ col = 3,
20
+ max = MAX_SIZE_DESKTOP,
21
+ min = MIN_IMAGE_SIZE,
22
+ children,
23
+ className,
24
+ style,
25
+ }) => {
26
+ const { styles, cx } = useStyles({ col, gap, max, min });
27
+
28
+ return (
29
+ <Flexbox className={cx(styles.container, className)} gap={gap} horizontal style={style}>
30
+ {children}
31
+ </Flexbox>
32
+ );
33
+ },
34
+ );
35
+
36
+ export default Grid;
@@ -0,0 +1,58 @@
1
+ import { useResponsive } from 'antd-style';
2
+ import { ReactNode, memo, useMemo } from 'react';
3
+ import { Flexbox } from 'react-layout-kit';
4
+
5
+ import Grid from './Grid';
6
+ import { MAX_SIZE_DESKTOP, MAX_SIZE_MOBILE } from './style';
7
+
8
+ interface GalleyGridProps<T = any> {
9
+ items: T[];
10
+ renderItem: (props: T) => ReactNode;
11
+ }
12
+
13
+ const GalleyGrid = memo<GalleyGridProps>(({ items, renderItem: Render }) => {
14
+ const { mobile } = useResponsive();
15
+
16
+ const { firstRow, lastRow } = useMemo(() => {
17
+ if (items.length === 4) {
18
+ return {
19
+ firstRow: items.slice(0, 2),
20
+ lastRow: items.slice(2, 4),
21
+ };
22
+ }
23
+
24
+ const firstCol = items.length % 3 === 0 ? 3 : items.length % 3;
25
+
26
+ return {
27
+ firstRow: items.slice(0, firstCol),
28
+ lastRow: items.slice(firstCol, items.length),
29
+ };
30
+ }, [items]);
31
+
32
+ const { gap, max } = useMemo(
33
+ () => ({
34
+ gap: mobile ? 4 : 6,
35
+ max: mobile ? MAX_SIZE_MOBILE : MAX_SIZE_DESKTOP,
36
+ }),
37
+ [mobile],
38
+ );
39
+
40
+ return (
41
+ <Flexbox gap={gap}>
42
+ <Grid col={firstRow.length} gap={gap} max={max}>
43
+ {firstRow.map((i, index) => (
44
+ <Render {...i} key={index} />
45
+ ))}
46
+ </Grid>
47
+ {lastRow.length > 0 && (
48
+ <Grid col={lastRow.length > 2 ? 3 : lastRow.length} gap={gap} max={max}>
49
+ {lastRow.map((i, index) => (
50
+ <Render {...i} key={index} />
51
+ ))}
52
+ </Grid>
53
+ )}
54
+ </Flexbox>
55
+ );
56
+ });
57
+
58
+ export default GalleyGrid;
@@ -0,0 +1,24 @@
1
+ import { createStyles } from 'antd-style';
2
+
3
+ export const MIN_IMAGE_SIZE = 64;
4
+ export const MAX_SIZE_DESKTOP = 640;
5
+ export const MAX_SIZE_MOBILE = 280;
6
+ export const useStyles = createStyles(
7
+ ({ css }, { col, gap, max, min }: { col: number; gap: number; max: number; min: number }) => ({
8
+ container: css`
9
+ display: grid;
10
+ grid-gap: ${gap}px;
11
+ grid-template-columns: repeat(${col}, 1fr);
12
+
13
+ width: 100%;
14
+ max-width: ${max}px;
15
+
16
+ & > div {
17
+ width: 100%;
18
+ min-width: ${min}px;
19
+ min-height: ${min}px;
20
+ max-height: ${(max - gap * (col - 1)) / col}px;
21
+ }
22
+ `,
23
+ }),
24
+ );
@@ -72,9 +72,16 @@ export const DEFAULT_TTS_CONFIG: GlobalTTSConfig = {
72
72
  sttServer: 'openai',
73
73
  };
74
74
 
75
+ export const DEFAULT_TOOL_CONFIG = {
76
+ dalle: {
77
+ autoGenerate: false,
78
+ },
79
+ };
80
+
75
81
  export const DEFAULT_SETTINGS: GlobalSettings = {
76
82
  defaultAgent: DEFAULT_AGENT,
77
83
  languageModel: DEFAULT_LLM_CONFIG,
84
+ tool: DEFAULT_TOOL_CONFIG,
78
85
  tts: DEFAULT_TTS_CONFIG,
79
86
  ...DEFAULT_BASE_SETTINGS,
80
87
  };
@@ -14,6 +14,7 @@ export const DB_FileSchema = z.object({
14
14
  * @example 'image/png'
15
15
  */
16
16
  fileType: z.string(),
17
+ metadata: z.any().optional(),
17
18
  /**
18
19
  * file name
19
20
  * @example 'test.png'
@@ -11,7 +11,7 @@ const PluginSchema = z.object({
11
11
  identifier: z.string(),
12
12
  arguments: z.string(),
13
13
  apiName: z.string(),
14
- type: z.enum(['default', 'standalone']).default('default'),
14
+ type: z.enum(['default', 'standalone', 'builtin']).default('default'),
15
15
  });
16
16
 
17
17
  export const DB_MessageSchema = z.object({
@@ -10,7 +10,7 @@ import { FORM_STYLE } from '@/const/layoutTokens';
10
10
  import PluginStore from '@/features/PluginStore';
11
11
  import PluginTag from '@/features/PluginStore/PluginItem/PluginTag';
12
12
  import { pluginHelpers, useToolStore } from '@/store/tool';
13
- import { pluginSelectors } from '@/store/tool/selectors';
13
+ import { toolSelectors } from '@/store/tool/selectors';
14
14
 
15
15
  import { useStore } from '../store';
16
16
  import AddPluginButton from './AddPluginButton';
@@ -28,7 +28,7 @@ const AgentPlugin = memo(() => {
28
28
  s.toggleAgentPlugin,
29
29
  ]);
30
30
 
31
- const installedPlugins = useToolStore(pluginSelectors.installedPluginMetaList, isEqual);
31
+ const installedPlugins = useToolStore(toolSelectors.metaList, isEqual);
32
32
  const useFetchInstalledPlugins = useToolStore((s) => s.useFetchInstalledPlugins);
33
33
 
34
34
  const { isLoading } = useFetchInstalledPlugins();
@@ -1,5 +1,6 @@
1
1
  import { ActionIcon } from '@lobehub/ui';
2
2
  import { Dropdown } from 'antd';
3
+ import isEqual from 'fast-deep-equal';
3
4
  import { BrainCog } from 'lucide-react';
4
5
  import { memo } from 'react';
5
6
  import { useTranslation } from 'react-i18next';
@@ -16,7 +17,7 @@ const ModelSwitch = memo(() => {
16
17
  return [agentSelectors.currentAgentModel(s), s.updateAgentConfig];
17
18
  });
18
19
 
19
- const modelList = useGlobalStore(settingsSelectors.modelList);
20
+ const modelList = useGlobalStore(settingsSelectors.modelList, isEqual);
20
21
 
21
22
  return (
22
23
  <Dropdown
@@ -9,7 +9,7 @@ import { chatSelectors } from '@/store/chat/selectors';
9
9
  import { useSessionStore } from '@/store/session';
10
10
  import { agentSelectors } from '@/store/session/selectors';
11
11
  import { useToolStore } from '@/store/tool';
12
- import { pluginSelectors } from '@/store/tool/selectors';
12
+ import { toolSelectors } from '@/store/tool/selectors';
13
13
  import { LanguageModel } from '@/types/llm';
14
14
 
15
15
  const Token = memo(() => {
@@ -28,8 +28,8 @@ const Token = memo(() => {
28
28
  const plugins = useSessionStore(agentSelectors.currentAgentPlugins);
29
29
 
30
30
  const toolsString = useToolStore((s) => {
31
- const pluginSystemRoles = pluginSelectors.enabledPluginsSystemRoles(plugins)(s);
32
- const schemaNumber = pluginSelectors
31
+ const pluginSystemRoles = toolSelectors.enabledSystemRoles(plugins)(s);
32
+ const schemaNumber = toolSelectors
33
33
  .enabledSchema(plugins)(s)
34
34
  .map((i) => JSON.stringify(i))
35
35
  .join('');
@@ -11,7 +11,7 @@ import PluginStore from '@/features/PluginStore';
11
11
  import { useSessionStore } from '@/store/session';
12
12
  import { agentSelectors } from '@/store/session/selectors';
13
13
  import { pluginHelpers, useToolStore } from '@/store/tool';
14
- import { pluginSelectors } from '@/store/tool/selectors';
14
+ import { builtinToolSelectors, pluginSelectors } from '@/store/tool/selectors';
15
15
 
16
16
  import ToolItem from './ToolItem';
17
17
 
@@ -31,7 +31,15 @@ const useStyles = createStyles(({ css, prefixCls }) => ({
31
31
  const Tools = memo(() => {
32
32
  const { t } = useTranslation('setting');
33
33
  const list = useToolStore(pluginSelectors.installedPluginMetaList, isEqual);
34
- const enablePluginCount = useSessionStore((s) => agentSelectors.currentAgentPlugins(s).length);
34
+ const builtinList = useToolStore(builtinToolSelectors.metaList, isEqual);
35
+
36
+ const enablePluginCount = useSessionStore(
37
+ (s) =>
38
+ agentSelectors
39
+ .currentAgentPlugins(s)
40
+ .filter((i) => !builtinList.some((b) => b.identifier === i)).length,
41
+ );
42
+
35
43
  const [open, setOpen] = useState(false);
36
44
  const { styles } = useStyles();
37
45
 
@@ -42,18 +50,22 @@ const Tools = memo(() => {
42
50
  menu={{
43
51
  className: styles.menu,
44
52
  items: [
45
- // {
46
- // children: [
47
- // {
48
- // icon: <Icon icon={PaletteIcon} size={{ fontSize: 16 }} style={{ padding: 4 }} />,
49
- // key: 'dalle3',
50
- // label: <ToolItem identifier={'dalle3'} label={'DALL·E 3'} />,
51
- // },
52
- // ],
53
- // key: 'builtins',
54
- // label: t('tools.builtins.groupName'),
55
- // type: 'group',
56
- // },
53
+ {
54
+ children: builtinList.map((item) => ({
55
+ icon: <Avatar avatar={item.meta.avatar} size={24} />,
56
+ key: item.identifier,
57
+ label: (
58
+ <ToolItem
59
+ identifier={item.identifier}
60
+ label={item.meta?.title || item.identifier}
61
+ />
62
+ ),
63
+ })),
64
+
65
+ key: 'builtins',
66
+ label: t('tools.builtins.groupName'),
67
+ type: 'group',
68
+ },
57
69
  {
58
70
  children: [
59
71
  ...list.map((item) => ({
@@ -24,8 +24,8 @@ export const FunctionMessage = memo<ChatMessage>(({ id, content, plugin }) => {
24
24
  <PluginRender
25
25
  content={content}
26
26
  id={id}
27
+ identifier={plugin?.identifier}
27
28
  loading={fcProps.loading}
28
- name={plugin?.identifier}
29
29
  payload={fcProps.command}
30
30
  type={fcProps.type}
31
31
  />
@@ -15,7 +15,7 @@ import { useTranslation } from 'react-i18next';
15
15
  import { Flexbox } from 'react-layout-kit';
16
16
 
17
17
  import { pluginHelpers, useToolStore } from '@/store/tool';
18
- import { pluginSelectors } from '@/store/tool/selectors';
18
+ import { pluginSelectors, toolSelectors } from '@/store/tool/selectors';
19
19
 
20
20
  import PluginResult from './PluginResultJSON';
21
21
  import Settings from './Settings';
@@ -47,9 +47,11 @@ const Inspector = memo<InspectorProps>(
47
47
  const { styles } = useStyles();
48
48
  const [open, setOpen] = useState(false);
49
49
 
50
- const pluginMeta = useToolStore(pluginSelectors.getPluginMetaById(id), isEqual);
50
+ const pluginMeta = useToolStore(toolSelectors.getMetaById(id), isEqual);
51
+
51
52
  const showRightAction = useToolStore(pluginSelectors.isPluginHasUI(id));
52
53
  const pluginAvatar = pluginHelpers.getPluginAvatar(pluginMeta);
54
+
53
55
  const pluginTitle = pluginHelpers.getPluginTitle(pluginMeta) ?? t('plugins.loading');
54
56
 
55
57
  const avatar = pluginAvatar ? (