@lobehub/lobehub 2.0.0-next.48 → 2.0.0-next.49
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.
- package/CHANGELOG.md +33 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/changelog/v1.json +12 -0
- package/locales/ar/chat.json +1 -0
- package/locales/ar/topic.json +1 -0
- package/locales/bg-BG/chat.json +1 -0
- package/locales/bg-BG/topic.json +1 -0
- package/locales/de-DE/chat.json +1 -0
- package/locales/de-DE/topic.json +1 -0
- package/locales/en-US/chat.json +1 -0
- package/locales/en-US/topic.json +1 -0
- package/locales/es-ES/chat.json +1 -0
- package/locales/es-ES/topic.json +1 -0
- package/locales/fa-IR/chat.json +1 -0
- package/locales/fa-IR/topic.json +1 -0
- package/locales/fr-FR/chat.json +1 -0
- package/locales/fr-FR/topic.json +1 -0
- package/locales/it-IT/chat.json +1 -0
- package/locales/it-IT/topic.json +1 -0
- package/locales/ja-JP/chat.json +1 -0
- package/locales/ja-JP/topic.json +1 -0
- package/locales/ko-KR/chat.json +1 -0
- package/locales/ko-KR/topic.json +1 -0
- package/locales/nl-NL/chat.json +1 -0
- package/locales/nl-NL/topic.json +1 -0
- package/locales/pl-PL/chat.json +1 -0
- package/locales/pl-PL/topic.json +1 -0
- package/locales/pt-BR/chat.json +1 -0
- package/locales/pt-BR/topic.json +1 -0
- package/locales/ru-RU/chat.json +1 -0
- package/locales/ru-RU/topic.json +1 -0
- package/locales/tr-TR/chat.json +1 -0
- package/locales/tr-TR/topic.json +1 -0
- package/locales/vi-VN/chat.json +1 -0
- package/locales/vi-VN/topic.json +1 -0
- package/locales/zh-CN/chat.json +1 -0
- package/locales/zh-CN/discover.json +1 -1
- package/locales/zh-CN/topic.json +1 -0
- package/locales/zh-TW/chat.json +1 -0
- package/locales/zh-TW/topic.json +1 -0
- package/package.json +9 -3
- package/packages/agent-runtime/src/core/InterventionChecker.ts +5 -16
- package/packages/agent-runtime/src/core/__tests__/InterventionChecker.test.ts +27 -80
- package/packages/agent-runtime/src/core/__tests__/runtime.test.ts +32 -13
- package/packages/agent-runtime/src/core/runtime.ts +7 -3
- package/packages/agent-runtime/src/types/event.ts +2 -1
- package/packages/agent-runtime/src/types/generalAgent.ts +1 -0
- package/packages/agent-runtime/src/types/instruction.ts +3 -2
- package/packages/agent-runtime/src/types/state.ts +3 -1
- package/packages/conversation-flow/src/transformation/FlatListBuilder.ts +4 -1
- package/packages/database/src/models/message.ts +3 -0
- package/packages/obervability-otel/src/node.ts +15 -1
- package/packages/types/src/message/common/base.ts +2 -2
- package/packages/types/src/message/common/tools.ts +16 -10
- package/packages/types/src/message/ui/chat.ts +7 -1
- package/packages/types/src/tool/intervention.ts +2 -3
- package/packages/types/src/user/settings/tool.ts +15 -28
- package/renovate.json +28 -11
- package/src/app/[variants]/(main)/chat/components/topic/features/Topic/TopicListContent/TopicItem/TopicContent.tsx +1 -1
- package/src/app/[variants]/(main)/chat/session/features/SessionListContent/List/Item/Actions.tsx +1 -1
- package/src/features/Conversation/Messages/Group/GroupChildren.tsx +20 -15
- package/src/features/Conversation/Messages/Group/GroupContext.tsx +15 -0
- package/src/features/Conversation/Messages/Group/Tool/Inspector/BuiltinPluginTitle.tsx +2 -4
- package/src/features/Conversation/Messages/Group/Tool/Inspector/ToolTitle.tsx +3 -5
- package/src/features/Conversation/Messages/Group/Tool/Inspector/index.tsx +19 -7
- package/src/features/Conversation/Messages/Group/Tool/Render/Arguments/index.tsx +14 -12
- package/src/features/Conversation/Messages/Group/Tool/Render/Intervention/ApprovalActions.tsx +143 -0
- package/src/features/Conversation/Messages/Group/Tool/Render/Intervention/KeyValueEditor.tsx +213 -0
- package/src/features/Conversation/Messages/Group/Tool/Render/Intervention/ModeSelector.tsx +134 -0
- package/src/features/Conversation/Messages/Group/Tool/Render/Intervention/index.tsx +99 -0
- package/src/features/Conversation/Messages/Group/Tool/Render/RejectedResponse.tsx +45 -0
- package/src/features/Conversation/Messages/Group/Tool/Render/index.tsx +23 -1
- package/src/features/Conversation/Messages/Group/Tool/index.tsx +42 -18
- package/src/features/Conversation/Messages/Group/Tools.tsx +3 -1
- package/src/locales/default/chat.ts +22 -0
- package/src/locales/default/common.ts +1 -0
- package/src/locales/default/topic.ts +1 -0
- package/src/server/routers/lambda/message.ts +4 -1
- package/src/server/services/message/index.ts +13 -0
- package/src/services/message/index.ts +17 -2
- package/src/store/chat/agents/GeneralChatAgent.ts +141 -24
- package/src/store/chat/agents/__tests__/GeneralChatAgent.test.ts +605 -0
- package/src/store/chat/agents/createAgentExecutors.ts +144 -26
- package/src/store/chat/agents/createToolEngine.ts +22 -0
- package/src/store/chat/slices/aiChat/actions/conversationControl.ts +106 -0
- package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +54 -26
- package/src/store/chat/slices/message/reducer.ts +2 -1
- package/src/store/chat/slices/plugin/actions/optimisticUpdate.ts +26 -1
- package/src/store/user/slices/settings/action.ts +15 -0
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
} from '@lobechat/agent-runtime';
|
|
14
14
|
import type { ChatToolPayload, CreateMessageParams } from '@lobechat/types';
|
|
15
15
|
import debug from 'debug';
|
|
16
|
+
import pMap from 'p-map';
|
|
16
17
|
|
|
17
18
|
import type { ChatStore } from '@/store/chat/store';
|
|
18
19
|
|
|
@@ -43,6 +44,7 @@ export const createAgentExecutors = (context: {
|
|
|
43
44
|
}) => {
|
|
44
45
|
let shouldSkipCreateMessage = context.skipCreateFirstMessage;
|
|
45
46
|
|
|
47
|
+
/* eslint-disable sort-keys-fix/sort-keys-fix */
|
|
46
48
|
const executors: Partial<Record<AgentInstruction['type'], InstructionExecutor>> = {
|
|
47
49
|
/**
|
|
48
50
|
* Custom call_llm executor
|
|
@@ -239,39 +241,56 @@ export const createAgentExecutors = (context: {
|
|
|
239
241
|
// Find the last assistant message (should be created by call_llm)
|
|
240
242
|
const assistantMessage = latestMessages.findLast((m) => m.role === 'assistant');
|
|
241
243
|
|
|
242
|
-
|
|
243
|
-
// This ensures consistency and avoids duplicate execution
|
|
244
|
-
log(
|
|
245
|
-
'[%s][call_tool] Creating tool message for tool_call_id: %s',
|
|
246
|
-
sessionLogId,
|
|
247
|
-
chatToolPayload.id,
|
|
248
|
-
);
|
|
244
|
+
let toolMessageId: string;
|
|
249
245
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
role: 'tool',
|
|
256
|
-
sessionId: context.get().activeId,
|
|
257
|
-
threadId: context.params.threadId,
|
|
258
|
-
tool_call_id: chatToolPayload.id,
|
|
259
|
-
topicId: context.get().activeTopicId,
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
const createResult = await context.get().optimisticCreateMessage(toolMessageParams);
|
|
246
|
+
if (payload.skipCreateToolMessage) {
|
|
247
|
+
// Reuse existing tool message (resumption mode)
|
|
248
|
+
toolMessageId = payload.parentMessageId;
|
|
249
|
+
// Check if tool message already exists (e.g., from human approval flow)
|
|
250
|
+
const existingToolMessage = latestMessages.find((m) => m.id === toolMessageId)!;
|
|
263
251
|
|
|
264
|
-
if (!createResult) {
|
|
265
252
|
log(
|
|
266
|
-
'[%s][call_tool]
|
|
253
|
+
'[%s][call_tool] Resuming with existing tool message: %s (status: %s)',
|
|
254
|
+
sessionLogId,
|
|
255
|
+
toolMessageId,
|
|
256
|
+
existingToolMessage.pluginIntervention?.status,
|
|
257
|
+
);
|
|
258
|
+
} else {
|
|
259
|
+
// Create new tool message (normal mode)
|
|
260
|
+
log(
|
|
261
|
+
'[%s][call_tool] Creating tool message for tool_call_id: %s',
|
|
267
262
|
sessionLogId,
|
|
268
263
|
chatToolPayload.id,
|
|
269
264
|
);
|
|
270
|
-
throw new Error(`Failed to create tool message for tool_call_id: ${chatToolPayload.id}`);
|
|
271
|
-
}
|
|
272
265
|
|
|
273
|
-
|
|
274
|
-
|
|
266
|
+
const toolMessageParams: CreateMessageParams = {
|
|
267
|
+
content: '',
|
|
268
|
+
groupId: assistantMessage?.groupId,
|
|
269
|
+
parentId: payload.parentMessageId,
|
|
270
|
+
plugin: chatToolPayload,
|
|
271
|
+
role: 'tool',
|
|
272
|
+
sessionId: context.get().activeId,
|
|
273
|
+
threadId: context.params.threadId,
|
|
274
|
+
tool_call_id: chatToolPayload.id,
|
|
275
|
+
topicId: context.get().activeTopicId,
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
const createResult = await context.get().optimisticCreateMessage(toolMessageParams);
|
|
279
|
+
|
|
280
|
+
if (!createResult) {
|
|
281
|
+
log(
|
|
282
|
+
'[%s][call_tool] ERROR: Failed to create tool message for tool_call_id: %s',
|
|
283
|
+
sessionLogId,
|
|
284
|
+
chatToolPayload.id,
|
|
285
|
+
);
|
|
286
|
+
throw new Error(
|
|
287
|
+
`Failed to create tool message for tool_call_id: ${chatToolPayload.id}`,
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
toolMessageId = createResult.id;
|
|
292
|
+
log('[%s][call_tool] Created tool message, id: %s', sessionLogId, toolMessageId);
|
|
293
|
+
}
|
|
275
294
|
|
|
276
295
|
// Execute tool
|
|
277
296
|
log('[%s][call_tool] Executing tool %s ...', sessionLogId, toolName);
|
|
@@ -371,6 +390,105 @@ export const createAgentExecutors = (context: {
|
|
|
371
390
|
}
|
|
372
391
|
},
|
|
373
392
|
|
|
393
|
+
/** Create human approve executor */
|
|
394
|
+
request_human_approve: async (instruction, state) => {
|
|
395
|
+
const { pendingToolsCalling, reason, skipCreateToolMessage } = instruction as Extract<
|
|
396
|
+
AgentInstruction,
|
|
397
|
+
{ type: 'request_human_approve' }
|
|
398
|
+
>;
|
|
399
|
+
const newState = structuredClone(state);
|
|
400
|
+
const events: AgentEvent[] = [];
|
|
401
|
+
const sessionLogId = `${state.sessionId}:${state.stepCount}`;
|
|
402
|
+
|
|
403
|
+
log(
|
|
404
|
+
'[%s][request_human_approve] Executor start, pending tools count: %d, reason: %s',
|
|
405
|
+
sessionLogId,
|
|
406
|
+
pendingToolsCalling.length,
|
|
407
|
+
reason || 'human_intervention_required',
|
|
408
|
+
);
|
|
409
|
+
|
|
410
|
+
// Update state to waiting_for_human
|
|
411
|
+
newState.lastModified = new Date().toISOString();
|
|
412
|
+
newState.status = 'waiting_for_human';
|
|
413
|
+
newState.pendingToolsCalling = pendingToolsCalling;
|
|
414
|
+
|
|
415
|
+
// Get assistant message to extract groupId and parentId
|
|
416
|
+
const latestMessages = context.get().dbMessagesMap[context.messageKey] || [];
|
|
417
|
+
const assistantMessage = latestMessages.findLast((m) => m.role === 'assistant');
|
|
418
|
+
|
|
419
|
+
if (!assistantMessage) {
|
|
420
|
+
log('[%s][request_human_approve] ERROR: No assistant message found', sessionLogId);
|
|
421
|
+
throw new Error('No assistant message found for intervention');
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
log(
|
|
425
|
+
'[%s][request_human_approve] Found assistant message: %s',
|
|
426
|
+
sessionLogId,
|
|
427
|
+
assistantMessage.id,
|
|
428
|
+
);
|
|
429
|
+
|
|
430
|
+
if (skipCreateToolMessage) {
|
|
431
|
+
// Resumption mode: Tool messages already exist, just verify them
|
|
432
|
+
log('[%s][request_human_approve] Resuming with existing tool messages', sessionLogId);
|
|
433
|
+
} else {
|
|
434
|
+
// Create tool messages for each pending tool call with intervention status
|
|
435
|
+
await pMap(pendingToolsCalling, async (toolPayload) => {
|
|
436
|
+
const toolName = `${toolPayload.identifier}/${toolPayload.apiName}`;
|
|
437
|
+
log(
|
|
438
|
+
'[%s][request_human_approve] Creating tool message for %s with tool_call_id: %s',
|
|
439
|
+
sessionLogId,
|
|
440
|
+
toolName,
|
|
441
|
+
toolPayload.id,
|
|
442
|
+
);
|
|
443
|
+
|
|
444
|
+
const toolMessageParams: CreateMessageParams = {
|
|
445
|
+
content: '',
|
|
446
|
+
groupId: assistantMessage.groupId,
|
|
447
|
+
parentId: assistantMessage.id,
|
|
448
|
+
plugin: {
|
|
449
|
+
...toolPayload,
|
|
450
|
+
},
|
|
451
|
+
pluginIntervention: { status: 'pending' },
|
|
452
|
+
role: 'tool',
|
|
453
|
+
sessionId: context.get().activeId,
|
|
454
|
+
threadId: context.params.threadId,
|
|
455
|
+
tool_call_id: toolPayload.id,
|
|
456
|
+
topicId: context.get().activeTopicId,
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
const createResult = await context.get().optimisticCreateMessage(toolMessageParams);
|
|
460
|
+
|
|
461
|
+
if (!createResult) {
|
|
462
|
+
log(
|
|
463
|
+
'[%s][request_human_approve] ERROR: Failed to create tool message for %s',
|
|
464
|
+
sessionLogId,
|
|
465
|
+
toolName,
|
|
466
|
+
);
|
|
467
|
+
throw new Error(`Failed to create tool message for ${toolName}`);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
log(
|
|
471
|
+
'[%s][request_human_approve] Created tool message: %s for %s',
|
|
472
|
+
sessionLogId,
|
|
473
|
+
createResult.id,
|
|
474
|
+
toolName,
|
|
475
|
+
);
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
log(
|
|
480
|
+
'[%s][request_human_approve] All tool messages created, emitting human_approve_required event',
|
|
481
|
+
sessionLogId,
|
|
482
|
+
);
|
|
483
|
+
|
|
484
|
+
events.push({
|
|
485
|
+
pendingToolsCalling,
|
|
486
|
+
sessionId: newState.sessionId,
|
|
487
|
+
type: 'human_approve_required',
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
return { events, newState };
|
|
491
|
+
},
|
|
374
492
|
/**
|
|
375
493
|
* Finish executor
|
|
376
494
|
* Completes the runtime execution
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { WorkingModel } from '@lobechat/types';
|
|
2
|
+
|
|
3
|
+
import { getSearchConfig } from '@/helpers/getSearchConfig';
|
|
4
|
+
import { createToolsEngine } from '@/helpers/toolEngineering';
|
|
5
|
+
import { WebBrowsingManifest } from '@/tools/web-browsing';
|
|
6
|
+
|
|
7
|
+
export const createAgentToolsEngine = (workingModel: WorkingModel) =>
|
|
8
|
+
createToolsEngine({
|
|
9
|
+
// Add WebBrowsingManifest as default tool
|
|
10
|
+
defaultToolIds: [WebBrowsingManifest.identifier],
|
|
11
|
+
// Create search-aware enableChecker for this request
|
|
12
|
+
enableChecker: ({ pluginId }) => {
|
|
13
|
+
// For WebBrowsingManifest, apply search logic
|
|
14
|
+
if (pluginId === WebBrowsingManifest.identifier) {
|
|
15
|
+
const searchConfig = getSearchConfig(workingModel.model, workingModel.provider);
|
|
16
|
+
return searchConfig.useApplicationBuiltinSearchTool;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// For all other plugins, enable by default
|
|
20
|
+
return true;
|
|
21
|
+
},
|
|
22
|
+
});
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
/* eslint-disable sort-keys-fix/sort-keys-fix, typescript-sort-keys/interface */
|
|
2
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';
|
|
3
4
|
import { MESSAGE_CANCEL_FLAT } from '@lobechat/const';
|
|
4
5
|
import { produce } from 'immer';
|
|
5
6
|
import { StateCreator } from 'zustand/vanilla';
|
|
6
7
|
|
|
8
|
+
import { getAgentStoreState } from '@/store/agent';
|
|
9
|
+
import { agentSelectors } from '@/store/agent/slices/chat';
|
|
10
|
+
import { createAgentToolsEngine } from '@/store/chat/agents/createToolEngine';
|
|
7
11
|
import { ChatStore } from '@/store/chat/store';
|
|
8
12
|
import { setNamespace } from '@/utils/storeDebug';
|
|
9
13
|
|
|
14
|
+
import { displayMessageSelectors } from '../../../selectors';
|
|
10
15
|
import { messageMapKey } from '../../../utils/messageMapKey';
|
|
16
|
+
import { dbMessageSelectors } from '../../message/selectors';
|
|
11
17
|
import { MainSendMessageOperation } from '../initialState';
|
|
12
18
|
|
|
13
19
|
const n = setNamespace('ai');
|
|
@@ -32,6 +38,14 @@ export interface ConversationControlAction {
|
|
|
32
38
|
* Switches to a different branch of a message
|
|
33
39
|
*/
|
|
34
40
|
switchMessageBranch: (messageId: string, branchIndex: number) => Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Approve tool intervention
|
|
43
|
+
*/
|
|
44
|
+
approveToolCalling: (toolMessageId: string, assistantGroupId: string) => Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Reject tool intervention
|
|
47
|
+
*/
|
|
48
|
+
rejectToolCalling: (messageId: string, reason?: string) => Promise<void>;
|
|
35
49
|
/**
|
|
36
50
|
* Toggle sendMessage operation state
|
|
37
51
|
*/
|
|
@@ -131,6 +145,98 @@ export const conversationControl: StateCreator<
|
|
|
131
145
|
return undefined;
|
|
132
146
|
}
|
|
133
147
|
},
|
|
148
|
+
approveToolCalling: async (toolMessageId) => {
|
|
149
|
+
const { activeId, activeTopicId, activeThreadId, internal_execAgentRuntime } = get();
|
|
150
|
+
|
|
151
|
+
// 1. Get tool message and verify it exists
|
|
152
|
+
const toolMessage = dbMessageSelectors.getDbMessageById(toolMessageId)(get());
|
|
153
|
+
if (!toolMessage) return;
|
|
154
|
+
|
|
155
|
+
// 2. Update intervention status to approved
|
|
156
|
+
await get().optimisticUpdatePlugin(toolMessageId, { intervention: { status: 'approved' } });
|
|
157
|
+
|
|
158
|
+
// 3. Get current messages for state construction
|
|
159
|
+
const currentMessages = displayMessageSelectors.mainAIChats(get());
|
|
160
|
+
|
|
161
|
+
// 4. Get agent configuration and tools information
|
|
162
|
+
const agentStoreState = getAgentStoreState();
|
|
163
|
+
const agentConfigData = agentSelectors.currentAgentConfig(agentStoreState);
|
|
164
|
+
|
|
165
|
+
const toolsEngine = createAgentToolsEngine({
|
|
166
|
+
model: agentConfigData.model,
|
|
167
|
+
provider: agentConfigData.provider!,
|
|
168
|
+
});
|
|
169
|
+
const { enabledToolIds } = toolsEngine.generateToolsDetailed({
|
|
170
|
+
model: agentConfigData.model,
|
|
171
|
+
provider: agentConfigData.provider!,
|
|
172
|
+
toolIds: agentConfigData.plugins,
|
|
173
|
+
});
|
|
174
|
+
const toolManifestMap = Object.fromEntries(
|
|
175
|
+
toolsEngine.getEnabledPluginManifests(enabledToolIds).entries(),
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
// 5. Construct AgentState
|
|
179
|
+
const state = AgentRuntime.createInitialState({
|
|
180
|
+
sessionId: activeId,
|
|
181
|
+
messages: currentMessages,
|
|
182
|
+
maxSteps: 400,
|
|
183
|
+
metadata: {
|
|
184
|
+
sessionId: activeId,
|
|
185
|
+
topicId: activeTopicId,
|
|
186
|
+
threadId: activeThreadId,
|
|
187
|
+
},
|
|
188
|
+
toolManifestMap,
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
console.log('toolMessage:', toolMessage);
|
|
192
|
+
// 6. Construct AgentRuntimeContext with 'human_approved_tool' phase
|
|
193
|
+
const context: AgentRuntimeContext = {
|
|
194
|
+
phase: 'human_approved_tool',
|
|
195
|
+
payload: {
|
|
196
|
+
approvedToolCall: toolMessage.plugin,
|
|
197
|
+
parentMessageId: toolMessageId,
|
|
198
|
+
skipCreateToolMessage: true,
|
|
199
|
+
},
|
|
200
|
+
session: {
|
|
201
|
+
sessionId: activeId,
|
|
202
|
+
messageCount: currentMessages.length,
|
|
203
|
+
status: 'running',
|
|
204
|
+
stepCount: 0,
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
// 7. Execute agent runtime from tool message position
|
|
209
|
+
try {
|
|
210
|
+
await internal_execAgentRuntime({
|
|
211
|
+
messages: currentMessages,
|
|
212
|
+
parentMessageId: toolMessageId, // Start from tool message
|
|
213
|
+
parentMessageType: 'tool', // Type is 'tool'
|
|
214
|
+
threadId: activeThreadId,
|
|
215
|
+
initialState: state,
|
|
216
|
+
initialContext: context,
|
|
217
|
+
});
|
|
218
|
+
} catch (error) {
|
|
219
|
+
console.error('[approveToolCalling] Error executing agent runtime:', error);
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
|
|
223
|
+
rejectToolCalling: async (messageId, reason) => {
|
|
224
|
+
const toolMessage = dbMessageSelectors.getDbMessageById(messageId)(get());
|
|
225
|
+
if (!toolMessage) return;
|
|
226
|
+
|
|
227
|
+
// Optimistic update - update status to rejected and save reason
|
|
228
|
+
const intervention = {
|
|
229
|
+
rejectedReason: reason,
|
|
230
|
+
status: 'rejected',
|
|
231
|
+
} as const;
|
|
232
|
+
await get().optimisticUpdatePlugin(toolMessage.id, { intervention });
|
|
233
|
+
|
|
234
|
+
const toolContent = !!reason
|
|
235
|
+
? `User reject this tool calling with reason: ${reason}`
|
|
236
|
+
: 'User reject this tool calling without reason';
|
|
237
|
+
|
|
238
|
+
await get().optimisticUpdateMessageContent(messageId, toolContent);
|
|
239
|
+
},
|
|
134
240
|
|
|
135
241
|
internal_updateSendMessageOperation: (key, value, actionName) => {
|
|
136
242
|
const operationKey = typeof key === 'string' ? key : messageMapKey(key.sessionId, key.topicId);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable sort-keys-fix/sort-keys-fix, typescript-sort-keys/interface */
|
|
2
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';
|
|
3
|
+
import { AgentRuntime, type AgentRuntimeContext, type AgentState } from '@lobechat/agent-runtime';
|
|
4
4
|
import { isDesktop } from '@lobechat/const';
|
|
5
5
|
import { knowledgeBaseQAPrompts } from '@lobechat/prompts';
|
|
6
6
|
import {
|
|
@@ -23,6 +23,7 @@ import { agentChatConfigSelectors, agentSelectors } from '@/store/agent/selector
|
|
|
23
23
|
import { getAgentStoreState } from '@/store/agent/store';
|
|
24
24
|
import { GeneralChatAgent } from '@/store/chat/agents/GeneralChatAgent';
|
|
25
25
|
import { createAgentExecutors } from '@/store/chat/agents/createAgentExecutors';
|
|
26
|
+
import { createAgentToolsEngine } from '@/store/chat/agents/createToolEngine';
|
|
26
27
|
import { ChatStore } from '@/store/chat/store';
|
|
27
28
|
import { getFileStoreState } from '@/store/file/store';
|
|
28
29
|
import { setNamespace } from '@/utils/storeDebug';
|
|
@@ -88,6 +89,14 @@ export interface StreamingExecutorAction {
|
|
|
88
89
|
skipCreateFirstMessage?: boolean;
|
|
89
90
|
traceId?: string;
|
|
90
91
|
ragMetadata?: { ragQueryId: string; fileChunks: MessageSemanticSearchChunk[] };
|
|
92
|
+
/**
|
|
93
|
+
* Initial agent state (for resuming execution from a specific point)
|
|
94
|
+
*/
|
|
95
|
+
initialState?: AgentState;
|
|
96
|
+
/**
|
|
97
|
+
* Initial agent runtime context (for resuming execution from a specific phase)
|
|
98
|
+
*/
|
|
99
|
+
initialContext?: AgentRuntimeContext;
|
|
91
100
|
}) => Promise<void>;
|
|
92
101
|
}
|
|
93
102
|
|
|
@@ -397,6 +406,7 @@ export const streamingExecutor: StateCreator<
|
|
|
397
406
|
// ===========================================
|
|
398
407
|
// Step 1: RAG Preprocessing (if enabled)
|
|
399
408
|
// ===========================================
|
|
409
|
+
// Skip RAG preprocessing if initialState is provided (messages already preprocessed)
|
|
400
410
|
if (params.ragQuery && parentMessageType === 'user') {
|
|
401
411
|
const userMessageId = parentMessageId;
|
|
402
412
|
log('[internal_execAgentRuntime] RAG preprocessing start');
|
|
@@ -458,6 +468,17 @@ export const streamingExecutor: StateCreator<
|
|
|
458
468
|
provider: provider!,
|
|
459
469
|
},
|
|
460
470
|
});
|
|
471
|
+
|
|
472
|
+
const toolsEngine = createAgentToolsEngine({ model: model, provider: provider! });
|
|
473
|
+
const { enabledToolIds } = toolsEngine.generateToolsDetailed({
|
|
474
|
+
model: model,
|
|
475
|
+
provider: provider!,
|
|
476
|
+
toolIds: agentConfigData.plugins,
|
|
477
|
+
});
|
|
478
|
+
const toolManifestMap = Object.fromEntries(
|
|
479
|
+
toolsEngine.getEnabledPluginManifests(enabledToolIds).entries(),
|
|
480
|
+
);
|
|
481
|
+
|
|
461
482
|
const runtime = new AgentRuntime(agent, {
|
|
462
483
|
executors: createAgentExecutors({
|
|
463
484
|
get,
|
|
@@ -468,21 +489,24 @@ export const streamingExecutor: StateCreator<
|
|
|
468
489
|
}),
|
|
469
490
|
});
|
|
470
491
|
|
|
471
|
-
// Create initial state
|
|
472
|
-
let state =
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
// Prevent infinite loops
|
|
476
|
-
maxSteps: 400,
|
|
477
|
-
metadata: {
|
|
492
|
+
// Create initial state or use provided state
|
|
493
|
+
let state =
|
|
494
|
+
params.initialState ||
|
|
495
|
+
AgentRuntime.createInitialState({
|
|
478
496
|
sessionId: activeId,
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
497
|
+
messages,
|
|
498
|
+
// Prevent infinite loops
|
|
499
|
+
maxSteps: 400,
|
|
500
|
+
metadata: {
|
|
501
|
+
sessionId: activeId,
|
|
502
|
+
topicId: activeTopicId,
|
|
503
|
+
threadId: params.threadId,
|
|
504
|
+
},
|
|
505
|
+
toolManifestMap,
|
|
506
|
+
});
|
|
483
507
|
|
|
484
|
-
// Initial context - use
|
|
485
|
-
let nextContext: AgentRuntimeContext = {
|
|
508
|
+
// Initial context - use provided context or create default 'init' phase
|
|
509
|
+
let nextContext: AgentRuntimeContext = params.initialContext || {
|
|
486
510
|
phase: 'init',
|
|
487
511
|
payload: { model, provider, parentMessageId: params.parentMessageId },
|
|
488
512
|
session: {
|
|
@@ -520,20 +544,24 @@ export const streamingExecutor: StateCreator<
|
|
|
520
544
|
|
|
521
545
|
// Handle completion and error events
|
|
522
546
|
for (const event of result.events) {
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
547
|
+
switch (event.type) {
|
|
548
|
+
case 'done': {
|
|
549
|
+
log('[internal_execAgentRuntime] Received done event');
|
|
550
|
+
break;
|
|
551
|
+
}
|
|
526
552
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
553
|
+
case 'error': {
|
|
554
|
+
log('[internal_execAgentRuntime] Received error event: %o', event.error);
|
|
555
|
+
// Find the assistant message to update error
|
|
556
|
+
const currentMessages = get().messagesMap[messageKey] || [];
|
|
557
|
+
const assistantMessage = currentMessages.findLast((m) => m.role === 'assistant');
|
|
558
|
+
if (assistantMessage) {
|
|
559
|
+
await messageService.updateMessageError(assistantMessage.id, event.error);
|
|
560
|
+
}
|
|
561
|
+
const finalMessages = get().messagesMap[messageKey] || [];
|
|
562
|
+
get().replaceMessages(finalMessages);
|
|
563
|
+
break;
|
|
534
564
|
}
|
|
535
|
-
const finalMessages = get().messagesMap[messageKey] || [];
|
|
536
|
-
get().replaceMessages(finalMessages);
|
|
537
565
|
}
|
|
538
566
|
}
|
|
539
567
|
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
ChatPluginPayload,
|
|
4
4
|
ChatToolPayload,
|
|
5
5
|
CreateMessageParams,
|
|
6
|
+
MessagePluginItem,
|
|
6
7
|
UIChatMessage,
|
|
7
8
|
} from '@lobechat/types';
|
|
8
9
|
import isEqual from 'fast-deep-equal';
|
|
@@ -47,7 +48,7 @@ interface UpdatePluginState {
|
|
|
47
48
|
interface UpdateMessagePlugin {
|
|
48
49
|
id: string;
|
|
49
50
|
type: 'updateMessagePlugin';
|
|
50
|
-
value: Partial<
|
|
51
|
+
value: Partial<MessagePluginItem>;
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
interface UpdateMessageTools {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* eslint-disable sort-keys-fix/sort-keys-fix, typescript-sort-keys/interface */
|
|
2
|
-
import { ChatMessageError, ChatToolPayload } from '@lobechat/types';
|
|
2
|
+
import { ChatMessageError, ChatToolPayload, MessagePluginItem } from '@lobechat/types';
|
|
3
3
|
import isEqual from 'fast-deep-equal';
|
|
4
4
|
import { StateCreator } from 'zustand/vanilla';
|
|
5
5
|
|
|
@@ -29,6 +29,11 @@ export interface PluginOptimisticUpdateAction {
|
|
|
29
29
|
replace?: boolean,
|
|
30
30
|
) => Promise<void>;
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Update plugin with optimistic update (generic method for any plugin field)
|
|
34
|
+
*/
|
|
35
|
+
optimisticUpdatePlugin: (id: string, value: Partial<MessagePluginItem>) => Promise<void>;
|
|
36
|
+
|
|
32
37
|
/**
|
|
33
38
|
* Add tool to assistant message with optimistic update
|
|
34
39
|
*/
|
|
@@ -119,6 +124,26 @@ export const pluginOptimisticUpdate: StateCreator<
|
|
|
119
124
|
await refreshMessages();
|
|
120
125
|
},
|
|
121
126
|
|
|
127
|
+
optimisticUpdatePlugin: async (id, value) => {
|
|
128
|
+
const { replaceMessages } = get();
|
|
129
|
+
|
|
130
|
+
// optimistic update
|
|
131
|
+
get().internal_dispatchMessage({
|
|
132
|
+
id,
|
|
133
|
+
type: 'updateMessagePlugin',
|
|
134
|
+
value,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const result = await messageService.updateMessagePlugin(id, value, {
|
|
138
|
+
sessionId: get().activeId,
|
|
139
|
+
topicId: get().activeTopicId,
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
if (result?.success && result.messages) {
|
|
143
|
+
replaceMessages(result.messages);
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
|
|
122
147
|
optimisticAddToolToAssistantMessage: async (id, tool) => {
|
|
123
148
|
const assistantMessage = displayMessageSelectors.getDisplayMessageById(id)(get());
|
|
124
149
|
if (!assistantMessage) return;
|
|
@@ -18,6 +18,7 @@ import { difference } from '@/utils/difference';
|
|
|
18
18
|
import { merge } from '@/utils/merge';
|
|
19
19
|
|
|
20
20
|
export interface UserSettingsAction {
|
|
21
|
+
addToolToAllowList: (toolKey: string) => Promise<void>;
|
|
21
22
|
importAppSettings: (settings: UserSettings) => Promise<void>;
|
|
22
23
|
importUrlShareSettings: (settingsParams: string | null) => Promise<void>;
|
|
23
24
|
internal_createSignal: () => AbortController;
|
|
@@ -39,6 +40,20 @@ export const createSettingsSlice: StateCreator<
|
|
|
39
40
|
[],
|
|
40
41
|
UserSettingsAction
|
|
41
42
|
> = (set, get) => ({
|
|
43
|
+
addToolToAllowList: async (toolKey) => {
|
|
44
|
+
const currentAllowList = get().settings.tool?.humanIntervention?.allowList || [];
|
|
45
|
+
|
|
46
|
+
if (currentAllowList.includes(toolKey)) return;
|
|
47
|
+
|
|
48
|
+
await get().setSettings({
|
|
49
|
+
tool: {
|
|
50
|
+
humanIntervention: {
|
|
51
|
+
allowList: [...currentAllowList, toolKey],
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
},
|
|
56
|
+
|
|
42
57
|
importAppSettings: async (importAppSettings) => {
|
|
43
58
|
const { setSettings } = get();
|
|
44
59
|
|