@lobehub/lobehub 2.0.0-next.23 → 2.0.0-next.25

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 (82) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/changelog/v1.json +18 -0
  3. package/locales/ar/labs.json +4 -0
  4. package/locales/bg-BG/labs.json +4 -0
  5. package/locales/de-DE/labs.json +4 -0
  6. package/locales/en-US/labs.json +4 -0
  7. package/locales/es-ES/labs.json +4 -0
  8. package/locales/fa-IR/labs.json +4 -0
  9. package/locales/fr-FR/labs.json +4 -0
  10. package/locales/it-IT/labs.json +4 -0
  11. package/locales/ja-JP/labs.json +4 -0
  12. package/locales/ko-KR/labs.json +4 -0
  13. package/locales/nl-NL/labs.json +4 -0
  14. package/locales/pl-PL/labs.json +4 -0
  15. package/locales/pt-BR/labs.json +4 -0
  16. package/locales/ru-RU/labs.json +4 -0
  17. package/locales/tr-TR/labs.json +4 -0
  18. package/locales/vi-VN/labs.json +4 -0
  19. package/locales/zh-CN/labs.json +4 -0
  20. package/locales/zh-TW/labs.json +4 -0
  21. package/package.json +1 -1
  22. package/packages/const/src/user.ts +5 -2
  23. package/packages/types/src/index.ts +0 -1
  24. package/packages/types/src/user/index.ts +2 -88
  25. package/packages/types/src/user/preference.ts +105 -0
  26. package/renovate.json +1 -6
  27. package/src/app/[variants]/(main)/labs/components/LabCard.tsx +5 -5
  28. package/src/app/[variants]/(main)/labs/page.tsx +19 -22
  29. package/src/app/[variants]/(main)/settings/provider/detail/azure/index.tsx +1 -1
  30. package/src/app/[variants]/(main)/settings/provider/detail/azureai/index.tsx +1 -1
  31. package/src/app/[variants]/(main)/settings/provider/detail/bedrock/index.tsx +1 -1
  32. package/src/app/[variants]/(main)/settings/provider/detail/cloudflare/index.tsx +1 -1
  33. package/src/app/[variants]/(main)/settings/provider/detail/comfyui/index.tsx +1 -1
  34. package/src/app/[variants]/(main)/settings/provider/detail/github/index.tsx +1 -1
  35. package/src/app/[variants]/(main)/settings/provider/detail/vertexai/index.tsx +1 -1
  36. package/src/app/[variants]/(main)/settings/provider/features/ProviderConfig/index.tsx +2 -4
  37. package/src/components/Skeleton/SkeletonSwitch.tsx +13 -0
  38. package/src/components/Skeleton/index.ts +2 -0
  39. package/src/features/ChatInput/ActionBar/index.tsx +2 -2
  40. package/src/features/ChatInput/InputEditor/index.tsx +2 -2
  41. package/src/features/Conversation/Messages/Group/Actions/WithContentId.tsx +152 -0
  42. package/src/features/Conversation/Messages/Group/Actions/WithoutContentId.tsx +70 -0
  43. package/src/features/Conversation/Messages/Group/Actions/index.tsx +21 -0
  44. package/src/features/Conversation/Messages/Group/ContentBlock.tsx +91 -0
  45. package/src/features/Conversation/Messages/Group/EditState.tsx +51 -0
  46. package/src/features/Conversation/Messages/Group/Error/index.tsx +53 -0
  47. package/src/features/Conversation/Messages/Group/GroupChildren.tsx +73 -0
  48. package/src/features/Conversation/Messages/Group/MessageContent.tsx +39 -0
  49. package/src/features/Conversation/Messages/Group/Tool/Inspector/BuiltinPluginTitle.tsx +49 -0
  50. package/src/features/Conversation/Messages/Group/Tool/Inspector/Debug.tsx +70 -0
  51. package/src/features/Conversation/Messages/Group/Tool/Inspector/PluginResult.tsx +34 -0
  52. package/src/features/Conversation/Messages/Group/Tool/Inspector/PluginState.tsx +18 -0
  53. package/src/features/Conversation/Messages/Group/Tool/Inspector/Settings.tsx +40 -0
  54. package/src/features/Conversation/Messages/Group/Tool/Inspector/ToolTitle.tsx +92 -0
  55. package/src/features/Conversation/Messages/Group/Tool/Inspector/index.tsx +176 -0
  56. package/src/features/Conversation/Messages/Group/Tool/Render/Arguments/ObjectEntity.tsx +81 -0
  57. package/src/features/Conversation/Messages/Group/Tool/Render/Arguments/ValueCell.tsx +43 -0
  58. package/src/features/Conversation/Messages/Group/Tool/Render/Arguments/index.tsx +134 -0
  59. package/src/features/Conversation/Messages/Group/Tool/Render/CustomRender.tsx +88 -0
  60. package/src/features/Conversation/Messages/Group/Tool/Render/ErrorResponse.tsx +35 -0
  61. package/src/features/Conversation/Messages/Group/Tool/Render/LoadingPlaceholder/index.tsx +29 -0
  62. package/src/features/Conversation/Messages/Group/Tool/Render/PluginSettings.tsx +66 -0
  63. package/src/features/Conversation/Messages/Group/Tool/Render/index.tsx +105 -0
  64. package/src/features/Conversation/Messages/Group/Tool/index.tsx +75 -0
  65. package/src/features/Conversation/Messages/Group/Tools.tsx +46 -0
  66. package/src/features/Conversation/Messages/Group/index.tsx +140 -0
  67. package/src/features/Conversation/Messages/index.tsx +12 -0
  68. package/src/features/Conversation/components/ShareMessageModal/ShareImage/Preview.tsx +2 -2
  69. package/src/locales/default/labs.ts +4 -0
  70. package/src/server/routers/lambda/message.ts +5 -20
  71. package/src/services/chat/contextEngineering.ts +6 -5
  72. package/src/services/message/server.ts +10 -10
  73. package/src/services/message/type.ts +0 -2
  74. package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChatV2.test.ts +309 -2
  75. package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +2 -22
  76. package/src/store/chat/slices/aiChat/actions/generateAIChatV2.ts +272 -14
  77. package/src/store/user/selectors.ts +1 -1
  78. package/src/store/user/slices/preference/action.ts +8 -1
  79. package/src/store/user/slices/preference/selectors/index.ts +2 -0
  80. package/src/store/user/slices/preference/selectors/labPrefer.ts +13 -0
  81. package/src/store/user/slices/preference/{selectors.ts → selectors/preference.ts} +0 -2
  82. /package/src/{app/[variants]/(main)/settings/provider/features/ProviderConfig → components/Skeleton}/SkeletonInput.tsx +0 -0
@@ -0,0 +1,43 @@
1
+ import { copyToClipboard } from '@lobehub/ui';
2
+ import { App } from 'antd';
3
+ import { createStyles } from 'antd-style';
4
+ import { memo } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+
7
+ const useStyles = createStyles(({ css, token }) => ({
8
+ copyable: css`
9
+ cursor: pointer;
10
+ width: 100%;
11
+ margin-block: 2px;
12
+ padding: 4px;
13
+
14
+ &:hover {
15
+ border-radius: 6px;
16
+ background: ${token.colorFillTertiary};
17
+ }
18
+ `,
19
+ }));
20
+
21
+ interface ValueCellProps {
22
+ value: string;
23
+ }
24
+
25
+ const ValueCell = memo<ValueCellProps>(({ value }) => {
26
+ const { message } = App.useApp();
27
+ const { t } = useTranslation('common');
28
+ const { styles } = useStyles();
29
+
30
+ return (
31
+ <div
32
+ className={styles.copyable}
33
+ onClick={async () => {
34
+ await copyToClipboard(value);
35
+ message.success(t('copySuccess'));
36
+ }}
37
+ >
38
+ {value}
39
+ </div>
40
+ );
41
+ });
42
+
43
+ export default ValueCell;
@@ -0,0 +1,134 @@
1
+ import { Highlighter } from '@lobehub/ui';
2
+ import { createStyles } from 'antd-style';
3
+ import { parse } from 'partial-json';
4
+ import { ReactNode, memo, useMemo } from 'react';
5
+ import { Flexbox } from 'react-layout-kit';
6
+
7
+ import { useYamlArguments } from '@/hooks/useYamlArguments';
8
+
9
+ import ObjectEntity from './ObjectEntity';
10
+
11
+ const useStyles = createStyles(({ css, token, cx }) => ({
12
+ button: css`
13
+ color: ${token.colorTextSecondary};
14
+
15
+ &:hover {
16
+ color: ${token.colorText};
17
+ }
18
+ `,
19
+ codeContainer: css`
20
+ border-radius: ${token.borderRadiusLG}px;
21
+ `,
22
+ container: css`
23
+ position: relative;
24
+
25
+ overflow: auto;
26
+
27
+ max-height: 200px;
28
+ padding-block: 4px;
29
+ padding-inline: 12px 64px;
30
+ border-radius: ${token.borderRadiusLG}px;
31
+
32
+ font-family: ${token.fontFamilyCode};
33
+ font-size: 13px;
34
+ line-height: 1.5;
35
+
36
+ background: ${token.colorFillQuaternary};
37
+
38
+ pre {
39
+ margin: 0 !important;
40
+ background: none !important;
41
+ }
42
+
43
+ &:hover {
44
+ .actions {
45
+ opacity: 1;
46
+ }
47
+ }
48
+ `,
49
+ editButton: cx(
50
+ 'actions',
51
+ css`
52
+ position: absolute;
53
+ z-index: 10;
54
+ inset-block-start: 4px;
55
+ inset-inline-end: 4px;
56
+
57
+ opacity: 0;
58
+
59
+ transition: opacity 0.2s ${token.motionEaseInOut};
60
+ `,
61
+ ),
62
+ }));
63
+
64
+ export interface ArgumentsProps {
65
+ actions?: ReactNode;
66
+ arguments?: string;
67
+ shine?: boolean;
68
+ }
69
+
70
+ const Arguments = memo<ArgumentsProps>(({ arguments: args = '', shine, actions }) => {
71
+ const { styles } = useStyles();
72
+
73
+ const displayArgs = useMemo(() => {
74
+ try {
75
+ const obj = parse(args);
76
+ if (Object.keys(obj).length === 0) return {};
77
+ return obj;
78
+ } catch {
79
+ return args;
80
+ }
81
+ }, [args]);
82
+
83
+ const yaml = useYamlArguments(args);
84
+
85
+ const showActions = !!actions;
86
+
87
+ if (typeof displayArgs === 'string') {
88
+ return (
89
+ !!yaml && (
90
+ <div className={styles.container}>
91
+ <Highlighter language={'yaml'} showLanguage={false}>
92
+ {yaml}
93
+ </Highlighter>
94
+ </div>
95
+ )
96
+ );
97
+ }
98
+
99
+ // if (args.length > 100) {
100
+ // return (
101
+ // <Highlighter language={'json'} showLanguage={false} variant={'filled'}>
102
+ // {JSON.stringify(displayArgs, null, 2)}
103
+ // </Highlighter>
104
+ // );
105
+ // }
106
+
107
+ const hasMinWidth = Object.keys(displayArgs).length > 1;
108
+
109
+ if (Object.keys(displayArgs).length === 0) return null;
110
+
111
+ return (
112
+ <div className={styles.container}>
113
+ {showActions && (
114
+ <Flexbox className={styles.editButton} gap={4} horizontal>
115
+ {actions}
116
+ </Flexbox>
117
+ )}
118
+ {Object.entries(displayArgs).map(([key, value]) => {
119
+ return (
120
+ <ObjectEntity
121
+ editable={false}
122
+ hasMinWidth={hasMinWidth}
123
+ key={key}
124
+ objectKey={key}
125
+ shine={shine}
126
+ value={value}
127
+ />
128
+ );
129
+ })}
130
+ </div>
131
+ );
132
+ });
133
+
134
+ export default Arguments;
@@ -0,0 +1,88 @@
1
+ import { ChatPluginPayload } from '@lobechat/types';
2
+ import { Highlighter } from '@lobehub/ui';
3
+ import { memo, useEffect, useMemo } from 'react';
4
+ import { Flexbox } from 'react-layout-kit';
5
+
6
+ import PluginRender from '@/features/PluginsUI/Render';
7
+
8
+ import Arguments from './Arguments';
9
+
10
+ interface CustomRenderProps {
11
+ content: string;
12
+ id: string;
13
+ plugin?: ChatPluginPayload;
14
+ pluginState?: any;
15
+ requestArgs?: string;
16
+ setShowPluginRender: (value: boolean) => void;
17
+ showPluginRender: boolean;
18
+ }
19
+
20
+ /**
21
+ * Custom Render for Group Messages
22
+ *
23
+ * Group messages are already completed, so:
24
+ * - No loading state needed
25
+ * - No edit/re-run functionality
26
+ * - Results are directly available in content prop
27
+ */
28
+ const CustomRender = memo<CustomRenderProps>(
29
+ ({ id, content, pluginState, plugin, requestArgs, showPluginRender, setShowPluginRender }) => {
30
+ // Determine if plugin UI should be shown based on plugin type
31
+ useEffect(() => {
32
+ if (!plugin?.type) return;
33
+ setShowPluginRender(!['default', 'mcp'].includes(plugin.type));
34
+ }, [plugin?.type, setShowPluginRender]);
35
+
36
+ // Parse and display result content
37
+ const { data, language } = useMemo(() => {
38
+ try {
39
+ const parsed = JSON.parse(content || '');
40
+ // If parsed result is a string, return it directly
41
+ if (typeof parsed === 'string') {
42
+ return { data: parsed, language: 'plaintext' };
43
+ }
44
+ return { data: JSON.stringify(parsed, null, 2), language: 'json' };
45
+ } catch {
46
+ return { data: content || '', language: 'plaintext' };
47
+ }
48
+ }, [content]);
49
+
50
+ // Show plugin custom UI if applicable
51
+ if (showPluginRender) {
52
+ return (
53
+ <Flexbox gap={12} id={id} width={'100%'}>
54
+ <PluginRender
55
+ arguments={plugin?.arguments}
56
+ content={content}
57
+ id={id}
58
+ identifier={plugin?.identifier}
59
+ loading={false}
60
+ payload={plugin}
61
+ pluginState={pluginState}
62
+ type={plugin?.type}
63
+ />
64
+ </Flexbox>
65
+ );
66
+ }
67
+
68
+ // Default render: show arguments and result
69
+ return (
70
+ <Flexbox gap={12} id={id} width={'100%'}>
71
+ <Arguments arguments={requestArgs} />
72
+ {content && (
73
+ <Highlighter
74
+ language={language}
75
+ style={{ maxHeight: 200, overflow: 'scroll', width: '100%' }}
76
+ variant={'outlined'}
77
+ >
78
+ {data}
79
+ </Highlighter>
80
+ )}
81
+ </Flexbox>
82
+ );
83
+ },
84
+ );
85
+
86
+ CustomRender.displayName = 'GroupCustomRender';
87
+
88
+ export default CustomRender;
@@ -0,0 +1,35 @@
1
+ import { ChatMessageError, ChatPluginPayload } from '@lobechat/types';
2
+ import { Alert, Highlighter } from '@lobehub/ui';
3
+ import { memo } from 'react';
4
+ import { useTranslation } from 'react-i18next';
5
+ import { Flexbox } from 'react-layout-kit';
6
+
7
+ import PluginSettings from './PluginSettings';
8
+
9
+ interface ErrorResponseProps extends ChatMessageError {
10
+ id: string;
11
+ plugin?: ChatPluginPayload;
12
+ }
13
+
14
+ const ErrorResponse = memo<ErrorResponseProps>(({ id, type, body, message, plugin }) => {
15
+ const { t } = useTranslation('error');
16
+ if (type === 'PluginSettingsInvalid') {
17
+ return <PluginSettings id={id} plugin={plugin} />;
18
+ }
19
+
20
+ return (
21
+ <Alert
22
+ extra={
23
+ <Flexbox>
24
+ <Highlighter actionIconSize={'small'} language={'json'} variant={'borderless'}>
25
+ {JSON.stringify(body || { message, type }, null, 2)}
26
+ </Highlighter>
27
+ </Flexbox>
28
+ }
29
+ message={t(`response.${type}` as any)}
30
+ showIcon
31
+ type={'error'}
32
+ />
33
+ );
34
+ });
35
+ export default ErrorResponse;
@@ -0,0 +1,29 @@
1
+ import { safeParseJSON } from '@lobechat/utils';
2
+ import { memo } from 'react';
3
+
4
+ import { BuiltinToolPlaceholders } from '@/tools/placeholders';
5
+
6
+ import Arguments from '../Arguments';
7
+
8
+ interface LoadingPlaceholderProps {
9
+ apiName: string;
10
+ identifier: string;
11
+ loading?: boolean;
12
+ requestArgs?: string;
13
+ }
14
+
15
+ const LoadingPlaceholder = memo<LoadingPlaceholderProps>(
16
+ ({ identifier, requestArgs, apiName, loading }) => {
17
+ const Render = BuiltinToolPlaceholders[identifier || ''];
18
+
19
+ if (identifier && Render) {
20
+ return (
21
+ <Render apiName={apiName} args={safeParseJSON(requestArgs) || {}} identifier={identifier} />
22
+ );
23
+ }
24
+
25
+ return <Arguments arguments={requestArgs} shine={loading} />;
26
+ },
27
+ );
28
+
29
+ export default LoadingPlaceholder;
@@ -0,0 +1,66 @@
1
+ import { ChatPluginPayload } from '@lobechat/types';
2
+ import { Avatar, Button } from '@lobehub/ui';
3
+ import { Divider } from 'antd';
4
+ import { useTheme } from 'antd-style';
5
+ import isEqual from 'fast-deep-equal';
6
+ import { memo } from 'react';
7
+ import { useTranslation } from 'react-i18next';
8
+ import { Center, Flexbox } from 'react-layout-kit';
9
+
10
+ import PluginSettingsConfig from '@/features/PluginSettings';
11
+ import { useChatStore } from '@/store/chat';
12
+ import { pluginHelpers, useToolStore } from '@/store/tool';
13
+ import { pluginSelectors } from '@/store/tool/selectors';
14
+
15
+ import { ErrorActionContainer, useStyles } from '../../../../Error/style';
16
+
17
+ interface PluginSettingsProps {
18
+ id: string;
19
+ plugin?: ChatPluginPayload;
20
+ }
21
+
22
+ const PluginSettings = memo<PluginSettingsProps>(({ id, plugin }) => {
23
+ const { styles } = useStyles();
24
+ const { t } = useTranslation('error');
25
+ const theme = useTheme();
26
+ const [resend, deleteMessage] = useChatStore((s) => [s.regenerateMessage, s.deleteMessage]);
27
+ const pluginIdentifier = plugin?.identifier as string;
28
+ const pluginMeta = useToolStore(pluginSelectors.getPluginMetaById(pluginIdentifier), isEqual);
29
+ const manifest = useToolStore(pluginSelectors.getToolManifestById(pluginIdentifier), isEqual);
30
+
31
+ return (
32
+ !!manifest && (
33
+ <ErrorActionContainer>
34
+ <Center gap={16} style={{ maxWidth: 400 }}>
35
+ <Avatar
36
+ avatar={pluginHelpers.getPluginAvatar(pluginMeta) || '⚙️'}
37
+ background={theme.colorFillContent}
38
+ gap={12}
39
+ size={80}
40
+ />
41
+ <Flexbox style={{ fontSize: 20 }}>
42
+ {t('pluginSettings.title', { name: pluginHelpers.getPluginTitle(pluginMeta) })}
43
+ </Flexbox>
44
+ <Flexbox className={styles.desc}>{t('pluginSettings.desc')}</Flexbox>
45
+ <Divider style={{ margin: '0 16px' }} />
46
+ {manifest.settings && (
47
+ <PluginSettingsConfig id={manifest.identifier} schema={manifest.settings} />
48
+ )}
49
+ <Button
50
+ block
51
+ onClick={() => {
52
+ resend(id);
53
+ deleteMessage(id);
54
+ }}
55
+ style={{ marginTop: 8 }}
56
+ type={'primary'}
57
+ >
58
+ {t('unlock.confirm')}
59
+ </Button>
60
+ </Center>
61
+ </ErrorActionContainer>
62
+ )
63
+ );
64
+ });
65
+
66
+ export default PluginSettings;
@@ -0,0 +1,105 @@
1
+ import { LOADING_FLAT } from '@lobechat/const';
2
+ import { ChatToolResult } from '@lobechat/types';
3
+ import { Suspense, memo } from 'react';
4
+
5
+ import CustomRender from './CustomRender';
6
+ import ErrorResponse from './ErrorResponse';
7
+ import LoadingPlaceholder from './LoadingPlaceholder';
8
+
9
+ interface RenderProps {
10
+ apiName: string;
11
+ arguments?: string;
12
+ identifier: string;
13
+ /**
14
+ * ContentBlock ID (not the group message ID)
15
+ */
16
+ messageId: string;
17
+ result?: ChatToolResult;
18
+ setShowPluginRender: (show: boolean) => void;
19
+ showPluginRender: boolean;
20
+ toolCallId: string;
21
+ type?: string;
22
+ }
23
+
24
+ /**
25
+ * Tool Render for Group Messages
26
+ *
27
+ * In group messages, tool results are already embedded in the payload,
28
+ * so we don't need to query them from the store or handle streaming.
29
+ */
30
+ const Render = memo<RenderProps>(
31
+ ({
32
+ toolCallId,
33
+ messageId,
34
+ arguments: requestArgs,
35
+ showPluginRender,
36
+ setShowPluginRender,
37
+ identifier,
38
+ apiName,
39
+ result,
40
+ type,
41
+ }) => {
42
+ if (!result) return null;
43
+
44
+ // Handle error state
45
+ if (result.error) {
46
+ return (
47
+ <ErrorResponse
48
+ {...result.error}
49
+ id={messageId}
50
+ plugin={
51
+ type
52
+ ? ({
53
+ apiName,
54
+ arguments: requestArgs || '',
55
+ identifier,
56
+ type,
57
+ } as any)
58
+ : undefined
59
+ }
60
+ />
61
+ );
62
+ }
63
+
64
+ const placeholder = (
65
+ <LoadingPlaceholder
66
+ apiName={apiName}
67
+ identifier={identifier}
68
+ loading
69
+ requestArgs={requestArgs}
70
+ />
71
+ );
72
+
73
+ // Standalone plugins always have LOADING_FLAT as content
74
+ const inPlaceholder = result.content === LOADING_FLAT && type !== 'standalone';
75
+
76
+ if (inPlaceholder) return placeholder;
77
+
78
+ return (
79
+ <Suspense fallback={placeholder}>
80
+ <CustomRender
81
+ content={result.content || ''}
82
+ id={toolCallId}
83
+ plugin={
84
+ type
85
+ ? ({
86
+ apiName,
87
+ arguments: requestArgs || '',
88
+ identifier,
89
+ type,
90
+ } as any)
91
+ : undefined
92
+ }
93
+ pluginState={result.state}
94
+ requestArgs={requestArgs}
95
+ setShowPluginRender={setShowPluginRender}
96
+ showPluginRender={showPluginRender}
97
+ />
98
+ </Suspense>
99
+ );
100
+ },
101
+ );
102
+
103
+ Render.displayName = 'GroupToolRender';
104
+
105
+ export default Render;
@@ -0,0 +1,75 @@
1
+ import { ChatToolResult } from '@lobechat/types';
2
+ import { CSSProperties, memo, useState } from 'react';
3
+ import { Flexbox } from 'react-layout-kit';
4
+
5
+ import AnimatedCollapsed from '@/components/AnimatedCollapsed';
6
+
7
+ import Inspectors from './Inspector';
8
+ import Render from './Render';
9
+
10
+ export interface GroupToolProps {
11
+ apiName: string;
12
+ arguments?: string;
13
+ id: string;
14
+ identifier: string;
15
+ index: number;
16
+ /**
17
+ * ContentBlock ID (not the group message ID)
18
+ */
19
+ messageId: string;
20
+ result?: ChatToolResult;
21
+ style?: CSSProperties;
22
+ type?: string;
23
+ }
24
+
25
+ /**
26
+ * Tool component for Group Messages
27
+ *
28
+ * In group messages, all tools are completed (no streaming),
29
+ * so we always show the results directly.
30
+ */
31
+ const Tool = memo<GroupToolProps>(
32
+ ({ arguments: requestArgs, apiName, messageId, id, index, identifier, style, result, type }) => {
33
+ // Default to false since group messages are all completed
34
+ const [showDetail, setShowDetail] = useState(false);
35
+ const [showPluginRender, setShowPluginRender] = useState(false);
36
+
37
+ return (
38
+ <Flexbox gap={8} style={style}>
39
+ <Inspectors
40
+ apiName={apiName}
41
+ arguments={requestArgs}
42
+ // mcp don't have ui render
43
+ hidePluginUI={type === 'mcp'}
44
+ id={id}
45
+ identifier={identifier}
46
+ index={index}
47
+ messageId={messageId}
48
+ result={result}
49
+ setShowPluginRender={setShowPluginRender}
50
+ setShowRender={setShowDetail}
51
+ showPluginRender={showPluginRender}
52
+ showRender={showDetail}
53
+ type={type}
54
+ />
55
+ <AnimatedCollapsed open={showDetail} width={{ collapsed: 'auto' }}>
56
+ <Render
57
+ apiName={apiName}
58
+ arguments={requestArgs}
59
+ identifier={identifier}
60
+ messageId={messageId}
61
+ result={result}
62
+ setShowPluginRender={setShowPluginRender}
63
+ showPluginRender={showPluginRender}
64
+ toolCallId={id}
65
+ type={type}
66
+ />
67
+ </AnimatedCollapsed>
68
+ </Flexbox>
69
+ );
70
+ },
71
+ );
72
+
73
+ Tool.displayName = 'GroupTool';
74
+
75
+ export default Tool;
@@ -0,0 +1,46 @@
1
+ import { ChatToolPayloadWithResult } from '@lobechat/types';
2
+ import { createStyles } from 'antd-style';
3
+ import { memo } from 'react';
4
+ import { Flexbox } from 'react-layout-kit';
5
+
6
+ import Tool from './Tool';
7
+
8
+ const useStyles = createStyles(({ css, token }) => {
9
+ return {
10
+ toolsContainer: css`
11
+ padding-block: 6px;
12
+ padding-inline: 12px;
13
+ border: 1px solid ${token.colorBorderSecondary};
14
+ border-radius: 8px;
15
+ `,
16
+ };
17
+ });
18
+
19
+ interface ToolsRendererProps {
20
+ messageId: string;
21
+ tools: ChatToolPayloadWithResult[];
22
+ }
23
+
24
+ export const Tools = memo<ToolsRendererProps>(({ messageId, tools }) => {
25
+ const { styles, cx } = useStyles();
26
+
27
+ if (!tools || tools.length === 0) return null;
28
+
29
+ return (
30
+ <Flexbox className={cx(styles.toolsContainer, 'tool-blocks')} gap={8}>
31
+ {tools.map((tool, index) => (
32
+ <Tool
33
+ apiName={tool.apiName}
34
+ arguments={tool.arguments}
35
+ id={tool.id}
36
+ identifier={tool.identifier}
37
+ index={index}
38
+ key={tool.id}
39
+ messageId={messageId}
40
+ result={tool.result}
41
+ type={tool.type}
42
+ />
43
+ ))}
44
+ </Flexbox>
45
+ );
46
+ });