@lobehub/lobehub 2.0.0-next.24 → 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.
- package/CHANGELOG.md +25 -0
- package/changelog/v1.json +9 -0
- package/package.json +1 -1
- package/src/app/[variants]/(main)/labs/page.tsx +9 -8
- package/src/features/Conversation/Messages/Group/Actions/WithContentId.tsx +152 -0
- package/src/features/Conversation/Messages/Group/Actions/WithoutContentId.tsx +70 -0
- package/src/features/Conversation/Messages/Group/Actions/index.tsx +21 -0
- package/src/features/Conversation/Messages/Group/ContentBlock.tsx +91 -0
- package/src/features/Conversation/Messages/Group/EditState.tsx +51 -0
- package/src/features/Conversation/Messages/Group/Error/index.tsx +53 -0
- package/src/features/Conversation/Messages/Group/GroupChildren.tsx +73 -0
- package/src/features/Conversation/Messages/Group/MessageContent.tsx +39 -0
- package/src/features/Conversation/Messages/Group/Tool/Inspector/BuiltinPluginTitle.tsx +49 -0
- package/src/features/Conversation/Messages/Group/Tool/Inspector/Debug.tsx +70 -0
- package/src/features/Conversation/Messages/Group/Tool/Inspector/PluginResult.tsx +34 -0
- package/src/features/Conversation/Messages/Group/Tool/Inspector/PluginState.tsx +18 -0
- package/src/features/Conversation/Messages/Group/Tool/Inspector/Settings.tsx +40 -0
- package/src/features/Conversation/Messages/Group/Tool/Inspector/ToolTitle.tsx +92 -0
- package/src/features/Conversation/Messages/Group/Tool/Inspector/index.tsx +176 -0
- package/src/features/Conversation/Messages/Group/Tool/Render/Arguments/ObjectEntity.tsx +81 -0
- package/src/features/Conversation/Messages/Group/Tool/Render/Arguments/ValueCell.tsx +43 -0
- package/src/features/Conversation/Messages/Group/Tool/Render/Arguments/index.tsx +134 -0
- package/src/features/Conversation/Messages/Group/Tool/Render/CustomRender.tsx +88 -0
- package/src/features/Conversation/Messages/Group/Tool/Render/ErrorResponse.tsx +35 -0
- package/src/features/Conversation/Messages/Group/Tool/Render/LoadingPlaceholder/index.tsx +29 -0
- package/src/features/Conversation/Messages/Group/Tool/Render/PluginSettings.tsx +66 -0
- package/src/features/Conversation/Messages/Group/Tool/Render/index.tsx +105 -0
- package/src/features/Conversation/Messages/Group/Tool/index.tsx +75 -0
- package/src/features/Conversation/Messages/Group/Tools.tsx +46 -0
- package/src/features/Conversation/Messages/Group/index.tsx +140 -0
- package/src/features/Conversation/Messages/index.tsx +12 -0
- package/src/features/Conversation/components/ShareMessageModal/ShareImage/Preview.tsx +2 -2
- package/src/services/chat/contextEngineering.ts +6 -5
- package/src/services/message/server.ts +10 -0
- package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChatV2.test.ts +309 -2
- package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +2 -22
- package/src/store/chat/slices/aiChat/actions/generateAIChatV2.ts +272 -14
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 2.0.0-next.25](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.24...v2.0.0-next.25)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2025-11-04**</sup>
|
|
8
|
+
|
|
9
|
+
#### ✨ Features
|
|
10
|
+
|
|
11
|
+
- **misc**: Display assistant message in group.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### What's improved
|
|
19
|
+
|
|
20
|
+
- **misc**: Display assistant message in group, closes [#9941](https://github.com/lobehub/lobe-chat/issues/9941) ([59b6ac3](https://github.com/lobehub/lobe-chat/commit/59b6ac3))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
5
30
|
## [Version 2.0.0-next.24](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.23...v2.0.0-next.24)
|
|
6
31
|
|
|
7
32
|
<sup>Released on **2025-11-04**</sup>
|
package/changelog/v1.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/lobehub",
|
|
3
|
-
"version": "2.0.0-next.
|
|
3
|
+
"version": "2.0.0-next.25",
|
|
4
4
|
"description": "LobeHub - an open-source,comprehensive AI Agent 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",
|
|
@@ -24,13 +24,13 @@ const LabsPage = memo(() => {
|
|
|
24
24
|
const [
|
|
25
25
|
isPreferenceInit,
|
|
26
26
|
enableInputMarkdown,
|
|
27
|
-
|
|
27
|
+
enableAssistantMessageGroup,
|
|
28
28
|
// enableGroupChat,
|
|
29
29
|
updateLab,
|
|
30
30
|
] = useUserStore((s) => [
|
|
31
31
|
preferenceSelectors.isPreferenceInit(s),
|
|
32
32
|
labPreferSelectors.enableInputMarkdown(s),
|
|
33
|
-
|
|
33
|
+
labPreferSelectors.enableAssistantMessageGroup(s),
|
|
34
34
|
// labPreferSelectors.enableGroupChat(s),
|
|
35
35
|
s.updateLab,
|
|
36
36
|
]);
|
|
@@ -43,12 +43,13 @@ const LabsPage = memo(() => {
|
|
|
43
43
|
key: 'enableInputMarkdown',
|
|
44
44
|
title: t('features.inputMarkdown.title'),
|
|
45
45
|
},
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
{
|
|
47
|
+
checked: enableAssistantMessageGroup,
|
|
48
|
+
cover: 'https://github.com/user-attachments/assets/ba517751-1f3b-4269-979e-f8471e3ebb89',
|
|
49
|
+
desc: t('features.assistantMessageGroup.desc'),
|
|
50
|
+
key: 'enableAssistantMessageGroup',
|
|
51
|
+
title: t('features.assistantMessageGroup.title'),
|
|
52
|
+
},
|
|
52
53
|
// {
|
|
53
54
|
// checked: enableGroupChat,
|
|
54
55
|
// cover: 'https://github.com/user-attachments/assets/72894d24-a96a-4d7c-a823-ff9e6a1a8b6d',
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { AssistantContentBlock, UIChatMessage } from '@lobechat/types';
|
|
2
|
+
import { ActionIconGroup, type ActionIconGroupEvent, ActionIconGroupItemType } from '@lobehub/ui';
|
|
3
|
+
import { App } from 'antd';
|
|
4
|
+
import { useSearchParams } from 'next/navigation';
|
|
5
|
+
import { memo, use, useCallback, useContext, useMemo, useState } from 'react';
|
|
6
|
+
import { useTranslation } from 'react-i18next';
|
|
7
|
+
|
|
8
|
+
import ShareMessageModal from '@/features/Conversation/components/ShareMessageModal';
|
|
9
|
+
import { VirtuosoContext } from '@/features/Conversation/components/VirtualizedList/VirtuosoContext';
|
|
10
|
+
import { useChatStore } from '@/store/chat';
|
|
11
|
+
import { threadSelectors } from '@/store/chat/selectors';
|
|
12
|
+
import { useSessionStore } from '@/store/session';
|
|
13
|
+
import { sessionSelectors } from '@/store/session/selectors';
|
|
14
|
+
|
|
15
|
+
import { InPortalThreadContext } from '../../../context/InPortalThreadContext';
|
|
16
|
+
import { useChatListActionsBar } from '../../../hooks/useChatListActionsBar';
|
|
17
|
+
|
|
18
|
+
interface GroupActionsProps {
|
|
19
|
+
contentBlock?: AssistantContentBlock;
|
|
20
|
+
data: UIChatMessage;
|
|
21
|
+
id: string;
|
|
22
|
+
index: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const WithContentId = memo<GroupActionsProps>(({ id, data, index, contentBlock }) => {
|
|
26
|
+
const { tools } = data;
|
|
27
|
+
const [isThreadMode, hasThread] = useChatStore((s) => [
|
|
28
|
+
!!s.activeThreadId,
|
|
29
|
+
threadSelectors.hasThreadBySourceMsgId(id)(s),
|
|
30
|
+
]);
|
|
31
|
+
const isGroupSession = useSessionStore(sessionSelectors.isCurrentSessionGroupSession);
|
|
32
|
+
const [showShareModal, setShareModal] = useState(false);
|
|
33
|
+
|
|
34
|
+
const { edit, delAndRegenerate, copy, divider, del, branching, share } = useChatListActionsBar({
|
|
35
|
+
hasThread,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const hasTools = !!tools;
|
|
39
|
+
|
|
40
|
+
const inPortalThread = useContext(InPortalThreadContext);
|
|
41
|
+
const inThread = isThreadMode || inPortalThread;
|
|
42
|
+
|
|
43
|
+
const items = useMemo(() => {
|
|
44
|
+
if (hasTools) return [delAndRegenerate, copy];
|
|
45
|
+
|
|
46
|
+
return [edit, copy, inThread || isGroupSession ? null : branching].filter(
|
|
47
|
+
Boolean,
|
|
48
|
+
) as ActionIconGroupItemType[];
|
|
49
|
+
}, [inThread, hasTools, isGroupSession]);
|
|
50
|
+
|
|
51
|
+
const { t } = useTranslation('common');
|
|
52
|
+
const searchParams = useSearchParams();
|
|
53
|
+
const topic = searchParams.get('topic');
|
|
54
|
+
const [
|
|
55
|
+
deleteMessage,
|
|
56
|
+
translateMessage,
|
|
57
|
+
delAndRegenerateMessage,
|
|
58
|
+
copyMessage,
|
|
59
|
+
openThreadCreator,
|
|
60
|
+
delAndResendThreadMessage,
|
|
61
|
+
toggleMessageEditing,
|
|
62
|
+
] = useChatStore((s) => [
|
|
63
|
+
s.deleteMessage,
|
|
64
|
+
s.translateMessage,
|
|
65
|
+
s.delAndRegenerateMessage,
|
|
66
|
+
s.copyMessage,
|
|
67
|
+
s.openThreadCreator,
|
|
68
|
+
s.delAndResendThreadMessage,
|
|
69
|
+
s.toggleMessageEditing,
|
|
70
|
+
]);
|
|
71
|
+
const { message } = App.useApp();
|
|
72
|
+
const virtuosoRef = use(VirtuosoContext);
|
|
73
|
+
|
|
74
|
+
const onActionClick = useCallback(
|
|
75
|
+
async (action: ActionIconGroupEvent) => {
|
|
76
|
+
switch (action.key) {
|
|
77
|
+
case 'edit': {
|
|
78
|
+
toggleMessageEditing(id, true);
|
|
79
|
+
|
|
80
|
+
virtuosoRef?.current?.scrollIntoView({ align: 'start', behavior: 'auto', index });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (!data) return;
|
|
84
|
+
|
|
85
|
+
switch (action.key) {
|
|
86
|
+
case 'copy': {
|
|
87
|
+
if (!contentBlock) return;
|
|
88
|
+
await copyMessage(id, contentBlock.content);
|
|
89
|
+
message.success(t('copySuccess', { defaultValue: 'Copy Success' }));
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
case 'branching': {
|
|
93
|
+
if (!topic) {
|
|
94
|
+
message.warning(t('branchingRequiresSavedTopic'));
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
openThreadCreator(id);
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
case 'del': {
|
|
102
|
+
deleteMessage(id);
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
case 'delAndRegenerate': {
|
|
107
|
+
if (inPortalThread) {
|
|
108
|
+
delAndResendThreadMessage(id);
|
|
109
|
+
} else {
|
|
110
|
+
delAndRegenerateMessage(id);
|
|
111
|
+
}
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
case 'share': {
|
|
116
|
+
setShareModal(true);
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (action.keyPath.at(-1) === 'translate') {
|
|
122
|
+
// click the menu data with translate data, the result is:
|
|
123
|
+
// key: 'en-US'
|
|
124
|
+
// keyPath: ['en-US','translate']
|
|
125
|
+
const lang = action.keyPath[0];
|
|
126
|
+
translateMessage(id, lang);
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
[data, topic],
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<>
|
|
134
|
+
<ActionIconGroup
|
|
135
|
+
items={items}
|
|
136
|
+
menu={{
|
|
137
|
+
items: [edit, copy, divider, share, divider, delAndRegenerate, del],
|
|
138
|
+
}}
|
|
139
|
+
onActionClick={onActionClick}
|
|
140
|
+
/>
|
|
141
|
+
<ShareMessageModal
|
|
142
|
+
message={data!}
|
|
143
|
+
onCancel={() => {
|
|
144
|
+
setShareModal(false);
|
|
145
|
+
}}
|
|
146
|
+
open={showShareModal}
|
|
147
|
+
/>
|
|
148
|
+
</>
|
|
149
|
+
);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
export default WithContentId;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { UIChatMessage } from '@lobechat/types';
|
|
2
|
+
import { ActionIconGroup, type ActionIconGroupEvent, ActionIconGroupItemType } from '@lobehub/ui';
|
|
3
|
+
import { useSearchParams } from 'next/navigation';
|
|
4
|
+
import { memo, useCallback, useContext, useMemo } from 'react';
|
|
5
|
+
|
|
6
|
+
import { useChatStore } from '@/store/chat';
|
|
7
|
+
import { threadSelectors } from '@/store/chat/selectors';
|
|
8
|
+
import { useSessionStore } from '@/store/session';
|
|
9
|
+
import { sessionSelectors } from '@/store/session/selectors';
|
|
10
|
+
|
|
11
|
+
import { InPortalThreadContext } from '../../../context/InPortalThreadContext';
|
|
12
|
+
import { useChatListActionsBar } from '../../../hooks/useChatListActionsBar';
|
|
13
|
+
|
|
14
|
+
interface GroupActionsProps {
|
|
15
|
+
data: UIChatMessage;
|
|
16
|
+
id: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const WithoutContentId = memo<GroupActionsProps>(({ id, data }) => {
|
|
20
|
+
const [isThreadMode, hasThread] = useChatStore((s) => [
|
|
21
|
+
!!s.activeThreadId,
|
|
22
|
+
threadSelectors.hasThreadBySourceMsgId(id)(s),
|
|
23
|
+
]);
|
|
24
|
+
const isGroupSession = useSessionStore(sessionSelectors.isCurrentSessionGroupSession);
|
|
25
|
+
|
|
26
|
+
const { delAndRegenerate, del } = useChatListActionsBar({ hasThread });
|
|
27
|
+
|
|
28
|
+
const inPortalThread = useContext(InPortalThreadContext);
|
|
29
|
+
const inThread = isThreadMode || inPortalThread;
|
|
30
|
+
|
|
31
|
+
const items = useMemo(() => {
|
|
32
|
+
return [delAndRegenerate, del].filter(Boolean) as ActionIconGroupItemType[];
|
|
33
|
+
}, [inThread, isGroupSession]);
|
|
34
|
+
|
|
35
|
+
const searchParams = useSearchParams();
|
|
36
|
+
const topic = searchParams.get('topic');
|
|
37
|
+
|
|
38
|
+
const [deleteMessage, delAndRegenerateMessage, delAndResendThreadMessage] = useChatStore((s) => [
|
|
39
|
+
s.deleteMessage,
|
|
40
|
+
s.delAndRegenerateMessage,
|
|
41
|
+
s.delAndResendThreadMessage,
|
|
42
|
+
]);
|
|
43
|
+
|
|
44
|
+
const onActionClick = useCallback(
|
|
45
|
+
async (action: ActionIconGroupEvent) => {
|
|
46
|
+
if (!data) return;
|
|
47
|
+
|
|
48
|
+
switch (action.key) {
|
|
49
|
+
case 'del': {
|
|
50
|
+
deleteMessage(id);
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
case 'delAndRegenerate': {
|
|
55
|
+
if (inPortalThread) {
|
|
56
|
+
delAndResendThreadMessage(id);
|
|
57
|
+
} else {
|
|
58
|
+
delAndRegenerateMessage(id);
|
|
59
|
+
}
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
[data, topic],
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
return <ActionIconGroup items={items} onActionClick={onActionClick} />;
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
export default WithoutContentId;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { AssistantContentBlock, UIChatMessage } from '@lobechat/types';
|
|
2
|
+
import { memo } from 'react';
|
|
3
|
+
|
|
4
|
+
import WithContentId from './WithContentId';
|
|
5
|
+
import WithoutContentId from './WithoutContentId';
|
|
6
|
+
|
|
7
|
+
interface GroupActionsProps {
|
|
8
|
+
contentBlock?: AssistantContentBlock;
|
|
9
|
+
contentId?: string;
|
|
10
|
+
data: UIChatMessage;
|
|
11
|
+
id: string;
|
|
12
|
+
index: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const GroupActionsBar = memo<GroupActionsProps>(
|
|
16
|
+
({ id, data, contentBlock, index, contentId }) => {
|
|
17
|
+
if (!contentId) return <WithoutContentId data={data} id={id} />;
|
|
18
|
+
|
|
19
|
+
return <WithContentId contentBlock={contentBlock} data={data} id={contentId} index={index} />;
|
|
20
|
+
},
|
|
21
|
+
);
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { AssistantContentBlock } from '@lobechat/types';
|
|
2
|
+
import { MarkdownProps } from '@lobehub/ui';
|
|
3
|
+
import { memo, useMemo } from 'react';
|
|
4
|
+
import { Flexbox } from 'react-layout-kit';
|
|
5
|
+
|
|
6
|
+
import { LOADING_FLAT } from '@/const/message';
|
|
7
|
+
import { markdownElements } from '@/features/Conversation/MarkdownElements';
|
|
8
|
+
import Reasoning from '@/features/Conversation/Messages/Assistant/Reasoning';
|
|
9
|
+
import { useChatStore } from '@/store/chat';
|
|
10
|
+
import { aiChatSelectors, messageStateSelectors } from '@/store/chat/selectors';
|
|
11
|
+
import { useUserStore } from '@/store/user';
|
|
12
|
+
import { userGeneralSettingsSelectors } from '@/store/user/selectors';
|
|
13
|
+
|
|
14
|
+
import ImageFileListViewer from '../User/ImageFileListViewer';
|
|
15
|
+
import ErrorContent from './Error';
|
|
16
|
+
import MessageContent from './MessageContent';
|
|
17
|
+
import { Tools } from './Tools';
|
|
18
|
+
|
|
19
|
+
const rehypePlugins = markdownElements.map((element) => element.rehypePlugin).filter(Boolean);
|
|
20
|
+
const remarkPlugins = markdownElements.map((element) => element.remarkPlugin).filter(Boolean);
|
|
21
|
+
|
|
22
|
+
interface ContentBlockProps extends AssistantContentBlock {
|
|
23
|
+
index: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const ContentBlock = memo<ContentBlockProps>((props) => {
|
|
27
|
+
const { id, tools, content, imageList, reasoning, error } = props;
|
|
28
|
+
const showImageItems = !!imageList && imageList.length > 0;
|
|
29
|
+
const isReasoning = useChatStore(aiChatSelectors.isMessageInReasoning(id));
|
|
30
|
+
|
|
31
|
+
const hasTools = tools && tools.length > 0;
|
|
32
|
+
const showReasoning =
|
|
33
|
+
(!!reasoning && reasoning.content?.trim() !== '') || (!reasoning && isReasoning);
|
|
34
|
+
|
|
35
|
+
const { transitionMode, highlighterTheme, mermaidTheme } = useUserStore(
|
|
36
|
+
userGeneralSettingsSelectors.config,
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const generating = useChatStore(messageStateSelectors.isMessageGenerating(id));
|
|
40
|
+
|
|
41
|
+
const animated = transitionMode === 'fadeIn' && generating;
|
|
42
|
+
|
|
43
|
+
const components = useMemo(
|
|
44
|
+
() =>
|
|
45
|
+
Object.fromEntries(
|
|
46
|
+
markdownElements.map((element) => {
|
|
47
|
+
const Component = element.Component;
|
|
48
|
+
|
|
49
|
+
return [element.tag, (props: any) => <Component {...props} id={id} />];
|
|
50
|
+
}),
|
|
51
|
+
),
|
|
52
|
+
[id],
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const markdownProps: Omit<MarkdownProps, 'className' | 'style' | 'children'> = useMemo(
|
|
56
|
+
() => ({
|
|
57
|
+
animated,
|
|
58
|
+
componentProps: {
|
|
59
|
+
highlight: {
|
|
60
|
+
theme: highlighterTheme,
|
|
61
|
+
},
|
|
62
|
+
mermaid: { theme: mermaidTheme },
|
|
63
|
+
},
|
|
64
|
+
components,
|
|
65
|
+
enableCustomFootnotes: true,
|
|
66
|
+
rehypePlugins,
|
|
67
|
+
remarkPlugins,
|
|
68
|
+
}),
|
|
69
|
+
[animated, components, highlighterTheme, mermaidTheme],
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
if (error && (content === LOADING_FLAT || !content))
|
|
73
|
+
return <ErrorContent error={error} id={id} />;
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<Flexbox gap={8} id={id}>
|
|
77
|
+
{showReasoning && <Reasoning {...reasoning} id={id} />}
|
|
78
|
+
|
|
79
|
+
{/* Content - markdown text */}
|
|
80
|
+
{content && (
|
|
81
|
+
<MessageContent content={content} hasTools={hasTools} markdownProps={markdownProps} />
|
|
82
|
+
)}
|
|
83
|
+
|
|
84
|
+
{/* Image files */}
|
|
85
|
+
{showImageItems && <ImageFileListViewer items={imageList} />}
|
|
86
|
+
|
|
87
|
+
{/* Tools */}
|
|
88
|
+
{hasTools && <Tools messageId={id} tools={tools} />}
|
|
89
|
+
</Flexbox>
|
|
90
|
+
);
|
|
91
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { MessageInput } from '@lobehub/ui/chat';
|
|
2
|
+
import { memo, useMemo } from 'react';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import { Flexbox } from 'react-layout-kit';
|
|
5
|
+
|
|
6
|
+
import { useChatStore } from '@/store/chat';
|
|
7
|
+
|
|
8
|
+
export interface EditStateProps {
|
|
9
|
+
content: string;
|
|
10
|
+
id: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const EditState = memo<EditStateProps>(({ id, content }) => {
|
|
14
|
+
const { t } = useTranslation('common');
|
|
15
|
+
|
|
16
|
+
const text = useMemo(
|
|
17
|
+
() => ({
|
|
18
|
+
cancel: t('cancel'),
|
|
19
|
+
confirm: t('ok'),
|
|
20
|
+
edit: t('edit'),
|
|
21
|
+
}),
|
|
22
|
+
[],
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const [toggleMessageEditing, updateMessageContent] = useChatStore((s) => [
|
|
26
|
+
s.toggleMessageEditing,
|
|
27
|
+
s.modifyMessageContent,
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
const onEditingChange = (value: string) => {
|
|
31
|
+
updateMessageContent(id, value);
|
|
32
|
+
toggleMessageEditing(id, false);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<Flexbox paddingBlock={'0 8px'}>
|
|
37
|
+
<MessageInput
|
|
38
|
+
defaultValue={content ? String(content) : ''}
|
|
39
|
+
editButtonSize={'small'}
|
|
40
|
+
onCancel={() => {
|
|
41
|
+
toggleMessageEditing(id, false);
|
|
42
|
+
}}
|
|
43
|
+
onConfirm={onEditingChange}
|
|
44
|
+
text={text}
|
|
45
|
+
variant={'outlined'}
|
|
46
|
+
/>
|
|
47
|
+
</Flexbox>
|
|
48
|
+
);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
export default EditState;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { ChatMessageError } from '@lobechat/types';
|
|
2
|
+
import { Alert } from '@lobehub/ui';
|
|
3
|
+
import { Button } from 'antd';
|
|
4
|
+
import { memo } from 'react';
|
|
5
|
+
import { useTranslation } from 'react-i18next';
|
|
6
|
+
import { Flexbox } from 'react-layout-kit';
|
|
7
|
+
|
|
8
|
+
import ErrorMessageExtra, { useErrorContent } from '@/features/Conversation/Error';
|
|
9
|
+
import { useChatStore } from '@/store/chat';
|
|
10
|
+
|
|
11
|
+
export interface ErrorContentProps {
|
|
12
|
+
error?: ChatMessageError;
|
|
13
|
+
id: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const ErrorContent = memo<ErrorContentProps>(({ error, id }) => {
|
|
17
|
+
const { t } = useTranslation('common');
|
|
18
|
+
const errorProps = useErrorContent(error);
|
|
19
|
+
|
|
20
|
+
const [deleteMessage] = useChatStore((s) => [s.deleteMessage]);
|
|
21
|
+
const message = <ErrorMessageExtra block data={{ error, id }} />;
|
|
22
|
+
|
|
23
|
+
if (!error?.message) {
|
|
24
|
+
if (!message) return null;
|
|
25
|
+
return <Flexbox>{message}</Flexbox>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<Flexbox>
|
|
30
|
+
<Alert
|
|
31
|
+
action={
|
|
32
|
+
<Button
|
|
33
|
+
color={'default'}
|
|
34
|
+
onClick={() => {
|
|
35
|
+
deleteMessage(id);
|
|
36
|
+
}}
|
|
37
|
+
size={'small'}
|
|
38
|
+
variant={'filled'}
|
|
39
|
+
>
|
|
40
|
+
{t('delete')}
|
|
41
|
+
</Button>
|
|
42
|
+
}
|
|
43
|
+
closable={false}
|
|
44
|
+
extra={message}
|
|
45
|
+
showIcon
|
|
46
|
+
type={'error'}
|
|
47
|
+
{...errorProps}
|
|
48
|
+
/>
|
|
49
|
+
</Flexbox>
|
|
50
|
+
);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
export default ErrorContent;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { AssistantContentBlock } from '@lobechat/types';
|
|
2
|
+
import { createStyles } from 'antd-style';
|
|
3
|
+
import { motion } from 'framer-motion';
|
|
4
|
+
import { memo, use } from 'react';
|
|
5
|
+
import { Flexbox } from 'react-layout-kit';
|
|
6
|
+
|
|
7
|
+
import { VirtuosoContext } from '@/features/Conversation/components/VirtualizedList/VirtuosoContext';
|
|
8
|
+
import { useChatStore } from '@/store/chat';
|
|
9
|
+
|
|
10
|
+
import { ContentBlock } from './ContentBlock';
|
|
11
|
+
|
|
12
|
+
const useStyles = createStyles(({ css }) => {
|
|
13
|
+
return {
|
|
14
|
+
container: css`
|
|
15
|
+
&:has(.tool-blocks) {
|
|
16
|
+
width: 100%;
|
|
17
|
+
}
|
|
18
|
+
`,
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
interface GroupChildrenProps {
|
|
23
|
+
blocks: AssistantContentBlock[];
|
|
24
|
+
contentId?: string;
|
|
25
|
+
disableEditing?: boolean;
|
|
26
|
+
messageIndex: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const GroupChildren = memo<GroupChildrenProps>(
|
|
30
|
+
({ blocks, contentId, disableEditing, messageIndex }) => {
|
|
31
|
+
const { styles } = useStyles();
|
|
32
|
+
|
|
33
|
+
const [toggleMessageEditing] = useChatStore((s) => [s.toggleMessageEditing]);
|
|
34
|
+
const virtuosoRef = use(VirtuosoContext);
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<Flexbox className={styles.container} gap={8}>
|
|
38
|
+
{blocks.map((item, index) => {
|
|
39
|
+
return item.id === contentId ? (
|
|
40
|
+
<Flexbox
|
|
41
|
+
key={index}
|
|
42
|
+
onDoubleClick={(e) => {
|
|
43
|
+
if (disableEditing || item.error || !e.altKey) return;
|
|
44
|
+
|
|
45
|
+
toggleMessageEditing(item.id, true);
|
|
46
|
+
virtuosoRef?.current?.scrollIntoView({
|
|
47
|
+
align: 'start',
|
|
48
|
+
behavior: 'auto',
|
|
49
|
+
index: messageIndex,
|
|
50
|
+
});
|
|
51
|
+
}}
|
|
52
|
+
>
|
|
53
|
+
<ContentBlock index={index} {...item} />
|
|
54
|
+
</Flexbox>
|
|
55
|
+
) : (
|
|
56
|
+
<motion.div
|
|
57
|
+
animate={{ height: 'auto', opacity: 1 }}
|
|
58
|
+
exit={{ height: 0, opacity: 0 }}
|
|
59
|
+
initial={{ height: 0, opacity: 0 }}
|
|
60
|
+
key={index}
|
|
61
|
+
style={{ overflow: 'hidden' }}
|
|
62
|
+
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
|
63
|
+
>
|
|
64
|
+
<ContentBlock index={index} {...item} />
|
|
65
|
+
</motion.div>
|
|
66
|
+
);
|
|
67
|
+
})}
|
|
68
|
+
</Flexbox>
|
|
69
|
+
);
|
|
70
|
+
},
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
export default GroupChildren;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Markdown, MarkdownProps } from '@lobehub/ui';
|
|
2
|
+
import { createStyles } from 'antd-style';
|
|
3
|
+
import { memo } from 'react';
|
|
4
|
+
|
|
5
|
+
import BubblesLoading from '@/components/BubblesLoading';
|
|
6
|
+
import { LOADING_FLAT } from '@/const/message';
|
|
7
|
+
|
|
8
|
+
import { normalizeThinkTags, processWithArtifact } from '../../utils/markdown';
|
|
9
|
+
|
|
10
|
+
const useStyles = createStyles(({ css, token }) => {
|
|
11
|
+
return {
|
|
12
|
+
pWithTool: css`
|
|
13
|
+
color: ${token.colorTextTertiary};
|
|
14
|
+
`,
|
|
15
|
+
};
|
|
16
|
+
});
|
|
17
|
+
interface ContentBlockProps {
|
|
18
|
+
content: string;
|
|
19
|
+
hasTools?: boolean;
|
|
20
|
+
markdownProps?: Omit<MarkdownProps, 'className' | 'style' | 'children'>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const MessageContent = memo<ContentBlockProps>(({ content, hasTools, markdownProps }) => {
|
|
24
|
+
const message = normalizeThinkTags(processWithArtifact(content));
|
|
25
|
+
|
|
26
|
+
const { styles, cx } = useStyles();
|
|
27
|
+
|
|
28
|
+
if (content === LOADING_FLAT) return <BubblesLoading />;
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
content && (
|
|
32
|
+
<Markdown {...markdownProps} className={cx(hasTools && styles.pWithTool)} variant={'chat'}>
|
|
33
|
+
{message}
|
|
34
|
+
</Markdown>
|
|
35
|
+
)
|
|
36
|
+
);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
export default MessageContent;
|