@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
|
@@ -12,43 +12,16 @@ describe('InterventionChecker', () => {
|
|
|
12
12
|
|
|
13
13
|
it('should return the policy when config is a simple string', () => {
|
|
14
14
|
expect(InterventionChecker.shouldIntervene({ config: 'never', toolArgs: {} })).toBe('never');
|
|
15
|
-
expect(InterventionChecker.shouldIntervene({ config: '
|
|
16
|
-
'
|
|
15
|
+
expect(InterventionChecker.shouldIntervene({ config: 'require', toolArgs: {} })).toBe(
|
|
16
|
+
'require',
|
|
17
17
|
);
|
|
18
|
-
expect(InterventionChecker.shouldIntervene({ config: 'first', toolArgs: {} })).toBe('first');
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it('should handle "first" policy with confirmed history', () => {
|
|
22
|
-
const toolKey = 'web-browsing/crawlSinglePage';
|
|
23
|
-
const confirmedHistory = [toolKey];
|
|
24
|
-
|
|
25
|
-
const result = InterventionChecker.shouldIntervene({
|
|
26
|
-
config: 'first',
|
|
27
|
-
toolArgs: {},
|
|
28
|
-
confirmedHistory,
|
|
29
|
-
toolKey,
|
|
30
|
-
});
|
|
31
|
-
expect(result).toBe('never');
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('should require intervention for "first" policy without confirmation', () => {
|
|
35
|
-
const toolKey = 'web-browsing/crawlSinglePage';
|
|
36
|
-
const confirmedHistory: string[] = [];
|
|
37
|
-
|
|
38
|
-
const result = InterventionChecker.shouldIntervene({
|
|
39
|
-
config: 'first',
|
|
40
|
-
toolArgs: {},
|
|
41
|
-
confirmedHistory,
|
|
42
|
-
toolKey,
|
|
43
|
-
});
|
|
44
|
-
expect(result).toBe('first');
|
|
45
18
|
});
|
|
46
19
|
|
|
47
20
|
it('should match rules in order and return first match', () => {
|
|
48
21
|
const config: HumanInterventionConfig = [
|
|
49
22
|
{ match: { command: 'ls:*' }, policy: 'never' },
|
|
50
|
-
{ match: { command: 'git commit:*' }, policy: '
|
|
51
|
-
{ policy: '
|
|
23
|
+
{ match: { command: 'git commit:*' }, policy: 'require' },
|
|
24
|
+
{ policy: 'require' }, // Default rule
|
|
52
25
|
];
|
|
53
26
|
|
|
54
27
|
expect(InterventionChecker.shouldIntervene({ config, toolArgs: { command: 'ls:' } })).toBe(
|
|
@@ -56,20 +29,20 @@ describe('InterventionChecker', () => {
|
|
|
56
29
|
);
|
|
57
30
|
expect(
|
|
58
31
|
InterventionChecker.shouldIntervene({ config, toolArgs: { command: 'git commit:' } }),
|
|
59
|
-
).toBe('
|
|
32
|
+
).toBe('require');
|
|
60
33
|
expect(
|
|
61
34
|
InterventionChecker.shouldIntervene({ config, toolArgs: { command: 'rm -rf /' } }),
|
|
62
|
-
).toBe('
|
|
35
|
+
).toBe('require');
|
|
63
36
|
});
|
|
64
37
|
|
|
65
|
-
it('should return
|
|
38
|
+
it('should return require as default when no rule matches', () => {
|
|
66
39
|
const config: HumanInterventionConfig = [{ match: { command: 'ls:*' }, policy: 'never' }];
|
|
67
40
|
|
|
68
41
|
const result = InterventionChecker.shouldIntervene({
|
|
69
42
|
config,
|
|
70
43
|
toolArgs: { command: 'rm -rf /' },
|
|
71
44
|
});
|
|
72
|
-
expect(result).toBe('
|
|
45
|
+
expect(result).toBe('require');
|
|
73
46
|
});
|
|
74
47
|
|
|
75
48
|
it('should handle multiple parameter matching', () => {
|
|
@@ -81,7 +54,7 @@ describe('InterventionChecker', () => {
|
|
|
81
54
|
},
|
|
82
55
|
policy: 'never',
|
|
83
56
|
},
|
|
84
|
-
{ policy: '
|
|
57
|
+
{ policy: 'require' },
|
|
85
58
|
];
|
|
86
59
|
|
|
87
60
|
// Both match
|
|
@@ -104,20 +77,20 @@ describe('InterventionChecker', () => {
|
|
|
104
77
|
path: '/tmp/file.ts',
|
|
105
78
|
},
|
|
106
79
|
}),
|
|
107
|
-
).toBe('
|
|
80
|
+
).toBe('require');
|
|
108
81
|
});
|
|
109
82
|
|
|
110
83
|
it('should handle default rule without match', () => {
|
|
111
84
|
const config: HumanInterventionConfig = [
|
|
112
85
|
{ match: { command: 'ls:*' }, policy: 'never' },
|
|
113
|
-
{ policy: '
|
|
86
|
+
{ policy: 'require' }, // Default rule
|
|
114
87
|
];
|
|
115
88
|
|
|
116
89
|
const result = InterventionChecker.shouldIntervene({
|
|
117
90
|
config,
|
|
118
91
|
toolArgs: { command: 'anything' },
|
|
119
92
|
});
|
|
120
|
-
expect(result).toBe('
|
|
93
|
+
expect(result).toBe('require');
|
|
121
94
|
});
|
|
122
95
|
});
|
|
123
96
|
|
|
@@ -249,10 +222,10 @@ describe('InterventionChecker', () => {
|
|
|
249
222
|
it('should handle Bash tool scenario', () => {
|
|
250
223
|
const config: HumanInterventionConfig = [
|
|
251
224
|
{ match: { command: 'ls:*' }, policy: 'never' },
|
|
252
|
-
{ match: { command: 'git add:*' }, policy: '
|
|
253
|
-
{ match: { command: 'git commit:*' }, policy: '
|
|
254
|
-
{ match: { command: 'rm:*' }, policy: '
|
|
255
|
-
{ policy: '
|
|
225
|
+
{ match: { command: 'git add:*' }, policy: 'require' },
|
|
226
|
+
{ match: { command: 'git commit:*' }, policy: 'require' },
|
|
227
|
+
{ match: { command: 'rm:*' }, policy: 'require' },
|
|
228
|
+
{ policy: 'require' },
|
|
256
229
|
];
|
|
257
230
|
|
|
258
231
|
// Safe commands - never
|
|
@@ -260,27 +233,27 @@ describe('InterventionChecker', () => {
|
|
|
260
233
|
'never',
|
|
261
234
|
);
|
|
262
235
|
|
|
263
|
-
// Git commands -
|
|
236
|
+
// Git commands - require
|
|
264
237
|
expect(
|
|
265
238
|
InterventionChecker.shouldIntervene({ config, toolArgs: { command: 'git add:.' } }),
|
|
266
|
-
).toBe('
|
|
239
|
+
).toBe('require');
|
|
267
240
|
expect(
|
|
268
241
|
InterventionChecker.shouldIntervene({ config, toolArgs: { command: 'git commit:-m' } }),
|
|
269
|
-
).toBe('
|
|
242
|
+
).toBe('require');
|
|
270
243
|
|
|
271
|
-
// Dangerous commands -
|
|
244
|
+
// Dangerous commands - require
|
|
272
245
|
expect(InterventionChecker.shouldIntervene({ config, toolArgs: { command: 'rm:-rf' } })).toBe(
|
|
273
|
-
'
|
|
246
|
+
'require',
|
|
274
247
|
);
|
|
275
248
|
expect(
|
|
276
249
|
InterventionChecker.shouldIntervene({ config, toolArgs: { command: 'npm install' } }),
|
|
277
|
-
).toBe('
|
|
250
|
+
).toBe('require');
|
|
278
251
|
});
|
|
279
252
|
|
|
280
253
|
it('should handle LocalSystem tool scenario', () => {
|
|
281
254
|
const config: HumanInterventionConfig = [
|
|
282
255
|
{ match: { path: '/Users/project/*' }, policy: 'never' },
|
|
283
|
-
{ policy: '
|
|
256
|
+
{ policy: 'require' },
|
|
284
257
|
];
|
|
285
258
|
|
|
286
259
|
// Project directory - never
|
|
@@ -291,44 +264,18 @@ describe('InterventionChecker', () => {
|
|
|
291
264
|
}),
|
|
292
265
|
).toBe('never');
|
|
293
266
|
|
|
294
|
-
// Outside project -
|
|
267
|
+
// Outside project - require
|
|
295
268
|
expect(
|
|
296
269
|
InterventionChecker.shouldIntervene({ config, toolArgs: { path: '/tmp/file.ts' } }),
|
|
297
|
-
).toBe('
|
|
270
|
+
).toBe('require');
|
|
298
271
|
});
|
|
299
272
|
|
|
300
273
|
it('should handle Web Browsing tool with simple policy', () => {
|
|
301
|
-
const config: HumanInterventionConfig = '
|
|
274
|
+
const config: HumanInterventionConfig = 'require';
|
|
302
275
|
|
|
303
276
|
expect(
|
|
304
277
|
InterventionChecker.shouldIntervene({ config, toolArgs: { url: 'https://example.com' } }),
|
|
305
|
-
).toBe('
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
it('should handle first policy with confirmation history', () => {
|
|
309
|
-
const config: HumanInterventionConfig = [
|
|
310
|
-
{ match: { command: 'git add:*' }, policy: 'first' },
|
|
311
|
-
{ policy: 'always' },
|
|
312
|
-
];
|
|
313
|
-
|
|
314
|
-
const toolKey = 'bash/bash#abc123';
|
|
315
|
-
const args = { command: 'git add:.' };
|
|
316
|
-
|
|
317
|
-
// First time - requires intervention
|
|
318
|
-
expect(
|
|
319
|
-
InterventionChecker.shouldIntervene({
|
|
320
|
-
config,
|
|
321
|
-
toolArgs: args,
|
|
322
|
-
confirmedHistory: [],
|
|
323
|
-
toolKey,
|
|
324
|
-
}),
|
|
325
|
-
).toBe('first');
|
|
326
|
-
|
|
327
|
-
// After confirmation - never
|
|
328
|
-
const confirmedHistory = [toolKey];
|
|
329
|
-
expect(
|
|
330
|
-
InterventionChecker.shouldIntervene({ config, toolArgs: args, confirmedHistory, toolKey }),
|
|
331
|
-
).toBe('never');
|
|
278
|
+
).toBe('require');
|
|
332
279
|
});
|
|
333
280
|
});
|
|
334
281
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ChatToolPayload } from '@lobechat/types';
|
|
1
2
|
import { describe, expect, it, vi } from 'vitest';
|
|
2
3
|
|
|
3
4
|
import {
|
|
@@ -344,9 +345,11 @@ describe('AgentRuntime', () => {
|
|
|
344
345
|
type: 'request_human_approve',
|
|
345
346
|
pendingToolsCalling: [
|
|
346
347
|
{
|
|
348
|
+
apiName: 'test_tool',
|
|
349
|
+
arguments: '{}',
|
|
347
350
|
id: 'call_123',
|
|
348
|
-
|
|
349
|
-
|
|
351
|
+
identifier: 'test_tool',
|
|
352
|
+
type: 'default',
|
|
350
353
|
},
|
|
351
354
|
],
|
|
352
355
|
}),
|
|
@@ -357,14 +360,11 @@ describe('AgentRuntime', () => {
|
|
|
357
360
|
|
|
358
361
|
const result = await runtime.step(state);
|
|
359
362
|
|
|
360
|
-
expect(result.events).toHaveLength(
|
|
363
|
+
expect(result.events).toHaveLength(1);
|
|
361
364
|
expect(result.events[0]).toMatchObject({
|
|
362
365
|
type: 'human_approve_required',
|
|
363
366
|
sessionId: 'test-session',
|
|
364
367
|
});
|
|
365
|
-
expect(result.events[1]).toMatchObject({
|
|
366
|
-
type: 'tool_pending',
|
|
367
|
-
});
|
|
368
368
|
|
|
369
369
|
expect(result.newState.status).toBe('waiting_for_human');
|
|
370
370
|
expect(result.newState.pendingToolsCalling).toBeDefined();
|
|
@@ -947,12 +947,29 @@ describe('AgentRuntime', () => {
|
|
|
947
947
|
case 'llm_result':
|
|
948
948
|
const llmPayload = context.payload as { result: any; hasToolCalls: boolean };
|
|
949
949
|
if (llmPayload.hasToolCalls) {
|
|
950
|
+
// Convert OpenAI format tool_calls to ChatToolPayload format
|
|
951
|
+
const pendingToolsCalling = llmPayload.result.tool_calls.map((tc: any) => ({
|
|
952
|
+
apiName: tc.function.name,
|
|
953
|
+
arguments: tc.function.arguments,
|
|
954
|
+
id: tc.id,
|
|
955
|
+
identifier: tc.function.name,
|
|
956
|
+
type: 'default' as const,
|
|
957
|
+
}));
|
|
950
958
|
return Promise.resolve({
|
|
959
|
+
pendingToolsCalling,
|
|
951
960
|
type: 'request_human_approve',
|
|
952
|
-
pendingToolsCalling: llmPayload.result.tool_calls,
|
|
953
961
|
});
|
|
954
962
|
}
|
|
955
963
|
return Promise.resolve({ type: 'finish', reason: 'completed', reasonDetail: 'Done' });
|
|
964
|
+
case 'human_approved_tool':
|
|
965
|
+
const approvedPayload = context.payload as { approvedToolCall: ChatToolPayload };
|
|
966
|
+
return Promise.resolve({
|
|
967
|
+
payload: {
|
|
968
|
+
parentMessageId: 'user-msg-id',
|
|
969
|
+
toolCalling: approvedPayload.approvedToolCall,
|
|
970
|
+
},
|
|
971
|
+
type: 'call_tool',
|
|
972
|
+
});
|
|
956
973
|
case 'tool_result':
|
|
957
974
|
return Promise.resolve({ type: 'call_llm', payload: { messages: state.messages } });
|
|
958
975
|
default:
|
|
@@ -1021,10 +1038,10 @@ describe('AgentRuntime', () => {
|
|
|
1021
1038
|
// Step 2: Approve and execute tool call
|
|
1022
1039
|
const pendingToolCall = result.newState.pendingToolsCalling![0];
|
|
1023
1040
|
const toolCall = {
|
|
1041
|
+
apiName: pendingToolCall.apiName,
|
|
1042
|
+
arguments: pendingToolCall.arguments,
|
|
1024
1043
|
id: pendingToolCall.id,
|
|
1025
|
-
|
|
1026
|
-
identifier: pendingToolCall.function.name,
|
|
1027
|
-
arguments: pendingToolCall.function.arguments,
|
|
1044
|
+
identifier: pendingToolCall.identifier,
|
|
1028
1045
|
type: 'default' as const,
|
|
1029
1046
|
};
|
|
1030
1047
|
result = await runtime.approveToolCall(result.newState, toolCall);
|
|
@@ -1203,9 +1220,11 @@ describe('AgentRuntime', () => {
|
|
|
1203
1220
|
{
|
|
1204
1221
|
pendingToolsCalling: [
|
|
1205
1222
|
{
|
|
1223
|
+
apiName: 'danger_tool',
|
|
1224
|
+
arguments: '{}',
|
|
1206
1225
|
id: 'call_danger',
|
|
1207
|
-
|
|
1208
|
-
|
|
1226
|
+
identifier: 'danger_tool',
|
|
1227
|
+
type: 'default' as const,
|
|
1209
1228
|
},
|
|
1210
1229
|
],
|
|
1211
1230
|
type: 'request_human_approve' as const,
|
|
@@ -1233,7 +1252,7 @@ describe('AgentRuntime', () => {
|
|
|
1233
1252
|
|
|
1234
1253
|
// Should have pending tool calls
|
|
1235
1254
|
expect(result.newState.pendingToolsCalling).toHaveLength(1);
|
|
1236
|
-
expect(result.newState.pendingToolsCalling![0].
|
|
1255
|
+
expect(result.newState.pendingToolsCalling![0].apiName).toBe('danger_tool');
|
|
1237
1256
|
|
|
1238
1257
|
// Should have both tool_result and human_approve_required events
|
|
1239
1258
|
expect(result.events).toContainEqual(expect.objectContaining({ type: 'tool_result' }));
|
|
@@ -85,12 +85,17 @@ export class AgentRuntime {
|
|
|
85
85
|
|
|
86
86
|
// Handle human approved tool calls
|
|
87
87
|
if (runtimeContext.phase === 'human_approved_tool') {
|
|
88
|
-
const approvedPayload = runtimeContext.payload as {
|
|
88
|
+
const approvedPayload = runtimeContext.payload as {
|
|
89
|
+
approvedToolCall: ChatToolPayload;
|
|
90
|
+
parentMessageId: string;
|
|
91
|
+
skipCreateToolMessage: boolean;
|
|
92
|
+
};
|
|
89
93
|
const toolCalling = approvedPayload.approvedToolCall;
|
|
90
94
|
|
|
91
95
|
rawInstructions = {
|
|
92
96
|
payload: {
|
|
93
|
-
parentMessageId:
|
|
97
|
+
parentMessageId: approvedPayload.parentMessageId,
|
|
98
|
+
skipCreateToolMessage: approvedPayload.skipCreateToolMessage,
|
|
94
99
|
toolCalling,
|
|
95
100
|
},
|
|
96
101
|
type: 'call_tool',
|
|
@@ -538,7 +543,6 @@ export class AgentRuntime {
|
|
|
538
543
|
sessionId: newState.sessionId,
|
|
539
544
|
type: 'human_approve_required',
|
|
540
545
|
},
|
|
541
|
-
{ toolCalls: pendingToolsCalling, type: 'tool_pending' },
|
|
542
546
|
];
|
|
543
547
|
|
|
544
548
|
return { events, newState };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* eslint-disable sort-keys-fix/sort-keys-fix, typescript-sort-keys/interface */
|
|
2
2
|
import type { AgentState, ToolsCalling } from './state';
|
|
3
|
+
import { ChatToolPayload } from '@/types/message';
|
|
3
4
|
|
|
4
5
|
export interface AgentEventInit {
|
|
5
6
|
type: 'init';
|
|
@@ -33,7 +34,7 @@ export interface AgentEventToolResult {
|
|
|
33
34
|
|
|
34
35
|
export interface AgentEventHumanApproveRequired {
|
|
35
36
|
type: 'human_approve_required';
|
|
36
|
-
pendingToolsCalling:
|
|
37
|
+
pendingToolsCalling: ChatToolPayload[];
|
|
37
38
|
sessionId: string;
|
|
38
39
|
}
|
|
39
40
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ChatToolPayload, ModelUsage } from '@lobechat/types';
|
|
2
2
|
|
|
3
3
|
import type { FinishReason } from './event';
|
|
4
|
-
import { AgentState, ToolRegistry
|
|
4
|
+
import { AgentState, ToolRegistry } from './state';
|
|
5
5
|
import type { Cost, CostCalculationContext, Usage } from './usage';
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -137,8 +137,9 @@ export interface AgentInstructionRequestHumanSelect {
|
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
export interface AgentInstructionRequestHumanApprove {
|
|
140
|
-
pendingToolsCalling:
|
|
140
|
+
pendingToolsCalling: ChatToolPayload[];
|
|
141
141
|
reason?: string;
|
|
142
|
+
skipCreateToolMessage?: boolean;
|
|
142
143
|
type: 'request_human_approve';
|
|
143
144
|
}
|
|
144
145
|
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
/* eslint-disable sort-keys-fix/sort-keys-fix, typescript-sort-keys/interface */
|
|
2
|
+
import { ChatToolPayload } from '@/types/message';
|
|
3
|
+
|
|
2
4
|
import type { Cost, CostLimit, Usage } from './usage';
|
|
3
5
|
|
|
4
6
|
/**
|
|
@@ -49,7 +51,7 @@ export interface AgentState {
|
|
|
49
51
|
* When status is 'waiting_for_human', this stores pending requests
|
|
50
52
|
* for human-in-the-loop operations.
|
|
51
53
|
*/
|
|
52
|
-
pendingToolsCalling?:
|
|
54
|
+
pendingToolsCalling?: ChatToolPayload[];
|
|
53
55
|
pendingHumanPrompt?: { metadata?: Record<string, unknown>; prompt: string };
|
|
54
56
|
pendingHumanSelect?: {
|
|
55
57
|
metadata?: Record<string, unknown>;
|
|
@@ -427,11 +427,14 @@ export class FlatListBuilder {
|
|
|
427
427
|
if (toolMsg.error) result.error = toolMsg.error;
|
|
428
428
|
if (toolMsg.pluginState) result.state = toolMsg.pluginState;
|
|
429
429
|
|
|
430
|
-
|
|
430
|
+
const toolWithResult: ChatToolPayloadWithResult = {
|
|
431
431
|
...tool,
|
|
432
|
+
intervention: toolMsg.pluginIntervention,
|
|
432
433
|
result,
|
|
433
434
|
result_msg_id: toolMsg.id,
|
|
434
435
|
};
|
|
436
|
+
|
|
437
|
+
return toolWithResult;
|
|
435
438
|
}
|
|
436
439
|
return tool;
|
|
437
440
|
}) || [];
|
|
@@ -97,6 +97,7 @@ export class MessageModel {
|
|
|
97
97
|
type: messagePlugins.type,
|
|
98
98
|
},
|
|
99
99
|
pluginError: messagePlugins.error,
|
|
100
|
+
pluginIntervention: messagePlugins.intervention,
|
|
100
101
|
pluginState: messagePlugins.state,
|
|
101
102
|
|
|
102
103
|
translate: {
|
|
@@ -462,6 +463,7 @@ export class MessageModel {
|
|
|
462
463
|
provider: fromProvider,
|
|
463
464
|
files,
|
|
464
465
|
plugin,
|
|
466
|
+
pluginIntervention,
|
|
465
467
|
pluginState,
|
|
466
468
|
fileChunks,
|
|
467
469
|
ragQueryId,
|
|
@@ -496,6 +498,7 @@ export class MessageModel {
|
|
|
496
498
|
arguments: plugin?.arguments,
|
|
497
499
|
id,
|
|
498
500
|
identifier: plugin?.identifier,
|
|
501
|
+
intervention: pluginIntervention,
|
|
499
502
|
state: pluginState,
|
|
500
503
|
toolCallId: message.tool_call_id,
|
|
501
504
|
type: plugin?.type,
|
|
@@ -8,6 +8,7 @@ import { resourceFromAttributes } from '@opentelemetry/resources';
|
|
|
8
8
|
import { PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';
|
|
9
9
|
import { NodeSDK } from '@opentelemetry/sdk-node';
|
|
10
10
|
import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions';
|
|
11
|
+
import { env } from 'node:process';
|
|
11
12
|
|
|
12
13
|
export function register(options?: { debug?: true | DiagLogLevel; version?: string }) {
|
|
13
14
|
const attributes: Record<string, string> = {
|
|
@@ -23,13 +24,26 @@ export function register(options?: { debug?: true | DiagLogLevel; version?: stri
|
|
|
23
24
|
);
|
|
24
25
|
}
|
|
25
26
|
|
|
27
|
+
let metricsExporterInterval = 1000;
|
|
28
|
+
if (env.OTEL_METRICS_EXPORTER_INTERVAL) {
|
|
29
|
+
const parsed = parseInt(env.OTEL_METRICS_EXPORTER_INTERVAL, 10);
|
|
30
|
+
if (!isNaN(parsed)) {
|
|
31
|
+
metricsExporterInterval = parsed;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
26
35
|
const sdk = new NodeSDK({
|
|
27
36
|
instrumentations: [
|
|
28
37
|
new PgInstrumentation(),
|
|
29
38
|
new HttpInstrumentation(),
|
|
30
39
|
getNodeAutoInstrumentations(),
|
|
31
40
|
],
|
|
32
|
-
metricReaders: [
|
|
41
|
+
metricReaders: [
|
|
42
|
+
new PeriodicExportingMetricReader({
|
|
43
|
+
exportIntervalMillis: metricsExporterInterval,
|
|
44
|
+
exporter: new OTLPMetricExporter(),
|
|
45
|
+
}),
|
|
46
|
+
],
|
|
33
47
|
resource: resourceFromAttributes(attributes),
|
|
34
48
|
traceExporter: new OTLPTraceExporter(),
|
|
35
49
|
});
|
|
@@ -9,13 +9,13 @@ import { ErrorType } from '../../fetch';
|
|
|
9
9
|
*/
|
|
10
10
|
export interface ChatMessageError {
|
|
11
11
|
body?: any;
|
|
12
|
-
message
|
|
12
|
+
message?: string;
|
|
13
13
|
type: ErrorType | IPluginErrorType | ILobeAgentRuntimeErrorType;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export const ChatMessageErrorSchema = z.object({
|
|
17
17
|
body: z.any().optional(),
|
|
18
|
-
message: z.string(),
|
|
18
|
+
message: z.string().optional(),
|
|
19
19
|
type: z.union([z.string(), z.number()]),
|
|
20
20
|
});
|
|
21
21
|
|
|
@@ -4,10 +4,22 @@ import { z } from 'zod';
|
|
|
4
4
|
|
|
5
5
|
import { LobeToolRenderType } from '../../tool';
|
|
6
6
|
|
|
7
|
+
// ToolIntervention must be defined first to avoid circular dependency
|
|
8
|
+
export interface ToolIntervention {
|
|
9
|
+
rejectedReason?: string;
|
|
10
|
+
status?: 'pending' | 'approved' | 'rejected' | 'none';
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const ToolInterventionSchema = z.object({
|
|
14
|
+
rejectedReason: z.string().optional(),
|
|
15
|
+
status: z.enum(['pending', 'approved', 'rejected', 'none']).optional(),
|
|
16
|
+
});
|
|
17
|
+
|
|
7
18
|
export interface ChatPluginPayload {
|
|
8
19
|
apiName: string;
|
|
9
20
|
arguments: string;
|
|
10
21
|
identifier: string;
|
|
22
|
+
intervention?: ToolIntervention;
|
|
11
23
|
type: LobeToolRenderType;
|
|
12
24
|
}
|
|
13
25
|
|
|
@@ -16,6 +28,7 @@ export interface ChatToolPayload {
|
|
|
16
28
|
arguments: string;
|
|
17
29
|
id: string;
|
|
18
30
|
identifier: string;
|
|
31
|
+
intervention?: ToolIntervention;
|
|
19
32
|
result_msg_id?: string;
|
|
20
33
|
type: LobeToolRenderType;
|
|
21
34
|
}
|
|
@@ -34,7 +47,9 @@ export interface ChatToolResult {
|
|
|
34
47
|
* Chat tool payload with merged execution result
|
|
35
48
|
*/
|
|
36
49
|
export interface ChatToolPayloadWithResult extends ChatToolPayload {
|
|
50
|
+
intervention?: ToolIntervention;
|
|
37
51
|
result?: ChatToolResult;
|
|
52
|
+
result_msg_id?: string;
|
|
38
53
|
}
|
|
39
54
|
|
|
40
55
|
export interface ToolsCallingContext {
|
|
@@ -91,6 +106,7 @@ export const ChatToolPayloadSchema = z.object({
|
|
|
91
106
|
arguments: z.string(),
|
|
92
107
|
id: z.string(),
|
|
93
108
|
identifier: z.string(),
|
|
109
|
+
intervention: ToolInterventionSchema.optional(),
|
|
94
110
|
result_msg_id: z.string().optional(),
|
|
95
111
|
type: z.string(),
|
|
96
112
|
});
|
|
@@ -103,13 +119,3 @@ export interface ChatMessagePluginError {
|
|
|
103
119
|
message: string;
|
|
104
120
|
type: IPluginErrorType;
|
|
105
121
|
}
|
|
106
|
-
|
|
107
|
-
export interface ToolIntervention {
|
|
108
|
-
rejectedReason?: string;
|
|
109
|
-
status?: 'pending' | 'approved' | 'rejected' | 'none';
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export const ToolInterventionSchema = z.object({
|
|
113
|
-
rejectedReason: z.string().optional(),
|
|
114
|
-
status: z.enum(['pending', 'approved', 'rejected', 'none']).optional(),
|
|
115
|
-
});
|
|
@@ -8,7 +8,12 @@ import {
|
|
|
8
8
|
ModelReasoning,
|
|
9
9
|
ModelUsage,
|
|
10
10
|
} from '../common';
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
ChatPluginPayload,
|
|
13
|
+
ChatToolPayload,
|
|
14
|
+
ChatToolPayloadWithResult,
|
|
15
|
+
ToolIntervention,
|
|
16
|
+
} from '../common/tools';
|
|
12
17
|
import { ChatMessageExtra } from './extra';
|
|
13
18
|
import { ChatFileChunk } from './rag';
|
|
14
19
|
import { ChatVideoItem } from './video';
|
|
@@ -95,6 +100,7 @@ export interface UIChatMessage {
|
|
|
95
100
|
performance?: ModelPerformance;
|
|
96
101
|
plugin?: ChatPluginPayload;
|
|
97
102
|
pluginError?: any;
|
|
103
|
+
pluginIntervention?: ToolIntervention;
|
|
98
104
|
pluginState?: any;
|
|
99
105
|
provider?: string | null;
|
|
100
106
|
/**
|
|
@@ -5,10 +5,9 @@ import { z } from 'zod';
|
|
|
5
5
|
*/
|
|
6
6
|
export type HumanInterventionPolicy =
|
|
7
7
|
| 'never' // Never intervene, auto-execute
|
|
8
|
-
| '
|
|
9
|
-
| 'first'; // Require intervention on first call only
|
|
8
|
+
| 'require'; // Always require intervention
|
|
10
9
|
|
|
11
|
-
export const HumanInterventionPolicySchema = z.enum(['never', '
|
|
10
|
+
export const HumanInterventionPolicySchema = z.enum(['never', 'require']);
|
|
12
11
|
|
|
13
12
|
/**
|
|
14
13
|
* Argument Matcher for parameter-level filtering
|
|
@@ -1,42 +1,29 @@
|
|
|
1
|
-
import type { HumanInterventionConfig } from '../../tool';
|
|
2
|
-
|
|
3
1
|
export interface UserToolConfig {
|
|
2
|
+
/**
|
|
3
|
+
* Tool approval mode
|
|
4
|
+
* - auto-run: Automatically approve all tools without user consent
|
|
5
|
+
* - allow-list: Only approve tools in the allow list
|
|
6
|
+
* - manual: Require manual approval for each tool execution
|
|
7
|
+
*/
|
|
8
|
+
approvalMode?: 'auto-run' | 'allow-list' | 'manual';
|
|
9
|
+
|
|
4
10
|
dalle: {
|
|
5
11
|
autoGenerate: boolean;
|
|
6
12
|
};
|
|
13
|
+
|
|
7
14
|
/**
|
|
8
|
-
*
|
|
15
|
+
* Tool intervention configuration
|
|
9
16
|
*/
|
|
10
17
|
humanIntervention?: {
|
|
11
18
|
/**
|
|
12
|
-
*
|
|
13
|
-
* Format: "identifier/apiName"
|
|
19
|
+
* Allow list of approved tools (used in 'allow-list' mode)
|
|
20
|
+
* Format: "identifier/apiName"
|
|
14
21
|
*
|
|
15
22
|
* Examples:
|
|
16
23
|
* - "web-browsing/crawlSinglePage"
|
|
17
|
-
* - "bash/bash
|
|
18
|
-
|
|
19
|
-
confirmed?: string[];
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Whether human intervention is enabled globally
|
|
23
|
-
* @default true
|
|
24
|
-
*/
|
|
25
|
-
enabled: boolean;
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Per-tool intervention policy overrides
|
|
29
|
-
* Key format: "identifier/apiName"
|
|
30
|
-
*
|
|
31
|
-
* Example:
|
|
32
|
-
* {
|
|
33
|
-
* "web-browsing/crawlSinglePage": "confirm",
|
|
34
|
-
* "bash/bash": [
|
|
35
|
-
* { match: { command: "git add:*" }, policy: "auto" },
|
|
36
|
-
* { policy: "confirm" }
|
|
37
|
-
* ]
|
|
38
|
-
* }
|
|
24
|
+
* - "bash/bash"
|
|
25
|
+
* - "search/search"
|
|
39
26
|
*/
|
|
40
|
-
|
|
27
|
+
allowList?: string[];
|
|
41
28
|
};
|
|
42
29
|
}
|