@lobehub/lobehub 2.0.0-next.287 → 2.0.0-next.289
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 +50 -0
- package/changelog/v1.json +18 -0
- package/locales/en-US/plugin.json +3 -5
- package/locales/zh-CN/plugin.json +3 -5
- package/locales/zh-CN/tool.json +2 -0
- package/package.json +1 -1
- package/packages/builtin-agents/src/agents/group-supervisor/index.ts +12 -1
- package/packages/builtin-agents/src/agents/group-supervisor/systemRole.ts +0 -7
- package/packages/builtin-tool-cloud-sandbox/src/client/Inspector/EditLocalFile/index.tsx +93 -0
- package/packages/builtin-tool-cloud-sandbox/src/client/Inspector/GlobLocalFiles/index.tsx +73 -0
- package/packages/builtin-tool-cloud-sandbox/src/client/Inspector/GrepContent/index.tsx +69 -0
- package/packages/builtin-tool-cloud-sandbox/src/client/Inspector/ListLocalFiles/index.tsx +68 -0
- package/packages/builtin-tool-cloud-sandbox/src/client/Inspector/ReadLocalFile/index.tsx +74 -0
- package/packages/builtin-tool-cloud-sandbox/src/client/Inspector/SearchLocalFiles/index.tsx +70 -0
- package/packages/builtin-tool-cloud-sandbox/src/client/Inspector/WriteLocalFile/index.tsx +57 -0
- package/packages/builtin-tool-cloud-sandbox/src/client/Inspector/index.ts +14 -0
- package/packages/builtin-tool-cloud-sandbox/src/client/Render/WriteFile/index.tsx +54 -35
- package/packages/builtin-tool-cloud-sandbox/src/client/components/FilePathDisplay.tsx +52 -0
- package/packages/builtin-tool-group-management/src/client/Inspector/ExecuteTasks/index.tsx +90 -0
- package/packages/builtin-tool-group-management/src/client/Inspector/index.ts +2 -0
- package/packages/builtin-tool-group-management/src/client/Intervention/ExecuteTasks.tsx +237 -0
- package/packages/builtin-tool-group-management/src/client/Intervention/index.ts +4 -1
- package/packages/builtin-tool-group-management/src/client/Render/index.ts +1 -1
- package/packages/builtin-tool-group-management/src/client/Streaming/ExecuteTask/index.tsx +69 -0
- package/packages/builtin-tool-group-management/src/client/Streaming/ExecuteTasks/index.tsx +87 -0
- package/packages/builtin-tool-group-management/src/client/Streaming/index.ts +4 -0
- package/packages/builtin-tool-group-management/src/executor.test.ts +8 -311
- package/packages/builtin-tool-group-management/src/executor.ts +5 -160
- package/packages/builtin-tool-group-management/src/manifest.ts +50 -94
- package/packages/builtin-tool-group-management/src/systemRole.ts +251 -172
- package/packages/builtin-tool-group-management/src/types.ts +29 -40
- package/packages/context-engine/src/engine/messages/MessagesEngine.ts +22 -4
- package/packages/context-engine/src/engine/messages/types.ts +4 -4
- package/packages/context-engine/src/processors/GroupOrchestrationFilter.ts +211 -0
- package/packages/context-engine/src/processors/GroupRoleTransform.ts +261 -0
- package/packages/context-engine/src/processors/__tests__/GroupOrchestrationFilter.test.ts +770 -0
- package/packages/context-engine/src/processors/__tests__/GroupRoleTransform.test.ts +553 -0
- package/packages/context-engine/src/processors/index.ts +7 -2
- package/packages/context-engine/src/providers/__tests__/GroupContextInjector.test.ts +4 -16
- package/packages/context-engine/src/providers/__tests__/__snapshots__/GroupContextInjector.test.ts.snap +23 -28
- package/packages/prompts/src/prompts/agentGroup/__snapshots__/index.test.ts.snap +0 -7
- package/packages/prompts/src/prompts/agentGroup/groupContext.ts +0 -7
- package/src/app/[variants]/(main)/group/features/Conversation/AgentWelcome/OpeningQuestions.tsx +4 -8
- package/src/app/[variants]/(main)/group/features/Conversation/MainChatInput/GroupChat.tsx +0 -3
- package/src/app/[variants]/(main)/group/features/Conversation/useGroupContext.ts +3 -0
- package/src/features/ChatInput/Desktop/index.tsx +1 -3
- package/src/features/Conversation/store/slices/message/action/crud.ts +2 -2
- package/src/locales/default/plugin.ts +3 -5
- package/src/locales/default/tool.ts +3 -0
- package/src/services/chat/mecha/agentConfigResolver.test.ts +160 -0
- package/src/services/chat/mecha/agentConfigResolver.ts +15 -3
- package/src/services/chat/mecha/contextEngineering.ts +2 -1
- package/src/store/chat/agents/GroupOrchestration/createGroupOrchestrationExecutors.ts +4 -2
- package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +2 -0
- package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +1 -18
- package/src/store/chat/slices/message/selectors/displayMessage.test.ts +24 -0
- package/src/store/chat/slices/message/selectors/displayMessage.ts +6 -1
- package/src/store/chat/slices/topic/action.test.ts +10 -4
- package/src/store/chat/slices/topic/action.ts +3 -2
- package/src/store/document/slices/document/action.ts +8 -0
- package/packages/context-engine/src/processors/GroupMessageSender.ts +0 -138
- package/packages/context-engine/src/processors/__tests__/GroupMessageSender.test.ts +0 -274
|
@@ -0,0 +1,770 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import type { PipelineContext } from '../../types';
|
|
4
|
+
import { GroupOrchestrationFilterProcessor } from '../GroupOrchestrationFilter';
|
|
5
|
+
|
|
6
|
+
describe('GroupOrchestrationFilterProcessor', () => {
|
|
7
|
+
const createContext = (messages: any[]): PipelineContext => ({
|
|
8
|
+
initialState: { messages: [] },
|
|
9
|
+
isAborted: false,
|
|
10
|
+
messages,
|
|
11
|
+
metadata: {},
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const defaultConfig = {
|
|
15
|
+
agentMap: {
|
|
16
|
+
'agent-a': { role: 'participant' as const },
|
|
17
|
+
'agent-b': { role: 'participant' as const },
|
|
18
|
+
'supervisor': { role: 'supervisor' as const },
|
|
19
|
+
},
|
|
20
|
+
currentAgentId: 'agent-a', // Default to participant agent
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
describe('filtering supervisor orchestration messages', () => {
|
|
24
|
+
it('should filter supervisor assistant message with broadcast tool', async () => {
|
|
25
|
+
const processor = new GroupOrchestrationFilterProcessor(defaultConfig);
|
|
26
|
+
const context = createContext([
|
|
27
|
+
{ content: 'User question', id: 'msg_1', role: 'user' },
|
|
28
|
+
{
|
|
29
|
+
agentId: 'supervisor',
|
|
30
|
+
content: 'Let me coordinate the agents...',
|
|
31
|
+
id: 'msg_2',
|
|
32
|
+
role: 'assistant',
|
|
33
|
+
tools: [
|
|
34
|
+
{
|
|
35
|
+
apiName: 'broadcast',
|
|
36
|
+
arguments: '{"agentIds": ["agent-a", "agent-b"], "instruction": "Please respond"}',
|
|
37
|
+
id: 'call_1',
|
|
38
|
+
identifier: 'lobe-group-management',
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
{ content: 'Agent response', id: 'msg_3', role: 'assistant' },
|
|
43
|
+
]);
|
|
44
|
+
|
|
45
|
+
const result = await processor.process(context);
|
|
46
|
+
|
|
47
|
+
expect(result.messages).toHaveLength(2);
|
|
48
|
+
expect(result.messages[0].id).toBe('msg_1');
|
|
49
|
+
expect(result.messages[1].id).toBe('msg_3');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should filter supervisor assistant message with speak tool', async () => {
|
|
53
|
+
const processor = new GroupOrchestrationFilterProcessor(defaultConfig);
|
|
54
|
+
const context = createContext([
|
|
55
|
+
{
|
|
56
|
+
agentId: 'supervisor',
|
|
57
|
+
content: 'Asking agent-a to respond',
|
|
58
|
+
id: 'msg_1',
|
|
59
|
+
role: 'assistant',
|
|
60
|
+
tools: [
|
|
61
|
+
{
|
|
62
|
+
apiName: 'speak',
|
|
63
|
+
arguments: '{"agentId": "agent-a", "instruction": "Please help"}',
|
|
64
|
+
id: 'call_1',
|
|
65
|
+
identifier: 'lobe-group-management',
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
]);
|
|
70
|
+
|
|
71
|
+
const result = await processor.process(context);
|
|
72
|
+
|
|
73
|
+
expect(result.messages).toHaveLength(0);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('should filter supervisor assistant message with executeTask tool', async () => {
|
|
77
|
+
const processor = new GroupOrchestrationFilterProcessor(defaultConfig);
|
|
78
|
+
const context = createContext([
|
|
79
|
+
{
|
|
80
|
+
agentId: 'supervisor',
|
|
81
|
+
content: 'Executing task',
|
|
82
|
+
id: 'msg_1',
|
|
83
|
+
role: 'assistant',
|
|
84
|
+
tools: [
|
|
85
|
+
{
|
|
86
|
+
apiName: 'executeTask',
|
|
87
|
+
arguments: '{"task": "do something"}',
|
|
88
|
+
id: 'call_1',
|
|
89
|
+
identifier: 'lobe-group-management',
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
},
|
|
93
|
+
]);
|
|
94
|
+
|
|
95
|
+
const result = await processor.process(context);
|
|
96
|
+
|
|
97
|
+
expect(result.messages).toHaveLength(0);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should filter supervisor assistant message with executeTasks tool', async () => {
|
|
101
|
+
const processor = new GroupOrchestrationFilterProcessor(defaultConfig);
|
|
102
|
+
const context = createContext([
|
|
103
|
+
{
|
|
104
|
+
agentId: 'supervisor',
|
|
105
|
+
content: 'Executing multiple tasks',
|
|
106
|
+
id: 'msg_1',
|
|
107
|
+
role: 'assistant',
|
|
108
|
+
tools: [
|
|
109
|
+
{
|
|
110
|
+
apiName: 'executeTasks',
|
|
111
|
+
arguments: '{"tasks": ["task1", "task2"]}',
|
|
112
|
+
id: 'call_1',
|
|
113
|
+
identifier: 'lobe-group-management',
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
},
|
|
117
|
+
]);
|
|
118
|
+
|
|
119
|
+
const result = await processor.process(context);
|
|
120
|
+
|
|
121
|
+
expect(result.messages).toHaveLength(0);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe('filtering supervisor tool results', () => {
|
|
126
|
+
it('should filter supervisor tool result for broadcast', async () => {
|
|
127
|
+
const processor = new GroupOrchestrationFilterProcessor(defaultConfig);
|
|
128
|
+
const context = createContext([
|
|
129
|
+
{ content: 'User question', id: 'msg_1', role: 'user' },
|
|
130
|
+
{
|
|
131
|
+
agentId: 'supervisor',
|
|
132
|
+
content: 'Triggered broadcast to agents: agent-a, agent-b',
|
|
133
|
+
id: 'msg_2',
|
|
134
|
+
plugin: {
|
|
135
|
+
apiName: 'broadcast',
|
|
136
|
+
identifier: 'lobe-group-management',
|
|
137
|
+
},
|
|
138
|
+
role: 'tool',
|
|
139
|
+
tool_call_id: 'call_1',
|
|
140
|
+
},
|
|
141
|
+
{ content: 'Instruction from supervisor', id: 'msg_3', role: 'user' },
|
|
142
|
+
]);
|
|
143
|
+
|
|
144
|
+
const result = await processor.process(context);
|
|
145
|
+
|
|
146
|
+
expect(result.messages).toHaveLength(2);
|
|
147
|
+
expect(result.messages[0].id).toBe('msg_1');
|
|
148
|
+
expect(result.messages[1].id).toBe('msg_3');
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('should filter supervisor tool result for speak', async () => {
|
|
152
|
+
const processor = new GroupOrchestrationFilterProcessor(defaultConfig);
|
|
153
|
+
const context = createContext([
|
|
154
|
+
{
|
|
155
|
+
agentId: 'supervisor',
|
|
156
|
+
content: 'Triggered speak to agent-a',
|
|
157
|
+
id: 'msg_1',
|
|
158
|
+
plugin: {
|
|
159
|
+
apiName: 'speak',
|
|
160
|
+
identifier: 'lobe-group-management',
|
|
161
|
+
},
|
|
162
|
+
role: 'tool',
|
|
163
|
+
tool_call_id: 'call_1',
|
|
164
|
+
},
|
|
165
|
+
]);
|
|
166
|
+
|
|
167
|
+
const result = await processor.process(context);
|
|
168
|
+
|
|
169
|
+
expect(result.messages).toHaveLength(0);
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
describe('keeping non-orchestration messages', () => {
|
|
174
|
+
it('should keep supervisor assistant message without tools', async () => {
|
|
175
|
+
const processor = new GroupOrchestrationFilterProcessor(defaultConfig);
|
|
176
|
+
const context = createContext([
|
|
177
|
+
{
|
|
178
|
+
agentId: 'supervisor',
|
|
179
|
+
content: 'Here is a summary of the discussion...',
|
|
180
|
+
id: 'msg_1',
|
|
181
|
+
role: 'assistant',
|
|
182
|
+
},
|
|
183
|
+
]);
|
|
184
|
+
|
|
185
|
+
const result = await processor.process(context);
|
|
186
|
+
|
|
187
|
+
expect(result.messages).toHaveLength(1);
|
|
188
|
+
expect(result.messages[0].content).toBe('Here is a summary of the discussion...');
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('should keep supervisor assistant message with non-orchestration tools', async () => {
|
|
192
|
+
const processor = new GroupOrchestrationFilterProcessor(defaultConfig);
|
|
193
|
+
const context = createContext([
|
|
194
|
+
{
|
|
195
|
+
agentId: 'supervisor',
|
|
196
|
+
content: 'Let me search for information',
|
|
197
|
+
id: 'msg_1',
|
|
198
|
+
role: 'assistant',
|
|
199
|
+
tools: [
|
|
200
|
+
{
|
|
201
|
+
apiName: 'search',
|
|
202
|
+
arguments: '{"query": "test"}',
|
|
203
|
+
id: 'call_1',
|
|
204
|
+
identifier: 'web-search',
|
|
205
|
+
},
|
|
206
|
+
],
|
|
207
|
+
},
|
|
208
|
+
]);
|
|
209
|
+
|
|
210
|
+
const result = await processor.process(context);
|
|
211
|
+
|
|
212
|
+
expect(result.messages).toHaveLength(1);
|
|
213
|
+
expect(result.messages[0].id).toBe('msg_1');
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('should keep supervisor tool result for non-orchestration tools', async () => {
|
|
217
|
+
const processor = new GroupOrchestrationFilterProcessor(defaultConfig);
|
|
218
|
+
const context = createContext([
|
|
219
|
+
{
|
|
220
|
+
agentId: 'supervisor',
|
|
221
|
+
content: '{"results": ["item1", "item2"]}',
|
|
222
|
+
id: 'msg_1',
|
|
223
|
+
plugin: {
|
|
224
|
+
apiName: 'search',
|
|
225
|
+
identifier: 'web-search',
|
|
226
|
+
},
|
|
227
|
+
role: 'tool',
|
|
228
|
+
tool_call_id: 'call_1',
|
|
229
|
+
},
|
|
230
|
+
]);
|
|
231
|
+
|
|
232
|
+
const result = await processor.process(context);
|
|
233
|
+
|
|
234
|
+
expect(result.messages).toHaveLength(1);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('should keep all participant agent messages', async () => {
|
|
238
|
+
const processor = new GroupOrchestrationFilterProcessor(defaultConfig);
|
|
239
|
+
const context = createContext([
|
|
240
|
+
{
|
|
241
|
+
agentId: 'agent-a',
|
|
242
|
+
content: 'Participant response',
|
|
243
|
+
id: 'msg_1',
|
|
244
|
+
role: 'assistant',
|
|
245
|
+
tools: [
|
|
246
|
+
{
|
|
247
|
+
apiName: 'broadcast',
|
|
248
|
+
arguments: '{}',
|
|
249
|
+
id: 'call_1',
|
|
250
|
+
identifier: 'lobe-group-management',
|
|
251
|
+
},
|
|
252
|
+
],
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
agentId: 'agent-b',
|
|
256
|
+
content: 'Tool result',
|
|
257
|
+
id: 'msg_2',
|
|
258
|
+
plugin: {
|
|
259
|
+
apiName: 'broadcast',
|
|
260
|
+
identifier: 'lobe-group-management',
|
|
261
|
+
},
|
|
262
|
+
role: 'tool',
|
|
263
|
+
tool_call_id: 'call_1',
|
|
264
|
+
},
|
|
265
|
+
]);
|
|
266
|
+
|
|
267
|
+
const result = await processor.process(context);
|
|
268
|
+
|
|
269
|
+
// Participant messages are never filtered, even with orchestration tools
|
|
270
|
+
expect(result.messages).toHaveLength(2);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it('should keep user messages unchanged', async () => {
|
|
274
|
+
const processor = new GroupOrchestrationFilterProcessor(defaultConfig);
|
|
275
|
+
const context = createContext([
|
|
276
|
+
{ content: 'User question 1', id: 'msg_1', role: 'user' },
|
|
277
|
+
{ content: 'User question 2', id: 'msg_2', role: 'user' },
|
|
278
|
+
]);
|
|
279
|
+
|
|
280
|
+
const result = await processor.process(context);
|
|
281
|
+
|
|
282
|
+
expect(result.messages).toHaveLength(2);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it('should keep messages without agentId', async () => {
|
|
286
|
+
const processor = new GroupOrchestrationFilterProcessor(defaultConfig);
|
|
287
|
+
const context = createContext([
|
|
288
|
+
{
|
|
289
|
+
content: 'System message',
|
|
290
|
+
id: 'msg_1',
|
|
291
|
+
role: 'system',
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
content: 'Assistant without agentId',
|
|
295
|
+
id: 'msg_2',
|
|
296
|
+
role: 'assistant',
|
|
297
|
+
tools: [
|
|
298
|
+
{
|
|
299
|
+
apiName: 'broadcast',
|
|
300
|
+
arguments: '{}',
|
|
301
|
+
id: 'call_1',
|
|
302
|
+
identifier: 'lobe-group-management',
|
|
303
|
+
},
|
|
304
|
+
],
|
|
305
|
+
},
|
|
306
|
+
]);
|
|
307
|
+
|
|
308
|
+
const result = await processor.process(context);
|
|
309
|
+
|
|
310
|
+
expect(result.messages).toHaveLength(2);
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
describe('configuration options', () => {
|
|
315
|
+
it('should skip processing when disabled', async () => {
|
|
316
|
+
const processor = new GroupOrchestrationFilterProcessor({
|
|
317
|
+
...defaultConfig,
|
|
318
|
+
enabled: false,
|
|
319
|
+
});
|
|
320
|
+
const context = createContext([
|
|
321
|
+
{
|
|
322
|
+
agentId: 'supervisor',
|
|
323
|
+
content: 'Orchestration message',
|
|
324
|
+
id: 'msg_1',
|
|
325
|
+
role: 'assistant',
|
|
326
|
+
tools: [
|
|
327
|
+
{
|
|
328
|
+
apiName: 'broadcast',
|
|
329
|
+
arguments: '{}',
|
|
330
|
+
id: 'call_1',
|
|
331
|
+
identifier: 'lobe-group-management',
|
|
332
|
+
},
|
|
333
|
+
],
|
|
334
|
+
},
|
|
335
|
+
]);
|
|
336
|
+
|
|
337
|
+
const result = await processor.process(context);
|
|
338
|
+
|
|
339
|
+
expect(result.messages).toHaveLength(1);
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it('should skip processing when current agent is supervisor', async () => {
|
|
343
|
+
const processor = new GroupOrchestrationFilterProcessor({
|
|
344
|
+
...defaultConfig,
|
|
345
|
+
currentAgentId: 'supervisor', // Supervisor as current agent
|
|
346
|
+
});
|
|
347
|
+
const context = createContext([
|
|
348
|
+
{
|
|
349
|
+
agentId: 'supervisor',
|
|
350
|
+
content: 'Orchestration message',
|
|
351
|
+
id: 'msg_1',
|
|
352
|
+
role: 'assistant',
|
|
353
|
+
tools: [
|
|
354
|
+
{
|
|
355
|
+
apiName: 'broadcast',
|
|
356
|
+
arguments: '{}',
|
|
357
|
+
id: 'call_1',
|
|
358
|
+
identifier: 'lobe-group-management',
|
|
359
|
+
},
|
|
360
|
+
],
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
agentId: 'supervisor',
|
|
364
|
+
content: 'Tool result',
|
|
365
|
+
id: 'msg_2',
|
|
366
|
+
plugin: {
|
|
367
|
+
apiName: 'broadcast',
|
|
368
|
+
identifier: 'lobe-group-management',
|
|
369
|
+
},
|
|
370
|
+
role: 'tool',
|
|
371
|
+
tool_call_id: 'call_1',
|
|
372
|
+
},
|
|
373
|
+
]);
|
|
374
|
+
|
|
375
|
+
const result = await processor.process(context);
|
|
376
|
+
|
|
377
|
+
// Supervisor should see all messages including orchestration ones
|
|
378
|
+
expect(result.messages).toHaveLength(2);
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
it('should skip processing when no agentMap provided', async () => {
|
|
382
|
+
const processor = new GroupOrchestrationFilterProcessor({});
|
|
383
|
+
const context = createContext([
|
|
384
|
+
{
|
|
385
|
+
agentId: 'supervisor',
|
|
386
|
+
content: 'Orchestration message',
|
|
387
|
+
id: 'msg_1',
|
|
388
|
+
role: 'assistant',
|
|
389
|
+
tools: [
|
|
390
|
+
{
|
|
391
|
+
apiName: 'broadcast',
|
|
392
|
+
arguments: '{}',
|
|
393
|
+
id: 'call_1',
|
|
394
|
+
identifier: 'lobe-group-management',
|
|
395
|
+
},
|
|
396
|
+
],
|
|
397
|
+
},
|
|
398
|
+
]);
|
|
399
|
+
|
|
400
|
+
const result = await processor.process(context);
|
|
401
|
+
|
|
402
|
+
expect(result.messages).toHaveLength(1);
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
it('should skip processing when no currentAgentId provided', async () => {
|
|
406
|
+
const processor = new GroupOrchestrationFilterProcessor({
|
|
407
|
+
agentMap: defaultConfig.agentMap,
|
|
408
|
+
// No currentAgentId
|
|
409
|
+
});
|
|
410
|
+
const context = createContext([
|
|
411
|
+
{
|
|
412
|
+
agentId: 'supervisor',
|
|
413
|
+
content: 'Orchestration message',
|
|
414
|
+
id: 'msg_1',
|
|
415
|
+
role: 'assistant',
|
|
416
|
+
tools: [
|
|
417
|
+
{
|
|
418
|
+
apiName: 'broadcast',
|
|
419
|
+
arguments: '{}',
|
|
420
|
+
id: 'call_1',
|
|
421
|
+
identifier: 'lobe-group-management',
|
|
422
|
+
},
|
|
423
|
+
],
|
|
424
|
+
},
|
|
425
|
+
]);
|
|
426
|
+
|
|
427
|
+
const result = await processor.process(context);
|
|
428
|
+
|
|
429
|
+
// Without currentAgentId, can't determine if supervisor, so treat as participant and filter
|
|
430
|
+
// Actually, isCurrentAgentSupervisor returns false when no currentAgentId, so filtering happens
|
|
431
|
+
expect(result.messages).toHaveLength(0);
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
it('should use custom orchestration tool identifiers', async () => {
|
|
435
|
+
const processor = new GroupOrchestrationFilterProcessor({
|
|
436
|
+
...defaultConfig,
|
|
437
|
+
orchestrationToolIdentifiers: ['custom-orchestration'],
|
|
438
|
+
});
|
|
439
|
+
const context = createContext([
|
|
440
|
+
{
|
|
441
|
+
agentId: 'supervisor',
|
|
442
|
+
content: 'Custom orchestration',
|
|
443
|
+
id: 'msg_1',
|
|
444
|
+
role: 'assistant',
|
|
445
|
+
tools: [
|
|
446
|
+
{
|
|
447
|
+
apiName: 'broadcast',
|
|
448
|
+
arguments: '{}',
|
|
449
|
+
id: 'call_1',
|
|
450
|
+
identifier: 'custom-orchestration',
|
|
451
|
+
},
|
|
452
|
+
],
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
agentId: 'supervisor',
|
|
456
|
+
content: 'Default orchestration - should not be filtered',
|
|
457
|
+
id: 'msg_2',
|
|
458
|
+
role: 'assistant',
|
|
459
|
+
tools: [
|
|
460
|
+
{
|
|
461
|
+
apiName: 'broadcast',
|
|
462
|
+
arguments: '{}',
|
|
463
|
+
id: 'call_2',
|
|
464
|
+
identifier: 'lobe-group-management',
|
|
465
|
+
},
|
|
466
|
+
],
|
|
467
|
+
},
|
|
468
|
+
]);
|
|
469
|
+
|
|
470
|
+
const result = await processor.process(context);
|
|
471
|
+
|
|
472
|
+
expect(result.messages).toHaveLength(1);
|
|
473
|
+
expect(result.messages[0].id).toBe('msg_2');
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
it('should use custom orchestration api names', async () => {
|
|
477
|
+
const processor = new GroupOrchestrationFilterProcessor({
|
|
478
|
+
...defaultConfig,
|
|
479
|
+
orchestrationApiNames: ['customBroadcast'],
|
|
480
|
+
});
|
|
481
|
+
const context = createContext([
|
|
482
|
+
{
|
|
483
|
+
agentId: 'supervisor',
|
|
484
|
+
content: 'Custom api name',
|
|
485
|
+
id: 'msg_1',
|
|
486
|
+
role: 'assistant',
|
|
487
|
+
tools: [
|
|
488
|
+
{
|
|
489
|
+
apiName: 'customBroadcast',
|
|
490
|
+
arguments: '{}',
|
|
491
|
+
id: 'call_1',
|
|
492
|
+
identifier: 'lobe-group-management',
|
|
493
|
+
},
|
|
494
|
+
],
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
agentId: 'supervisor',
|
|
498
|
+
content: 'Default broadcast - should not be filtered',
|
|
499
|
+
id: 'msg_2',
|
|
500
|
+
role: 'assistant',
|
|
501
|
+
tools: [
|
|
502
|
+
{
|
|
503
|
+
apiName: 'broadcast',
|
|
504
|
+
arguments: '{}',
|
|
505
|
+
id: 'call_2',
|
|
506
|
+
identifier: 'lobe-group-management',
|
|
507
|
+
},
|
|
508
|
+
],
|
|
509
|
+
},
|
|
510
|
+
]);
|
|
511
|
+
|
|
512
|
+
const result = await processor.process(context);
|
|
513
|
+
|
|
514
|
+
expect(result.messages).toHaveLength(1);
|
|
515
|
+
expect(result.messages[0].id).toBe('msg_2');
|
|
516
|
+
});
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
describe('edge cases', () => {
|
|
520
|
+
it('should handle empty messages array', async () => {
|
|
521
|
+
const processor = new GroupOrchestrationFilterProcessor(defaultConfig);
|
|
522
|
+
const context = createContext([]);
|
|
523
|
+
|
|
524
|
+
const result = await processor.process(context);
|
|
525
|
+
|
|
526
|
+
expect(result.messages).toHaveLength(0);
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
it('should handle message with empty tools array', async () => {
|
|
530
|
+
const processor = new GroupOrchestrationFilterProcessor(defaultConfig);
|
|
531
|
+
const context = createContext([
|
|
532
|
+
{
|
|
533
|
+
agentId: 'supervisor',
|
|
534
|
+
content: 'Message with empty tools',
|
|
535
|
+
id: 'msg_1',
|
|
536
|
+
role: 'assistant',
|
|
537
|
+
tools: [],
|
|
538
|
+
},
|
|
539
|
+
]);
|
|
540
|
+
|
|
541
|
+
const result = await processor.process(context);
|
|
542
|
+
|
|
543
|
+
// Empty tools array means no orchestration tools, so message is kept
|
|
544
|
+
expect(result.messages).toHaveLength(1);
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
it('should handle tool without identifier', async () => {
|
|
548
|
+
const processor = new GroupOrchestrationFilterProcessor(defaultConfig);
|
|
549
|
+
const context = createContext([
|
|
550
|
+
{
|
|
551
|
+
agentId: 'supervisor',
|
|
552
|
+
content: 'Tool without identifier',
|
|
553
|
+
id: 'msg_1',
|
|
554
|
+
role: 'assistant',
|
|
555
|
+
tools: [
|
|
556
|
+
{
|
|
557
|
+
apiName: 'broadcast',
|
|
558
|
+
arguments: '{}',
|
|
559
|
+
id: 'call_1',
|
|
560
|
+
// Missing identifier
|
|
561
|
+
},
|
|
562
|
+
],
|
|
563
|
+
},
|
|
564
|
+
]);
|
|
565
|
+
|
|
566
|
+
const result = await processor.process(context);
|
|
567
|
+
|
|
568
|
+
// Tool without identifier doesn't match orchestration pattern
|
|
569
|
+
expect(result.messages).toHaveLength(1);
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
it('should handle tool without apiName', async () => {
|
|
573
|
+
const processor = new GroupOrchestrationFilterProcessor(defaultConfig);
|
|
574
|
+
const context = createContext([
|
|
575
|
+
{
|
|
576
|
+
agentId: 'supervisor',
|
|
577
|
+
content: 'Tool without apiName',
|
|
578
|
+
id: 'msg_1',
|
|
579
|
+
role: 'assistant',
|
|
580
|
+
tools: [
|
|
581
|
+
{
|
|
582
|
+
arguments: '{}',
|
|
583
|
+
id: 'call_1',
|
|
584
|
+
identifier: 'lobe-group-management',
|
|
585
|
+
// Missing apiName
|
|
586
|
+
},
|
|
587
|
+
],
|
|
588
|
+
},
|
|
589
|
+
]);
|
|
590
|
+
|
|
591
|
+
const result = await processor.process(context);
|
|
592
|
+
|
|
593
|
+
// Tool without apiName doesn't match orchestration pattern
|
|
594
|
+
expect(result.messages).toHaveLength(1);
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
it('should track filter counts in metadata', async () => {
|
|
598
|
+
const processor = new GroupOrchestrationFilterProcessor(defaultConfig);
|
|
599
|
+
const context = createContext([
|
|
600
|
+
{
|
|
601
|
+
agentId: 'supervisor',
|
|
602
|
+
content: 'Broadcast',
|
|
603
|
+
id: 'msg_1',
|
|
604
|
+
role: 'assistant',
|
|
605
|
+
tools: [
|
|
606
|
+
{
|
|
607
|
+
apiName: 'broadcast',
|
|
608
|
+
arguments: '{}',
|
|
609
|
+
id: 'call_1',
|
|
610
|
+
identifier: 'lobe-group-management',
|
|
611
|
+
},
|
|
612
|
+
],
|
|
613
|
+
},
|
|
614
|
+
{
|
|
615
|
+
agentId: 'supervisor',
|
|
616
|
+
content: 'Tool result',
|
|
617
|
+
id: 'msg_2',
|
|
618
|
+
plugin: {
|
|
619
|
+
apiName: 'broadcast',
|
|
620
|
+
identifier: 'lobe-group-management',
|
|
621
|
+
},
|
|
622
|
+
role: 'tool',
|
|
623
|
+
tool_call_id: 'call_1',
|
|
624
|
+
},
|
|
625
|
+
{
|
|
626
|
+
agentId: 'supervisor',
|
|
627
|
+
content: 'Speak',
|
|
628
|
+
id: 'msg_3',
|
|
629
|
+
role: 'assistant',
|
|
630
|
+
tools: [
|
|
631
|
+
{
|
|
632
|
+
apiName: 'speak',
|
|
633
|
+
arguments: '{}',
|
|
634
|
+
id: 'call_2',
|
|
635
|
+
identifier: 'lobe-group-management',
|
|
636
|
+
},
|
|
637
|
+
],
|
|
638
|
+
},
|
|
639
|
+
]);
|
|
640
|
+
|
|
641
|
+
const result = await processor.process(context);
|
|
642
|
+
|
|
643
|
+
expect(result.metadata.orchestrationFilterProcessed).toEqual({
|
|
644
|
+
assistantFiltered: 2,
|
|
645
|
+
filteredCount: 3,
|
|
646
|
+
toolFiltered: 1,
|
|
647
|
+
});
|
|
648
|
+
});
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
describe('comprehensive end-to-end filtering', () => {
|
|
652
|
+
it('should correctly filter a full group conversation with orchestration messages', async () => {
|
|
653
|
+
const processor = new GroupOrchestrationFilterProcessor({
|
|
654
|
+
agentMap: {
|
|
655
|
+
'agent-a': { role: 'participant' },
|
|
656
|
+
'agent-b': { role: 'participant' },
|
|
657
|
+
'supervisor': { role: 'supervisor' },
|
|
658
|
+
},
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
const inputMessages = [
|
|
662
|
+
// 1. User's original question
|
|
663
|
+
{ content: '帮我规划杭州行程', id: 'msg_1', role: 'user' },
|
|
664
|
+
// 2. Supervisor broadcasts - SHOULD BE FILTERED
|
|
665
|
+
{
|
|
666
|
+
agentId: 'supervisor',
|
|
667
|
+
content: '好的,让我协调专家们...',
|
|
668
|
+
id: 'msg_2',
|
|
669
|
+
role: 'assistant',
|
|
670
|
+
tools: [
|
|
671
|
+
{
|
|
672
|
+
apiName: 'broadcast',
|
|
673
|
+
arguments: '{"agentIds": ["agent-a", "agent-b"], "instruction": "请给建议"}',
|
|
674
|
+
id: 'call_1',
|
|
675
|
+
identifier: 'lobe-group-management',
|
|
676
|
+
},
|
|
677
|
+
],
|
|
678
|
+
},
|
|
679
|
+
// 3. Broadcast tool result - SHOULD BE FILTERED
|
|
680
|
+
{
|
|
681
|
+
agentId: 'supervisor',
|
|
682
|
+
content: 'Triggered broadcast to agents: agent-a, agent-b',
|
|
683
|
+
id: 'msg_3',
|
|
684
|
+
plugin: {
|
|
685
|
+
apiName: 'broadcast',
|
|
686
|
+
identifier: 'lobe-group-management',
|
|
687
|
+
},
|
|
688
|
+
role: 'tool',
|
|
689
|
+
tool_call_id: 'call_1',
|
|
690
|
+
},
|
|
691
|
+
// 4. Actual instruction (injected by broadcast) - SHOULD BE KEPT
|
|
692
|
+
{ content: '请各位专家给出杭州行程建议', id: 'msg_4', role: 'user' },
|
|
693
|
+
// 5. Agent A response - SHOULD BE KEPT
|
|
694
|
+
{ agentId: 'agent-a', content: '推荐西湖景区', id: 'msg_5', role: 'assistant' },
|
|
695
|
+
// 6. Agent B response - SHOULD BE KEPT
|
|
696
|
+
{ agentId: 'agent-b', content: '推荐楼外楼', id: 'msg_6', role: 'assistant' },
|
|
697
|
+
// 7. Supervisor uses speak - SHOULD BE FILTERED
|
|
698
|
+
{
|
|
699
|
+
agentId: 'supervisor',
|
|
700
|
+
content: '让 agent-a 总结一下',
|
|
701
|
+
id: 'msg_7',
|
|
702
|
+
role: 'assistant',
|
|
703
|
+
tools: [
|
|
704
|
+
{
|
|
705
|
+
apiName: 'speak',
|
|
706
|
+
arguments: '{"agentId": "agent-a", "instruction": "请总结"}',
|
|
707
|
+
id: 'call_2',
|
|
708
|
+
identifier: 'lobe-group-management',
|
|
709
|
+
},
|
|
710
|
+
],
|
|
711
|
+
},
|
|
712
|
+
// 8. Speak tool result - SHOULD BE FILTERED
|
|
713
|
+
{
|
|
714
|
+
agentId: 'supervisor',
|
|
715
|
+
content: 'Triggered speak to agent-a',
|
|
716
|
+
id: 'msg_8',
|
|
717
|
+
plugin: {
|
|
718
|
+
apiName: 'speak',
|
|
719
|
+
identifier: 'lobe-group-management',
|
|
720
|
+
},
|
|
721
|
+
role: 'tool',
|
|
722
|
+
tool_call_id: 'call_2',
|
|
723
|
+
},
|
|
724
|
+
// 9. Supervisor's summary (pure text, no tools) - SHOULD BE KEPT
|
|
725
|
+
{
|
|
726
|
+
agentId: 'supervisor',
|
|
727
|
+
content: '以上就是专家们的建议汇总',
|
|
728
|
+
id: 'msg_9',
|
|
729
|
+
role: 'assistant',
|
|
730
|
+
},
|
|
731
|
+
// 10. Supervisor uses search tool - SHOULD BE KEPT
|
|
732
|
+
{
|
|
733
|
+
agentId: 'supervisor',
|
|
734
|
+
content: '让我搜索一下更多信息',
|
|
735
|
+
id: 'msg_10',
|
|
736
|
+
role: 'assistant',
|
|
737
|
+
tools: [
|
|
738
|
+
{
|
|
739
|
+
apiName: 'search',
|
|
740
|
+
arguments: '{"query": "杭州景点"}',
|
|
741
|
+
id: 'call_3',
|
|
742
|
+
identifier: 'web-search',
|
|
743
|
+
},
|
|
744
|
+
],
|
|
745
|
+
},
|
|
746
|
+
];
|
|
747
|
+
|
|
748
|
+
const context = createContext(inputMessages);
|
|
749
|
+
const result = await processor.process(context);
|
|
750
|
+
|
|
751
|
+
// Should have: msg_1, msg_4, msg_5, msg_6, msg_9, msg_10 (6 messages)
|
|
752
|
+
expect(result.messages).toHaveLength(6);
|
|
753
|
+
expect(result.messages.map((m) => m.id)).toEqual([
|
|
754
|
+
'msg_1',
|
|
755
|
+
'msg_4',
|
|
756
|
+
'msg_5',
|
|
757
|
+
'msg_6',
|
|
758
|
+
'msg_9',
|
|
759
|
+
'msg_10',
|
|
760
|
+
]);
|
|
761
|
+
|
|
762
|
+
// Verify metadata
|
|
763
|
+
expect(result.metadata.orchestrationFilterProcessed).toEqual({
|
|
764
|
+
assistantFiltered: 2, // msg_2, msg_7
|
|
765
|
+
filteredCount: 4, // msg_2, msg_3, msg_7, msg_8
|
|
766
|
+
toolFiltered: 2, // msg_3, msg_8
|
|
767
|
+
});
|
|
768
|
+
});
|
|
769
|
+
});
|
|
770
|
+
});
|