@lobehub/lobehub 2.0.0-next.34 → 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 (282) 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/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/ChatRouter.tsx +83 -0
  37. package/src/app/[variants]/(main)/chat/_layout/ChatLayout.tsx +22 -0
  38. package/src/app/[variants]/(main)/chat/_layout/Desktop/SessionPanel.tsx +12 -7
  39. package/src/app/[variants]/(main)/chat/_layout/Desktop/index.tsx +2 -2
  40. package/src/app/[variants]/(main)/chat/_layout/FeatureFlagsProvider.tsx +24 -0
  41. package/src/app/[variants]/(main)/chat/_layout/Mobile.tsx +3 -2
  42. package/src/app/[variants]/(main)/chat/_layout/type.ts +0 -1
  43. package/src/app/[variants]/(main)/chat/components/ConversationArea.tsx +29 -0
  44. package/src/app/[variants]/(main)/chat/components/MainChatPage.tsx +25 -0
  45. package/src/app/[variants]/(main)/chat/components/PortalPanel.tsx +28 -0
  46. package/src/app/[variants]/(main)/chat/components/SessionPanel.tsx +33 -0
  47. package/src/app/[variants]/(main)/chat/{settings/page.tsx → components/SettingsPage.tsx} +35 -3
  48. package/src/app/[variants]/(main)/chat/components/TopicSidebar.tsx +30 -0
  49. package/src/app/[variants]/(main)/chat/components/WorkspaceLayout.tsx +73 -0
  50. package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatInput/Desktop/MessageFromUrl.tsx +3 -3
  51. package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatInput/V1Mobile/index.tsx +1 -1
  52. package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatInput/V1Mobile/useSend.ts +3 -3
  53. package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatInput/useSend.ts +6 -6
  54. package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatList/ChatItem/index.tsx +1 -1
  55. package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatList/Content.tsx +5 -3
  56. package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatList/WelcomeChatItem/AgentWelcome/OpeningQuestions.tsx +2 -2
  57. package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatList/WelcomeChatItem/GroupWelcome/GroupUsageSuggest.tsx +2 -2
  58. package/src/app/[variants]/(main)/chat/{layout.ts → layout.tsx} +0 -1
  59. package/src/app/[variants]/(main)/chat/page.tsx +12 -0
  60. package/src/app/[variants]/(main)/labs/page.tsx +0 -9
  61. package/src/features/ChatInput/ActionBar/STT/browser.tsx +3 -3
  62. package/src/features/ChatInput/ActionBar/STT/openai.tsx +3 -3
  63. package/src/features/Conversation/Error/AccessCodeForm.tsx +1 -1
  64. package/src/features/Conversation/Error/ChatInvalidApiKey.tsx +1 -1
  65. package/src/features/Conversation/Error/ClerkLogin/index.tsx +1 -1
  66. package/src/features/Conversation/Error/OAuthForm.tsx +1 -1
  67. package/src/features/Conversation/Error/index.tsx +0 -5
  68. package/src/features/Conversation/Messages/Assistant/Actions/index.tsx +13 -10
  69. package/src/features/Conversation/Messages/Assistant/Extra/index.test.tsx +3 -8
  70. package/src/features/Conversation/Messages/Assistant/Extra/index.tsx +2 -6
  71. package/src/features/Conversation/Messages/Assistant/MessageContent.tsx +7 -9
  72. package/src/features/Conversation/Messages/Assistant/Tool/Inspector/PluginResult.tsx +2 -2
  73. package/src/features/Conversation/Messages/Assistant/Tool/Inspector/PluginState.tsx +2 -2
  74. package/src/features/Conversation/Messages/Assistant/Tool/Render/PluginSettings.tsx +4 -1
  75. package/src/features/Conversation/Messages/Assistant/Tool/Render/index.tsx +2 -3
  76. package/src/features/Conversation/Messages/Assistant/index.tsx +57 -60
  77. package/src/features/Conversation/Messages/Default.tsx +1 -0
  78. package/src/features/Conversation/Messages/Group/Actions/WithContentId.tsx +38 -10
  79. package/src/features/Conversation/Messages/Group/Actions/index.tsx +1 -1
  80. package/src/features/Conversation/Messages/Group/ContentBlock.tsx +1 -3
  81. package/src/features/Conversation/Messages/Group/GroupChildren.tsx +12 -12
  82. package/src/features/Conversation/Messages/Group/MessageContent.tsx +7 -1
  83. package/src/features/Conversation/Messages/Group/Tool/Render/PluginSettings.tsx +1 -1
  84. package/src/features/Conversation/Messages/Group/index.tsx +2 -1
  85. package/src/features/Conversation/Messages/Supervisor/index.tsx +2 -2
  86. package/src/features/Conversation/Messages/User/{Actions.tsx → Actions/ActionsBar.tsx} +26 -25
  87. package/src/features/Conversation/Messages/User/Actions/MessageBranch.tsx +107 -0
  88. package/src/features/Conversation/Messages/User/Actions/index.tsx +42 -0
  89. package/src/features/Conversation/Messages/User/index.tsx +43 -44
  90. package/src/features/Conversation/Messages/index.tsx +3 -3
  91. package/src/features/Conversation/components/AutoScroll.tsx +3 -3
  92. package/src/features/Conversation/components/Extras/Usage/UsageDetail/AnimatedNumber.tsx +55 -0
  93. package/src/features/Conversation/components/Extras/Usage/UsageDetail/index.tsx +5 -2
  94. package/src/features/Conversation/components/VirtualizedList/index.tsx +29 -20
  95. package/src/features/Conversation/hooks/useChatListActionsBar.tsx +8 -10
  96. package/src/features/Portal/GroupThread/Body/index.tsx +1 -1
  97. package/src/features/Portal/Thread/Chat/ChatInput/useSend.ts +3 -3
  98. package/src/hooks/useHotkeys/chatScope.ts +16 -8
  99. package/src/server/routers/lambda/__tests__/aiChat.test.ts +1 -1
  100. package/src/server/routers/lambda/__tests__/integration/message.integration.test.ts +0 -26
  101. package/src/server/routers/lambda/aiChat.ts +3 -2
  102. package/src/server/routers/lambda/message.ts +8 -16
  103. package/src/server/services/message/__tests__/index.test.ts +29 -39
  104. package/src/server/services/message/index.ts +41 -36
  105. package/src/services/electron/desktopNotification.ts +6 -6
  106. package/src/services/electron/file.ts +6 -6
  107. package/src/services/file/ClientS3/index.ts +8 -8
  108. package/src/services/message/__tests__/metadata-race-condition.test.ts +157 -0
  109. package/src/services/message/index.ts +21 -15
  110. package/src/services/upload.ts +11 -11
  111. package/src/services/utils/abortableRequest.test.ts +161 -0
  112. package/src/services/utils/abortableRequest.ts +67 -0
  113. package/src/store/chat/agents/GeneralChatAgent.ts +137 -0
  114. package/src/store/chat/agents/createAgentExecutors.ts +395 -0
  115. package/src/store/chat/helpers.test.ts +0 -99
  116. package/src/store/chat/helpers.ts +0 -11
  117. package/src/store/chat/slices/aiChat/actions/__tests__/conversationControl.test.ts +332 -0
  118. package/src/store/chat/slices/aiChat/actions/__tests__/conversationLifecycle.test.ts +257 -0
  119. package/src/store/chat/slices/aiChat/actions/__tests__/helpers.ts +11 -2
  120. package/src/store/chat/slices/aiChat/actions/__tests__/rag.test.ts +6 -6
  121. package/src/store/chat/slices/aiChat/actions/__tests__/streamingExecutor.test.ts +391 -0
  122. package/src/store/chat/slices/aiChat/actions/__tests__/streamingStates.test.ts +179 -0
  123. package/src/store/chat/slices/aiChat/actions/conversationControl.ts +157 -0
  124. package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +329 -0
  125. package/src/store/chat/slices/aiChat/actions/generateAIGroupChat.ts +14 -14
  126. package/src/store/chat/slices/aiChat/actions/index.ts +12 -6
  127. package/src/store/chat/slices/aiChat/actions/rag.ts +9 -6
  128. package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +604 -0
  129. package/src/store/chat/slices/aiChat/actions/streamingStates.ts +84 -0
  130. package/src/store/chat/slices/builtinTool/actions/__tests__/localSystem.test.ts +4 -4
  131. package/src/store/chat/slices/builtinTool/actions/__tests__/search.test.ts +11 -11
  132. package/src/store/chat/slices/builtinTool/actions/interpreter.ts +8 -8
  133. package/src/store/chat/slices/builtinTool/actions/localSystem.ts +2 -2
  134. package/src/store/chat/slices/builtinTool/actions/search.ts +8 -8
  135. package/src/store/chat/slices/message/action.test.ts +79 -68
  136. package/src/store/chat/slices/message/actions/index.ts +39 -0
  137. package/src/store/chat/slices/message/actions/internals.ts +77 -0
  138. package/src/store/chat/slices/message/actions/optimisticUpdate.ts +260 -0
  139. package/src/store/chat/slices/message/actions/publicApi.ts +224 -0
  140. package/src/store/chat/slices/message/actions/query.ts +120 -0
  141. package/src/store/chat/slices/message/actions/runtimeState.ts +108 -0
  142. package/src/store/chat/slices/message/initialState.ts +13 -0
  143. package/src/store/chat/slices/message/reducer.test.ts +48 -370
  144. package/src/store/chat/slices/message/reducer.ts +17 -81
  145. package/src/store/chat/slices/message/selectors/chat.test.ts +13 -50
  146. package/src/store/chat/slices/message/selectors/chat.ts +78 -242
  147. package/src/store/chat/slices/message/selectors/dbMessage.ts +140 -0
  148. package/src/store/chat/slices/message/selectors/displayMessage.ts +301 -0
  149. package/src/store/chat/slices/message/selectors/messageState.ts +5 -2
  150. package/src/store/chat/slices/plugin/action.test.ts +62 -64
  151. package/src/store/chat/slices/plugin/action.ts +34 -28
  152. package/src/store/chat/slices/thread/action.test.ts +28 -31
  153. package/src/store/chat/slices/thread/action.ts +13 -10
  154. package/src/store/chat/slices/thread/selectors/index.ts +8 -6
  155. package/src/store/chat/slices/topic/reducer.ts +11 -3
  156. package/src/store/chat/store.ts +1 -1
  157. package/src/store/user/slices/preference/selectors/labPrefer.ts +0 -3
  158. package/packages/database/src/models/__tests__/message.grouping.test.ts +0 -812
  159. package/packages/database/src/utils/__tests__/groupMessages.test.ts +0 -1132
  160. package/packages/database/src/utils/groupMessages.ts +0 -361
  161. package/packages/utils/src/tokenizer/client.ts +0 -35
  162. package/packages/utils/src/tokenizer/estimated.ts +0 -4
  163. package/packages/utils/src/tokenizer/server.ts +0 -11
  164. package/packages/utils/src/tokenizer/tokenizer.worker.ts +0 -12
  165. package/src/app/(backend)/webapi/tokenizer/index.test.ts +0 -32
  166. package/src/app/(backend)/webapi/tokenizer/route.ts +0 -8
  167. package/src/app/[variants]/(main)/chat/(workspace)/layout.ts +0 -11
  168. package/src/app/[variants]/(main)/chat/(workspace)/page.tsx +0 -53
  169. package/src/app/[variants]/(main)/chat/@session/default.tsx +0 -31
  170. package/src/app/[variants]/(main)/chat/settings/layout.tsx +0 -21
  171. package/src/features/Conversation/Error/InvalidAccessCode.tsx +0 -79
  172. package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChat.test.ts +0 -975
  173. package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChatV2.test.ts +0 -1050
  174. package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +0 -720
  175. package/src/store/chat/slices/aiChat/actions/generateAIChatV2.ts +0 -849
  176. package/src/store/chat/slices/message/action.ts +0 -629
  177. /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/default.tsx +0 -0
  178. /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatHydration/index.tsx +0 -0
  179. /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatInput/Desktop/ClassicChat.tsx +0 -0
  180. /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatInput/Desktop/GroupChat.tsx +0 -0
  181. /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatInput/Desktop/index.tsx +0 -0
  182. /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatInput/Desktop/useSendMenuItems.tsx +0 -0
  183. /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatInput/Mobile/MentionedUsers/MentionedUserItem.tsx +0 -0
  184. /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatInput/Mobile/MentionedUsers/index.tsx +0 -0
  185. /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatInput/Mobile/index.tsx +0 -0
  186. /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatInput/V1Mobile/ActionBar.tsx +0 -0
  187. /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatInput/V1Mobile/Files/index.tsx +0 -0
  188. /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatInput/V1Mobile/InputArea/Container.tsx +0 -0
  189. /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatInput/V1Mobile/InputArea/index.tsx +0 -0
  190. /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatInput/V1Mobile/Send.tsx +0 -0
  191. /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatInput/index.tsx +0 -0
  192. /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatList/ChatItem/OrchestratorThinking.tsx +0 -0
  193. /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatList/ChatItem/Thread.tsx +0 -0
  194. /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatList/ChatItem/ThreadItem.tsx +0 -0
  195. /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatList/WelcomeChatItem/AgentWelcome/AddButton.tsx +0 -0
  196. /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatList/WelcomeChatItem/AgentWelcome/index.tsx +0 -0
  197. /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatList/WelcomeChatItem/GroupWelcome/index.tsx +0 -0
  198. /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatList/WelcomeChatItem/GroupWelcome/useTemplateMatching.ts +0 -0
  199. /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatList/WelcomeChatItem/index.tsx +0 -0
  200. /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatList/index.tsx +0 -0
  201. /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ChatMinimap/index.tsx +0 -0
  202. /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ThreadHydration.tsx +0 -0
  203. /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ZenModeToast/Toast.tsx +0 -0
  204. /package/src/app/[variants]/(main)/chat/{(workspace)/@conversation → components/conversation}/features/ZenModeToast/index.tsx +0 -0
  205. /package/src/app/[variants]/(main)/chat/{(workspace) → components}/features/AgentSettings/index.tsx +0 -0
  206. /package/src/app/[variants]/(main)/chat/{(workspace) → components}/features/AgentTeamSettings/index.tsx +0 -0
  207. /package/src/app/[variants]/(main)/chat/{(workspace) → components}/features/ChangelogModal.tsx +0 -0
  208. /package/src/app/[variants]/(main)/chat/{(workspace) → components}/features/SettingButton.tsx +0 -0
  209. /package/src/app/[variants]/(main)/chat/{(workspace) → components}/features/ShareButton/index.tsx +0 -0
  210. /package/src/app/[variants]/(main)/chat/{(workspace) → components}/features/TelemetryNotification.tsx +0 -0
  211. /package/src/app/[variants]/(main)/chat/{(workspace)/_layout → components/layout}/Desktop/ChatHeader/HeaderAction.tsx +0 -0
  212. /package/src/app/[variants]/(main)/chat/{(workspace)/_layout → components/layout}/Desktop/ChatHeader/Main.tsx +0 -0
  213. /package/src/app/[variants]/(main)/chat/{(workspace)/_layout → components/layout}/Desktop/ChatHeader/Tags/HistoryLimitTags.tsx +0 -0
  214. /package/src/app/[variants]/(main)/chat/{(workspace)/_layout → components/layout}/Desktop/ChatHeader/Tags/KnowledgeTag.tsx +0 -0
  215. /package/src/app/[variants]/(main)/chat/{(workspace)/_layout → components/layout}/Desktop/ChatHeader/Tags/MemberCountTag.tsx +0 -0
  216. /package/src/app/[variants]/(main)/chat/{(workspace)/_layout → components/layout}/Desktop/ChatHeader/Tags/SearchTags.tsx +0 -0
  217. /package/src/app/[variants]/(main)/chat/{(workspace)/_layout → components/layout}/Desktop/ChatHeader/Tags/index.tsx +0 -0
  218. /package/src/app/[variants]/(main)/chat/{(workspace)/_layout → components/layout}/Desktop/ChatHeader/index.tsx +0 -0
  219. /package/src/app/[variants]/(main)/chat/{(workspace)/_layout → components/layout}/Desktop/Portal.tsx +0 -0
  220. /package/src/app/[variants]/(main)/chat/{(workspace)/_layout → components/layout}/Desktop/TopicPanel.tsx +0 -0
  221. /package/src/app/[variants]/(main)/chat/{(workspace)/_layout → components/layout}/Desktop/index.tsx +0 -0
  222. /package/src/app/[variants]/(main)/chat/{(workspace)/_layout → components/layout}/Mobile/ChatHeader/ChatHeaderTitle.tsx +0 -0
  223. /package/src/app/[variants]/(main)/chat/{(workspace)/_layout → components/layout}/Mobile/ChatHeader/index.tsx +0 -0
  224. /package/src/app/[variants]/(main)/chat/{(workspace)/_layout → components/layout}/Mobile/TopicModal.tsx +0 -0
  225. /package/src/app/[variants]/(main)/chat/{(workspace)/_layout → components/layout}/Mobile/index.tsx +0 -0
  226. /package/src/app/[variants]/(main)/chat/{(workspace)/_layout → components/layout}/type.ts +0 -0
  227. /package/src/app/[variants]/(main)/chat/{(workspace)/@portal → components/portal}/_layout/Desktop.tsx +0 -0
  228. /package/src/app/[variants]/(main)/chat/{(workspace)/@portal → components/portal}/_layout/Mobile.tsx +0 -0
  229. /package/src/app/[variants]/(main)/chat/{(workspace)/@portal → components/portal}/default.tsx +0 -0
  230. /package/src/app/[variants]/(main)/chat/{(workspace)/@portal → components/portal}/error.tsx +0 -0
  231. /package/src/app/[variants]/(main)/chat/{(workspace)/@portal → components/portal}/features/Body.tsx +0 -0
  232. /package/src/app/[variants]/(main)/chat/{(workspace)/@portal → components/portal}/loading.tsx +0 -0
  233. /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/_layout/Desktop.tsx +0 -0
  234. /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/_layout/Mobile.tsx +0 -0
  235. /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/default.tsx +0 -0
  236. /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/AgentConfig/SystemRole.tsx +0 -0
  237. /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/AgentConfig/index.tsx +0 -0
  238. /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/ConfigLayout.tsx +0 -0
  239. /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/ConfigSwitcher.tsx +0 -0
  240. /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/GroupConfig/GroupMember.tsx +0 -0
  241. /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/GroupConfig/GroupMemberItem.tsx +0 -0
  242. /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/GroupConfig/GroupRole.tsx +0 -0
  243. /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/GroupConfig/index.tsx +0 -0
  244. /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/GroupConfig/style.ts +0 -0
  245. /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/SkeletonList.tsx +0 -0
  246. /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/Topic/Header.tsx +0 -0
  247. /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/Topic/TopicListContent/ByTimeMode/GroupItem.tsx +0 -0
  248. /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/Topic/TopicListContent/ByTimeMode/index.tsx +0 -0
  249. /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/Topic/TopicListContent/FlatMode/index.tsx +0 -0
  250. /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/Topic/TopicListContent/SearchResult/index.tsx +0 -0
  251. /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/Topic/TopicListContent/ThreadItem/Content.tsx +0 -0
  252. /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/Topic/TopicListContent/ThreadItem/index.tsx +0 -0
  253. /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/Topic/TopicListContent/ThreadList/index.tsx +0 -0
  254. /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/Topic/TopicListContent/TopicItem/DefaultContent.tsx +0 -0
  255. /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/Topic/TopicListContent/TopicItem/TopicContent.tsx +0 -0
  256. /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/Topic/TopicListContent/TopicItem/index.tsx +0 -0
  257. /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/Topic/TopicListContent/index.tsx +0 -0
  258. /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/Topic/TopicSearchBar/index.tsx +0 -0
  259. /package/src/app/[variants]/(main)/chat/{(workspace)/@topic → components/topic}/features/Topic/index.tsx +0 -0
  260. /package/src/app/[variants]/(main)/chat/{@session → session}/features/SessionHydration.tsx +0 -0
  261. /package/src/app/[variants]/(main)/chat/{@session → session}/features/SessionListContent/CollapseGroup/Actions.tsx +0 -0
  262. /package/src/app/[variants]/(main)/chat/{@session → session}/features/SessionListContent/CollapseGroup/index.tsx +0 -0
  263. /package/src/app/[variants]/(main)/chat/{@session → session}/features/SessionListContent/DefaultMode.tsx +0 -0
  264. /package/src/app/[variants]/(main)/chat/{@session → session}/features/SessionListContent/Inbox/index.tsx +0 -0
  265. /package/src/app/[variants]/(main)/chat/{@session → session}/features/SessionListContent/List/AddButton.tsx +0 -0
  266. /package/src/app/[variants]/(main)/chat/{@session → session}/features/SessionListContent/List/Item/Actions.tsx +0 -0
  267. /package/src/app/[variants]/(main)/chat/{@session → session}/features/SessionListContent/List/Item/index.tsx +0 -0
  268. /package/src/app/[variants]/(main)/chat/{@session → session}/features/SessionListContent/List/index.tsx +0 -0
  269. /package/src/app/[variants]/(main)/chat/{@session → session}/features/SessionListContent/ListItem/index.tsx +0 -0
  270. /package/src/app/[variants]/(main)/chat/{@session → session}/features/SessionListContent/Modals/ConfigGroupModal/GroupItem.tsx +0 -0
  271. /package/src/app/[variants]/(main)/chat/{@session → session}/features/SessionListContent/Modals/ConfigGroupModal/index.tsx +0 -0
  272. /package/src/app/[variants]/(main)/chat/{@session → session}/features/SessionListContent/Modals/CreateGroupModal.tsx +0 -0
  273. /package/src/app/[variants]/(main)/chat/{@session → session}/features/SessionListContent/Modals/RenameGroupModal.tsx +0 -0
  274. /package/src/app/[variants]/(main)/chat/{@session → session}/features/SessionListContent/SearchMode.tsx +0 -0
  275. /package/src/app/[variants]/(main)/chat/{@session → session}/features/SessionListContent/index.tsx +0 -0
  276. /package/src/app/[variants]/(main)/chat/{@session → session}/features/SessionSearchBar.tsx +0 -0
  277. /package/src/app/[variants]/(main)/chat/{@session → session}/features/SkeletonList.tsx +0 -0
  278. /package/src/app/[variants]/(main)/chat/{@session/_layout → session/layout}/Desktop/PanelBody.tsx +0 -0
  279. /package/src/app/[variants]/(main)/chat/{@session/_layout → session/layout}/Desktop/SessionHeader.tsx +0 -0
  280. /package/src/app/[variants]/(main)/chat/{@session/_layout → session/layout}/Desktop/index.tsx +0 -0
  281. /package/src/app/[variants]/(main)/chat/{@session/_layout → session/layout}/Mobile/SessionHeader.tsx +0 -0
  282. /package/src/app/[variants]/(main)/chat/{@session/_layout → session/layout}/Mobile/index.tsx +0 -0
@@ -1,1132 +0,0 @@
1
- import { UIChatMessage } from '@lobechat/types';
2
- import { describe, expect, it } from 'vitest';
3
-
4
- import { groupAssistantMessages } from '../groupMessages';
5
-
6
- describe('groupAssistantMessages', () => {
7
- describe('Basic Scenarios', () => {
8
- it('should group single assistant with single tool result', () => {
9
- const input: UIChatMessage[] = [
10
- {
11
- id: 'msg-1',
12
- role: 'assistant',
13
- content: 'Checking weather',
14
- tools: [
15
- {
16
- id: 'tool-1',
17
- identifier: 'weather',
18
- apiName: 'getWeather',
19
- arguments: '{"city":"Beijing"}',
20
- type: 'default',
21
- },
22
- ],
23
- createdAt: Date.now(),
24
- updatedAt: Date.now(),
25
- meta: {},
26
- } as UIChatMessage,
27
- {
28
- id: 'msg-2',
29
- role: 'tool',
30
- tool_call_id: 'tool-1',
31
- content: 'Beijing: Sunny, 25°C',
32
- pluginState: { cached: true },
33
- createdAt: Date.now(),
34
- updatedAt: Date.now(),
35
- meta: {},
36
- } as UIChatMessage,
37
- ];
38
-
39
- const result = groupAssistantMessages(input);
40
-
41
- expect(result).toHaveLength(1);
42
- expect(result[0].role).toBe('group');
43
- expect(result[0].content).toBe('');
44
- expect(result[0].children).toHaveLength(1);
45
-
46
- const block = result[0].children![0];
47
- expect(block.content).toBe('Checking weather');
48
- expect(block.tools).toHaveLength(1);
49
- expect(block.tools![0]).toMatchObject({
50
- id: 'tool-1',
51
- identifier: 'weather',
52
- apiName: 'getWeather',
53
- result: {
54
- content: 'Beijing: Sunny, 25°C',
55
- state: { cached: true },
56
- },
57
- result_msg_id: 'msg-2',
58
- });
59
- });
60
-
61
- it('should group assistant message with multiple tool results', () => {
62
- const input: UIChatMessage[] = [
63
- {
64
- id: 'msg-1',
65
- role: 'assistant',
66
- content: 'Checking weather and news',
67
- tools: [
68
- {
69
- id: 'tool-1',
70
- identifier: 'weather',
71
- apiName: 'getWeather',
72
- arguments: '{"city":"Beijing"}',
73
- type: 'default',
74
- },
75
- {
76
- id: 'tool-2',
77
- identifier: 'news',
78
- apiName: 'getNews',
79
- arguments: '{"category":"tech"}',
80
- type: 'default',
81
- },
82
- ],
83
- createdAt: Date.now(),
84
- updatedAt: Date.now(),
85
- meta: {},
86
- } as UIChatMessage,
87
- {
88
- id: 'msg-2',
89
- role: 'tool',
90
- tool_call_id: 'tool-1',
91
- content: 'Beijing: Sunny, 25°C',
92
- createdAt: Date.now(),
93
- updatedAt: Date.now(),
94
- meta: {},
95
- } as UIChatMessage,
96
- {
97
- id: 'msg-3',
98
- role: 'tool',
99
- tool_call_id: 'tool-2',
100
- content: 'Latest tech news: AI breakthrough',
101
- createdAt: Date.now(),
102
- updatedAt: Date.now(),
103
- meta: {},
104
- } as UIChatMessage,
105
- ];
106
-
107
- const result = groupAssistantMessages(input);
108
-
109
- expect(result).toHaveLength(1);
110
- expect(result[0].role).toBe('group');
111
- expect(result[0].children).toHaveLength(1);
112
-
113
- const block = result[0].children![0];
114
- expect(block.tools).toHaveLength(2);
115
- expect(block.tools![0].result?.content).toBe('Beijing: Sunny, 25°C');
116
- expect(block.tools![0].result_msg_id).toBe('msg-2');
117
- expect(block.tools![1].result?.content).toBe('Latest tech news: AI breakthrough');
118
- expect(block.tools![1].result_msg_id).toBe('msg-3');
119
- });
120
-
121
- it('should handle assistant message without tools', () => {
122
- const input: UIChatMessage[] = [
123
- {
124
- id: 'msg-1',
125
- role: 'assistant',
126
- content: 'Hello!',
127
- createdAt: Date.now(),
128
- updatedAt: Date.now(),
129
- meta: {},
130
- } as UIChatMessage,
131
- ];
132
-
133
- const result = groupAssistantMessages(input);
134
-
135
- expect(result).toHaveLength(1);
136
- expect(result[0].role).toBe('assistant');
137
- expect(result[0].content).toBe('Hello!');
138
- expect(result[0].children).toBeUndefined();
139
- });
140
-
141
- it('should handle tool result not yet available', () => {
142
- const input: UIChatMessage[] = [
143
- {
144
- id: 'msg-1',
145
- role: 'assistant',
146
- content: 'Checking weather',
147
- tools: [
148
- {
149
- id: 'tool-1',
150
- identifier: 'weather',
151
- apiName: 'getWeather',
152
- arguments: '{"city":"Beijing"}',
153
- type: 'default',
154
- },
155
- ],
156
- createdAt: Date.now(),
157
- updatedAt: Date.now(),
158
- meta: {},
159
- } as UIChatMessage,
160
- ];
161
-
162
- const result = groupAssistantMessages(input);
163
-
164
- expect(result).toHaveLength(1);
165
- expect(result[0].role).toBe('group');
166
- expect(result[0].children).toHaveLength(1);
167
-
168
- const block = result[0].children![0];
169
- expect(block.tools).toHaveLength(1);
170
- expect(block.tools![0].result).toBeUndefined();
171
- expect(block.tools![0].result_msg_id).toBeUndefined();
172
- });
173
- });
174
-
175
- describe('Multi-turn Conversation', () => {
176
- it('should group messages in multi-turn conversation', () => {
177
- const input: UIChatMessage[] = [
178
- {
179
- id: 'msg-1',
180
- role: 'user',
181
- content: 'What is the weather?',
182
- createdAt: Date.now(),
183
- updatedAt: Date.now(),
184
- meta: {},
185
- } as UIChatMessage,
186
- {
187
- id: 'msg-2',
188
- role: 'assistant',
189
- content: 'Checking weather',
190
- tools: [
191
- {
192
- id: 'tool-1',
193
- identifier: 'weather',
194
- apiName: 'getWeather',
195
- arguments: '{}',
196
- type: 'default',
197
- },
198
- ],
199
- createdAt: Date.now(),
200
- updatedAt: Date.now(),
201
- meta: {},
202
- } as UIChatMessage,
203
- {
204
- id: 'msg-3',
205
- role: 'tool',
206
- tool_call_id: 'tool-1',
207
- content: 'Sunny, 25°C',
208
- createdAt: Date.now(),
209
- updatedAt: Date.now(),
210
- meta: {},
211
- } as UIChatMessage,
212
- {
213
- id: 'msg-4',
214
- role: 'user',
215
- content: 'What about news?',
216
- createdAt: Date.now(),
217
- updatedAt: Date.now(),
218
- meta: {},
219
- } as UIChatMessage,
220
- {
221
- id: 'msg-5',
222
- role: 'assistant',
223
- content: 'Checking news',
224
- tools: [
225
- {
226
- id: 'tool-2',
227
- identifier: 'news',
228
- apiName: 'getNews',
229
- arguments: '{}',
230
- type: 'default',
231
- },
232
- ],
233
- createdAt: Date.now(),
234
- updatedAt: Date.now(),
235
- meta: {},
236
- } as UIChatMessage,
237
- {
238
- id: 'msg-6',
239
- role: 'tool',
240
- tool_call_id: 'tool-2',
241
- content: 'AI breakthrough',
242
- createdAt: Date.now(),
243
- updatedAt: Date.now(),
244
- meta: {},
245
- } as UIChatMessage,
246
- ];
247
-
248
- const result = groupAssistantMessages(input);
249
-
250
- expect(result).toHaveLength(4); // 2 users + 2 grouped assistants
251
- expect(result[0].role).toBe('user');
252
- expect(result[1].role).toBe('group');
253
- expect(result[2].role).toBe('user');
254
- expect(result[3].role).toBe('group');
255
- });
256
-
257
- it('should handle mixed grouped and non-grouped messages', () => {
258
- const input: UIChatMessage[] = [
259
- {
260
- id: 'msg-1',
261
- role: 'assistant',
262
- content: 'Hello!',
263
- createdAt: Date.now(),
264
- updatedAt: Date.now(),
265
- meta: {},
266
- } as UIChatMessage,
267
- {
268
- id: 'msg-2',
269
- role: 'assistant',
270
- content: 'Using tools',
271
- tools: [
272
- {
273
- id: 'tool-1',
274
- identifier: 'test',
275
- apiName: 'test',
276
- arguments: '{}',
277
- type: 'default',
278
- },
279
- ],
280
- createdAt: Date.now(),
281
- updatedAt: Date.now(),
282
- meta: {},
283
- } as UIChatMessage,
284
- {
285
- id: 'msg-3',
286
- role: 'tool',
287
- tool_call_id: 'tool-1',
288
- content: 'Result',
289
- createdAt: Date.now(),
290
- updatedAt: Date.now(),
291
- meta: {},
292
- } as UIChatMessage,
293
- ];
294
-
295
- const result = groupAssistantMessages(input);
296
-
297
- expect(result).toHaveLength(2);
298
- expect(result[0].role).toBe('assistant');
299
- expect(result[0].children).toBeUndefined();
300
- expect(result[1].role).toBe('group');
301
- expect(result[1].children).toHaveLength(1);
302
- });
303
- });
304
-
305
- describe('Edge Cases', () => {
306
- it('should group follow-up assistant with its own tools and results', () => {
307
- const input: UIChatMessage[] = [
308
- {
309
- id: 'msg-1',
310
- role: 'assistant',
311
- content: 'Let me check the weather',
312
- tools: [
313
- {
314
- id: 'tool-1',
315
- identifier: 'weather',
316
- apiName: 'getWeather',
317
- arguments: '{}',
318
- type: 'default',
319
- },
320
- ],
321
- createdAt: Date.now(),
322
- updatedAt: Date.now(),
323
- meta: {},
324
- } as UIChatMessage,
325
- {
326
- id: 'msg-2',
327
- role: 'tool',
328
- tool_call_id: 'tool-1',
329
- content: 'Sunny, 25°C',
330
- createdAt: Date.now(),
331
- updatedAt: Date.now(),
332
- meta: {},
333
- } as UIChatMessage,
334
- {
335
- id: 'msg-3',
336
- role: 'assistant',
337
- content: 'Based on weather, let me check news',
338
- parentId: 'msg-2',
339
- tools: [
340
- {
341
- id: 'tool-2',
342
- identifier: 'news',
343
- apiName: 'getNews',
344
- arguments: '{}',
345
- type: 'default',
346
- },
347
- ],
348
- createdAt: Date.now(),
349
- updatedAt: Date.now(),
350
- meta: {},
351
- } as UIChatMessage,
352
- {
353
- id: 'msg-4',
354
- role: 'tool',
355
- tool_call_id: 'tool-2',
356
- content: 'Breaking news',
357
- createdAt: Date.now(),
358
- updatedAt: Date.now(),
359
- meta: {},
360
- } as UIChatMessage,
361
- ];
362
-
363
- const result = groupAssistantMessages(input);
364
-
365
- // Should have 1 group with 2 children
366
- expect(result).toHaveLength(1);
367
- expect(result[0].role).toBe('group');
368
- expect(result[0].children).toHaveLength(2);
369
-
370
- // First child: original assistant with tool result
371
- expect(result[0].children![0].id).toBe('msg-1');
372
- expect(result[0].children![0].tools![0].result?.content).toBe('Sunny, 25°C');
373
- expect(result[0].children![0].tools![0].result_msg_id).toBe('msg-2');
374
-
375
- // Second child: follow-up assistant with its own tool result
376
- expect(result[0].children![1].id).toBe('msg-3');
377
- expect(result[0].children![1].tools).toHaveLength(1);
378
- expect(result[0].children![1].tools![0].result?.content).toBe('Breaking news');
379
- expect(result[0].children![1].tools![0].result_msg_id).toBe('msg-4');
380
- });
381
-
382
- it('should group multiple follow-up assistants in chain (3+ assistants)', () => {
383
- const input: UIChatMessage[] = [
384
- {
385
- id: 'msg-1',
386
- role: 'assistant',
387
- content: 'Step 1',
388
- tools: [
389
- {
390
- id: 'tool-1',
391
- identifier: 'test1',
392
- apiName: 'test1',
393
- arguments: '{}',
394
- type: 'default',
395
- },
396
- ],
397
- createdAt: Date.now(),
398
- updatedAt: Date.now(),
399
- meta: {},
400
- } as UIChatMessage,
401
- {
402
- id: 'msg-2',
403
- role: 'tool',
404
- tool_call_id: 'tool-1',
405
- content: 'Result 1',
406
- createdAt: Date.now(),
407
- updatedAt: Date.now(),
408
- meta: {},
409
- } as UIChatMessage,
410
- {
411
- id: 'msg-3',
412
- role: 'assistant',
413
- content: 'Step 2',
414
- parentId: 'msg-2',
415
- tools: [
416
- {
417
- id: 'tool-2',
418
- identifier: 'test2',
419
- apiName: 'test2',
420
- arguments: '{}',
421
- type: 'default',
422
- },
423
- ],
424
- createdAt: Date.now(),
425
- updatedAt: Date.now(),
426
- meta: {},
427
- } as UIChatMessage,
428
- {
429
- id: 'msg-4',
430
- role: 'tool',
431
- tool_call_id: 'tool-2',
432
- content: 'Result 2',
433
- createdAt: Date.now(),
434
- updatedAt: Date.now(),
435
- meta: {},
436
- } as UIChatMessage,
437
- {
438
- id: 'msg-5',
439
- role: 'assistant',
440
- content: 'Step 3 final',
441
- parentId: 'msg-4',
442
- createdAt: Date.now(),
443
- updatedAt: Date.now(),
444
- meta: {},
445
- } as UIChatMessage,
446
- ];
447
-
448
- const result = groupAssistantMessages(input);
449
-
450
- // Should have 1 group with 3 children
451
- expect(result).toHaveLength(1);
452
- expect(result[0].role).toBe('group');
453
- expect(result[0].children).toHaveLength(3);
454
-
455
- expect(result[0].children![0].id).toBe('msg-1');
456
- expect(result[0].children![0].tools![0].result?.content).toBe('Result 1');
457
- expect(result[0].children![0].tools![0].result_msg_id).toBe('msg-2');
458
-
459
- expect(result[0].children![1].id).toBe('msg-3');
460
- expect(result[0].children![1].tools![0].result?.content).toBe('Result 2');
461
- expect(result[0].children![1].tools![0].result_msg_id).toBe('msg-4');
462
-
463
- expect(result[0].children![2].id).toBe('msg-5');
464
- expect(result[0].children![2].content).toBe('Step 3 final');
465
- });
466
-
467
- it('should group follow-up assistant with parentId pointing to tool', () => {
468
- const input: UIChatMessage[] = [
469
- {
470
- id: 'msg-1',
471
- role: 'assistant',
472
- content: 'Let me check',
473
- tools: [
474
- {
475
- id: 'tool-1',
476
- identifier: 'test',
477
- apiName: 'test',
478
- arguments: '{}',
479
- type: 'default',
480
- },
481
- ],
482
- createdAt: Date.now(),
483
- updatedAt: Date.now(),
484
- meta: {},
485
- } as UIChatMessage,
486
- {
487
- id: 'msg-2',
488
- role: 'tool',
489
- tool_call_id: 'tool-1',
490
- content: 'Tool result',
491
- createdAt: Date.now(),
492
- updatedAt: Date.now(),
493
- meta: {},
494
- } as UIChatMessage,
495
- {
496
- id: 'msg-3',
497
- role: 'assistant',
498
- content: 'Based on the result, here is my answer',
499
- parentId: 'msg-2', // Points to tool message
500
- createdAt: Date.now(),
501
- updatedAt: Date.now(),
502
- meta: {},
503
- } as UIChatMessage,
504
- ];
505
-
506
- const result = groupAssistantMessages(input);
507
-
508
- // Should have 1 group containing both assistants
509
- expect(result).toHaveLength(1);
510
- expect(result[0].role).toBe('group');
511
- expect(result[0].children).toHaveLength(2);
512
-
513
- // First child: original assistant with tool result
514
- expect(result[0].children![0].id).toBe('msg-1');
515
- expect(result[0].children![0].content).toBe('Let me check');
516
- expect(result[0].children![0].tools![0].result?.content).toBe('Tool result');
517
-
518
- // Second child: follow-up assistant
519
- expect(result[0].children![1].id).toBe('msg-3');
520
- expect(result[0].children![1].content).toBe('Based on the result, here is my answer');
521
- });
522
-
523
- it('should handle orphaned tool messages', () => {
524
- const input: UIChatMessage[] = [
525
- {
526
- id: 'msg-1',
527
- role: 'tool',
528
- tool_call_id: 'unknown-tool',
529
- content: 'Orphaned result',
530
- createdAt: Date.now(),
531
- updatedAt: Date.now(),
532
- meta: {},
533
- } as UIChatMessage,
534
- ];
535
-
536
- const result = groupAssistantMessages(input);
537
-
538
- expect(result).toHaveLength(1);
539
- expect(result[0].role).toBe('tool');
540
- });
541
-
542
- it('should handle tool messages with errors', () => {
543
- const input: UIChatMessage[] = [
544
- {
545
- id: 'msg-1',
546
- role: 'assistant',
547
- content: 'Checking',
548
- tools: [
549
- {
550
- id: 'tool-1',
551
- identifier: 'test',
552
- apiName: 'test',
553
- arguments: '{}',
554
- type: 'default',
555
- },
556
- ],
557
- createdAt: Date.now(),
558
- updatedAt: Date.now(),
559
- meta: {},
560
- } as UIChatMessage,
561
- {
562
- id: 'msg-2',
563
- role: 'tool',
564
- tool_call_id: 'tool-1',
565
- content: '',
566
- pluginError: { message: 'Failed to execute' },
567
- createdAt: Date.now(),
568
- updatedAt: Date.now(),
569
- meta: {},
570
- } as UIChatMessage,
571
- ];
572
-
573
- const result = groupAssistantMessages(input);
574
-
575
- expect(result).toHaveLength(1);
576
- expect(result[0].role).toBe('group');
577
-
578
- const block = result[0].children![0];
579
- expect(block.tools![0].result?.error).toEqual({ message: 'Failed to execute' });
580
- });
581
-
582
- it('should preserve message order', () => {
583
- const input: UIChatMessage[] = [
584
- {
585
- id: 'msg-1',
586
- role: 'user',
587
- content: 'First',
588
- createdAt: Date.now(),
589
- updatedAt: Date.now(),
590
- meta: {},
591
- } as UIChatMessage,
592
- {
593
- id: 'msg-2',
594
- role: 'assistant',
595
- content: 'Second',
596
- createdAt: Date.now(),
597
- updatedAt: Date.now(),
598
- meta: {},
599
- } as UIChatMessage,
600
- {
601
- id: 'msg-3',
602
- role: 'user',
603
- content: 'Third',
604
- createdAt: Date.now(),
605
- updatedAt: Date.now(),
606
- meta: {},
607
- } as UIChatMessage,
608
- ];
609
-
610
- const result = groupAssistantMessages(input);
611
-
612
- expect(result).toHaveLength(3);
613
- expect(result[0].content).toBe('First');
614
- expect(result[1].content).toBe('Second');
615
- expect(result[2].content).toBe('Third');
616
- });
617
- });
618
-
619
- describe('Children Structure Validation', () => {
620
- it('should use message ID as block ID', () => {
621
- const input: UIChatMessage[] = [
622
- {
623
- id: 'msg-1',
624
- role: 'assistant',
625
- content: 'Test',
626
- tools: [
627
- {
628
- id: 'tool-1',
629
- identifier: 'test',
630
- apiName: 'test',
631
- arguments: '{}',
632
- type: 'default',
633
- },
634
- ],
635
- createdAt: Date.now(),
636
- updatedAt: Date.now(),
637
- meta: {},
638
- } as UIChatMessage,
639
- ];
640
-
641
- const result = groupAssistantMessages(input);
642
-
643
- expect(result[0].children![0].id).toBe('msg-1');
644
- });
645
-
646
- it('should clear parent fields when creating children', () => {
647
- const input: UIChatMessage[] = [
648
- {
649
- id: 'msg-1',
650
- role: 'assistant',
651
- content: 'Test',
652
- tools: [
653
- {
654
- id: 'tool-1',
655
- identifier: 'test',
656
- apiName: 'test',
657
- arguments: '{}',
658
- type: 'default',
659
- },
660
- ],
661
- imageList: [{ id: 'img-1', url: 'http://example.com/img.png', alt: 'test' }],
662
- fileList: [
663
- {
664
- id: 'file-1',
665
- url: 'http://example.com/file.pdf',
666
- name: 'test.pdf',
667
- size: 1024,
668
- fileType: 'application/pdf',
669
- },
670
- ],
671
- createdAt: Date.now(),
672
- updatedAt: Date.now(),
673
- meta: {},
674
- } as UIChatMessage,
675
- ];
676
-
677
- const result = groupAssistantMessages(input);
678
-
679
- expect(result[0].tools).toBeUndefined();
680
- expect(result[0].imageList).toBeUndefined();
681
- expect(result[0].fileList).toBeUndefined();
682
- expect(result[0].content).toBe('');
683
-
684
- const block = result[0].children![0];
685
- expect(block.content).toBe('Test');
686
- expect(block.tools).toHaveLength(1);
687
- expect(block.imageList).toHaveLength(1);
688
- });
689
-
690
- it('should preserve all tool result fields', () => {
691
- const input: UIChatMessage[] = [
692
- {
693
- id: 'msg-1',
694
- role: 'assistant',
695
- content: 'Test',
696
- tools: [
697
- {
698
- id: 'tool-1',
699
- identifier: 'test',
700
- apiName: 'test',
701
- arguments: '{}',
702
- type: 'default',
703
- },
704
- ],
705
- createdAt: Date.now(),
706
- updatedAt: Date.now(),
707
- meta: {},
708
- } as UIChatMessage,
709
- {
710
- id: 'msg-2',
711
- role: 'tool',
712
- tool_call_id: 'tool-1',
713
- content: 'Result content',
714
- pluginState: { step: 1 },
715
- pluginError: null,
716
- createdAt: Date.now(),
717
- updatedAt: Date.now(),
718
- meta: {},
719
- } as UIChatMessage,
720
- ];
721
-
722
- const result = groupAssistantMessages(input);
723
-
724
- const block = result[0].children![0];
725
- expect(block.tools![0].result).toMatchObject({
726
- content: 'Result content',
727
- state: { step: 1 },
728
- error: null,
729
- });
730
- expect(block.tools![0].result_msg_id).toBe('msg-2');
731
- });
732
- });
733
-
734
- describe('Metadata Handling', () => {
735
- it('should preserve usage and performance in children blocks', () => {
736
- const input: UIChatMessage[] = [
737
- {
738
- id: 'msg-1',
739
- role: 'assistant',
740
- content: 'Test',
741
- tools: [
742
- {
743
- id: 'tool-1',
744
- identifier: 'test',
745
- apiName: 'test',
746
- arguments: '{}',
747
- type: 'default',
748
- },
749
- ],
750
- metadata: {
751
- totalInputTokens: 100,
752
- totalOutputTokens: 50,
753
- totalTokens: 150,
754
- cost: 0.01,
755
- tps: 50,
756
- ttft: 100,
757
- },
758
- createdAt: Date.now(),
759
- updatedAt: Date.now(),
760
- meta: {},
761
- } as UIChatMessage,
762
- ];
763
-
764
- const result = groupAssistantMessages(input);
765
-
766
- // Child should have usage
767
- expect(result[0].children![0].usage).toEqual({
768
- totalInputTokens: 100,
769
- totalOutputTokens: 50,
770
- totalTokens: 150,
771
- cost: 0.01,
772
- });
773
-
774
- // Child should have performance
775
- expect(result[0].children![0].performance).toEqual({
776
- tps: 50,
777
- ttft: 100,
778
- });
779
- });
780
-
781
- it('should aggregate usage and performance from multiple children', () => {
782
- const input: UIChatMessage[] = [
783
- {
784
- id: 'msg-1',
785
- role: 'assistant',
786
- content: 'Step 1',
787
- tools: [
788
- {
789
- id: 'tool-1',
790
- identifier: 'test1',
791
- apiName: 'test1',
792
- arguments: '{}',
793
- type: 'default',
794
- },
795
- ],
796
- metadata: {
797
- totalInputTokens: 100,
798
- totalOutputTokens: 50,
799
- totalTokens: 150,
800
- cost: 0.01,
801
- },
802
- createdAt: Date.now(),
803
- updatedAt: Date.now(),
804
- meta: {},
805
- } as UIChatMessage,
806
- {
807
- id: 'msg-2',
808
- role: 'tool',
809
- tool_call_id: 'tool-1',
810
- content: 'Result 1',
811
- createdAt: Date.now(),
812
- updatedAt: Date.now(),
813
- meta: {},
814
- } as UIChatMessage,
815
- {
816
- id: 'msg-3',
817
- role: 'assistant',
818
- content: 'Step 2',
819
- parentId: 'msg-2',
820
- metadata: {
821
- totalInputTokens: 200,
822
- totalOutputTokens: 100,
823
- totalTokens: 300,
824
- cost: 0.02,
825
- },
826
- createdAt: Date.now(),
827
- updatedAt: Date.now(),
828
- meta: {},
829
- } as UIChatMessage,
830
- ];
831
-
832
- const result = groupAssistantMessages(input);
833
-
834
- // Group should have aggregated usage
835
- expect(result[0].usage).toEqual({
836
- totalInputTokens: 300, // 100 + 200
837
- totalOutputTokens: 150, // 50 + 100
838
- totalTokens: 450, // 150 + 300
839
- cost: 0.03, // 0.01 + 0.02
840
- });
841
-
842
- // metadata should be cleared in favor of usage/performance
843
- expect(result[0].metadata).toBeUndefined();
844
- });
845
-
846
- it('should handle speed metrics correctly', () => {
847
- const input: UIChatMessage[] = [
848
- {
849
- id: 'msg-1',
850
- role: 'assistant',
851
- content: 'Step 1',
852
- tools: [
853
- {
854
- id: 'tool-1',
855
- identifier: 'test1',
856
- apiName: 'test1',
857
- arguments: '{}',
858
- type: 'default',
859
- },
860
- ],
861
- metadata: {
862
- ttft: 100,
863
- tps: 50,
864
- duration: 1000,
865
- latency: 1200,
866
- },
867
- createdAt: Date.now(),
868
- updatedAt: Date.now(),
869
- meta: {},
870
- } as UIChatMessage,
871
- {
872
- id: 'msg-2',
873
- role: 'tool',
874
- tool_call_id: 'tool-1',
875
- content: 'Result 1',
876
- createdAt: Date.now(),
877
- updatedAt: Date.now(),
878
- meta: {},
879
- } as UIChatMessage,
880
- {
881
- id: 'msg-3',
882
- role: 'assistant',
883
- content: 'Step 2',
884
- parentId: 'msg-2',
885
- metadata: {
886
- ttft: 200, // Should be ignored
887
- tps: 60,
888
- duration: 1500,
889
- latency: 1800,
890
- },
891
- createdAt: Date.now(),
892
- updatedAt: Date.now(),
893
- meta: {},
894
- } as UIChatMessage,
895
- ];
896
-
897
- const result = groupAssistantMessages(input);
898
-
899
- // Verify performance metrics aggregation
900
- expect(result[0].performance).toEqual({
901
- ttft: 100, // First child's value only
902
- tps: 55, // Average: (50 + 60) / 2
903
- duration: 2500, // Sum: 1000 + 1500
904
- latency: 3000, // Sum: 1200 + 1800
905
- });
906
-
907
- // metadata should be cleared in favor of usage/performance
908
- expect(result[0].metadata).toBeUndefined();
909
- });
910
-
911
- it('should have no usage or performance if children have no metadata', () => {
912
- const input: UIChatMessage[] = [
913
- {
914
- id: 'msg-1',
915
- role: 'assistant',
916
- content: 'Test',
917
- tools: [
918
- {
919
- id: 'tool-1',
920
- identifier: 'test',
921
- apiName: 'test',
922
- arguments: '{}',
923
- type: 'default',
924
- },
925
- ],
926
- createdAt: Date.now(),
927
- updatedAt: Date.now(),
928
- meta: {},
929
- } as UIChatMessage,
930
- ];
931
-
932
- const result = groupAssistantMessages(input);
933
-
934
- // Should have no usage or performance
935
- expect(result[0].usage).toBeUndefined();
936
- expect(result[0].performance).toBeUndefined();
937
- expect(result[0].metadata).toBeUndefined();
938
- });
939
-
940
- it('should map reasoning field to reasoning in children blocks', () => {
941
- const input: UIChatMessage[] = [
942
- {
943
- id: 'msg-1',
944
- role: 'assistant',
945
- content: 'Test response',
946
- reasoning: {
947
- content: 'This is my reasoning process',
948
- duration: 1500,
949
- },
950
- tools: [
951
- {
952
- id: 'tool-1',
953
- identifier: 'test',
954
- apiName: 'test',
955
- arguments: '{}',
956
- type: 'default',
957
- },
958
- ],
959
- createdAt: Date.now(),
960
- updatedAt: Date.now(),
961
- meta: {},
962
- } as UIChatMessage,
963
- {
964
- id: 'msg-2',
965
- role: 'tool',
966
- tool_call_id: 'tool-1',
967
- content: 'Tool result',
968
- createdAt: Date.now(),
969
- updatedAt: Date.now(),
970
- meta: {},
971
- } as UIChatMessage,
972
- {
973
- id: 'msg-3',
974
- role: 'assistant',
975
- content: 'Follow-up response',
976
- parentId: 'msg-2',
977
- reasoning: {
978
- content: 'Follow-up reasoning',
979
- duration: 2000,
980
- },
981
- createdAt: Date.now(),
982
- updatedAt: Date.now(),
983
- meta: {},
984
- } as UIChatMessage,
985
- ];
986
-
987
- const result = groupAssistantMessages(input);
988
-
989
- // First block should have reasoning
990
- expect(result[0].children![0].reasoning).toEqual({
991
- content: 'This is my reasoning process',
992
- duration: 1500,
993
- });
994
-
995
- // Second block (follow-up) should also have reasoning
996
- expect(result[0].children![1].reasoning).toEqual({
997
- content: 'Follow-up reasoning',
998
- duration: 2000,
999
- });
1000
-
1001
- // Group message should not have reasoning (moved to children)
1002
- expect(result[0].reasoning).toBeUndefined();
1003
- });
1004
-
1005
- it('should preserve error field in children blocks', () => {
1006
- const input = [
1007
- {
1008
- id: 'msg-1',
1009
- role: 'assistant',
1010
- content: 'Failed to process',
1011
- error: {
1012
- type: 'InvalidAPIKey',
1013
- message: 'API key is invalid',
1014
- },
1015
- tools: [
1016
- {
1017
- id: 'tool-1',
1018
- identifier: 'test',
1019
- apiName: 'test',
1020
- arguments: '{}',
1021
- type: 'default',
1022
- },
1023
- ],
1024
- createdAt: Date.now(),
1025
- updatedAt: Date.now(),
1026
- meta: {},
1027
- } as unknown as UIChatMessage,
1028
- ] as UIChatMessage[];
1029
-
1030
- const result = groupAssistantMessages(input);
1031
-
1032
- // Child block should have error
1033
- expect(result[0].children![0].error).toEqual({
1034
- type: 'InvalidAPIKey',
1035
- message: 'API key is invalid',
1036
- });
1037
- });
1038
-
1039
- it('should preserve imageList in children blocks', () => {
1040
- const input: UIChatMessage[] = [
1041
- {
1042
- id: 'msg-1',
1043
- role: 'assistant',
1044
- content: 'Here are the images',
1045
- imageList: [
1046
- { id: 'img-1', url: 'https://example.com/img1.jpg', alt: 'Image 1' },
1047
- { id: 'img-2', url: 'https://example.com/img2.jpg', alt: 'Image 2' },
1048
- ],
1049
- tools: [
1050
- {
1051
- id: 'tool-1',
1052
- identifier: 'test',
1053
- apiName: 'test',
1054
- arguments: '{}',
1055
- type: 'default',
1056
- },
1057
- ],
1058
- createdAt: Date.now(),
1059
- updatedAt: Date.now(),
1060
- meta: {},
1061
- } as UIChatMessage,
1062
- ];
1063
-
1064
- const result = groupAssistantMessages(input);
1065
-
1066
- // Child block should have imageList
1067
- expect(result[0].children![0].imageList).toEqual([
1068
- { id: 'img-1', url: 'https://example.com/img1.jpg', alt: 'Image 1' },
1069
- { id: 'img-2', url: 'https://example.com/img2.jpg', alt: 'Image 2' },
1070
- ]);
1071
-
1072
- // Parent should not have imageList (moved to children)
1073
- expect(result[0].imageList).toBeUndefined();
1074
- });
1075
- });
1076
-
1077
- describe('Empty and Null Cases', () => {
1078
- it('should convert empty arrays to undefined in children', () => {
1079
- const input: UIChatMessage[] = [
1080
- {
1081
- id: 'msg-1',
1082
- role: 'assistant',
1083
- content: 'Test',
1084
- tools: [
1085
- {
1086
- id: 'tool-1',
1087
- identifier: 'test',
1088
- apiName: 'test',
1089
- arguments: '{}',
1090
- type: 'default',
1091
- },
1092
- ],
1093
- imageList: [], // Empty array
1094
- fileList: [], // Empty array
1095
- createdAt: Date.now(),
1096
- updatedAt: Date.now(),
1097
- meta: {},
1098
- } as UIChatMessage,
1099
- ];
1100
-
1101
- const result = groupAssistantMessages(input);
1102
-
1103
- // Empty arrays should become undefined
1104
- expect(result[0].children![0].imageList).toBeUndefined();
1105
- });
1106
-
1107
- it('should handle empty message list', () => {
1108
- const result = groupAssistantMessages([]);
1109
- expect(result).toEqual([]);
1110
- });
1111
-
1112
- it('should handle empty tools array', () => {
1113
- const input: UIChatMessage[] = [
1114
- {
1115
- id: 'msg-1',
1116
- role: 'assistant',
1117
- content: 'Test',
1118
- tools: [],
1119
- createdAt: Date.now(),
1120
- updatedAt: Date.now(),
1121
- meta: {},
1122
- } as UIChatMessage,
1123
- ];
1124
-
1125
- const result = groupAssistantMessages(input);
1126
-
1127
- expect(result).toHaveLength(1);
1128
- expect(result[0].role).toBe('assistant');
1129
- expect(result[0].children).toBeUndefined();
1130
- });
1131
- });
1132
- });