@lobehub/lobehub 2.0.0-next.35 → 2.0.0-next.37

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 (156) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/changelog/v1.json +18 -0
  3. package/next.config.ts +5 -6
  4. package/package.json +2 -2
  5. package/packages/agent-runtime/src/core/__tests__/runtime.test.ts +112 -77
  6. package/packages/agent-runtime/src/core/runtime.ts +63 -18
  7. package/packages/agent-runtime/src/types/generalAgent.ts +55 -0
  8. package/packages/agent-runtime/src/types/index.ts +1 -0
  9. package/packages/agent-runtime/src/types/instruction.ts +10 -3
  10. package/packages/const/src/user.ts +0 -1
  11. package/packages/context-engine/src/processors/GroupMessageFlatten.ts +8 -6
  12. package/packages/context-engine/src/processors/__tests__/GroupMessageFlatten.test.ts +12 -12
  13. package/packages/conversation-flow/src/__tests__/fixtures/inputs/branch/assistant-group-branches.json +249 -0
  14. package/packages/conversation-flow/src/__tests__/fixtures/inputs/branch/index.ts +4 -0
  15. package/packages/conversation-flow/src/__tests__/fixtures/inputs/branch/multi-assistant-group.json +260 -0
  16. package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/active-index-1.json +4 -0
  17. package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/assistant-group-branches.json +481 -0
  18. package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/conversation.json +5 -1
  19. package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/index.ts +4 -0
  20. package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/multi-assistant-group.json +407 -0
  21. package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/nested.json +18 -2
  22. package/packages/conversation-flow/src/__tests__/fixtures/outputs/complex-scenario.json +25 -3
  23. package/packages/conversation-flow/src/__tests__/parse.test.ts +12 -0
  24. package/packages/conversation-flow/src/index.ts +1 -1
  25. package/packages/conversation-flow/src/transformation/FlatListBuilder.ts +112 -34
  26. package/packages/conversation-flow/src/types/flatMessageList.ts +0 -12
  27. package/packages/conversation-flow/src/{types.ts → types/index.ts} +3 -14
  28. package/packages/database/src/models/__tests__/apiKey.test.ts +444 -0
  29. package/packages/database/src/models/message.ts +18 -19
  30. package/packages/types/src/aiChat.ts +2 -0
  31. package/packages/types/src/importer.ts +2 -2
  32. package/packages/types/src/message/ui/chat.ts +17 -1
  33. package/packages/types/src/message/ui/extra.ts +2 -2
  34. package/packages/types/src/message/ui/params.ts +2 -2
  35. package/packages/types/src/user/preference.ts +0 -4
  36. package/packages/utils/src/tokenizer/index.ts +3 -11
  37. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/Desktop/MessageFromUrl.tsx +3 -3
  38. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/V1Mobile/index.tsx +1 -1
  39. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/V1Mobile/useSend.ts +3 -3
  40. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/useSend.ts +6 -6
  41. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatList/Content.tsx +5 -3
  42. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatList/WelcomeChatItem/AgentWelcome/OpeningQuestions.tsx +2 -2
  43. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatList/WelcomeChatItem/GroupWelcome/GroupUsageSuggest.tsx +2 -2
  44. package/src/app/[variants]/(main)/labs/page.tsx +0 -9
  45. package/src/features/ChatInput/ActionBar/STT/browser.tsx +3 -3
  46. package/src/features/ChatInput/ActionBar/STT/openai.tsx +3 -3
  47. package/src/features/Conversation/Error/AccessCodeForm.tsx +1 -1
  48. package/src/features/Conversation/Error/ChatInvalidApiKey.tsx +1 -1
  49. package/src/features/Conversation/Error/ClerkLogin/index.tsx +1 -1
  50. package/src/features/Conversation/Error/OAuthForm.tsx +1 -1
  51. package/src/features/Conversation/Error/index.tsx +0 -5
  52. package/src/features/Conversation/Messages/Assistant/Actions/index.tsx +13 -10
  53. package/src/features/Conversation/Messages/Assistant/Extra/index.test.tsx +3 -8
  54. package/src/features/Conversation/Messages/Assistant/Extra/index.tsx +2 -6
  55. package/src/features/Conversation/Messages/Assistant/MessageContent.tsx +7 -9
  56. package/src/features/Conversation/Messages/Assistant/Tool/Inspector/PluginResult.tsx +2 -2
  57. package/src/features/Conversation/Messages/Assistant/Tool/Inspector/PluginState.tsx +2 -2
  58. package/src/features/Conversation/Messages/Assistant/Tool/Render/PluginSettings.tsx +4 -1
  59. package/src/features/Conversation/Messages/Assistant/Tool/Render/index.tsx +2 -3
  60. package/src/features/Conversation/Messages/Assistant/index.tsx +57 -60
  61. package/src/features/Conversation/Messages/Default.tsx +1 -0
  62. package/src/features/Conversation/Messages/Group/Actions/WithContentId.tsx +38 -10
  63. package/src/features/Conversation/Messages/Group/Actions/index.tsx +1 -1
  64. package/src/features/Conversation/Messages/Group/ContentBlock.tsx +1 -3
  65. package/src/features/Conversation/Messages/Group/GroupChildren.tsx +12 -12
  66. package/src/features/Conversation/Messages/Group/MessageContent.tsx +7 -1
  67. package/src/features/Conversation/Messages/Group/Tool/Render/PluginSettings.tsx +1 -1
  68. package/src/features/Conversation/Messages/Group/index.tsx +2 -1
  69. package/src/features/Conversation/Messages/Supervisor/index.tsx +2 -2
  70. package/src/features/Conversation/Messages/User/{Actions.tsx → Actions/ActionsBar.tsx} +26 -25
  71. package/src/features/Conversation/Messages/User/Actions/MessageBranch.tsx +107 -0
  72. package/src/features/Conversation/Messages/User/Actions/index.tsx +42 -0
  73. package/src/features/Conversation/Messages/User/index.tsx +43 -44
  74. package/src/features/Conversation/Messages/index.tsx +3 -3
  75. package/src/features/Conversation/components/AutoScroll.tsx +3 -3
  76. package/src/features/Conversation/components/Extras/Usage/UsageDetail/AnimatedNumber.tsx +55 -0
  77. package/src/features/Conversation/components/Extras/Usage/UsageDetail/index.tsx +5 -2
  78. package/src/features/Conversation/components/VirtualizedList/index.tsx +29 -20
  79. package/src/features/Conversation/hooks/useChatListActionsBar.tsx +8 -10
  80. package/src/features/Portal/Thread/Chat/ChatInput/useSend.ts +3 -3
  81. package/src/hooks/useHotkeys/chatScope.ts +15 -7
  82. package/src/libs/trpc/client/lambda.ts +4 -3
  83. package/src/server/routers/lambda/__tests__/aiChat.test.ts +1 -1
  84. package/src/server/routers/lambda/__tests__/integration/message.integration.test.ts +0 -26
  85. package/src/server/routers/lambda/aiChat.ts +3 -2
  86. package/src/server/routers/lambda/message.ts +8 -16
  87. package/src/server/services/message/__tests__/index.test.ts +29 -39
  88. package/src/server/services/message/index.ts +41 -36
  89. package/src/services/electron/desktopNotification.ts +6 -6
  90. package/src/services/electron/file.ts +6 -6
  91. package/src/services/file/ClientS3/index.ts +8 -8
  92. package/src/services/message/__tests__/metadata-race-condition.test.ts +157 -0
  93. package/src/services/message/index.ts +21 -15
  94. package/src/services/upload.ts +11 -11
  95. package/src/services/utils/abortableRequest.test.ts +161 -0
  96. package/src/services/utils/abortableRequest.ts +67 -0
  97. package/src/store/chat/agents/GeneralChatAgent.ts +137 -0
  98. package/src/store/chat/agents/createAgentExecutors.ts +395 -0
  99. package/src/store/chat/helpers.test.ts +0 -99
  100. package/src/store/chat/helpers.ts +0 -11
  101. package/src/store/chat/slices/aiChat/actions/__tests__/conversationControl.test.ts +332 -0
  102. package/src/store/chat/slices/aiChat/actions/__tests__/conversationLifecycle.test.ts +257 -0
  103. package/src/store/chat/slices/aiChat/actions/__tests__/helpers.ts +11 -2
  104. package/src/store/chat/slices/aiChat/actions/__tests__/rag.test.ts +6 -6
  105. package/src/store/chat/slices/aiChat/actions/__tests__/streamingExecutor.test.ts +391 -0
  106. package/src/store/chat/slices/aiChat/actions/__tests__/streamingStates.test.ts +179 -0
  107. package/src/store/chat/slices/aiChat/actions/conversationControl.ts +157 -0
  108. package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +329 -0
  109. package/src/store/chat/slices/aiChat/actions/generateAIGroupChat.ts +14 -14
  110. package/src/store/chat/slices/aiChat/actions/index.ts +12 -6
  111. package/src/store/chat/slices/aiChat/actions/rag.ts +9 -6
  112. package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +604 -0
  113. package/src/store/chat/slices/aiChat/actions/streamingStates.ts +84 -0
  114. package/src/store/chat/slices/builtinTool/actions/__tests__/localSystem.test.ts +4 -4
  115. package/src/store/chat/slices/builtinTool/actions/__tests__/search.test.ts +11 -11
  116. package/src/store/chat/slices/builtinTool/actions/interpreter.ts +8 -8
  117. package/src/store/chat/slices/builtinTool/actions/localSystem.ts +2 -2
  118. package/src/store/chat/slices/builtinTool/actions/search.ts +8 -8
  119. package/src/store/chat/slices/message/action.test.ts +79 -68
  120. package/src/store/chat/slices/message/actions/index.ts +39 -0
  121. package/src/store/chat/slices/message/actions/internals.ts +77 -0
  122. package/src/store/chat/slices/message/actions/optimisticUpdate.ts +260 -0
  123. package/src/store/chat/slices/message/actions/publicApi.ts +224 -0
  124. package/src/store/chat/slices/message/actions/query.ts +120 -0
  125. package/src/store/chat/slices/message/actions/runtimeState.ts +108 -0
  126. package/src/store/chat/slices/message/initialState.ts +13 -0
  127. package/src/store/chat/slices/message/reducer.test.ts +48 -370
  128. package/src/store/chat/slices/message/reducer.ts +17 -81
  129. package/src/store/chat/slices/message/selectors/chat.test.ts +13 -50
  130. package/src/store/chat/slices/message/selectors/chat.ts +78 -242
  131. package/src/store/chat/slices/message/selectors/dbMessage.ts +140 -0
  132. package/src/store/chat/slices/message/selectors/displayMessage.ts +301 -0
  133. package/src/store/chat/slices/message/selectors/messageState.ts +5 -2
  134. package/src/store/chat/slices/plugin/action.test.ts +62 -64
  135. package/src/store/chat/slices/plugin/action.ts +34 -28
  136. package/src/store/chat/slices/thread/action.test.ts +28 -31
  137. package/src/store/chat/slices/thread/action.ts +13 -10
  138. package/src/store/chat/slices/thread/selectors/index.ts +8 -6
  139. package/src/store/chat/slices/topic/reducer.ts +11 -3
  140. package/src/store/chat/store.ts +1 -1
  141. package/src/store/user/slices/preference/selectors/labPrefer.ts +0 -3
  142. package/packages/database/src/models/__tests__/message.grouping.test.ts +0 -812
  143. package/packages/database/src/utils/__tests__/groupMessages.test.ts +0 -1132
  144. package/packages/database/src/utils/groupMessages.ts +0 -361
  145. package/packages/utils/src/tokenizer/client.ts +0 -35
  146. package/packages/utils/src/tokenizer/estimated.ts +0 -4
  147. package/packages/utils/src/tokenizer/server.ts +0 -11
  148. package/packages/utils/src/tokenizer/tokenizer.worker.ts +0 -12
  149. package/src/app/(backend)/webapi/tokenizer/index.test.ts +0 -32
  150. package/src/app/(backend)/webapi/tokenizer/route.ts +0 -8
  151. package/src/features/Conversation/Error/InvalidAccessCode.tsx +0 -79
  152. package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChat.test.ts +0 -975
  153. package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChatV2.test.ts +0 -1050
  154. package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +0 -720
  155. package/src/store/chat/slices/aiChat/actions/generateAIChatV2.ts +0 -849
  156. package/src/store/chat/slices/message/action.ts +0 -629
@@ -2,14 +2,14 @@ import { Highlighter } from '@lobehub/ui';
2
2
  import { memo } from 'react';
3
3
 
4
4
  import { useChatStore } from '@/store/chat';
5
- import { chatSelectors } from '@/store/chat/selectors';
5
+ import { dbMessageSelectors } from '@/store/chat/selectors';
6
6
 
7
7
  export interface FunctionMessageProps {
8
8
  toolCallId: string;
9
9
  }
10
10
 
11
11
  const PluginState = memo<FunctionMessageProps>(({ toolCallId }) => {
12
- const toolMessage = useChatStore(chatSelectors.getMessageByToolCallId(toolCallId));
12
+ const toolMessage = useChatStore(dbMessageSelectors.getDbMessageByToolCallId(toolCallId));
13
13
 
14
14
  return (
15
15
  toolMessage?.pluginState && (
@@ -23,7 +23,10 @@ const PluginSettings = memo<PluginSettingsProps>(({ id, plugin }) => {
23
23
  const { styles } = useStyles();
24
24
  const { t } = useTranslation('error');
25
25
  const theme = useTheme();
26
- const [resend, deleteMessage] = useChatStore((s) => [s.regenerateMessage, s.deleteMessage]);
26
+ const [resend, deleteMessage] = useChatStore((s) => [
27
+ s.regenerateAssistantMessage,
28
+ s.deleteMessage,
29
+ ]);
27
30
  const pluginIdentifier = plugin?.identifier as string;
28
31
  const pluginMeta = useToolStore(pluginSelectors.getPluginMetaById(pluginIdentifier), isEqual);
29
32
  const manifest = useToolStore(pluginSelectors.getToolManifestById(pluginIdentifier), isEqual);
@@ -1,9 +1,8 @@
1
1
  import { LOADING_FLAT } from '@lobechat/const';
2
- import isEqual from 'fast-deep-equal';
3
2
  import { Suspense, memo } from 'react';
4
3
 
5
4
  import { useChatStore } from '@/store/chat';
6
- import { chatSelectors, messageStateSelectors } from '@/store/chat/selectors';
5
+ import { dbMessageSelectors, messageStateSelectors } from '@/store/chat/selectors';
7
6
 
8
7
  import CustomRender from './CustomRender';
9
8
  import ErrorResponse from './ErrorResponse';
@@ -32,7 +31,7 @@ const Render = memo<RenderProps>(
32
31
  apiName,
33
32
  }) => {
34
33
  const loading = useChatStore(messageStateSelectors.isToolCallStreaming(messageId, toolIndex));
35
- const toolMessage = useChatStore(chatSelectors.getMessageByToolCallId(toolCallId), isEqual);
34
+ const toolMessage = useChatStore(dbMessageSelectors.getDbMessageByToolCallId(toolCallId));
36
35
 
37
36
  if (loading || !toolMessage) return null;
38
37
 
@@ -204,75 +204,72 @@ const AssistantMessage = memo<AssistantMessageProps>((props) => {
204
204
  );
205
205
  const errorMessage = <ErrorMessageExtra data={props} />;
206
206
  return (
207
- <Flexbox
208
- className={styles.container}
209
- direction={placement === 'left' ? 'horizontal' : 'horizontal-reverse'}
210
- gap={mobile ? 6 : 12}
211
- >
212
- <Avatar
213
- alt={avatar.title || 'avatar'}
214
- avatar={avatar}
215
- loading={loading}
216
- onClick={onAvatarClick}
217
- placement={placement}
218
- size={mobile ? MOBILE_AVATAR_SIZE : undefined}
219
- style={{ marginTop: 6 }}
220
- />
221
- <Flexbox align={'flex-start'} className={styles.messageContainer}>
207
+ <Flexbox className={styles.container} gap={mobile ? 6 : 12}>
208
+ <Flexbox gap={4} horizontal>
209
+ <Avatar
210
+ alt={avatar.title || 'avatar'}
211
+ avatar={avatar}
212
+ loading={loading}
213
+ onClick={onAvatarClick}
214
+ placement={placement}
215
+ size={MOBILE_AVATAR_SIZE}
216
+ style={{ marginTop: 6 }}
217
+ />
222
218
  <Title
223
219
  avatar={avatar}
224
220
  placement={placement}
225
- showTitle={showTitle}
221
+ showTitle
226
222
  time={createdAt}
227
223
  titleAddon={dmIndicator}
228
224
  />
229
- <Flexbox
230
- align={'flex-start'}
231
- className={styles.messageContent}
232
- data-layout={'vertical'} // 添加数据属性以方便样式选择
233
- direction={'vertical'}
234
- gap={8}
235
- >
236
- <Flexbox style={{ flex: 1, maxWidth: '100%' }}>
237
- {error && (message === LOADING_FLAT || !message) ? (
238
- <ErrorContent error={errorContent} message={errorMessage} placement={placement} />
239
- ) : (
240
- <MessageContent
241
- editing={editing}
242
- id={id}
243
- markdownProps={markdownProps}
244
- message={reducted ? `*${t('hideForYou')}*` : message}
245
- messageExtra={
246
- <>
247
- {errorContent && (
248
- <ErrorContent
249
- error={errorContent}
250
- message={errorMessage}
251
- placement={placement}
252
- />
253
- )}
254
- <AssistantMessageExtra
255
- content={content}
256
- extra={extra}
257
- id={id}
258
- metadata={metadata}
259
- tools={tools}
225
+ </Flexbox>
226
+ <Flexbox
227
+ align={'flex-start'}
228
+ className={styles.messageContent}
229
+ data-layout={'vertical'} // 添加数据属性以方便样式选择
230
+ direction={'vertical'}
231
+ gap={8}
232
+ width={'fit-content'}
233
+ >
234
+ <Flexbox style={{ flex: 1, maxWidth: '100%' }}>
235
+ {error && (message === LOADING_FLAT || !message) ? (
236
+ <ErrorContent error={errorContent} message={errorMessage} placement={placement} />
237
+ ) : (
238
+ <MessageContent
239
+ editing={editing}
240
+ id={id}
241
+ markdownProps={markdownProps}
242
+ message={reducted ? `*${t('hideForYou')}*` : message}
243
+ messageExtra={
244
+ <>
245
+ {errorContent && (
246
+ <ErrorContent
247
+ error={errorContent}
248
+ message={errorMessage}
249
+ placement={placement}
260
250
  />
261
- </>
262
- }
263
- onDoubleClick={onDoubleClick}
264
- placement={placement}
265
- renderMessage={renderMessage}
266
- variant={variant}
267
- />
268
- )}
269
- </Flexbox>
270
- {!disableEditing && !editing && (
271
- <Flexbox align={'flex-start'} className={styles.actions} role="menubar">
272
- <AssistantActionsBar data={props} id={id} index={index} />
273
- </Flexbox>
251
+ )}
252
+ <AssistantMessageExtra
253
+ content={content}
254
+ extra={extra}
255
+ id={id}
256
+ metadata={metadata}
257
+ tools={tools}
258
+ />
259
+ </>
260
+ }
261
+ onDoubleClick={onDoubleClick}
262
+ placement={placement}
263
+ renderMessage={renderMessage}
264
+ variant={variant}
265
+ />
274
266
  )}
275
267
  </Flexbox>
268
+ {!disableEditing && !editing && (
269
+ <Flexbox align={'flex-start'} className={styles.actions} role="menubar">
270
+ <AssistantActionsBar data={props} id={id} index={index} />
271
+ </Flexbox>
272
+ )}
276
273
  </Flexbox>
277
274
  {mobile && <BorderSpacing borderSpacing={MOBILE_AVATAR_SIZE} />}
278
275
  </Flexbox>
@@ -17,6 +17,7 @@ export const DefaultMessage = memo<
17
17
 
18
18
  if (isToolCallGenerating) return;
19
19
 
20
+ if (!content) return <BubblesLoading />;
20
21
  if (content === LOADING_FLAT && !editing) return <BubblesLoading />;
21
22
 
22
23
  return <div id={addIdOnDOM ? id : undefined}>{editableContent}</div>;
@@ -8,7 +8,7 @@ import { useTranslation } from 'react-i18next';
8
8
  import ShareMessageModal from '@/features/Conversation/components/ShareMessageModal';
9
9
  import { VirtuosoContext } from '@/features/Conversation/components/VirtualizedList/VirtuosoContext';
10
10
  import { useChatStore } from '@/store/chat';
11
- import { threadSelectors } from '@/store/chat/selectors';
11
+ import { messageStateSelectors, threadSelectors } from '@/store/chat/selectors';
12
12
  import { useSessionStore } from '@/store/session';
13
13
  import { sessionSelectors } from '@/store/session/selectors';
14
14
 
@@ -24,16 +24,19 @@ interface GroupActionsProps {
24
24
 
25
25
  const WithContentId = memo<GroupActionsProps>(({ id, data, index, contentBlock }) => {
26
26
  const { tools } = data;
27
- const [isThreadMode, hasThread] = useChatStore((s) => [
27
+ const [isThreadMode, hasThread, isRegenerating] = useChatStore((s) => [
28
28
  !!s.activeThreadId,
29
29
  threadSelectors.hasThreadBySourceMsgId(id)(s),
30
+ messageStateSelectors.isMessageRegenerating(id)(s),
30
31
  ]);
31
32
  const isGroupSession = useSessionStore(sessionSelectors.isCurrentSessionGroupSession);
32
33
  const [showShareModal, setShareModal] = useState(false);
33
34
 
34
- const { edit, delAndRegenerate, copy, divider, del, branching, share } = useChatListActionsBar({
35
- hasThread,
36
- });
35
+ const { edit, delAndRegenerate, regenerate, copy, divider, del, branching, share } =
36
+ useChatListActionsBar({
37
+ hasThread,
38
+ isRegenerating,
39
+ });
37
40
 
38
41
  const hasTools = !!tools;
39
42
 
@@ -43,28 +46,34 @@ const WithContentId = memo<GroupActionsProps>(({ id, data, index, contentBlock }
43
46
  const items = useMemo(() => {
44
47
  if (hasTools) return [delAndRegenerate, copy];
45
48
 
46
- return [edit, copy, inThread || isGroupSession ? null : branching].filter(
47
- Boolean,
48
- ) as ActionIconGroupItemType[];
49
- }, [inThread, hasTools, isGroupSession]);
49
+ return [
50
+ edit,
51
+ copy,
52
+ // inThread || isGroupSession ? null : branching
53
+ ].filter(Boolean) as ActionIconGroupItemType[];
54
+ }, [inThread, hasTools, isGroupSession, delAndRegenerate, copy, edit, branching]);
50
55
 
51
56
  const { t } = useTranslation('common');
52
57
  const searchParams = useSearchParams();
53
58
  const topic = searchParams.get('topic');
54
59
  const [
55
60
  deleteMessage,
61
+ regenerateAssistantMessage,
56
62
  translateMessage,
57
63
  delAndRegenerateMessage,
58
64
  copyMessage,
59
65
  openThreadCreator,
66
+ resendThreadMessage,
60
67
  delAndResendThreadMessage,
61
68
  toggleMessageEditing,
62
69
  ] = useChatStore((s) => [
63
70
  s.deleteMessage,
71
+ s.regenerateAssistantMessage,
64
72
  s.translateMessage,
65
73
  s.delAndRegenerateMessage,
66
74
  s.copyMessage,
67
75
  s.openThreadCreator,
76
+ s.resendThreadMessage,
68
77
  s.delAndResendThreadMessage,
69
78
  s.toggleMessageEditing,
70
79
  ]);
@@ -103,6 +112,16 @@ const WithContentId = memo<GroupActionsProps>(({ id, data, index, contentBlock }
103
112
  break;
104
113
  }
105
114
 
115
+ case 'regenerate': {
116
+ if (inPortalThread) {
117
+ resendThreadMessage(id);
118
+ } else regenerateAssistantMessage(id);
119
+
120
+ // if this message is an error message, we need to delete it
121
+ if (data.error) deleteMessage(id);
122
+ break;
123
+ }
124
+
106
125
  case 'delAndRegenerate': {
107
126
  if (inPortalThread) {
108
127
  delAndResendThreadMessage(id);
@@ -134,7 +153,16 @@ const WithContentId = memo<GroupActionsProps>(({ id, data, index, contentBlock }
134
153
  <ActionIconGroup
135
154
  items={items}
136
155
  menu={{
137
- items: [edit, copy, divider, share, divider, delAndRegenerate, del],
156
+ items: [
157
+ edit,
158
+ copy,
159
+ divider,
160
+ share,
161
+ divider,
162
+ regenerate,
163
+ // delAndRegenerate,
164
+ del,
165
+ ],
138
166
  }}
139
167
  onActionClick={onActionClick}
140
168
  />
@@ -16,6 +16,6 @@ export const GroupActionsBar = memo<GroupActionsProps>(
16
16
  ({ id, data, contentBlock, index, contentId }) => {
17
17
  if (!contentId) return <WithoutContentId data={data} id={id} />;
18
18
 
19
- return <WithContentId contentBlock={contentBlock} data={data} id={contentId} index={index} />;
19
+ return <WithContentId contentBlock={contentBlock} data={data} id={id} index={index} />;
20
20
  },
21
21
  );
@@ -77,9 +77,7 @@ export const ContentBlock = memo<ContentBlockProps>((props) => {
77
77
  {showReasoning && <Reasoning {...reasoning} id={id} />}
78
78
 
79
79
  {/* Content - markdown text */}
80
- {content && (
81
- <MessageContent content={content} hasTools={hasTools} markdownProps={markdownProps} />
82
- )}
80
+ <MessageContent content={content} hasTools={hasTools} markdownProps={markdownProps} />
83
81
 
84
82
  {/* Image files */}
85
83
  {showImageItems && <ImageFileListViewer items={imageList} />}
@@ -1,6 +1,5 @@
1
1
  import { AssistantContentBlock } from '@lobechat/types';
2
2
  import { createStyles } from 'antd-style';
3
- import { motion } from 'framer-motion';
4
3
  import { memo, use } from 'react';
5
4
  import { Flexbox } from 'react-layout-kit';
6
5
 
@@ -23,11 +22,12 @@ interface GroupChildrenProps {
23
22
  blocks: AssistantContentBlock[];
24
23
  contentId?: string;
25
24
  disableEditing?: boolean;
25
+ id: string;
26
26
  messageIndex: number;
27
27
  }
28
28
 
29
29
  const GroupChildren = memo<GroupChildrenProps>(
30
- ({ blocks, contentId, disableEditing, messageIndex }) => {
30
+ ({ blocks, contentId, disableEditing, messageIndex, id }) => {
31
31
  const { styles } = useStyles();
32
32
 
33
33
  const [toggleMessageEditing] = useChatStore((s) => [s.toggleMessageEditing]);
@@ -53,16 +53,16 @@ const GroupChildren = memo<GroupChildrenProps>(
53
53
  <ContentBlock index={index} {...item} />
54
54
  </Flexbox>
55
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>
56
+ // <motion.div
57
+ // animate={{ height: 'auto', opacity: 1 }}
58
+ // exit={{ height: 0, opacity: 0 }}
59
+ // initial={{ height: 0, opacity: 0 }}
60
+ // key={`${id}_${index}`}
61
+ // style={{ overflow: 'hidden' }}
62
+ // transition={{ duration: 0.3, ease: 'easeInOut' }}
63
+ // >
64
+ <ContentBlock index={index} key={`${id}_${index}`} {...item} />
65
+ // </motion.div>
66
66
  );
67
67
  })}
68
68
  </Flexbox>
@@ -25,7 +25,13 @@ const MessageContent = memo<ContentBlockProps>(({ content, hasTools, markdownPro
25
25
 
26
26
  const { styles, cx } = useStyles();
27
27
 
28
- if (content === LOADING_FLAT) return <BubblesLoading />;
28
+ if (!content && !hasTools) return <BubblesLoading />;
29
+
30
+ if (content === LOADING_FLAT) {
31
+ if (hasTools) return null;
32
+
33
+ return <BubblesLoading />;
34
+ }
29
35
 
30
36
  return (
31
37
  content && (
@@ -23,7 +23,7 @@ const PluginSettings = memo<PluginSettingsProps>(({ id, plugin }) => {
23
23
  const { styles } = useStyles();
24
24
  const { t } = useTranslation('error');
25
25
  const theme = useTheme();
26
- const [resend, deleteMessage] = useChatStore((s) => [s.regenerateMessage, s.deleteMessage]);
26
+ const [resend, deleteMessage] = useChatStore((s) => [s.delAndRegenerateMessage, s.deleteMessage]);
27
27
  const pluginIdentifier = plugin?.identifier as string;
28
28
  const pluginMeta = useToolStore(pluginSelectors.getPluginMetaById(pluginIdentifier), isEqual);
29
29
  const manifest = useToolStore(pluginSelectors.getToolManifestById(pluginIdentifier), isEqual);
@@ -90,7 +90,7 @@ const GroupMessage = memo<GroupMessageProps>((props) => {
90
90
  avatar={avatar}
91
91
  onClick={onAvatarClick}
92
92
  placement={placement}
93
- size={mobile ? MOBILE_AVATAR_SIZE : undefined}
93
+ size={MOBILE_AVATAR_SIZE}
94
94
  style={{ marginTop: 6 }}
95
95
  />
96
96
  <Title avatar={avatar} placement={placement} showTitle time={createdAt} />
@@ -111,6 +111,7 @@ const GroupMessage = memo<GroupMessageProps>((props) => {
111
111
  blocks={children}
112
112
  contentId={contentId}
113
113
  disableEditing={disableEditing}
114
+ id={id}
114
115
  messageIndex={index}
115
116
  />
116
117
  )}
@@ -128,8 +128,8 @@ const SupervisorMessage = memo<SupervisorMessageProps>((props) => {
128
128
 
129
129
  // Render todo message with dedicated component
130
130
  if (isTodoMessage && todoData) {
131
- const model = props.extra?.fromModel;
132
- const provider = props.extra?.fromProvider;
131
+ const model = props.extra?.model;
132
+ const provider = props.extra?.provider;
133
133
  const hasModelInfo = model || provider;
134
134
 
135
135
  return (
@@ -4,17 +4,15 @@ import { ActionIconGroupItemType } from '@lobehub/ui/es/ActionIconGroup';
4
4
  import { ActionIconGroupEvent } from '@lobehub/ui/es/ActionIconGroup/type';
5
5
  import { App } from 'antd';
6
6
  import { useSearchParams } from 'next/navigation';
7
- import { memo, use, useCallback, useMemo } from 'react';
7
+ import { memo, use, useCallback } from 'react';
8
8
  import { useTranslation } from 'react-i18next';
9
9
 
10
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';
11
+ import { messageStateSelectors, threadSelectors } from '@/store/chat/selectors';
14
12
 
15
- import { VirtuosoContext } from '../../components/VirtualizedList/VirtuosoContext';
16
- import { InPortalThreadContext } from '../../context/InPortalThreadContext';
17
- import { useChatListActionsBar } from '../../hooks/useChatListActionsBar';
13
+ import { VirtuosoContext } from '../../../components/VirtualizedList/VirtuosoContext';
14
+ import { InPortalThreadContext } from '../../../context/InPortalThreadContext';
15
+ import { useChatListActionsBar } from '../../../hooks/useChatListActionsBar';
18
16
 
19
17
  interface UserActionsProps {
20
18
  data: UIChatMessage;
@@ -28,11 +26,12 @@ export const UserActionsBar = memo<UserActionsProps>(({ id, data, index }) => {
28
26
  const topic = searchParams.get('topic');
29
27
 
30
28
  const [
31
- isThreadMode,
29
+ // isThreadMode,
32
30
  hasThread,
31
+ isRegenerating,
33
32
  toggleMessageEditing,
34
33
  deleteMessage,
35
- regenerateMessage,
34
+ regenerateUserMessage,
36
35
  translateMessage,
37
36
  ttsMessage,
38
37
  delAndRegenerateMessage,
@@ -41,12 +40,13 @@ export const UserActionsBar = memo<UserActionsProps>(({ id, data, index }) => {
41
40
  resendThreadMessage,
42
41
  delAndResendThreadMessage,
43
42
  ] = useChatStore((s) => [
44
- !!s.activeThreadId,
43
+ // !!s.activeThreadId,
45
44
  threadSelectors.hasThreadBySourceMsgId(id)(s),
45
+ messageStateSelectors.isMessageRegenerating(id)(s),
46
46
 
47
47
  s.toggleMessageEditing,
48
48
  s.deleteMessage,
49
- s.regenerateMessage,
49
+ s.regenerateUserMessage,
50
50
  s.translateMessage,
51
51
  s.ttsMessage,
52
52
  s.delAndRegenerateMessage,
@@ -56,22 +56,21 @@ export const UserActionsBar = memo<UserActionsProps>(({ id, data, index }) => {
56
56
  s.delAndResendThreadMessage,
57
57
  ]);
58
58
 
59
- const isGroupSession = useSessionStore(sessionSelectors.isCurrentSessionGroupSession);
59
+ // const isGroupSession = useSessionStore(sessionSelectors.isCurrentSessionGroupSession);
60
60
 
61
- const { regenerate, edit, copy, divider, del, branching, tts, translate } = useChatListActionsBar(
62
- { hasThread },
63
- );
61
+ const { regenerate, edit, copy, divider, del, tts, translate } = useChatListActionsBar({
62
+ hasThread,
63
+ isRegenerating,
64
+ });
64
65
 
65
66
  const inPortalThread = use(InPortalThreadContext);
66
- const inThread = isThreadMode || inPortalThread;
67
-
68
- const items = useMemo(
69
- () =>
70
- [regenerate, edit, inThread || isGroupSession ? null : branching].filter(
71
- Boolean,
72
- ) as ActionIconGroupItemType[],
73
- [inThread, isGroupSession],
74
- );
67
+ // const inThread = isThreadMode || inPortalThread;
68
+
69
+ const items = [
70
+ regenerate,
71
+ edit,
72
+ // inThread || isGroupSession ? null : branching
73
+ ].filter(Boolean) as ActionIconGroupItemType[];
75
74
 
76
75
  const { message } = App.useApp();
77
76
 
@@ -113,7 +112,7 @@ export const UserActionsBar = memo<UserActionsProps>(({ id, data, index }) => {
113
112
  case 'regenerate': {
114
113
  if (inPortalThread) {
115
114
  resendThreadMessage(id);
116
- } else regenerateMessage(id);
115
+ } else regenerateUserMessage(id);
117
116
 
118
117
  // if this message is an error message, we need to delete it
119
118
  if (data.error) deleteMessage(id);
@@ -153,6 +152,8 @@ export const UserActionsBar = memo<UserActionsProps>(({ id, data, index }) => {
153
152
  items: [edit, copy, divider, tts, translate, divider, regenerate, del],
154
153
  }}
155
154
  onActionClick={onActionClick}
155
+ size={'small'}
156
+ variant={'borderless'}
156
157
  />
157
158
  );
158
159
  });
@@ -0,0 +1,107 @@
1
+ import { Icon } from '@lobehub/ui';
2
+ import { createStyles } from 'antd-style';
3
+ import { ChevronLeft, ChevronRight } from 'lucide-react';
4
+ import { memo } from 'react';
5
+ import { Center, Flexbox } from 'react-layout-kit';
6
+
7
+ import { useChatStore } from '@/store/chat';
8
+
9
+ const useStyles = createStyles(({ css, token, prefixCls }) => ({
10
+ button: css`
11
+ cursor: pointer;
12
+
13
+ display: flex;
14
+ align-items: center;
15
+ justify-content: center;
16
+
17
+ width: 20px;
18
+ height: 20px;
19
+ border-radius: 4px;
20
+
21
+ color: ${token.colorTextSecondary};
22
+
23
+ transition: all 0.2s ease;
24
+
25
+ &:hover:not(.${prefixCls}-disabled) {
26
+ color: ${token.colorText};
27
+ background: ${token.colorFillSecondary};
28
+ }
29
+
30
+ &.${prefixCls}-disabled {
31
+ cursor: not-allowed;
32
+ opacity: 0.4;
33
+ }
34
+ `,
35
+ container: css`
36
+ user-select: none;
37
+
38
+ display: inline-flex;
39
+ gap: 4px;
40
+ align-items: center;
41
+
42
+ height: 20px;
43
+ padding-inline: 4px;
44
+ border-radius: ${token.borderRadiusSM}px;
45
+ `,
46
+ text: css`
47
+ min-width: 24px;
48
+ height: 20px;
49
+
50
+ font-size: 12px;
51
+ font-variant-numeric: tabular-nums;
52
+ color: ${token.colorTextSecondary};
53
+ text-align: center;
54
+ `,
55
+ }));
56
+
57
+ interface MessageBranchProps {
58
+ activeBranchIndex: number;
59
+ count: number;
60
+ messageId: string;
61
+ }
62
+
63
+ const MessageBranch = memo<MessageBranchProps>(({ activeBranchIndex, count, messageId }) => {
64
+ const { styles, cx, prefixCls } = useStyles();
65
+ const switchMessageBranch = useChatStore((s) => s.switchMessageBranch);
66
+
67
+ const handlePrevious = () => {
68
+ if (activeBranchIndex > 0) {
69
+ switchMessageBranch(messageId, activeBranchIndex - 1);
70
+ }
71
+ };
72
+
73
+ const handleNext = () => {
74
+ if (activeBranchIndex < count - 1) {
75
+ switchMessageBranch(messageId, activeBranchIndex + 1);
76
+ }
77
+ };
78
+
79
+ const canGoPrevious = activeBranchIndex > 0;
80
+ const canGoNext = activeBranchIndex < count - 1;
81
+
82
+ return (
83
+ <Flexbox className={styles.container} horizontal>
84
+ <div
85
+ className={cx(styles.button, !canGoPrevious && `${prefixCls}-disabled`)}
86
+ onClick={handlePrevious}
87
+ role="button"
88
+ tabIndex={canGoPrevious ? 0 : -1}
89
+ >
90
+ <Icon icon={ChevronLeft} size={16} />
91
+ </div>
92
+ <Center className={styles.text}>
93
+ {activeBranchIndex + 1}/{count}
94
+ </Center>
95
+ <div
96
+ className={cx(styles.button, !canGoNext && `${prefixCls}-disabled`)}
97
+ onClick={handleNext}
98
+ role="button"
99
+ tabIndex={canGoNext ? 0 : -1}
100
+ >
101
+ <Icon icon={ChevronRight} size={16} />
102
+ </div>
103
+ </Flexbox>
104
+ );
105
+ });
106
+
107
+ export default MessageBranch;