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

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 (154) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/changelog/v1.json +9 -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/message.ts +18 -19
  29. package/packages/types/src/aiChat.ts +2 -0
  30. package/packages/types/src/importer.ts +2 -2
  31. package/packages/types/src/message/ui/chat.ts +17 -1
  32. package/packages/types/src/message/ui/extra.ts +2 -2
  33. package/packages/types/src/message/ui/params.ts +2 -2
  34. package/packages/types/src/user/preference.ts +0 -4
  35. package/packages/utils/src/tokenizer/index.ts +3 -11
  36. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/Desktop/MessageFromUrl.tsx +3 -3
  37. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/V1Mobile/index.tsx +1 -1
  38. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/V1Mobile/useSend.ts +3 -3
  39. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/useSend.ts +6 -6
  40. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatList/Content.tsx +5 -3
  41. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatList/WelcomeChatItem/AgentWelcome/OpeningQuestions.tsx +2 -2
  42. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatList/WelcomeChatItem/GroupWelcome/GroupUsageSuggest.tsx +2 -2
  43. package/src/app/[variants]/(main)/labs/page.tsx +0 -9
  44. package/src/features/ChatInput/ActionBar/STT/browser.tsx +3 -3
  45. package/src/features/ChatInput/ActionBar/STT/openai.tsx +3 -3
  46. package/src/features/Conversation/Error/AccessCodeForm.tsx +1 -1
  47. package/src/features/Conversation/Error/ChatInvalidApiKey.tsx +1 -1
  48. package/src/features/Conversation/Error/ClerkLogin/index.tsx +1 -1
  49. package/src/features/Conversation/Error/OAuthForm.tsx +1 -1
  50. package/src/features/Conversation/Error/index.tsx +0 -5
  51. package/src/features/Conversation/Messages/Assistant/Actions/index.tsx +13 -10
  52. package/src/features/Conversation/Messages/Assistant/Extra/index.test.tsx +3 -8
  53. package/src/features/Conversation/Messages/Assistant/Extra/index.tsx +2 -6
  54. package/src/features/Conversation/Messages/Assistant/MessageContent.tsx +7 -9
  55. package/src/features/Conversation/Messages/Assistant/Tool/Inspector/PluginResult.tsx +2 -2
  56. package/src/features/Conversation/Messages/Assistant/Tool/Inspector/PluginState.tsx +2 -2
  57. package/src/features/Conversation/Messages/Assistant/Tool/Render/PluginSettings.tsx +4 -1
  58. package/src/features/Conversation/Messages/Assistant/Tool/Render/index.tsx +2 -3
  59. package/src/features/Conversation/Messages/Assistant/index.tsx +57 -60
  60. package/src/features/Conversation/Messages/Default.tsx +1 -0
  61. package/src/features/Conversation/Messages/Group/Actions/WithContentId.tsx +38 -10
  62. package/src/features/Conversation/Messages/Group/Actions/index.tsx +1 -1
  63. package/src/features/Conversation/Messages/Group/ContentBlock.tsx +1 -3
  64. package/src/features/Conversation/Messages/Group/GroupChildren.tsx +12 -12
  65. package/src/features/Conversation/Messages/Group/MessageContent.tsx +7 -1
  66. package/src/features/Conversation/Messages/Group/Tool/Render/PluginSettings.tsx +1 -1
  67. package/src/features/Conversation/Messages/Group/index.tsx +2 -1
  68. package/src/features/Conversation/Messages/Supervisor/index.tsx +2 -2
  69. package/src/features/Conversation/Messages/User/{Actions.tsx → Actions/ActionsBar.tsx} +26 -25
  70. package/src/features/Conversation/Messages/User/Actions/MessageBranch.tsx +107 -0
  71. package/src/features/Conversation/Messages/User/Actions/index.tsx +42 -0
  72. package/src/features/Conversation/Messages/User/index.tsx +43 -44
  73. package/src/features/Conversation/Messages/index.tsx +3 -3
  74. package/src/features/Conversation/components/AutoScroll.tsx +3 -3
  75. package/src/features/Conversation/components/Extras/Usage/UsageDetail/AnimatedNumber.tsx +55 -0
  76. package/src/features/Conversation/components/Extras/Usage/UsageDetail/index.tsx +5 -2
  77. package/src/features/Conversation/components/VirtualizedList/index.tsx +29 -20
  78. package/src/features/Conversation/hooks/useChatListActionsBar.tsx +8 -10
  79. package/src/features/Portal/Thread/Chat/ChatInput/useSend.ts +3 -3
  80. package/src/hooks/useHotkeys/chatScope.ts +15 -7
  81. package/src/server/routers/lambda/__tests__/aiChat.test.ts +1 -1
  82. package/src/server/routers/lambda/__tests__/integration/message.integration.test.ts +0 -26
  83. package/src/server/routers/lambda/aiChat.ts +3 -2
  84. package/src/server/routers/lambda/message.ts +8 -16
  85. package/src/server/services/message/__tests__/index.test.ts +29 -39
  86. package/src/server/services/message/index.ts +41 -36
  87. package/src/services/electron/desktopNotification.ts +6 -6
  88. package/src/services/electron/file.ts +6 -6
  89. package/src/services/file/ClientS3/index.ts +8 -8
  90. package/src/services/message/__tests__/metadata-race-condition.test.ts +157 -0
  91. package/src/services/message/index.ts +21 -15
  92. package/src/services/upload.ts +11 -11
  93. package/src/services/utils/abortableRequest.test.ts +161 -0
  94. package/src/services/utils/abortableRequest.ts +67 -0
  95. package/src/store/chat/agents/GeneralChatAgent.ts +137 -0
  96. package/src/store/chat/agents/createAgentExecutors.ts +395 -0
  97. package/src/store/chat/helpers.test.ts +0 -99
  98. package/src/store/chat/helpers.ts +0 -11
  99. package/src/store/chat/slices/aiChat/actions/__tests__/conversationControl.test.ts +332 -0
  100. package/src/store/chat/slices/aiChat/actions/__tests__/conversationLifecycle.test.ts +257 -0
  101. package/src/store/chat/slices/aiChat/actions/__tests__/helpers.ts +11 -2
  102. package/src/store/chat/slices/aiChat/actions/__tests__/rag.test.ts +6 -6
  103. package/src/store/chat/slices/aiChat/actions/__tests__/streamingExecutor.test.ts +391 -0
  104. package/src/store/chat/slices/aiChat/actions/__tests__/streamingStates.test.ts +179 -0
  105. package/src/store/chat/slices/aiChat/actions/conversationControl.ts +157 -0
  106. package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +329 -0
  107. package/src/store/chat/slices/aiChat/actions/generateAIGroupChat.ts +14 -14
  108. package/src/store/chat/slices/aiChat/actions/index.ts +12 -6
  109. package/src/store/chat/slices/aiChat/actions/rag.ts +9 -6
  110. package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +604 -0
  111. package/src/store/chat/slices/aiChat/actions/streamingStates.ts +84 -0
  112. package/src/store/chat/slices/builtinTool/actions/__tests__/localSystem.test.ts +4 -4
  113. package/src/store/chat/slices/builtinTool/actions/__tests__/search.test.ts +11 -11
  114. package/src/store/chat/slices/builtinTool/actions/interpreter.ts +8 -8
  115. package/src/store/chat/slices/builtinTool/actions/localSystem.ts +2 -2
  116. package/src/store/chat/slices/builtinTool/actions/search.ts +8 -8
  117. package/src/store/chat/slices/message/action.test.ts +79 -68
  118. package/src/store/chat/slices/message/actions/index.ts +39 -0
  119. package/src/store/chat/slices/message/actions/internals.ts +77 -0
  120. package/src/store/chat/slices/message/actions/optimisticUpdate.ts +260 -0
  121. package/src/store/chat/slices/message/actions/publicApi.ts +224 -0
  122. package/src/store/chat/slices/message/actions/query.ts +120 -0
  123. package/src/store/chat/slices/message/actions/runtimeState.ts +108 -0
  124. package/src/store/chat/slices/message/initialState.ts +13 -0
  125. package/src/store/chat/slices/message/reducer.test.ts +48 -370
  126. package/src/store/chat/slices/message/reducer.ts +17 -81
  127. package/src/store/chat/slices/message/selectors/chat.test.ts +13 -50
  128. package/src/store/chat/slices/message/selectors/chat.ts +78 -242
  129. package/src/store/chat/slices/message/selectors/dbMessage.ts +140 -0
  130. package/src/store/chat/slices/message/selectors/displayMessage.ts +301 -0
  131. package/src/store/chat/slices/message/selectors/messageState.ts +5 -2
  132. package/src/store/chat/slices/plugin/action.test.ts +62 -64
  133. package/src/store/chat/slices/plugin/action.ts +34 -28
  134. package/src/store/chat/slices/thread/action.test.ts +28 -31
  135. package/src/store/chat/slices/thread/action.ts +13 -10
  136. package/src/store/chat/slices/thread/selectors/index.ts +8 -6
  137. package/src/store/chat/slices/topic/reducer.ts +11 -3
  138. package/src/store/chat/store.ts +1 -1
  139. package/src/store/user/slices/preference/selectors/labPrefer.ts +0 -3
  140. package/packages/database/src/models/__tests__/message.grouping.test.ts +0 -812
  141. package/packages/database/src/utils/__tests__/groupMessages.test.ts +0 -1132
  142. package/packages/database/src/utils/groupMessages.ts +0 -361
  143. package/packages/utils/src/tokenizer/client.ts +0 -35
  144. package/packages/utils/src/tokenizer/estimated.ts +0 -4
  145. package/packages/utils/src/tokenizer/server.ts +0 -11
  146. package/packages/utils/src/tokenizer/tokenizer.worker.ts +0 -12
  147. package/src/app/(backend)/webapi/tokenizer/index.test.ts +0 -32
  148. package/src/app/(backend)/webapi/tokenizer/route.ts +0 -8
  149. package/src/features/Conversation/Error/InvalidAccessCode.tsx +0 -79
  150. package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChat.test.ts +0 -975
  151. package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChatV2.test.ts +0 -1050
  152. package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +0 -720
  153. package/src/store/chat/slices/aiChat/actions/generateAIChatV2.ts +0 -849
  154. package/src/store/chat/slices/message/action.ts +0 -629
@@ -19,7 +19,7 @@ const AccessCodeForm = memo<AccessCodeFormProps>(({ id }) => {
19
19
  keyVaultsConfigSelectors.password(s),
20
20
  s.updateKeyVaults,
21
21
  ]);
22
- const [resend, deleteMessage] = useChatStore((s) => [s.regenerateMessage, s.deleteMessage]);
22
+ const [resend, deleteMessage] = useChatStore((s) => [s.delAndRegenerateMessage, s.deleteMessage]);
23
23
 
24
24
  return (
25
25
  <>
@@ -13,7 +13,7 @@ interface ChatInvalidAPIKeyProps {
13
13
  const ChatInvalidAPIKey = memo<ChatInvalidAPIKeyProps>(({ id, provider }) => {
14
14
  const { t } = useTranslation('modelProvider');
15
15
  const { t: modelProviderErrorT } = useTranslation(['modelProvider', 'error']);
16
- const [resend, deleteMessage] = useChatStore((s) => [s.regenerateMessage, s.deleteMessage]);
16
+ const [resend, deleteMessage] = useChatStore((s) => [s.delAndRegenerateMessage, s.deleteMessage]);
17
17
  const providerName = useProviderName(provider as GlobalLLMProviderKey);
18
18
 
19
19
  return (
@@ -15,7 +15,7 @@ const ClerkLogin = memo<{ id: string }>(({ id }) => {
15
15
  const [openSignIn, isSignedIn] = useUserStore((s) => [s.openLogin, s.isSignedIn]);
16
16
  const greeting = useGreeting();
17
17
  const nickName = useUserStore(userProfileSelectors.nickName);
18
- const [resend, deleteMessage] = useChatStore((s) => [s.regenerateMessage, s.deleteMessage]);
18
+ const [resend, deleteMessage] = useChatStore((s) => [s.delAndRegenerateMessage, s.deleteMessage]);
19
19
 
20
20
  return (
21
21
  <ErrorActionContainer>
@@ -18,7 +18,7 @@ const OAuthForm = memo<{ id: string }>(({ id }) => {
18
18
  const user = useUserStore(userProfileSelectors.userProfile);
19
19
  const isOAuthLoggedIn = useUserStore(authSelectors.isLoginWithAuth);
20
20
 
21
- const [resend, deleteMessage] = useChatStore((s) => [s.regenerateMessage, s.deleteMessage]);
21
+ const [resend, deleteMessage] = useChatStore((s) => [s.delAndRegenerateMessage, s.deleteMessage]);
22
22
 
23
23
  const { message, modal } = App.useApp();
24
24
 
@@ -12,7 +12,6 @@ import { useProviderName } from '@/hooks/useProviderName';
12
12
  import ChatInvalidAPIKey from './ChatInvalidApiKey';
13
13
  import ClerkLogin from './ClerkLogin';
14
14
  import ErrorJsonViewer from './ErrorJsonViewer';
15
- import InvalidAccessCode from './InvalidAccessCode';
16
15
  import { ErrorActionContainer } from './style';
17
16
 
18
17
  interface ErrorMessageData {
@@ -118,10 +117,6 @@ const ErrorMessageExtra = memo<ErrorExtraProps>(({ data, block }) => {
118
117
  return <ClerkLogin id={data.id} />;
119
118
  }
120
119
 
121
- case ChatErrorType.InvalidAccessCode: {
122
- return <InvalidAccessCode id={data.id} provider={data.error?.body?.provider} />;
123
- }
124
-
125
120
  case AgentRuntimeErrorType.NoOpenAIAPIKey: {
126
121
  {
127
122
  return <ChatInvalidAPIKey id={data.id} provider={data.error?.body?.provider} />;
@@ -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
 
@@ -23,9 +23,10 @@ interface AssistantActionsProps {
23
23
  }
24
24
  export const AssistantActionsBar = memo<AssistantActionsProps>(({ id, data, index }) => {
25
25
  const { error, tools } = data;
26
- const [isThreadMode, hasThread] = useChatStore((s) => [
26
+ const [isThreadMode, hasThread, isRegenerating] = useChatStore((s) => [
27
27
  !!s.activeThreadId,
28
28
  threadSelectors.hasThreadBySourceMsgId(id)(s),
29
+ messageStateSelectors.isMessageRegenerating(id)(s),
29
30
  ]);
30
31
  const isGroupSession = useSessionStore(sessionSelectors.isCurrentSessionGroupSession);
31
32
  const [showShareModal, setShareModal] = useState(false);
@@ -42,7 +43,7 @@ export const AssistantActionsBar = memo<AssistantActionsProps>(({ id, data, inde
42
43
  share,
43
44
  tts,
44
45
  translate,
45
- } = useChatListActionsBar({ hasThread });
46
+ } = useChatListActionsBar({ hasThread, isRegenerating });
46
47
 
47
48
  const hasTools = !!tools;
48
49
 
@@ -52,17 +53,19 @@ export const AssistantActionsBar = memo<AssistantActionsProps>(({ id, data, inde
52
53
  const items = useMemo(() => {
53
54
  if (hasTools) return [delAndRegenerate, copy];
54
55
 
55
- return [edit, copy, inThread || isGroupSession ? null : branching].filter(
56
- Boolean,
57
- ) as ActionIconGroupItemType[];
58
- }, [inThread, hasTools, isGroupSession]);
56
+ return [
57
+ edit,
58
+ copy,
59
+ // inThread || isGroupSession ? null : branching
60
+ ].filter(Boolean) as ActionIconGroupItemType[];
61
+ }, [inThread, hasTools, isGroupSession, delAndRegenerate, copy, edit, branching]);
59
62
 
60
63
  const { t } = useTranslation('common');
61
64
  const searchParams = useSearchParams();
62
65
  const topic = searchParams.get('topic');
63
66
  const [
64
67
  deleteMessage,
65
- regenerateMessage,
68
+ regenerateAssistantMessage,
66
69
  translateMessage,
67
70
  ttsMessage,
68
71
  delAndRegenerateMessage,
@@ -73,7 +76,7 @@ export const AssistantActionsBar = memo<AssistantActionsProps>(({ id, data, inde
73
76
  toggleMessageEditing,
74
77
  ] = useChatStore((s) => [
75
78
  s.deleteMessage,
76
- s.regenerateMessage,
79
+ s.regenerateAssistantMessage,
77
80
  s.translateMessage,
78
81
  s.ttsMessage,
79
82
  s.delAndRegenerateMessage,
@@ -120,7 +123,7 @@ export const AssistantActionsBar = memo<AssistantActionsProps>(({ id, data, inde
120
123
  case 'regenerate': {
121
124
  if (inPortalThread) {
122
125
  resendThreadMessage(id);
123
- } else regenerateMessage(id);
126
+ } else regenerateAssistantMessage(id);
124
127
 
125
128
  // if this message is an error message, we need to delete it
126
129
  if (data.error) deleteMessage(id);
@@ -46,20 +46,15 @@ describe('AssistantMessageExtra', () => {
46
46
  expect(screen.queryByText('Translate Component')).toBeNull();
47
47
  });
48
48
 
49
- it('should not render content if extra is defined but does not contain fromModel, tts, or translate', async () => {
49
+ it('should not render content if extra is defined but does not contain model, tts, or translate', async () => {
50
50
  render(<AssistantMessageExtra {...mockData} extra={{}} />);
51
51
  expect(screen.queryByText('Usage Component')).toBeNull();
52
52
  expect(screen.queryByText('TTS Component')).toBeNull();
53
53
  expect(screen.queryByText('Translate Component')).toBeNull();
54
54
  });
55
55
 
56
- it('should render Usage component if extra.fromModel exists', async () => {
57
- render(
58
- <AssistantMessageExtra
59
- {...mockData}
60
- extra={{ fromModel: 'gpt-4', fromProvider: 'openai' }}
61
- />,
62
- );
56
+ it('should render Usage component if extra.model exists', async () => {
57
+ render(<AssistantMessageExtra {...mockData} extra={{ model: 'gpt-4', provider: 'openai' }} />);
63
58
 
64
59
  expect(screen.getByText('Usage Component')).toBeInTheDocument();
65
60
  });
@@ -24,12 +24,8 @@ export const AssistantMessageExtra = memo<AssistantMessageExtraProps>(
24
24
 
25
25
  return (
26
26
  <Flexbox gap={8} style={{ marginTop: !!tools?.length ? 8 : 4 }}>
27
- {content !== LOADING_FLAT && extra?.fromModel && (
28
- <Usage
29
- metadata={metadata || {}}
30
- model={extra?.fromModel}
31
- provider={extra.fromProvider!}
32
- />
27
+ {content !== LOADING_FLAT && extra?.model && (
28
+ <Usage metadata={metadata || {}} model={extra?.model} provider={extra.provider!} />
33
29
  )}
34
30
  <>
35
31
  {!!extra?.tts && (
@@ -58,15 +58,13 @@ export const AssistantMessageContent = memo<
58
58
  {isIntentUnderstanding ? (
59
59
  <IntentUnderstanding />
60
60
  ) : (
61
- content && (
62
- <DefaultMessage
63
- addIdOnDOM={false}
64
- content={content}
65
- id={id}
66
- isToolCallGenerating={isToolCallGenerating}
67
- {...props}
68
- />
69
- )
61
+ <DefaultMessage
62
+ addIdOnDOM={false}
63
+ content={content}
64
+ id={id}
65
+ isToolCallGenerating={isToolCallGenerating}
66
+ {...props}
67
+ />
70
68
  )}
71
69
  {showImageItems && <ImageFileListViewer items={imageList} />}
72
70
  {tools && (
@@ -2,7 +2,7 @@ import { Highlighter } from '@lobehub/ui';
2
2
  import { memo, useMemo } 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;
@@ -10,7 +10,7 @@ export interface FunctionMessageProps {
10
10
  }
11
11
 
12
12
  const PluginResult = memo<FunctionMessageProps>(({ toolCallId, variant }) => {
13
- const toolMessage = useChatStore(chatSelectors.getMessageByToolCallId(toolCallId));
13
+ const toolMessage = useChatStore(dbMessageSelectors.getDbMessageByToolCallId(toolCallId));
14
14
 
15
15
  const { data, language } = useMemo(() => {
16
16
  try {
@@ -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 (