@lobehub/chat 1.1.16 → 1.1.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/locales/ar/plugin.json +1 -0
- package/locales/ar/portal.json +4 -0
- package/locales/bg-BG/plugin.json +1 -0
- package/locales/bg-BG/portal.json +4 -0
- package/locales/de-DE/plugin.json +1 -0
- package/locales/de-DE/portal.json +4 -0
- package/locales/en-US/plugin.json +1 -0
- package/locales/en-US/portal.json +4 -0
- package/locales/es-ES/plugin.json +1 -0
- package/locales/es-ES/portal.json +4 -0
- package/locales/fr-FR/plugin.json +1 -0
- package/locales/fr-FR/portal.json +4 -0
- package/locales/it-IT/plugin.json +1 -0
- package/locales/it-IT/portal.json +4 -0
- package/locales/ja-JP/plugin.json +1 -0
- package/locales/ja-JP/portal.json +4 -0
- package/locales/ko-KR/plugin.json +1 -0
- package/locales/ko-KR/portal.json +4 -0
- package/locales/nl-NL/plugin.json +1 -0
- package/locales/nl-NL/portal.json +4 -0
- package/locales/pl-PL/plugin.json +1 -0
- package/locales/pl-PL/portal.json +4 -0
- package/locales/pt-BR/plugin.json +1 -0
- package/locales/pt-BR/portal.json +4 -0
- package/locales/ru-RU/plugin.json +1 -0
- package/locales/ru-RU/portal.json +4 -0
- package/locales/tr-TR/plugin.json +1 -0
- package/locales/tr-TR/portal.json +4 -0
- package/locales/vi-VN/plugin.json +1 -0
- package/locales/vi-VN/portal.json +4 -0
- package/locales/zh-CN/plugin.json +1 -0
- package/locales/zh-CN/portal.json +4 -0
- package/locales/zh-TW/plugin.json +1 -0
- package/locales/zh-TW/portal.json +4 -0
- package/package.json +1 -1
- package/src/app/(main)/chat/(workspace)/@portal/_layout/Desktop.tsx +17 -0
- package/src/app/(main)/chat/(workspace)/@portal/_layout/Mobile.tsx +18 -0
- package/src/app/(main)/chat/(workspace)/@portal/default.tsx +27 -0
- package/src/app/(main)/chat/(workspace)/@portal/features/Header.tsx +52 -0
- package/src/app/(main)/chat/(workspace)/@portal/features/Tools/ToolList/Item/index.tsx +74 -0
- package/src/app/(main)/chat/(workspace)/@portal/features/Tools/ToolList/Item/style.ts +46 -0
- package/src/app/(main)/chat/(workspace)/@portal/features/Tools/ToolList/index.tsx +39 -0
- package/src/app/(main)/chat/(workspace)/@portal/features/Tools/ToolUI/Footer.tsx +33 -0
- package/src/app/(main)/chat/(workspace)/@portal/features/Tools/ToolUI/ToolRender.tsx +50 -0
- package/src/app/(main)/chat/(workspace)/@portal/features/Tools/ToolUI/index.tsx +37 -0
- package/src/app/(main)/chat/(workspace)/@portal/index.tsx +18 -0
- package/src/app/(main)/chat/(workspace)/_layout/Desktop/Portal.tsx +79 -0
- package/src/app/(main)/chat/(workspace)/_layout/Desktop/TopicPanel.tsx +27 -22
- package/src/app/(main)/chat/(workspace)/_layout/Desktop/index.tsx +3 -1
- package/src/app/(main)/chat/(workspace)/_layout/type.ts +1 -0
- package/src/app/(main)/chat/loading.tsx +2 -20
- package/src/components/CircleLoading/index.tsx +21 -0
- package/src/const/layoutTokens.test.ts +11 -0
- package/src/const/layoutTokens.ts +4 -0
- package/src/const/message.ts +0 -14
- package/src/database/client/models/__tests__/message.test.ts +9 -12
- package/src/database/client/models/message.ts +6 -0
- package/src/database/server/models/__tests__/message.test.ts +70 -0
- package/src/database/server/models/message.ts +10 -0
- package/src/database/server/schemas/lobechat.ts +3 -1
- package/src/features/Conversation/Messages/Assistant/ToolCalls/index.tsx +3 -17
- package/src/features/Conversation/Messages/Tool/Inspector/index.tsx +26 -2
- package/src/features/Conversation/Messages/Tool/index.tsx +33 -6
- package/src/features/Conversation/Messages/components/Arguments.tsx +1 -1
- package/src/features/PluginAvatar/index.tsx +28 -0
- package/src/features/PluginsUI/Render/BuiltinType/index.tsx +44 -0
- package/src/features/{Conversation/Plugins → PluginsUI}/Render/StandaloneType/index.tsx +8 -1
- package/src/features/{Conversation/Plugins → PluginsUI}/Render/index.tsx +12 -1
- package/src/{features/Conversation/Messages/hooks → hooks}/useYamlArguments.ts +3 -1
- package/src/locales/default/index.ts +2 -0
- package/src/locales/default/plugin.ts +1 -0
- package/src/locales/default/portal.ts +4 -0
- package/src/server/routers/lambda/message.ts +12 -0
- package/src/services/message/client.test.ts +35 -0
- package/src/services/message/client.ts +6 -0
- package/src/services/message/server.ts +9 -0
- package/src/store/chat/initialState.ts +10 -1
- package/src/store/chat/selectors.ts +1 -0
- package/src/store/chat/slices/message/action.test.ts +1 -1
- package/src/store/chat/slices/message/action.ts +5 -5
- package/src/store/chat/slices/message/reducer.test.ts +230 -7
- package/src/store/chat/slices/message/reducer.ts +45 -22
- package/src/store/chat/slices/message/selectors.test.ts +133 -2
- package/src/store/chat/slices/message/selectors.ts +7 -0
- package/src/store/chat/slices/plugin/action.test.ts +309 -2
- package/src/store/chat/slices/plugin/action.ts +51 -1
- package/src/store/chat/slices/portal/action.test.ts +109 -0
- package/src/store/chat/slices/portal/action.ts +31 -0
- package/src/store/chat/slices/portal/initialState.ts +8 -0
- package/src/store/chat/slices/portal/selectors.test.ts +73 -0
- package/src/store/chat/slices/portal/selectors.ts +15 -0
- package/src/store/chat/store.ts +7 -1
- package/src/store/tool/selectors/tool.test.ts +14 -0
- package/src/store/tool/selectors/tool.ts +13 -0
- package/src/tools/docks.ts +3 -0
- package/src/types/tool/builtin.ts +11 -1
- package/src/utils/safeParseJSON.test.ts +71 -0
- package/src/utils/safeParseJSON.ts +12 -0
- package/src/const/message.test.ts +0 -55
- package/src/features/Conversation/Plugins/Render/BuiltinType/index.tsx +0 -30
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/BuiltinType/index.test.tsx +0 -0
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/DefaultType/IFrameRender/index.tsx +0 -0
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/DefaultType/SystemJsRender/index.tsx +0 -0
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/DefaultType/SystemJsRender/utils.ts +0 -0
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/DefaultType/index.tsx +0 -0
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/Loading.tsx +0 -0
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/MarkdownType/index.tsx +0 -0
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/StandaloneType/Iframe.tsx +0 -0
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/useParseContent.ts +0 -0
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/utils/iframeOnReady.test.ts +0 -0
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/utils/iframeOnReady.ts +0 -0
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/utils/listenToPlugin.test.ts +0 -0
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/utils/listenToPlugin.ts +0 -0
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/utils/pluginSettings.test.ts +0 -0
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/utils/pluginSettings.ts +0 -0
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/utils/pluginState.test.ts +0 -0
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/utils/pluginState.ts +0 -0
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/utils/postMessage.test.ts +0 -0
- /package/src/features/{Conversation/Plugins → PluginsUI}/Render/utils/postMessage.ts +0 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import isEqual from 'fast-deep-equal';
|
|
2
|
+
|
|
3
|
+
import PluginRender from '@/features/PluginsUI/Render';
|
|
4
|
+
import { useChatStore } from '@/store/chat';
|
|
5
|
+
import { chatPortalSelectors, chatSelectors } from '@/store/chat/selectors';
|
|
6
|
+
import { BuiltinToolsDocks } from '@/tools/docks';
|
|
7
|
+
import { safeParseJSON } from '@/utils/safeParseJSON';
|
|
8
|
+
|
|
9
|
+
const ToolRender = () => {
|
|
10
|
+
const messageId = useChatStore(chatPortalSelectors.toolUIMessageId);
|
|
11
|
+
const message = useChatStore(chatSelectors.getMessageById(messageId || ''), isEqual);
|
|
12
|
+
|
|
13
|
+
// make sure the message and id is valid
|
|
14
|
+
if (!messageId || !message) return;
|
|
15
|
+
|
|
16
|
+
const { plugin, pluginState } = message;
|
|
17
|
+
|
|
18
|
+
// make sure the plugin and identifier is valid
|
|
19
|
+
if (!plugin || !plugin.identifier) return;
|
|
20
|
+
|
|
21
|
+
const args = safeParseJSON(plugin.arguments);
|
|
22
|
+
|
|
23
|
+
if (!args) return;
|
|
24
|
+
|
|
25
|
+
const Render = BuiltinToolsDocks[plugin.identifier];
|
|
26
|
+
|
|
27
|
+
if (!Render)
|
|
28
|
+
return (
|
|
29
|
+
<PluginRender
|
|
30
|
+
arguments={plugin.arguments}
|
|
31
|
+
content={message.content}
|
|
32
|
+
id={messageId}
|
|
33
|
+
identifier={plugin.identifier}
|
|
34
|
+
payload={plugin}
|
|
35
|
+
pluginState={pluginState}
|
|
36
|
+
type={plugin?.type}
|
|
37
|
+
/>
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<Render
|
|
42
|
+
arguments={args}
|
|
43
|
+
identifier={plugin.identifier}
|
|
44
|
+
messageId={messageId}
|
|
45
|
+
state={pluginState}
|
|
46
|
+
/>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export default ToolRender;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import isEqual from 'fast-deep-equal';
|
|
2
|
+
import { Flexbox } from 'react-layout-kit';
|
|
3
|
+
|
|
4
|
+
import { useChatStore } from '@/store/chat';
|
|
5
|
+
import { chatPortalSelectors, chatSelectors } from '@/store/chat/selectors';
|
|
6
|
+
import { safeParseJSON } from '@/utils/safeParseJSON';
|
|
7
|
+
|
|
8
|
+
import Footer from './Footer';
|
|
9
|
+
import ToolRender from './ToolRender';
|
|
10
|
+
|
|
11
|
+
const ToolUI = () => {
|
|
12
|
+
const messageId = useChatStore(chatPortalSelectors.toolUIMessageId);
|
|
13
|
+
const message = useChatStore(chatSelectors.getMessageById(messageId || ''), isEqual);
|
|
14
|
+
|
|
15
|
+
// make sure the message and id is valid
|
|
16
|
+
if (!messageId || !message) return;
|
|
17
|
+
|
|
18
|
+
const { plugin } = message;
|
|
19
|
+
|
|
20
|
+
// make sure the plugin and identifier is valid
|
|
21
|
+
if (!plugin || !plugin.identifier) return;
|
|
22
|
+
|
|
23
|
+
const args = safeParseJSON(plugin.arguments);
|
|
24
|
+
|
|
25
|
+
if (!args) return;
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<>
|
|
29
|
+
<Flexbox height={'100%'} paddingInline={12}>
|
|
30
|
+
<ToolRender />
|
|
31
|
+
</Flexbox>
|
|
32
|
+
<Footer />
|
|
33
|
+
</>
|
|
34
|
+
);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default ToolUI;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { memo } from 'react';
|
|
4
|
+
import { Flexbox } from 'react-layout-kit';
|
|
5
|
+
|
|
6
|
+
import { useChatStore } from '@/store/chat';
|
|
7
|
+
import { chatPortalSelectors } from '@/store/chat/selectors';
|
|
8
|
+
|
|
9
|
+
import ToolList from './features/Tools/ToolList';
|
|
10
|
+
import ToolUI from './features/Tools/ToolUI';
|
|
11
|
+
|
|
12
|
+
const Inspector = memo(() => {
|
|
13
|
+
const showToolUI = useChatStore(chatPortalSelectors.showToolUI);
|
|
14
|
+
|
|
15
|
+
return <Flexbox height={'100%'}>{showToolUI ? <ToolUI /> : <ToolList />}</Flexbox>;
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export default Inspector;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { DraggablePanel, DraggablePanelContainer } from '@lobehub/ui';
|
|
4
|
+
import { createStyles, useResponsive } from 'antd-style';
|
|
5
|
+
import { PropsWithChildren, memo } from 'react';
|
|
6
|
+
import { Flexbox } from 'react-layout-kit';
|
|
7
|
+
|
|
8
|
+
import SafeSpacing from '@/components/SafeSpacing';
|
|
9
|
+
import { CHAT_DOCK_TOOL_UI_WIDTH, CHAT_DOCK_WIDTH, MAX_WIDTH } from '@/const/layoutTokens';
|
|
10
|
+
import { useChatStore } from '@/store/chat';
|
|
11
|
+
import { chatPortalSelectors } from '@/store/chat/slices/portal/selectors';
|
|
12
|
+
|
|
13
|
+
const useStyles = createStyles(({ css, token }) => ({
|
|
14
|
+
content: css`
|
|
15
|
+
display: flex;
|
|
16
|
+
flex-direction: column;
|
|
17
|
+
height: 100% !important;
|
|
18
|
+
`,
|
|
19
|
+
drawer: css`
|
|
20
|
+
z-index: 10;
|
|
21
|
+
background: ${token.colorBgLayout};
|
|
22
|
+
`,
|
|
23
|
+
header: css`
|
|
24
|
+
border-bottom: 1px solid ${token.colorBorder};
|
|
25
|
+
`,
|
|
26
|
+
panel: css`
|
|
27
|
+
overflow: hidden;
|
|
28
|
+
|
|
29
|
+
height: 100%;
|
|
30
|
+
margin: 12px;
|
|
31
|
+
|
|
32
|
+
background: ${token.colorBgContainer};
|
|
33
|
+
border: 1px solid ${token.colorPrimaryActive};
|
|
34
|
+
border-radius: 8px;
|
|
35
|
+
`,
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
const PortalPanel = memo(({ children }: PropsWithChildren) => {
|
|
39
|
+
const { styles } = useStyles();
|
|
40
|
+
const { md = true } = useResponsive();
|
|
41
|
+
|
|
42
|
+
const [showInspector, showToolUI] = useChatStore((s) => [
|
|
43
|
+
chatPortalSelectors.showDock(s),
|
|
44
|
+
chatPortalSelectors.showToolUI(s),
|
|
45
|
+
]);
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
showInspector && (
|
|
49
|
+
<DraggablePanel
|
|
50
|
+
className={styles.drawer}
|
|
51
|
+
classNames={{
|
|
52
|
+
content: styles.content,
|
|
53
|
+
}}
|
|
54
|
+
expand
|
|
55
|
+
hanlderStyle={{ display: 'none' }}
|
|
56
|
+
maxWidth={MAX_WIDTH}
|
|
57
|
+
minWidth={showToolUI ? CHAT_DOCK_TOOL_UI_WIDTH : CHAT_DOCK_WIDTH}
|
|
58
|
+
mode={md ? 'fixed' : 'float'}
|
|
59
|
+
placement={'right'}
|
|
60
|
+
showHandlerWhenUnexpand={false}
|
|
61
|
+
showHandlerWideArea={false}
|
|
62
|
+
>
|
|
63
|
+
<DraggablePanelContainer
|
|
64
|
+
style={{
|
|
65
|
+
flex: 'none',
|
|
66
|
+
height: '100%',
|
|
67
|
+
maxHeight: '100vh',
|
|
68
|
+
minWidth: CHAT_DOCK_WIDTH,
|
|
69
|
+
}}
|
|
70
|
+
>
|
|
71
|
+
<SafeSpacing />
|
|
72
|
+
<Flexbox className={styles.panel}>{children}</Flexbox>
|
|
73
|
+
</DraggablePanelContainer>
|
|
74
|
+
</DraggablePanel>
|
|
75
|
+
)
|
|
76
|
+
);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
export default PortalPanel;
|
|
@@ -7,6 +7,8 @@ import { PropsWithChildren, memo, useEffect, useState } from 'react';
|
|
|
7
7
|
|
|
8
8
|
import SafeSpacing from '@/components/SafeSpacing';
|
|
9
9
|
import { CHAT_SIDEBAR_WIDTH } from '@/const/layoutTokens';
|
|
10
|
+
import { useChatStore } from '@/store/chat';
|
|
11
|
+
import { chatPortalSelectors } from '@/store/chat/slices/portal/selectors';
|
|
10
12
|
import { useGlobalStore } from '@/store/global';
|
|
11
13
|
import { systemStatusSelectors } from '@/store/global/selectors';
|
|
12
14
|
|
|
@@ -32,6 +34,7 @@ const TopicPanel = memo(({ children }: PropsWithChildren) => {
|
|
|
32
34
|
systemStatusSelectors.showChatSideBar(s),
|
|
33
35
|
s.toggleChatSideBar,
|
|
34
36
|
]);
|
|
37
|
+
const showInspector = useChatStore(chatPortalSelectors.showDock);
|
|
35
38
|
|
|
36
39
|
const [cacheExpand, setCacheExpand] = useState<boolean>(Boolean(showAgentSettings));
|
|
37
40
|
|
|
@@ -47,30 +50,32 @@ const TopicPanel = memo(({ children }: PropsWithChildren) => {
|
|
|
47
50
|
}, [lg, cacheExpand]);
|
|
48
51
|
|
|
49
52
|
return (
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
expand={showAgentSettings}
|
|
56
|
-
minWidth={CHAT_SIDEBAR_WIDTH}
|
|
57
|
-
mode={md ? 'fixed' : 'float'}
|
|
58
|
-
onExpandChange={handleExpand}
|
|
59
|
-
placement={'right'}
|
|
60
|
-
showHandlerWideArea={false}
|
|
61
|
-
>
|
|
62
|
-
<DraggablePanelContainer
|
|
63
|
-
style={{
|
|
64
|
-
flex: 'none',
|
|
65
|
-
height: '100%',
|
|
66
|
-
maxHeight: '100vh',
|
|
67
|
-
minWidth: CHAT_SIDEBAR_WIDTH,
|
|
53
|
+
!showInspector && (
|
|
54
|
+
<DraggablePanel
|
|
55
|
+
className={styles.drawer}
|
|
56
|
+
classNames={{
|
|
57
|
+
content: styles.content,
|
|
68
58
|
}}
|
|
59
|
+
expand={showAgentSettings}
|
|
60
|
+
minWidth={CHAT_SIDEBAR_WIDTH}
|
|
61
|
+
mode={md ? 'fixed' : 'float'}
|
|
62
|
+
onExpandChange={handleExpand}
|
|
63
|
+
placement={'right'}
|
|
64
|
+
showHandlerWideArea={false}
|
|
69
65
|
>
|
|
70
|
-
<
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
66
|
+
<DraggablePanelContainer
|
|
67
|
+
style={{
|
|
68
|
+
flex: 'none',
|
|
69
|
+
height: '100%',
|
|
70
|
+
maxHeight: '100vh',
|
|
71
|
+
minWidth: CHAT_SIDEBAR_WIDTH,
|
|
72
|
+
}}
|
|
73
|
+
>
|
|
74
|
+
<SafeSpacing />
|
|
75
|
+
{children}
|
|
76
|
+
</DraggablePanelContainer>
|
|
77
|
+
</DraggablePanel>
|
|
78
|
+
)
|
|
74
79
|
);
|
|
75
80
|
});
|
|
76
81
|
|
|
@@ -2,10 +2,11 @@ import { Flexbox } from 'react-layout-kit';
|
|
|
2
2
|
|
|
3
3
|
import { LayoutProps } from '../type';
|
|
4
4
|
import ChatHeader from './ChatHeader';
|
|
5
|
+
import Inspector from './Portal';
|
|
5
6
|
import HotKeys from './HotKeys';
|
|
6
7
|
import TopicPanel from './TopicPanel';
|
|
7
8
|
|
|
8
|
-
const Layout = ({ children, topic, conversation }: LayoutProps) => {
|
|
9
|
+
const Layout = ({ children, topic, conversation, portal }: LayoutProps) => {
|
|
9
10
|
return (
|
|
10
11
|
<>
|
|
11
12
|
<ChatHeader />
|
|
@@ -23,6 +24,7 @@ const Layout = ({ children, topic, conversation }: LayoutProps) => {
|
|
|
23
24
|
{conversation}
|
|
24
25
|
</Flexbox>
|
|
25
26
|
{children}
|
|
27
|
+
<Inspector>{portal}</Inspector>
|
|
26
28
|
<TopicPanel>{topic}</TopicPanel>
|
|
27
29
|
</Flexbox>
|
|
28
30
|
<HotKeys />
|
|
@@ -1,21 +1,3 @@
|
|
|
1
|
-
|
|
1
|
+
import CircleLoading from '@/components/CircleLoading';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
import { Typography } from 'antd';
|
|
5
|
-
import { LoaderCircle } from 'lucide-react';
|
|
6
|
-
import { useTranslation } from 'react-i18next';
|
|
7
|
-
import { Center, Flexbox } from 'react-layout-kit';
|
|
8
|
-
|
|
9
|
-
export default () => {
|
|
10
|
-
const { t } = useTranslation('common');
|
|
11
|
-
return (
|
|
12
|
-
<Center height={'100%'} width={'100%'}>
|
|
13
|
-
<Flexbox align={'center'} gap={8}>
|
|
14
|
-
<div>
|
|
15
|
-
<Icon icon={LoaderCircle} size={'large'} spin />
|
|
16
|
-
</div>
|
|
17
|
-
<Typography.Text type={'secondary'}>{t('loading')}</Typography.Text>
|
|
18
|
-
</Flexbox>
|
|
19
|
-
</Center>
|
|
20
|
-
);
|
|
21
|
-
};
|
|
3
|
+
export default () => <CircleLoading />;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Icon } from '@lobehub/ui';
|
|
4
|
+
import { Typography } from 'antd';
|
|
5
|
+
import { LoaderCircle } from 'lucide-react';
|
|
6
|
+
import { useTranslation } from 'react-i18next';
|
|
7
|
+
import { Center, Flexbox } from 'react-layout-kit';
|
|
8
|
+
|
|
9
|
+
export default () => {
|
|
10
|
+
const { t } = useTranslation('common');
|
|
11
|
+
return (
|
|
12
|
+
<Center height={'100%'} width={'100%'}>
|
|
13
|
+
<Flexbox align={'center'} gap={8}>
|
|
14
|
+
<div>
|
|
15
|
+
<Icon icon={LoaderCircle} size={'large'} spin />
|
|
16
|
+
</div>
|
|
17
|
+
<Typography.Text type={'secondary'}>{t('loading')}</Typography.Text>
|
|
18
|
+
</Flexbox>
|
|
19
|
+
</Center>
|
|
20
|
+
);
|
|
21
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { HEADER_ICON_SIZE } from './layoutTokens';
|
|
2
|
+
|
|
3
|
+
describe('HEADER_ICON_SIZE', () => {
|
|
4
|
+
it('mobile', () => {
|
|
5
|
+
expect(HEADER_ICON_SIZE(true)).toEqual({ blockSize: 36, fontSize: 22 });
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
it('desktop', () => {
|
|
9
|
+
expect(HEADER_ICON_SIZE(false)).toEqual({ fontSize: 24 });
|
|
10
|
+
});
|
|
11
|
+
});
|
|
@@ -7,6 +7,10 @@ export const CHAT_TEXTAREA_MAX_HEIGHT = 800;
|
|
|
7
7
|
export const CHAT_TEXTAREA_HEIGHT = 230;
|
|
8
8
|
export const CHAT_TEXTAREA_HEIGHT_MOBILE = 108;
|
|
9
9
|
export const CHAT_SIDEBAR_WIDTH = 280;
|
|
10
|
+
|
|
11
|
+
export const CHAT_DOCK_WIDTH = 400;
|
|
12
|
+
export const CHAT_DOCK_TOOL_UI_WIDTH = 800;
|
|
13
|
+
|
|
10
14
|
export const MARKET_SIDEBAR_WIDTH = 400;
|
|
11
15
|
export const FOLDER_WIDTH = 270;
|
|
12
16
|
export const MAX_WIDTH = 1024;
|
package/src/const/message.ts
CHANGED
|
@@ -1,15 +1 @@
|
|
|
1
1
|
export const LOADING_FLAT = '...';
|
|
2
|
-
|
|
3
|
-
// start with this,it should be a function message
|
|
4
|
-
export const FUNCTION_MESSAGE_FLAG = '{"tool_calls"';
|
|
5
|
-
|
|
6
|
-
export const isFunctionMessageAtStart = (content: string) => {
|
|
7
|
-
return content.startsWith(FUNCTION_MESSAGE_FLAG);
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export const testFunctionMessageAtEnd = (content: string) => {
|
|
11
|
-
const regExp = /{"tool_calls":.*?]}$/;
|
|
12
|
-
const match = content?.trim().match(regExp);
|
|
13
|
-
|
|
14
|
-
return { content: match ? match[0] : '', valid: !!match };
|
|
15
|
-
};
|
|
@@ -390,21 +390,18 @@ describe('MessageModel', () => {
|
|
|
390
390
|
expect(updatedMessage.pluginState).toHaveProperty('testKey', 'testValue');
|
|
391
391
|
});
|
|
392
392
|
});
|
|
393
|
-
describe('clearTable', () => {
|
|
394
|
-
it('should clear the table', async () => {
|
|
395
|
-
await MessageModel.create(messageData);
|
|
396
|
-
await MessageModel.clearTable();
|
|
397
|
-
const messages = await MessageModel.queryAll();
|
|
398
|
-
expect(messages).toHaveLength(0);
|
|
399
|
-
});
|
|
400
|
-
});
|
|
401
393
|
|
|
402
|
-
describe('
|
|
403
|
-
it('should update plugin
|
|
394
|
+
describe('updatePlugin', () => {
|
|
395
|
+
it('should update plugin', async () => {
|
|
396
|
+
const value = {
|
|
397
|
+
identifier: 'testValue',
|
|
398
|
+
arguments: 'abc',
|
|
399
|
+
apiName: 'abc',
|
|
400
|
+
};
|
|
404
401
|
const createdMessage = await MessageModel.create(messageData);
|
|
405
|
-
await MessageModel.
|
|
402
|
+
await MessageModel.updatePlugin(createdMessage.id, value);
|
|
406
403
|
const updatedMessage = await MessageModel.findById(createdMessage.id);
|
|
407
|
-
expect(updatedMessage.
|
|
404
|
+
expect(updatedMessage.plugin).toEqual(value);
|
|
408
405
|
});
|
|
409
406
|
});
|
|
410
407
|
|
|
@@ -190,6 +190,12 @@ class _MessageModel extends BaseModel {
|
|
|
190
190
|
return this.update(id, { pluginState: { ...item.pluginState, ...value } });
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
+
async updatePlugin(id: string, value: any) {
|
|
194
|
+
const item = await this.findById(id);
|
|
195
|
+
|
|
196
|
+
return this.update(id, { plugin: { ...item.plugin, ...value } });
|
|
197
|
+
}
|
|
198
|
+
|
|
193
199
|
/**
|
|
194
200
|
* Batch updates multiple fields of the specified messages.
|
|
195
201
|
*
|
|
@@ -544,6 +544,47 @@ describe('MessageModel', () => {
|
|
|
544
544
|
const result = await serverDB.select().from(messages).where(eq(messages.id, '1')).execute();
|
|
545
545
|
expect(result[0].content).toBe('message 1');
|
|
546
546
|
});
|
|
547
|
+
|
|
548
|
+
it('should update message tools', async () => {
|
|
549
|
+
// 创建测试数据
|
|
550
|
+
await serverDB.insert(messages).values([
|
|
551
|
+
{
|
|
552
|
+
id: '1',
|
|
553
|
+
userId,
|
|
554
|
+
role: 'user',
|
|
555
|
+
content: 'message 1',
|
|
556
|
+
tools: [
|
|
557
|
+
{
|
|
558
|
+
id: 'call_Z8UU8LedZcoJHFGkfqYecjmT',
|
|
559
|
+
type: 'builtin',
|
|
560
|
+
apiName: 'searchWithSearXNG',
|
|
561
|
+
arguments:
|
|
562
|
+
'{"query":"杭州洪水 2023","searchEngines":["google","bing","baidu","duckduckgo","brave"]}',
|
|
563
|
+
identifier: 'lobe-web-browsing',
|
|
564
|
+
},
|
|
565
|
+
],
|
|
566
|
+
},
|
|
567
|
+
]);
|
|
568
|
+
|
|
569
|
+
// 调用 updateMessage 方法
|
|
570
|
+
await messageModel.update('1', {
|
|
571
|
+
tools: [
|
|
572
|
+
{
|
|
573
|
+
id: 'call_Z8UU8LedZcoJHFGkfqYecjmT',
|
|
574
|
+
type: 'builtin',
|
|
575
|
+
apiName: 'searchWithSearXNG',
|
|
576
|
+
arguments: '{"query":"2024 杭州暴雨","searchEngines":["duckduckgo","google","brave"]}',
|
|
577
|
+
identifier: 'lobe-web-browsing',
|
|
578
|
+
},
|
|
579
|
+
],
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
// 断言结果
|
|
583
|
+
const result = await serverDB.select().from(messages).where(eq(messages.id, '1')).execute();
|
|
584
|
+
expect(result[0].tools[0].arguments).toBe(
|
|
585
|
+
'{"query":"2024 杭州暴雨","searchEngines":["duckduckgo","google","brave"]}',
|
|
586
|
+
);
|
|
587
|
+
});
|
|
547
588
|
});
|
|
548
589
|
|
|
549
590
|
describe('deleteMessage', () => {
|
|
@@ -662,6 +703,35 @@ describe('MessageModel', () => {
|
|
|
662
703
|
);
|
|
663
704
|
});
|
|
664
705
|
});
|
|
706
|
+
describe('updateMessagePlugin', () => {
|
|
707
|
+
it('should update the state field in messagePlugins table', async () => {
|
|
708
|
+
// 创建测试数据
|
|
709
|
+
await serverDB.insert(messages).values({ id: '1', content: 'abc', role: 'user', userId });
|
|
710
|
+
await serverDB
|
|
711
|
+
.insert(messagePlugins)
|
|
712
|
+
.values([
|
|
713
|
+
{ id: '1', toolCallId: 'tool1', identifier: 'plugin1', state: { key1: 'value1' } },
|
|
714
|
+
]);
|
|
715
|
+
|
|
716
|
+
// 调用 updatePluginState 方法
|
|
717
|
+
await messageModel.updateMessagePlugin('1', { identifier: 'plugin2' });
|
|
718
|
+
|
|
719
|
+
// 断言结果
|
|
720
|
+
const result = await serverDB
|
|
721
|
+
.select()
|
|
722
|
+
.from(messagePlugins)
|
|
723
|
+
.where(eq(messagePlugins.id, '1'))
|
|
724
|
+
.execute();
|
|
725
|
+
expect(result[0].identifier).toEqual('plugin2');
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
it('should throw an error if plugin does not exist', async () => {
|
|
729
|
+
// 调用 updatePluginState 方法
|
|
730
|
+
await expect(messageModel.updatePluginState('1', { key: 'value' })).rejects.toThrowError(
|
|
731
|
+
'Plugin not found',
|
|
732
|
+
);
|
|
733
|
+
});
|
|
734
|
+
});
|
|
665
735
|
|
|
666
736
|
describe('updateTranslate', () => {
|
|
667
737
|
it('should insert a new record if message does not exist in messageTranslates table', async () => {
|
|
@@ -10,6 +10,7 @@ import { merge } from '@/utils/merge';
|
|
|
10
10
|
|
|
11
11
|
import {
|
|
12
12
|
MessageItem,
|
|
13
|
+
MessagePluginItem,
|
|
13
14
|
filesToMessages,
|
|
14
15
|
messagePlugins,
|
|
15
16
|
messageTTS,
|
|
@@ -271,6 +272,15 @@ export class MessageModel {
|
|
|
271
272
|
.where(eq(messagePlugins.id, id));
|
|
272
273
|
}
|
|
273
274
|
|
|
275
|
+
async updateMessagePlugin(id: string, value: Partial<MessagePluginItem>) {
|
|
276
|
+
const item = await serverDB.query.messagePlugins.findFirst({
|
|
277
|
+
where: eq(messagePlugins.id, id),
|
|
278
|
+
});
|
|
279
|
+
if (!item) throw new Error('Plugin not found');
|
|
280
|
+
|
|
281
|
+
return serverDB.update(messagePlugins).set(value).where(eq(messagePlugins.id, id));
|
|
282
|
+
}
|
|
283
|
+
|
|
274
284
|
async updateTranslate(id: string, translate: Partial<MessageItem>) {
|
|
275
285
|
const result = await serverDB.query.messageTranslates.findFirst({
|
|
276
286
|
where: and(eq(messageTranslates.id, id)),
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
uniqueIndex,
|
|
16
16
|
varchar,
|
|
17
17
|
} from 'drizzle-orm/pg-core';
|
|
18
|
-
import { createInsertSchema } from 'drizzle-zod';
|
|
18
|
+
import { createInsertSchema, createSelectSchema } from 'drizzle-zod';
|
|
19
19
|
|
|
20
20
|
import { DEFAULT_PREFERENCE } from '@/const/user';
|
|
21
21
|
import { LobeAgentChatConfig, LobeAgentTTSConfig } from '@/types/agent';
|
|
@@ -451,6 +451,8 @@ export const messagePlugins = pgTable('message_plugins', {
|
|
|
451
451
|
state: jsonb('state'),
|
|
452
452
|
error: jsonb('error'),
|
|
453
453
|
});
|
|
454
|
+
export type MessagePluginItem = typeof messagePlugins.$inferSelect;
|
|
455
|
+
export const updateMessagePluginSchema = createSelectSchema(messagePlugins);
|
|
454
456
|
|
|
455
457
|
export const messageTTS = pgTable('message_tts', {
|
|
456
458
|
id: text('id')
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Icon } from '@lobehub/ui';
|
|
2
2
|
import isEqual from 'fast-deep-equal';
|
|
3
3
|
import { Loader2, LucideChevronDown, LucideChevronRight, LucideToyBrick } from 'lucide-react';
|
|
4
4
|
import { CSSProperties, memo, useState } from 'react';
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
|
-
import {
|
|
6
|
+
import { Flexbox } from 'react-layout-kit';
|
|
7
7
|
|
|
8
8
|
import { useChatStore } from '@/store/chat';
|
|
9
9
|
import { chatSelectors } from '@/store/chat/selectors';
|
|
@@ -30,16 +30,8 @@ const CallItem = memo<InspectorProps>(
|
|
|
30
30
|
|
|
31
31
|
const pluginMeta = useToolStore(toolSelectors.getMetaById(identifier), isEqual);
|
|
32
32
|
|
|
33
|
-
const pluginAvatar = pluginHelpers.getPluginAvatar(pluginMeta);
|
|
34
|
-
|
|
35
33
|
const pluginTitle = pluginHelpers.getPluginTitle(pluginMeta) ?? t('unknownPlugin');
|
|
36
34
|
|
|
37
|
-
const avatar = pluginAvatar ? (
|
|
38
|
-
<Avatar alt={pluginTitle} avatar={pluginAvatar} size={32} />
|
|
39
|
-
) : (
|
|
40
|
-
<Icon icon={LucideToyBrick} />
|
|
41
|
-
);
|
|
42
|
-
|
|
43
35
|
return (
|
|
44
36
|
<Flexbox gap={8} style={style}>
|
|
45
37
|
<Flexbox
|
|
@@ -54,13 +46,7 @@ const CallItem = memo<InspectorProps>(
|
|
|
54
46
|
}}
|
|
55
47
|
>
|
|
56
48
|
<Flexbox align={'center'} gap={8} horizontal>
|
|
57
|
-
{loading ?
|
|
58
|
-
<Center height={30} width={24}>
|
|
59
|
-
<Icon icon={Loader2} spin />
|
|
60
|
-
</Center>
|
|
61
|
-
) : (
|
|
62
|
-
avatar
|
|
63
|
-
)}
|
|
49
|
+
{loading ? <Icon icon={Loader2} spin /> : <Icon icon={LucideToyBrick} />}
|
|
64
50
|
{pluginTitle}
|
|
65
51
|
</Flexbox>
|
|
66
52
|
<Icon icon={open ? LucideChevronDown : LucideChevronRight} />
|
|
@@ -3,6 +3,7 @@ import { ActionIcon, Avatar, Highlighter, Icon, Tag } from '@lobehub/ui';
|
|
|
3
3
|
import { Tabs } from 'antd';
|
|
4
4
|
import isEqual from 'fast-deep-equal';
|
|
5
5
|
import {
|
|
6
|
+
InspectionPanel,
|
|
6
7
|
LucideBug,
|
|
7
8
|
LucideBugOff,
|
|
8
9
|
LucideChevronDown,
|
|
@@ -13,8 +14,11 @@ import { memo, useState } from 'react';
|
|
|
13
14
|
import { useTranslation } from 'react-i18next';
|
|
14
15
|
import { Flexbox } from 'react-layout-kit';
|
|
15
16
|
|
|
17
|
+
import { DESKTOP_HEADER_ICON_SIZE } from '@/const/layoutTokens';
|
|
18
|
+
import { useChatStore } from '@/store/chat';
|
|
19
|
+
import { chatPortalSelectors } from '@/store/chat/slices/portal/selectors';
|
|
16
20
|
import { pluginHelpers, useToolStore } from '@/store/tool';
|
|
17
|
-
import {
|
|
21
|
+
import { toolSelectors } from '@/store/tool/selectors';
|
|
18
22
|
import { ChatPluginPayload } from '@/types/message';
|
|
19
23
|
|
|
20
24
|
import PluginResult from './PluginResultJSON';
|
|
@@ -24,6 +28,7 @@ import { useStyles } from './style';
|
|
|
24
28
|
export interface InspectorProps {
|
|
25
29
|
arguments?: string;
|
|
26
30
|
content: string;
|
|
31
|
+
id: string;
|
|
27
32
|
identifier?: string;
|
|
28
33
|
loading?: boolean;
|
|
29
34
|
payload?: ChatPluginPayload;
|
|
@@ -40,14 +45,20 @@ const Inspector = memo<InspectorProps>(
|
|
|
40
45
|
setShow,
|
|
41
46
|
content,
|
|
42
47
|
identifier = 'unknown',
|
|
48
|
+
id,
|
|
43
49
|
}) => {
|
|
44
50
|
const { t } = useTranslation('plugin');
|
|
45
51
|
const { styles } = useStyles();
|
|
46
52
|
const [open, setOpen] = useState(false);
|
|
53
|
+
const [isMessageToolUIOpen, openToolUI, toggleInspector] = useChatStore((s) => [
|
|
54
|
+
chatPortalSelectors.isMessageToolUIOpen(id)(s),
|
|
55
|
+
s.openToolUI,
|
|
56
|
+
s.toggleDock,
|
|
57
|
+
]);
|
|
47
58
|
|
|
48
59
|
const pluginMeta = useToolStore(toolSelectors.getMetaById(identifier), isEqual);
|
|
49
60
|
|
|
50
|
-
const showRightAction = useToolStore(
|
|
61
|
+
const showRightAction = useToolStore(toolSelectors.isToolHasUI(identifier));
|
|
51
62
|
const pluginAvatar = pluginHelpers.getPluginAvatar(pluginMeta);
|
|
52
63
|
|
|
53
64
|
const pluginTitle = pluginHelpers.getPluginTitle(pluginMeta) ?? t('unknownPlugin');
|
|
@@ -94,6 +105,19 @@ const Inspector = memo<InspectorProps>(
|
|
|
94
105
|
</Flexbox>
|
|
95
106
|
|
|
96
107
|
<Flexbox horizontal>
|
|
108
|
+
{showRightAction && false && (
|
|
109
|
+
<ActionIcon
|
|
110
|
+
icon={InspectionPanel}
|
|
111
|
+
onClick={() => {
|
|
112
|
+
if (!isMessageToolUIOpen) openToolUI(id, identifier);
|
|
113
|
+
else {
|
|
114
|
+
toggleInspector(false);
|
|
115
|
+
}
|
|
116
|
+
}}
|
|
117
|
+
size={DESKTOP_HEADER_ICON_SIZE}
|
|
118
|
+
title={'inspector'}
|
|
119
|
+
/>
|
|
120
|
+
)}
|
|
97
121
|
<ActionIcon
|
|
98
122
|
icon={open ? LucideBugOff : LucideBug}
|
|
99
123
|
onClick={() => {
|