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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/changelog/v1.json +9 -0
  3. package/next.config.ts +5 -6
  4. package/package.json +2 -2
  5. package/packages/agent-runtime/src/core/__tests__/runtime.test.ts +112 -77
  6. package/packages/agent-runtime/src/core/runtime.ts +63 -18
  7. package/packages/agent-runtime/src/types/generalAgent.ts +55 -0
  8. package/packages/agent-runtime/src/types/index.ts +1 -0
  9. package/packages/agent-runtime/src/types/instruction.ts +10 -3
  10. package/packages/const/src/user.ts +0 -1
  11. package/packages/context-engine/src/processors/GroupMessageFlatten.ts +8 -6
  12. package/packages/context-engine/src/processors/__tests__/GroupMessageFlatten.test.ts +12 -12
  13. package/packages/conversation-flow/src/__tests__/fixtures/inputs/branch/assistant-group-branches.json +249 -0
  14. package/packages/conversation-flow/src/__tests__/fixtures/inputs/branch/index.ts +4 -0
  15. package/packages/conversation-flow/src/__tests__/fixtures/inputs/branch/multi-assistant-group.json +260 -0
  16. package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/active-index-1.json +4 -0
  17. package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/assistant-group-branches.json +481 -0
  18. package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/conversation.json +5 -1
  19. package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/index.ts +4 -0
  20. package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/multi-assistant-group.json +407 -0
  21. package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/nested.json +18 -2
  22. package/packages/conversation-flow/src/__tests__/fixtures/outputs/complex-scenario.json +25 -3
  23. package/packages/conversation-flow/src/__tests__/parse.test.ts +12 -0
  24. package/packages/conversation-flow/src/index.ts +1 -1
  25. package/packages/conversation-flow/src/transformation/FlatListBuilder.ts +112 -34
  26. package/packages/conversation-flow/src/types/flatMessageList.ts +0 -12
  27. package/packages/conversation-flow/src/{types.ts → types/index.ts} +3 -14
  28. package/packages/database/src/models/message.ts +18 -19
  29. package/packages/types/src/aiChat.ts +2 -0
  30. package/packages/types/src/importer.ts +2 -2
  31. package/packages/types/src/message/ui/chat.ts +17 -1
  32. package/packages/types/src/message/ui/extra.ts +2 -2
  33. package/packages/types/src/message/ui/params.ts +2 -2
  34. package/packages/types/src/user/preference.ts +0 -4
  35. package/packages/utils/src/tokenizer/index.ts +3 -11
  36. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/Desktop/MessageFromUrl.tsx +3 -3
  37. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/V1Mobile/index.tsx +1 -1
  38. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/V1Mobile/useSend.ts +3 -3
  39. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/useSend.ts +6 -6
  40. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatList/Content.tsx +5 -3
  41. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatList/WelcomeChatItem/AgentWelcome/OpeningQuestions.tsx +2 -2
  42. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatList/WelcomeChatItem/GroupWelcome/GroupUsageSuggest.tsx +2 -2
  43. package/src/app/[variants]/(main)/labs/page.tsx +0 -9
  44. package/src/features/ChatInput/ActionBar/STT/browser.tsx +3 -3
  45. package/src/features/ChatInput/ActionBar/STT/openai.tsx +3 -3
  46. package/src/features/Conversation/Error/AccessCodeForm.tsx +1 -1
  47. package/src/features/Conversation/Error/ChatInvalidApiKey.tsx +1 -1
  48. package/src/features/Conversation/Error/ClerkLogin/index.tsx +1 -1
  49. package/src/features/Conversation/Error/OAuthForm.tsx +1 -1
  50. package/src/features/Conversation/Error/index.tsx +0 -5
  51. package/src/features/Conversation/Messages/Assistant/Actions/index.tsx +13 -10
  52. package/src/features/Conversation/Messages/Assistant/Extra/index.test.tsx +3 -8
  53. package/src/features/Conversation/Messages/Assistant/Extra/index.tsx +2 -6
  54. package/src/features/Conversation/Messages/Assistant/MessageContent.tsx +7 -9
  55. package/src/features/Conversation/Messages/Assistant/Tool/Inspector/PluginResult.tsx +2 -2
  56. package/src/features/Conversation/Messages/Assistant/Tool/Inspector/PluginState.tsx +2 -2
  57. package/src/features/Conversation/Messages/Assistant/Tool/Render/PluginSettings.tsx +4 -1
  58. package/src/features/Conversation/Messages/Assistant/Tool/Render/index.tsx +2 -3
  59. package/src/features/Conversation/Messages/Assistant/index.tsx +57 -60
  60. package/src/features/Conversation/Messages/Default.tsx +1 -0
  61. package/src/features/Conversation/Messages/Group/Actions/WithContentId.tsx +38 -10
  62. package/src/features/Conversation/Messages/Group/Actions/index.tsx +1 -1
  63. package/src/features/Conversation/Messages/Group/ContentBlock.tsx +1 -3
  64. package/src/features/Conversation/Messages/Group/GroupChildren.tsx +12 -12
  65. package/src/features/Conversation/Messages/Group/MessageContent.tsx +7 -1
  66. package/src/features/Conversation/Messages/Group/Tool/Render/PluginSettings.tsx +1 -1
  67. package/src/features/Conversation/Messages/Group/index.tsx +2 -1
  68. package/src/features/Conversation/Messages/Supervisor/index.tsx +2 -2
  69. package/src/features/Conversation/Messages/User/{Actions.tsx → Actions/ActionsBar.tsx} +26 -25
  70. package/src/features/Conversation/Messages/User/Actions/MessageBranch.tsx +107 -0
  71. package/src/features/Conversation/Messages/User/Actions/index.tsx +42 -0
  72. package/src/features/Conversation/Messages/User/index.tsx +43 -44
  73. package/src/features/Conversation/Messages/index.tsx +3 -3
  74. package/src/features/Conversation/components/AutoScroll.tsx +3 -3
  75. package/src/features/Conversation/components/Extras/Usage/UsageDetail/AnimatedNumber.tsx +55 -0
  76. package/src/features/Conversation/components/Extras/Usage/UsageDetail/index.tsx +5 -2
  77. package/src/features/Conversation/components/VirtualizedList/index.tsx +29 -20
  78. package/src/features/Conversation/hooks/useChatListActionsBar.tsx +8 -10
  79. package/src/features/Portal/Thread/Chat/ChatInput/useSend.ts +3 -3
  80. package/src/hooks/useHotkeys/chatScope.ts +15 -7
  81. package/src/server/routers/lambda/__tests__/aiChat.test.ts +1 -1
  82. package/src/server/routers/lambda/__tests__/integration/message.integration.test.ts +0 -26
  83. package/src/server/routers/lambda/aiChat.ts +3 -2
  84. package/src/server/routers/lambda/message.ts +8 -16
  85. package/src/server/services/message/__tests__/index.test.ts +29 -39
  86. package/src/server/services/message/index.ts +41 -36
  87. package/src/services/electron/desktopNotification.ts +6 -6
  88. package/src/services/electron/file.ts +6 -6
  89. package/src/services/file/ClientS3/index.ts +8 -8
  90. package/src/services/message/__tests__/metadata-race-condition.test.ts +157 -0
  91. package/src/services/message/index.ts +21 -15
  92. package/src/services/upload.ts +11 -11
  93. package/src/services/utils/abortableRequest.test.ts +161 -0
  94. package/src/services/utils/abortableRequest.ts +67 -0
  95. package/src/store/chat/agents/GeneralChatAgent.ts +137 -0
  96. package/src/store/chat/agents/createAgentExecutors.ts +395 -0
  97. package/src/store/chat/helpers.test.ts +0 -99
  98. package/src/store/chat/helpers.ts +0 -11
  99. package/src/store/chat/slices/aiChat/actions/__tests__/conversationControl.test.ts +332 -0
  100. package/src/store/chat/slices/aiChat/actions/__tests__/conversationLifecycle.test.ts +257 -0
  101. package/src/store/chat/slices/aiChat/actions/__tests__/helpers.ts +11 -2
  102. package/src/store/chat/slices/aiChat/actions/__tests__/rag.test.ts +6 -6
  103. package/src/store/chat/slices/aiChat/actions/__tests__/streamingExecutor.test.ts +391 -0
  104. package/src/store/chat/slices/aiChat/actions/__tests__/streamingStates.test.ts +179 -0
  105. package/src/store/chat/slices/aiChat/actions/conversationControl.ts +157 -0
  106. package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +329 -0
  107. package/src/store/chat/slices/aiChat/actions/generateAIGroupChat.ts +14 -14
  108. package/src/store/chat/slices/aiChat/actions/index.ts +12 -6
  109. package/src/store/chat/slices/aiChat/actions/rag.ts +9 -6
  110. package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +604 -0
  111. package/src/store/chat/slices/aiChat/actions/streamingStates.ts +84 -0
  112. package/src/store/chat/slices/builtinTool/actions/__tests__/localSystem.test.ts +4 -4
  113. package/src/store/chat/slices/builtinTool/actions/__tests__/search.test.ts +11 -11
  114. package/src/store/chat/slices/builtinTool/actions/interpreter.ts +8 -8
  115. package/src/store/chat/slices/builtinTool/actions/localSystem.ts +2 -2
  116. package/src/store/chat/slices/builtinTool/actions/search.ts +8 -8
  117. package/src/store/chat/slices/message/action.test.ts +79 -68
  118. package/src/store/chat/slices/message/actions/index.ts +39 -0
  119. package/src/store/chat/slices/message/actions/internals.ts +77 -0
  120. package/src/store/chat/slices/message/actions/optimisticUpdate.ts +260 -0
  121. package/src/store/chat/slices/message/actions/publicApi.ts +224 -0
  122. package/src/store/chat/slices/message/actions/query.ts +120 -0
  123. package/src/store/chat/slices/message/actions/runtimeState.ts +108 -0
  124. package/src/store/chat/slices/message/initialState.ts +13 -0
  125. package/src/store/chat/slices/message/reducer.test.ts +48 -370
  126. package/src/store/chat/slices/message/reducer.ts +17 -81
  127. package/src/store/chat/slices/message/selectors/chat.test.ts +13 -50
  128. package/src/store/chat/slices/message/selectors/chat.ts +78 -242
  129. package/src/store/chat/slices/message/selectors/dbMessage.ts +140 -0
  130. package/src/store/chat/slices/message/selectors/displayMessage.ts +301 -0
  131. package/src/store/chat/slices/message/selectors/messageState.ts +5 -2
  132. package/src/store/chat/slices/plugin/action.test.ts +62 -64
  133. package/src/store/chat/slices/plugin/action.ts +34 -28
  134. package/src/store/chat/slices/thread/action.test.ts +28 -31
  135. package/src/store/chat/slices/thread/action.ts +13 -10
  136. package/src/store/chat/slices/thread/selectors/index.ts +8 -6
  137. package/src/store/chat/slices/topic/reducer.ts +11 -3
  138. package/src/store/chat/store.ts +1 -1
  139. package/src/store/user/slices/preference/selectors/labPrefer.ts +0 -3
  140. package/packages/database/src/models/__tests__/message.grouping.test.ts +0 -812
  141. package/packages/database/src/utils/__tests__/groupMessages.test.ts +0 -1132
  142. package/packages/database/src/utils/groupMessages.ts +0 -361
  143. package/packages/utils/src/tokenizer/client.ts +0 -35
  144. package/packages/utils/src/tokenizer/estimated.ts +0 -4
  145. package/packages/utils/src/tokenizer/server.ts +0 -11
  146. package/packages/utils/src/tokenizer/tokenizer.worker.ts +0 -12
  147. package/src/app/(backend)/webapi/tokenizer/index.test.ts +0 -32
  148. package/src/app/(backend)/webapi/tokenizer/route.ts +0 -8
  149. package/src/features/Conversation/Error/InvalidAccessCode.tsx +0 -79
  150. package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChat.test.ts +0 -975
  151. package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChatV2.test.ts +0 -1050
  152. package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +0 -720
  153. package/src/store/chat/slices/aiChat/actions/generateAIChatV2.ts +0 -849
  154. package/src/store/chat/slices/message/action.ts +0 -629
package/CHANGELOG.md CHANGED
@@ -2,6 +2,31 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## [Version 2.0.0-next.36](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.35...v2.0.0-next.36)
6
+
7
+ <sup>Released on **2025-11-07**</sup>
8
+
9
+ #### ✨ Features
10
+
11
+ - **misc**: Refactor to use agent runtime as the generation core and support branch mode.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's improved
19
+
20
+ - **misc**: Refactor to use agent runtime as the generation core and support branch mode, closes [#10080](https://github.com/lobehub/lobe-chat/issues/10080) ([b95e741](https://github.com/lobehub/lobe-chat/commit/b95e741))
21
+
22
+ </details>
23
+
24
+ <div align="right">
25
+
26
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
27
+
28
+ </div>
29
+
5
30
  ## [Version 2.0.0-next.35](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.34...v2.0.0-next.35)
6
31
 
7
32
  <sup>Released on **2025-11-07**</sup>
package/changelog/v1.json CHANGED
@@ -1,4 +1,13 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "features": [
5
+ "Refactor to use agent runtime as the generation core and support branch mode."
6
+ ]
7
+ },
8
+ "date": "2025-11-07",
9
+ "version": "2.0.0-next.36"
10
+ },
2
11
  {
3
12
  "children": {
4
13
  "improvements": [
package/next.config.ts CHANGED
@@ -36,7 +36,6 @@ const nextConfig: NextConfig = {
36
36
  '@icons-pack/react-simple-icons',
37
37
  '@lobehub/ui',
38
38
  '@lobehub/icons',
39
- 'gpt-tokenizer',
40
39
  ],
41
40
  // oidc provider depend on constructor.name
42
41
  // but swc minification will remove the name
@@ -269,7 +268,7 @@ const nextConfig: NextConfig = {
269
268
  ],
270
269
 
271
270
  // when external packages in dev mode with turbopack, this config will lead to bundle error
272
- serverExternalPackages: isProd ? ['@electric-sql/pglite', "pdfkit"] : ["pdfkit"],
271
+ serverExternalPackages: isProd ? ['@electric-sql/pglite', 'pdfkit'] : ['pdfkit'],
273
272
  transpilePackages: ['pdfjs-dist', 'mermaid'],
274
273
  turbopack: {},
275
274
 
@@ -335,10 +334,10 @@ const withBundleAnalyzer = process.env.ANALYZE === 'true' ? analyzer() : noWrapp
335
334
  const withPWA =
336
335
  isProd && !isDesktop
337
336
  ? withSerwistInit({
338
- register: false,
339
- swDest: 'public/sw.js',
340
- swSrc: 'src/app/sw.ts',
341
- })
337
+ register: false,
338
+ swDest: 'public/sw.js',
339
+ swSrc: 'src/app/sw.ts',
340
+ })
342
341
  : noWrapper;
343
342
 
344
343
  export default withBundleAnalyzer(withPWA(nextConfig as NextConfig));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/lobehub",
3
- "version": "2.0.0-next.35",
3
+ "version": "2.0.0-next.36",
4
4
  "description": "LobeHub - an open-source,comprehensive AI Agent framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
5
5
  "keywords": [
6
6
  "framework",
@@ -150,6 +150,7 @@
150
150
  "@lobechat/agent-runtime": "workspace:*",
151
151
  "@lobechat/const": "workspace:*",
152
152
  "@lobechat/context-engine": "workspace:*",
153
+ "@lobechat/conversation-flow": "workspace:*",
153
154
  "@lobechat/database": "workspace:*",
154
155
  "@lobechat/electron-client-ipc": "workspace:*",
155
156
  "@lobechat/electron-server-ipc": "workspace:*",
@@ -208,7 +209,6 @@
208
209
  "fflate": "^0.8.2",
209
210
  "file-type": "^21.0.0",
210
211
  "framer-motion": "^12.23.24",
211
- "gpt-tokenizer": "^3.2.0",
212
212
  "gray-matter": "^4.0.3",
213
213
  "html-to-text": "^9.0.5",
214
214
  "i18next": "^25.6.0",
@@ -117,13 +117,12 @@ describe('AgentRuntime', () => {
117
117
  const runtime = new AgentRuntime(agent);
118
118
  const state = AgentRuntime.createInitialState({ sessionId: 'test-session' });
119
119
 
120
- const toolCall: ToolsCalling = {
120
+ const toolCall = {
121
121
  id: 'call_123',
122
- type: 'function',
123
- function: {
124
- name: 'test_tool',
125
- arguments: '{"input": "test"}',
126
- },
122
+ apiName: 'test_tool',
123
+ identifier: 'test_tool',
124
+ arguments: '{"input": "test"}',
125
+ type: 'default' as const,
127
126
  };
128
127
 
129
128
  const result = await runtime.approveToolCall(state, toolCall);
@@ -288,13 +287,12 @@ describe('AgentRuntime', () => {
288
287
  const runtime = new AgentRuntime(agent);
289
288
  const state = AgentRuntime.createInitialState({ sessionId: 'test-session' });
290
289
 
291
- const toolCall: ToolsCalling = {
290
+ const toolCall = {
292
291
  id: 'call_123',
293
- type: 'function',
294
- function: {
295
- name: 'calculator',
296
- arguments: '{"expression": "2+2"}',
297
- },
292
+ apiName: 'calculator',
293
+ identifier: 'calculator',
294
+ arguments: '{"expression": "2+2"}',
295
+ type: 'default' as const,
298
296
  };
299
297
 
300
298
  const result = await runtime.approveToolCall(state, toolCall);
@@ -320,13 +318,12 @@ describe('AgentRuntime', () => {
320
318
  const runtime = new AgentRuntime(agent);
321
319
  const state = AgentRuntime.createInitialState({ sessionId: 'test-session' });
322
320
 
323
- const toolCall: ToolsCalling = {
321
+ const toolCall = {
324
322
  id: 'call_123',
325
- type: 'function',
326
- function: {
327
- name: 'unknown_tool',
328
- arguments: '{}',
329
- },
323
+ apiName: 'unknown_tool',
324
+ identifier: 'unknown_tool',
325
+ arguments: '{}',
326
+ type: 'default' as const,
330
327
  };
331
328
 
332
329
  const result = await runtime.approveToolCall(state, toolCall);
@@ -1022,7 +1019,14 @@ describe('AgentRuntime', () => {
1022
1019
  expect(result.newState.pendingToolsCalling).toHaveLength(1);
1023
1020
 
1024
1021
  // Step 2: Approve and execute tool call
1025
- const toolCall = result.newState.pendingToolsCalling![0];
1022
+ const pendingToolCall = result.newState.pendingToolsCalling![0];
1023
+ const toolCall = {
1024
+ id: pendingToolCall.id,
1025
+ apiName: pendingToolCall.function.name,
1026
+ identifier: pendingToolCall.function.name,
1027
+ arguments: pendingToolCall.function.arguments,
1028
+ type: 'default' as const,
1029
+ };
1026
1030
  result = await runtime.approveToolCall(result.newState, toolCall);
1027
1031
 
1028
1032
  // Should have executed tool
@@ -1121,17 +1125,27 @@ describe('AgentRuntime', () => {
1121
1125
  return [
1122
1126
  {
1123
1127
  payload: {
1124
- id: 'call_1',
1125
- type: 'function' as const,
1126
- function: { name: 'tool_1', arguments: '{}' },
1128
+ parentMessageId: 'user-msg-id',
1129
+ toolCalling: {
1130
+ id: 'call_1',
1131
+ type: 'default' as const,
1132
+ apiName: 'tool_1',
1133
+ identifier: 'tool_1',
1134
+ arguments: '{}',
1135
+ },
1127
1136
  },
1128
1137
  type: 'call_tool' as const,
1129
1138
  },
1130
1139
  {
1131
1140
  payload: {
1132
- id: 'call_2',
1133
- type: 'function' as const,
1134
- function: { name: 'tool_2', arguments: '{}' },
1141
+ parentMessageId: 'user-msg-id',
1142
+ toolCalling: {
1143
+ id: 'call_2',
1144
+ type: 'default' as const,
1145
+ apiName: 'tool_2',
1146
+ identifier: 'tool_2',
1147
+ arguments: '{}',
1148
+ },
1135
1149
  },
1136
1150
  type: 'call_tool' as const,
1137
1151
  },
@@ -1175,9 +1189,14 @@ describe('AgentRuntime', () => {
1175
1189
  return [
1176
1190
  {
1177
1191
  payload: {
1178
- id: 'call_safe',
1179
- type: 'function' as const,
1180
- function: { name: 'safe_tool', arguments: '{}' },
1192
+ parentMessageId: 'user-msg-id',
1193
+ toolCalling: {
1194
+ id: 'call_safe',
1195
+ type: 'default' as const,
1196
+ apiName: 'safe_tool',
1197
+ identifier: 'safe_tool',
1198
+ arguments: '{}',
1199
+ },
1181
1200
  },
1182
1201
  type: 'call_tool' as const,
1183
1202
  },
@@ -1252,18 +1271,25 @@ describe('AgentRuntime', () => {
1252
1271
  async runner(context: AgentRuntimeContext, _state: AgentState) {
1253
1272
  if (context.phase === 'user_input') {
1254
1273
  return {
1255
- payload: [
1256
- {
1257
- id: 'call_expensive',
1258
- type: 'function' as const,
1259
- function: { name: 'expensive_tool', arguments: '{}' },
1260
- },
1261
- {
1262
- id: 'call_cheap',
1263
- type: 'function' as const,
1264
- function: { name: 'cheap_tool', arguments: '{}' },
1265
- },
1266
- ],
1274
+ payload: {
1275
+ parentMessageId: 'user-msg-id',
1276
+ toolsCalling: [
1277
+ {
1278
+ id: 'call_expensive',
1279
+ type: 'default' as const,
1280
+ apiName: 'expensive_tool',
1281
+ identifier: 'expensive_tool',
1282
+ arguments: '{}',
1283
+ },
1284
+ {
1285
+ id: 'call_cheap',
1286
+ type: 'default' as const,
1287
+ apiName: 'cheap_tool',
1288
+ identifier: 'cheap_tool',
1289
+ arguments: '{}',
1290
+ },
1291
+ ],
1292
+ },
1267
1293
  type: 'call_tools_batch' as const,
1268
1294
  };
1269
1295
  }
@@ -1380,11 +1406,14 @@ describe('AgentRuntime', () => {
1380
1406
  if (context.phase === 'user_input') {
1381
1407
  return {
1382
1408
  payload: {
1383
- apiName: 'expensive_tool',
1384
- arguments: '{}',
1385
- id: 'call_1',
1386
- identifier: 'expensive_tool',
1387
- type: 'default' as const,
1409
+ parentMessageId: 'user-msg-id',
1410
+ toolCalling: {
1411
+ apiName: 'expensive_tool',
1412
+ arguments: '{}',
1413
+ id: 'call_1',
1414
+ identifier: 'expensive_tool',
1415
+ type: 'default' as const,
1416
+ },
1388
1417
  },
1389
1418
  type: 'call_tool' as const,
1390
1419
  };
@@ -1458,22 +1487,25 @@ describe('AgentRuntime', () => {
1458
1487
  async runner(context: AgentRuntimeContext, _state: AgentState) {
1459
1488
  if (context.phase === 'user_input') {
1460
1489
  return {
1461
- payload: [
1462
- {
1463
- apiName: 'tool_1',
1464
- arguments: '{}',
1465
- id: 'call_1',
1466
- identifier: 'tool_1',
1467
- type: 'default' as const,
1468
- },
1469
- {
1470
- apiName: 'tool_2',
1471
- arguments: '{}',
1472
- id: 'call_2',
1473
- identifier: 'tool_2',
1474
- type: 'default' as const,
1475
- },
1476
- ],
1490
+ payload: {
1491
+ parentMessageId: 'user-msg-id',
1492
+ toolsCalling: [
1493
+ {
1494
+ apiName: 'tool_1',
1495
+ arguments: '{}',
1496
+ id: 'call_1',
1497
+ identifier: 'tool_1',
1498
+ type: 'default' as const,
1499
+ },
1500
+ {
1501
+ apiName: 'tool_2',
1502
+ arguments: '{}',
1503
+ id: 'call_2',
1504
+ identifier: 'tool_2',
1505
+ type: 'default' as const,
1506
+ },
1507
+ ],
1508
+ },
1477
1509
  type: 'call_tools_batch' as const,
1478
1510
  };
1479
1511
  }
@@ -1535,22 +1567,25 @@ describe('AgentRuntime', () => {
1535
1567
  async runner(context: AgentRuntimeContext, _state: AgentState) {
1536
1568
  if (context.phase === 'user_input') {
1537
1569
  return {
1538
- payload: [
1539
- {
1540
- apiName: 'analytics_tool',
1541
- arguments: '{}',
1542
- id: 'call_analytics',
1543
- identifier: 'analytics_tool',
1544
- type: 'default' as const,
1545
- },
1546
- {
1547
- apiName: 'logging_tool',
1548
- arguments: '{}',
1549
- id: 'call_logging',
1550
- identifier: 'logging_tool',
1551
- type: 'default' as const,
1552
- },
1553
- ],
1570
+ payload: {
1571
+ parentMessageId: 'user-msg-id',
1572
+ toolsCalling: [
1573
+ {
1574
+ apiName: 'analytics_tool',
1575
+ arguments: '{}',
1576
+ id: 'call_analytics',
1577
+ identifier: 'analytics_tool',
1578
+ type: 'default' as const,
1579
+ },
1580
+ {
1581
+ apiName: 'logging_tool',
1582
+ arguments: '{}',
1583
+ id: 'call_logging',
1584
+ identifier: 'logging_tool',
1585
+ type: 'default' as const,
1586
+ },
1587
+ ],
1588
+ },
1554
1589
  type: 'call_tools_batch' as const,
1555
1590
  };
1556
1591
  }
@@ -1,10 +1,16 @@
1
- import type {
1
+ import { ChatToolPayload } from '@lobechat/types';
2
+ import pMap from 'p-map';
3
+
4
+ import {
2
5
  Agent,
3
6
  AgentEvent,
4
7
  AgentInstruction,
8
+ AgentInstructionCallTool,
9
+ AgentInstructionCallToolsBatch,
5
10
  AgentRuntimeContext,
6
11
  AgentState,
7
12
  Cost,
13
+ GeneralAgentCallToolsBatchResultPayload,
8
14
  InstructionExecutor,
9
15
  RuntimeConfig,
10
16
  ToolRegistry,
@@ -79,8 +85,16 @@ export class AgentRuntime {
79
85
 
80
86
  // Handle human approved tool calls
81
87
  if (runtimeContext.phase === 'human_approved_tool') {
82
- const approvedPayload = runtimeContext.payload as { approvedToolCall: ToolsCalling };
83
- rawInstructions = { payload: approvedPayload.approvedToolCall, type: 'call_tool' };
88
+ const approvedPayload = runtimeContext.payload as { approvedToolCall: ChatToolPayload };
89
+ const toolCalling = approvedPayload.approvedToolCall;
90
+
91
+ rawInstructions = {
92
+ payload: {
93
+ parentMessageId: '', // Not required for approval flow
94
+ toolCalling,
95
+ },
96
+ type: 'call_tool',
97
+ };
84
98
  } else {
85
99
  // Standard flow: Plan -> Execute
86
100
  rawInstructions = await this.agent.runner(runtimeContext, newState);
@@ -89,12 +103,37 @@ export class AgentRuntime {
89
103
  // Normalize to array
90
104
  const instructions = Array.isArray(rawInstructions) ? rawInstructions : [rawInstructions];
91
105
 
106
+ // Convert old format to new format
107
+ const normalizedInstructions = instructions.map((instruction) => {
108
+ if (
109
+ instruction.type === 'call_tools_batch' && // Check if payload is array of ToolsCalling (old format)
110
+ Array.isArray(instruction.payload)
111
+ ) {
112
+ const toolsCalling = instruction.payload.map((tc: ToolsCalling) => ({
113
+ apiName: tc.function.name,
114
+ arguments: tc.function.arguments,
115
+ id: tc.id,
116
+ identifier: tc.function.name,
117
+ type: 'default' as any,
118
+ }));
119
+
120
+ return {
121
+ payload: {
122
+ parentMessageId: '',
123
+ toolsCalling,
124
+ },
125
+ type: 'call_tools_batch',
126
+ };
127
+ }
128
+ return instruction;
129
+ });
130
+
92
131
  // Execute all instructions sequentially
93
132
  let currentState = newState;
94
133
  const allEvents: AgentEvent[] = [];
95
134
  let finalNextContext: AgentRuntimeContext | undefined = undefined;
96
135
 
97
- for (const instruction of instructions) {
136
+ for (const instruction of normalizedInstructions) {
98
137
  let result;
99
138
 
100
139
  // Special handling for batch tool execution
@@ -147,7 +186,7 @@ export class AgentRuntime {
147
186
  */
148
187
  async approveToolCall(
149
188
  state: AgentState,
150
- approvedToolCall: any,
189
+ approvedToolCall: ChatToolPayload,
151
190
  ): Promise<{ events: AgentEvent[]; newState: AgentState; nextContext?: AgentRuntimeContext }> {
152
191
  const context: AgentRuntimeContext = {
153
192
  payload: { approvedToolCall },
@@ -414,7 +453,7 @@ export class AgentRuntime {
414
453
  /** Create call_tool executor */
415
454
  private createCallToolExecutor(): InstructionExecutor {
416
455
  return async (instruction, state) => {
417
- const { payload: toolCall } = instruction as Extract<AgentInstruction, { type: 'call_tool' }>;
456
+ const { payload } = instruction as AgentInstructionCallTool;
418
457
  const newState = structuredClone(state);
419
458
  const events: AgentEvent[] = [];
420
459
 
@@ -423,9 +462,10 @@ export class AgentRuntime {
423
462
 
424
463
  const tools = this.agent.tools || ({} as ToolRegistry);
425
464
 
465
+ const toolCall = payload.toolCalling;
426
466
  // Support both ToolsCalling (OpenAI format) and CallingToolPayload formats
427
- const toolName = toolCall.apiName || toolCall.function?.name;
428
- const toolArgs = toolCall.arguments || toolCall.function?.arguments;
467
+ const toolName = toolCall.apiName;
468
+ const toolArgs = toolCall.arguments;
429
469
  const toolId = toolCall.id;
430
470
 
431
471
  const handler = tools[toolName];
@@ -586,27 +626,30 @@ export class AgentRuntime {
586
626
  * Execute multiple tool calls concurrently
587
627
  */
588
628
  private async executeToolsBatch(
589
- instruction: { payload: any[]; type: 'call_tools_batch' },
629
+ instruction: AgentInstructionCallToolsBatch,
590
630
  baseState: AgentState,
591
631
  ): Promise<{
592
632
  events: AgentEvent[];
593
633
  newState: AgentState;
594
634
  nextContext?: AgentRuntimeContext;
595
635
  }> {
596
- const { payload: toolsCalling } = instruction;
636
+ const { payload } = instruction;
597
637
 
598
638
  // Execute all tools concurrently based on the same state
599
- const results = await Promise.all(
600
- toolsCalling.map((toolCall) =>
601
- this.executors.call_tool(
602
- { payload: toolCall, type: 'call_tool' } as any,
603
- structuredClone(baseState), // Each tool starts from the same base state
604
- ),
639
+ const results = await pMap(instruction.payload.toolsCalling, (toolCalling: ChatToolPayload) =>
640
+ this.executors.call_tool(
641
+ {
642
+ payload: { parentMessageId: payload.parentMessageId, toolCalling },
643
+ type: 'call_tool',
644
+ } as AgentInstructionCallTool,
645
+ structuredClone(baseState), // Each tool starts from the same base state
605
646
  ),
606
647
  );
607
648
 
649
+ const lastParentMessageId = (results.at(-1)!.nextContext?.payload as any)
650
+ ?.parentMessageId as string;
608
651
  // Merge results
609
- return this.mergeToolResults(results, baseState);
652
+ return this.mergeToolResults(results, baseState, lastParentMessageId);
610
653
  }
611
654
 
612
655
  /**
@@ -619,6 +662,7 @@ export class AgentRuntime {
619
662
  nextContext?: AgentRuntimeContext;
620
663
  }>,
621
664
  baseState: AgentState,
665
+ lastParentMessageId: string,
622
666
  ): {
623
667
  events: AgentEvent[];
624
668
  newState: AgentState;
@@ -679,9 +723,10 @@ export class AgentRuntime {
679
723
  newState,
680
724
  nextContext: {
681
725
  payload: {
726
+ parentMessageId: lastParentMessageId,
682
727
  toolCount: results.length,
683
728
  toolResults: results.map((r) => r.nextContext?.payload),
684
- },
729
+ } as GeneralAgentCallToolsBatchResultPayload,
685
730
  phase: 'tools_batch_result',
686
731
  session: this.createSessionContext(newState),
687
732
  },
@@ -0,0 +1,55 @@
1
+ import { ChatToolPayload, MessageToolCall } from '@lobechat/types';
2
+
3
+ export interface GeneralAgentCallLLMInstructionPayload {
4
+ isFirstMessage?: boolean;
5
+ messages: any[];
6
+ model: string;
7
+ parentMessageId?: string;
8
+ provider: string;
9
+ tools: any[];
10
+ }
11
+
12
+ export interface GeneralAgentCallLLMResultPayload {
13
+ hasToolsCalling: boolean;
14
+ parentMessageId: string;
15
+ result: { content: string; tool_calls: MessageToolCall[] };
16
+ toolsCalling: ChatToolPayload[];
17
+ }
18
+
19
+ export interface GeneralAgentCallingToolInstructionPayload {
20
+ parentMessageId: string;
21
+ toolCalling: ChatToolPayload;
22
+ }
23
+
24
+ export interface GeneralAgentCallToolResultPayload {
25
+ data: any;
26
+ executionTime: number;
27
+ isSuccess: boolean;
28
+ parentMessageId: string;
29
+ toolCall: ChatToolPayload;
30
+ toolCallId: string;
31
+ }
32
+
33
+ export interface GeneralAgentCallToolsBatchInstructionPayload {
34
+ parentMessageId: string;
35
+ toolsCalling: ChatToolPayload[];
36
+ }
37
+
38
+ export interface GeneralAgentCallToolsBatchResultPayload {
39
+ parentMessageId: string;
40
+ toolCount: number;
41
+ toolResults: GeneralAgentCallToolResultPayload[];
42
+ }
43
+
44
+ export interface GeneralAgentConfig {
45
+ agentConfig?: {
46
+ [key: string]: any;
47
+ maxSteps?: number;
48
+ };
49
+ modelRuntimeConfig?: {
50
+ model: string;
51
+ provider: string;
52
+ };
53
+ sessionId: string;
54
+ userId?: string;
55
+ }
@@ -1,4 +1,5 @@
1
1
  export * from './event';
2
+ export * from './generalAgent';
2
3
  export * from './instruction';
3
4
  export * from './runtime';
4
5
  export * from './state';
@@ -1,4 +1,4 @@
1
- import type { ModelUsage } from '@lobechat/types';
1
+ import { ChatToolPayload, ModelUsage } from '@lobechat/types';
2
2
 
3
3
  import type { FinishReason } from './event';
4
4
  import { AgentState, ToolRegistry, ToolsCalling } from './state';
@@ -86,6 +86,7 @@ export interface CallLLMPayload {
86
86
  isFirstMessage?: boolean;
87
87
  messages: any[];
88
88
  model: string;
89
+ parentId?: string;
89
90
  provider: string;
90
91
  tools: any[];
91
92
  }
@@ -104,12 +105,18 @@ export interface AgentInstructionCallLlm {
104
105
  }
105
106
 
106
107
  export interface AgentInstructionCallTool {
107
- payload: any;
108
+ payload: {
109
+ parentMessageId: string;
110
+ toolCalling: ChatToolPayload;
111
+ };
108
112
  type: 'call_tool';
109
113
  }
110
114
 
111
115
  export interface AgentInstructionCallToolsBatch {
112
- payload: any[];
116
+ payload: {
117
+ parentMessageId: string;
118
+ toolsCalling: ChatToolPayload[];
119
+ } & any;
113
120
  type: 'call_tools_batch';
114
121
  }
115
122
 
@@ -6,7 +6,6 @@ export const DEFAULT_PREFERENCE: UserPreference = {
6
6
  topic: true,
7
7
  },
8
8
  lab: {
9
- enableAssistantMessageGroup: false,
10
9
  enableGroupChat: false,
11
10
  enableInputMarkdown: true,
12
11
  },