@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
@@ -0,0 +1,604 @@
1
+ /* eslint-disable sort-keys-fix/sort-keys-fix, typescript-sort-keys/interface */
2
+ // Disable the auto sort key eslint rule to make the code more logic and readable
3
+ import { AgentRuntime, type AgentRuntimeContext } from '@lobechat/agent-runtime';
4
+ import { isDesktop } from '@lobechat/const';
5
+ import { knowledgeBaseQAPrompts } from '@lobechat/prompts';
6
+ import {
7
+ ChatImageItem,
8
+ ChatToolPayload,
9
+ MessageToolCall,
10
+ ModelUsage,
11
+ TraceNameMap,
12
+ UIChatMessage,
13
+ } from '@lobechat/types';
14
+ import type { MessageSemanticSearchChunk } from '@lobechat/types';
15
+ import debug from 'debug';
16
+ import { t } from 'i18next';
17
+ import { throttle } from 'lodash-es';
18
+ import { StateCreator } from 'zustand/vanilla';
19
+
20
+ import { chatService } from '@/services/chat';
21
+ import { messageService } from '@/services/message';
22
+ import { agentChatConfigSelectors, agentSelectors } from '@/store/agent/selectors';
23
+ import { getAgentStoreState } from '@/store/agent/store';
24
+ import { GeneralChatAgent } from '@/store/chat/agents/GeneralChatAgent';
25
+ import { createAgentExecutors } from '@/store/chat/agents/createAgentExecutors';
26
+ import { ChatStore } from '@/store/chat/store';
27
+ import { getFileStoreState } from '@/store/file/store';
28
+ import { setNamespace } from '@/utils/storeDebug';
29
+
30
+ import { topicSelectors } from '../../../selectors';
31
+ import { messageMapKey } from '../../../utils/messageMapKey';
32
+
33
+ const n = setNamespace('ai');
34
+ const log = debug('lobe-store:streaming-executor');
35
+
36
+ interface ProcessMessageParams {
37
+ traceId?: string;
38
+ isWelcomeQuestion?: boolean;
39
+ inSearchWorkflow?: boolean;
40
+ /**
41
+ * the RAG query content, should be embedding and used in the semantic search
42
+ */
43
+ ragQuery?: string;
44
+ threadId?: string;
45
+ inPortalThread?: boolean;
46
+
47
+ groupId?: string;
48
+ agentId?: string;
49
+ agentConfig?: any; // Agent configuration for group chat agents
50
+ }
51
+
52
+ /**
53
+ * Core streaming execution actions for AI chat
54
+ */
55
+ export interface StreamingExecutorAction {
56
+ /**
57
+ * Retrieves an AI-generated chat message from the backend service with streaming
58
+ */
59
+ internal_fetchAIChatMessage: (input: {
60
+ messages: UIChatMessage[];
61
+ messageId: string;
62
+ params?: ProcessMessageParams;
63
+ model: string;
64
+ provider: string;
65
+ }) => Promise<{
66
+ isFunctionCall: boolean;
67
+ tools?: ChatToolPayload[];
68
+ tool_calls?: MessageToolCall[];
69
+ content: string;
70
+ traceId?: string;
71
+ usage?: ModelUsage;
72
+ }>;
73
+ /**
74
+ * Executes the core processing logic for AI messages
75
+ * including preprocessing and postprocessing steps
76
+ */
77
+ internal_execAgentRuntime: (params: {
78
+ messages: UIChatMessage[];
79
+ parentMessageId: string;
80
+ parentMessageType: 'user' | 'assistant';
81
+ inSearchWorkflow?: boolean;
82
+ /**
83
+ * the RAG query content, should be embedding and used in the semantic search
84
+ */
85
+ ragQuery?: string;
86
+ threadId?: string;
87
+ inPortalThread?: boolean;
88
+ traceId?: string;
89
+ ragMetadata?: { ragQueryId: string; fileChunks: MessageSemanticSearchChunk[] };
90
+ }) => Promise<void>;
91
+ }
92
+
93
+ export const streamingExecutor: StateCreator<
94
+ ChatStore,
95
+ [['zustand/devtools', never]],
96
+ [],
97
+ StreamingExecutorAction
98
+ > = (set, get) => ({
99
+ internal_fetchAIChatMessage: async ({ messages, messageId, params, provider, model }) => {
100
+ const {
101
+ internal_toggleChatLoading,
102
+ refreshMessages,
103
+ optimisticUpdateMessageContent,
104
+ internal_dispatchMessage,
105
+ internal_toggleToolCallingStreaming,
106
+ internal_toggleChatReasoning,
107
+ } = get();
108
+
109
+ const abortController = internal_toggleChatLoading(
110
+ true,
111
+ messageId,
112
+ n('generateMessage(start)', { messageId, messages }),
113
+ );
114
+
115
+ const agentConfig =
116
+ params?.agentConfig || agentSelectors.currentAgentConfig(getAgentStoreState());
117
+ const chatConfig = agentChatConfigSelectors.currentChatConfig(getAgentStoreState());
118
+
119
+ // ================================== //
120
+ // messages uniformly preprocess //
121
+ // ================================== //
122
+ // 4. handle max_tokens
123
+ agentConfig.params.max_tokens = chatConfig.enableMaxTokens
124
+ ? agentConfig.params.max_tokens
125
+ : undefined;
126
+
127
+ // 5. handle reasoning_effort
128
+ agentConfig.params.reasoning_effort = chatConfig.enableReasoningEffort
129
+ ? agentConfig.params.reasoning_effort
130
+ : undefined;
131
+
132
+ let isFunctionCall = false;
133
+ let tools: ChatToolPayload[] | undefined;
134
+ let tool_calls: MessageToolCall[] | undefined;
135
+ let finalUsage;
136
+ let msgTraceId: string | undefined;
137
+ let output = '';
138
+ let thinking = '';
139
+ let thinkingStartAt: number;
140
+ let duration: number;
141
+ // to upload image
142
+ const uploadTasks: Map<string, Promise<{ id?: string; url?: string }>> = new Map();
143
+
144
+ // Throttle tool_calls updates to prevent excessive re-renders (max once per 300ms)
145
+ const throttledUpdateToolCalls = throttle(
146
+ (toolCalls: any[]) => {
147
+ internal_dispatchMessage({
148
+ id: messageId,
149
+ type: 'updateMessage',
150
+ value: { tools: get().internal_transformToolCalls(toolCalls) },
151
+ });
152
+ },
153
+ 300,
154
+ { leading: true, trailing: true },
155
+ );
156
+
157
+ const historySummary = chatConfig.enableCompressHistory
158
+ ? topicSelectors.currentActiveTopicSummary(get())
159
+ : undefined;
160
+ await chatService.createAssistantMessageStream({
161
+ abortController,
162
+ params: {
163
+ messages,
164
+ model,
165
+ provider,
166
+ ...agentConfig.params,
167
+ plugins: agentConfig.plugins,
168
+ },
169
+ historySummary: historySummary?.content,
170
+ trace: {
171
+ traceId: params?.traceId,
172
+ sessionId: get().activeId,
173
+ topicId: get().activeTopicId,
174
+ traceName: TraceNameMap.Conversation,
175
+ },
176
+ onErrorHandle: async (error) => {
177
+ await messageService.updateMessageError(messageId, error);
178
+ await refreshMessages();
179
+ },
180
+ onFinish: async (
181
+ content,
182
+ { traceId, observationId, toolCalls, reasoning, grounding, usage, speed },
183
+ ) => {
184
+ // if there is traceId, update it
185
+ if (traceId) {
186
+ msgTraceId = traceId;
187
+ messageService.updateMessage(messageId, {
188
+ traceId,
189
+ observationId: observationId ?? undefined,
190
+ });
191
+ }
192
+
193
+ // 等待所有图片上传完成
194
+ let finalImages: ChatImageItem[] = [];
195
+
196
+ if (uploadTasks.size > 0) {
197
+ try {
198
+ // 等待所有上传任务完成
199
+ const uploadResults = await Promise.all(uploadTasks.values());
200
+
201
+ // 使用上传后的 S3 URL 替换原始图像数据
202
+ finalImages = uploadResults.filter((i) => !!i.url) as ChatImageItem[];
203
+ } catch (error) {
204
+ console.error('Error waiting for image uploads:', error);
205
+ }
206
+ }
207
+
208
+ let parsedToolCalls = toolCalls;
209
+ if (parsedToolCalls && parsedToolCalls.length > 0) {
210
+ // Flush any pending throttled updates before finalizing
211
+ throttledUpdateToolCalls.flush();
212
+ internal_toggleToolCallingStreaming(messageId, undefined);
213
+
214
+ tools = get().internal_transformToolCalls(parsedToolCalls);
215
+ tool_calls = toolCalls;
216
+
217
+ parsedToolCalls = parsedToolCalls.map((item) => ({
218
+ ...item,
219
+ function: {
220
+ ...item.function,
221
+ arguments: !!item.function.arguments ? item.function.arguments : '{}',
222
+ },
223
+ }));
224
+
225
+ isFunctionCall = true;
226
+ }
227
+
228
+ finalUsage = usage;
229
+ internal_toggleChatReasoning(false, messageId, n('toggleChatReasoning/false') as string);
230
+
231
+ // update the content after fetch result
232
+ await optimisticUpdateMessageContent(messageId, content, {
233
+ toolCalls: parsedToolCalls,
234
+ reasoning: !!reasoning ? { ...reasoning, duration } : undefined,
235
+ search: !!grounding?.citations ? grounding : undefined,
236
+ imageList: finalImages.length > 0 ? finalImages : undefined,
237
+ metadata: speed ? { ...usage, ...speed } : usage,
238
+ });
239
+ },
240
+ onMessageHandle: async (chunk) => {
241
+ switch (chunk.type) {
242
+ case 'grounding': {
243
+ // if there is no citations, then stop
244
+ if (
245
+ !chunk.grounding ||
246
+ !chunk.grounding.citations ||
247
+ chunk.grounding.citations.length <= 0
248
+ )
249
+ return;
250
+
251
+ internal_dispatchMessage({
252
+ id: messageId,
253
+ type: 'updateMessage',
254
+ value: {
255
+ search: {
256
+ citations: chunk.grounding.citations,
257
+ searchQueries: chunk.grounding.searchQueries,
258
+ },
259
+ },
260
+ });
261
+ break;
262
+ }
263
+
264
+ case 'base64_image': {
265
+ internal_dispatchMessage({
266
+ id: messageId,
267
+ type: 'updateMessage',
268
+ value: {
269
+ imageList: chunk.images.map((i) => ({ id: i.id, url: i.data, alt: i.id })),
270
+ },
271
+ });
272
+ const image = chunk.image;
273
+
274
+ const task = getFileStoreState()
275
+ .uploadBase64FileWithProgress(image.data)
276
+ .then((value) => ({
277
+ id: value?.id,
278
+ url: value?.url,
279
+ alt: value?.filename || value?.id,
280
+ }));
281
+
282
+ uploadTasks.set(image.id, task);
283
+
284
+ break;
285
+ }
286
+
287
+ case 'text': {
288
+ output += chunk.text;
289
+
290
+ // if there is no duration, it means the end of reasoning
291
+ if (!duration) {
292
+ duration = Date.now() - thinkingStartAt;
293
+
294
+ const isInChatReasoning = get().reasoningLoadingIds.includes(messageId);
295
+ if (isInChatReasoning) {
296
+ internal_toggleChatReasoning(
297
+ false,
298
+ messageId,
299
+ n('toggleChatReasoning/false') as string,
300
+ );
301
+ }
302
+ }
303
+
304
+ internal_dispatchMessage({
305
+ id: messageId,
306
+ type: 'updateMessage',
307
+ value: {
308
+ content: output,
309
+ reasoning: !!thinking ? { content: thinking, duration } : undefined,
310
+ },
311
+ });
312
+ break;
313
+ }
314
+
315
+ case 'reasoning': {
316
+ // if there is no thinkingStartAt, it means the start of reasoning
317
+ if (!thinkingStartAt) {
318
+ thinkingStartAt = Date.now();
319
+ internal_toggleChatReasoning(
320
+ true,
321
+ messageId,
322
+ n('toggleChatReasoning/true') as string,
323
+ );
324
+ }
325
+
326
+ thinking += chunk.text;
327
+
328
+ internal_dispatchMessage({
329
+ id: messageId,
330
+ type: 'updateMessage',
331
+ value: { reasoning: { content: thinking } },
332
+ });
333
+ break;
334
+ }
335
+
336
+ // is this message is just a tool call
337
+ case 'tool_calls': {
338
+ internal_toggleToolCallingStreaming(messageId, chunk.isAnimationActives);
339
+ throttledUpdateToolCalls(chunk.tool_calls);
340
+ isFunctionCall = true;
341
+ const isInChatReasoning = get().reasoningLoadingIds.includes(messageId);
342
+ if (isInChatReasoning) {
343
+ internal_toggleChatReasoning(
344
+ false,
345
+ messageId,
346
+ n('toggleChatReasoning/false') as string,
347
+ );
348
+ }
349
+ }
350
+ }
351
+ },
352
+ });
353
+
354
+ internal_toggleChatLoading(false, messageId, n('generateMessage(end)') as string);
355
+
356
+ return {
357
+ isFunctionCall,
358
+ traceId: msgTraceId,
359
+ content: output,
360
+ tools,
361
+ usage: finalUsage,
362
+ tool_calls,
363
+ };
364
+ },
365
+
366
+ internal_execAgentRuntime: async (params) => {
367
+ const { messages: originalMessages, parentMessageId, parentMessageType } = params;
368
+
369
+ log(
370
+ '[internal_execAgentRuntime] start, parentMessageId: %s,parentMessageType: %s, messages count: %d',
371
+ parentMessageId,
372
+ parentMessageType,
373
+ originalMessages.length,
374
+ );
375
+
376
+ const { activeId, activeTopicId } = get();
377
+ const messageKey = messageMapKey(activeId, activeTopicId);
378
+
379
+ // Create a new array to avoid modifying the original messages
380
+ let messages = [...originalMessages];
381
+
382
+ const agentStoreState = getAgentStoreState();
383
+ const agentConfigData = agentSelectors.currentAgentConfig(agentStoreState);
384
+ const { chatConfig } = agentConfigData;
385
+
386
+ // Use current agent config
387
+ const model = agentConfigData.model;
388
+ const provider = agentConfigData.provider;
389
+
390
+ // ===========================================
391
+ // Step 1: RAG Preprocessing (if enabled)
392
+ // ===========================================
393
+ if (params.ragQuery && parentMessageType === 'user') {
394
+ const userMessageId = parentMessageId;
395
+ log('[internal_execAgentRuntime] RAG preprocessing start');
396
+
397
+ // Get relevant chunks from semantic search
398
+ const {
399
+ chunks,
400
+ queryId: ragQueryId,
401
+ rewriteQuery,
402
+ } = await get().internal_retrieveChunks(
403
+ userMessageId,
404
+ params.ragQuery,
405
+ // Skip the last message content when building context
406
+ messages.map((m) => m.content).slice(0, messages.length - 1),
407
+ );
408
+
409
+ log('[internal_execAgentRuntime] RAG chunks retrieved: %d chunks', chunks.length);
410
+
411
+ const lastMsg = messages.pop() as UIChatMessage;
412
+
413
+ // Build RAG context and append to user query
414
+ const knowledgeBaseQAContext = knowledgeBaseQAPrompts({
415
+ chunks,
416
+ userQuery: lastMsg.content,
417
+ rewriteQuery,
418
+ knowledge: agentSelectors.currentEnabledKnowledge(agentStoreState),
419
+ });
420
+
421
+ messages.push({
422
+ ...lastMsg,
423
+ content: (lastMsg.content + '\n\n' + knowledgeBaseQAContext).trim(),
424
+ });
425
+
426
+ // Update assistant message with RAG metadata
427
+ const fileChunks: MessageSemanticSearchChunk[] = chunks.map((c) => ({
428
+ id: c.id,
429
+ similarity: c.similarity,
430
+ }));
431
+
432
+ if (fileChunks.length > 0) {
433
+ // Note: RAG metadata will be updated after assistant message is created by call_llm executor
434
+ // Store RAG data temporarily in params for later use
435
+ params.ragMetadata = { ragQueryId: ragQueryId!, fileChunks };
436
+ }
437
+
438
+ log('[internal_execAgentRuntime] RAG preprocessing completed');
439
+ }
440
+
441
+ // ===========================================
442
+ // Step 3: Create and Execute Agent Runtime
443
+ // ===========================================
444
+ log('[internal_execAgentRuntime] Creating agent runtime');
445
+
446
+ const agent = new GeneralChatAgent({
447
+ agentConfig: { maxSteps: 1000 },
448
+ sessionId: `${messageKey}/${params.parentMessageId}`,
449
+ modelRuntimeConfig: {
450
+ model,
451
+ provider: provider!,
452
+ },
453
+ });
454
+ const runtime = new AgentRuntime(agent, {
455
+ executors: createAgentExecutors({
456
+ get,
457
+ messageKey,
458
+ parentId: params.parentMessageId,
459
+ parentMessageType,
460
+ params,
461
+ }),
462
+ });
463
+
464
+ // Create initial state
465
+ let state = AgentRuntime.createInitialState({
466
+ sessionId: activeId,
467
+ messages,
468
+ maxSteps: 20, // Prevent infinite loops
469
+ metadata: {
470
+ sessionId: activeId,
471
+ topicId: activeTopicId,
472
+ threadId: params.threadId,
473
+ },
474
+ });
475
+
476
+ // Initial context - use 'init' phase since state already contains messages
477
+ let nextContext: AgentRuntimeContext = {
478
+ phase: 'init',
479
+ payload: { model, provider, parentMessageId: params.parentMessageId },
480
+ session: {
481
+ sessionId: activeId,
482
+ messageCount: messages.length,
483
+ status: state.status,
484
+ stepCount: 0,
485
+ },
486
+ };
487
+
488
+ log(
489
+ '[internal_execAgentRuntime] Agent runtime loop start, initial phase: %s',
490
+ nextContext.phase,
491
+ );
492
+
493
+ // Execute the agent runtime loop
494
+ let stepCount = 0;
495
+ while (state.status !== 'done' && state.status !== 'error') {
496
+ stepCount++;
497
+ log(
498
+ '[internal_execAgentRuntime][step-%d]: phase=%s, status=%s',
499
+ stepCount,
500
+ nextContext.phase,
501
+ state.status,
502
+ );
503
+
504
+ const result = await runtime.step(state, nextContext);
505
+
506
+ log(
507
+ '[internal_execAgentRuntime] Step %d completed, events: %d, newStatus=%s',
508
+ stepCount,
509
+ result.events.length,
510
+ result.newState.status,
511
+ );
512
+
513
+ // Handle completion and error events
514
+ for (const event of result.events) {
515
+ if (event.type === 'done') {
516
+ log('[internal_execAgentRuntime] Received done event, syncing to database');
517
+ // Sync final state to database
518
+ const finalMessages = get().messagesMap[messageKey] || [];
519
+ get().replaceMessages(finalMessages);
520
+ }
521
+
522
+ if (event.type === 'error') {
523
+ log('[internal_execAgentRuntime] Received error event: %o', event.error);
524
+ // Find the assistant message to update error
525
+ const currentMessages = get().messagesMap[messageKey] || [];
526
+ const assistantMessage = currentMessages.findLast((m) => m.role === 'assistant');
527
+ if (assistantMessage) {
528
+ await messageService.updateMessageError(assistantMessage.id, event.error);
529
+ }
530
+ const finalMessages = get().messagesMap[messageKey] || [];
531
+ get().replaceMessages(finalMessages);
532
+ }
533
+ }
534
+
535
+ state = result.newState;
536
+
537
+ // If no nextContext, stop execution
538
+ if (!result.nextContext) {
539
+ log('[internal_execAgentRuntime] No next context, stopping loop');
540
+ break;
541
+ }
542
+
543
+ nextContext = result.nextContext;
544
+ }
545
+
546
+ log(
547
+ '[internal_execAgentRuntime] Agent runtime loop finished, final status: %s, total steps: %d',
548
+ state.status,
549
+ stepCount,
550
+ );
551
+
552
+ // Update RAG metadata if available
553
+ if (params.ragMetadata) {
554
+ const finalMessages = get().messagesMap[messageKey] || [];
555
+ const assistantMessage = finalMessages.findLast((m) => m.role === 'assistant');
556
+ if (assistantMessage) {
557
+ await get().optimisticUpdateMessageRAG(assistantMessage.id, params.ragMetadata);
558
+ log('[internal_execAgentRuntime] RAG metadata updated for assistant message');
559
+ }
560
+ }
561
+
562
+ log('[internal_execAgentRuntime] completed');
563
+
564
+ // Desktop notification (if not in tools calling mode)
565
+ if (isDesktop) {
566
+ try {
567
+ const messageKey = `${activeId}_${activeTopicId ?? null}`;
568
+ const finalMessages = get().messagesMap[messageKey] || [];
569
+ const lastAssistant = finalMessages.findLast((m) => m.role === 'assistant');
570
+
571
+ // Only show notification if there's content and no tools
572
+ if (lastAssistant?.content && !lastAssistant?.tools) {
573
+ const { desktopNotificationService } = await import(
574
+ '@/services/electron/desktopNotification'
575
+ );
576
+
577
+ await desktopNotificationService.showNotification({
578
+ body: lastAssistant.content,
579
+ title: t('notification.finishChatGeneration', { ns: 'electron' }),
580
+ });
581
+ }
582
+ } catch (error) {
583
+ console.error('Desktop notification error:', error);
584
+ }
585
+ }
586
+
587
+ // Summary history if context messages is larger than historyCount
588
+ const historyCount = agentChatConfigSelectors.historyCount(agentStoreState);
589
+
590
+ if (
591
+ agentChatConfigSelectors.enableHistoryCount(agentStoreState) &&
592
+ chatConfig.enableCompressHistory &&
593
+ messages.length > historyCount
594
+ ) {
595
+ // after generation: [u1,a1,u2,a2,u3,a3]
596
+ // but the `messages` is still: [u1,a1,u2,a2,u3]
597
+ // So if historyCount=2, we need to summary [u1,a1,u2,a2]
598
+ // because user find UI is [u1,a1,u2,a2 | u3,a3]
599
+ const historyMessages = messages.slice(0, -historyCount + 1);
600
+
601
+ await get().internal_summaryHistory(historyMessages);
602
+ }
603
+ },
604
+ });
@@ -0,0 +1,84 @@
1
+ import isEqual from 'fast-deep-equal';
2
+ import { produce } from 'immer';
3
+ import { StateCreator } from 'zustand/vanilla';
4
+
5
+ import { ChatStore } from '@/store/chat/store';
6
+ import { Action } from '@/utils/storeDebug';
7
+
8
+ /**
9
+ * Manages loading states during streaming operations
10
+ */
11
+ export interface StreamingStatesAction {
12
+ /**
13
+ * Toggles the loading state for AI message generation, managing the UI feedback
14
+ */
15
+ internal_toggleChatLoading: (
16
+ loading: boolean,
17
+ id?: string,
18
+ action?: Action,
19
+ ) => AbortController | undefined;
20
+ /**
21
+ * Toggles the loading state for AI message reasoning, managing the UI feedback
22
+ */
23
+ internal_toggleChatReasoning: (
24
+ loading: boolean,
25
+ id?: string,
26
+ action?: string,
27
+ ) => AbortController | undefined;
28
+ /**
29
+ * Toggles the loading state for messages in tools calling
30
+ */
31
+ internal_toggleMessageInToolsCalling: (
32
+ loading: boolean,
33
+ id?: string,
34
+ action?: Action,
35
+ ) => AbortController | undefined;
36
+ /**
37
+ * Toggles the loading state for search workflow
38
+ */
39
+ internal_toggleSearchWorkflow: (loading: boolean, id?: string) => void;
40
+ /**
41
+ * Controls the streaming state of tool calling processes, updating the UI accordingly
42
+ */
43
+ internal_toggleToolCallingStreaming: (id: string, streaming: boolean[] | undefined) => void;
44
+ }
45
+
46
+ export const streamingStates: StateCreator<
47
+ ChatStore,
48
+ [['zustand/devtools', never]],
49
+ [],
50
+ StreamingStatesAction
51
+ > = (set, get) => ({
52
+ internal_toggleChatLoading: (loading, id, action) => {
53
+ return get().internal_toggleLoadingArrays('chatLoadingIds', loading, id, action);
54
+ },
55
+ internal_toggleChatReasoning: (loading, id, action) => {
56
+ return get().internal_toggleLoadingArrays('reasoningLoadingIds', loading, id, action);
57
+ },
58
+ internal_toggleMessageInToolsCalling: (loading, id) => {
59
+ return get().internal_toggleLoadingArrays('messageInToolsCallingIds', loading, id);
60
+ },
61
+ internal_toggleSearchWorkflow: (loading, id) => {
62
+ return get().internal_toggleLoadingArrays('searchWorkflowLoadingIds', loading, id);
63
+ },
64
+
65
+ internal_toggleToolCallingStreaming: (id, streaming) => {
66
+ const previous = get().toolCallingStreamIds;
67
+ const next = produce(previous, (draft) => {
68
+ if (!!streaming) {
69
+ draft[id] = streaming;
70
+ } else {
71
+ delete draft[id];
72
+ }
73
+ });
74
+
75
+ if (isEqual(previous, next)) return;
76
+
77
+ set(
78
+ { toolCallingStreamIds: next },
79
+
80
+ false,
81
+ `toggleToolCallingStreaming/${!!streaming ? 'start' : 'end'}`,
82
+ );
83
+ },
84
+ });