@lobehub/lobehub 2.0.0-next.287 → 2.0.0-next.288
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 +25 -0
- package/changelog/v1.json +9 -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 +6 -4
- package/packages/context-engine/src/engine/messages/types.ts +4 -4
- package/packages/context-engine/src/processors/GroupRoleTransform.ts +261 -0
- package/packages/context-engine/src/processors/__tests__/GroupRoleTransform.test.ts +553 -0
- package/packages/context-engine/src/processors/index.ts +2 -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/packages/context-engine/src/processors/GroupMessageSender.ts +0 -138
- package/packages/context-engine/src/processors/__tests__/GroupMessageSender.test.ts +0 -274
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { DEFAULT_AVATAR } from '@lobechat/const';
|
|
4
|
+
import type { AgentGroupMember, BuiltinStreamingProps } from '@lobechat/types';
|
|
5
|
+
import { Avatar, Flexbox, Markdown } from '@lobehub/ui';
|
|
6
|
+
import { createStaticStyles, useTheme } from 'antd-style';
|
|
7
|
+
import { memo, useMemo } from 'react';
|
|
8
|
+
|
|
9
|
+
import { useAgentGroupStore } from '@/store/agentGroup';
|
|
10
|
+
import { agentGroupSelectors } from '@/store/agentGroup/selectors';
|
|
11
|
+
|
|
12
|
+
import type { ExecuteTasksParams } from '../../../types';
|
|
13
|
+
|
|
14
|
+
const styles = createStaticStyles(({ css, cssVar }) => ({
|
|
15
|
+
container: css`
|
|
16
|
+
display: flex;
|
|
17
|
+
flex-direction: column;
|
|
18
|
+
gap: 12px;
|
|
19
|
+
`,
|
|
20
|
+
instruction: css`
|
|
21
|
+
font-size: 13px;
|
|
22
|
+
color: ${cssVar.colorTextSecondary};
|
|
23
|
+
`,
|
|
24
|
+
taskCard: css`
|
|
25
|
+
padding: 12px;
|
|
26
|
+
border-radius: 8px;
|
|
27
|
+
background: ${cssVar.colorFillQuaternary};
|
|
28
|
+
`,
|
|
29
|
+
taskTitle: css`
|
|
30
|
+
font-size: 13px;
|
|
31
|
+
font-weight: 500;
|
|
32
|
+
color: ${cssVar.colorText};
|
|
33
|
+
`,
|
|
34
|
+
}));
|
|
35
|
+
|
|
36
|
+
export const ExecuteTasksStreaming = memo<BuiltinStreamingProps<ExecuteTasksParams>>(({ args }) => {
|
|
37
|
+
const { tasks } = args || {};
|
|
38
|
+
const theme = useTheme();
|
|
39
|
+
|
|
40
|
+
// Get active group ID and agents from store
|
|
41
|
+
const activeGroupId = useAgentGroupStore(agentGroupSelectors.activeGroupId);
|
|
42
|
+
const groupAgents = useAgentGroupStore((s) =>
|
|
43
|
+
activeGroupId ? agentGroupSelectors.getGroupAgents(activeGroupId)(s) : [],
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
// Get agent details for each task
|
|
47
|
+
const tasksWithAgents = useMemo(() => {
|
|
48
|
+
if (!tasks?.length || !groupAgents.length) return [];
|
|
49
|
+
return tasks.map((task) => ({
|
|
50
|
+
...task,
|
|
51
|
+
agent: groupAgents.find((agent) => agent.id === task.agentId) as AgentGroupMember | undefined,
|
|
52
|
+
}));
|
|
53
|
+
}, [tasks, groupAgents]);
|
|
54
|
+
|
|
55
|
+
if (!tasksWithAgents.length) return null;
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<div className={styles.container}>
|
|
59
|
+
{tasksWithAgents.map((task, index) => (
|
|
60
|
+
<div className={styles.taskCard} key={task.agentId || index}>
|
|
61
|
+
<Flexbox gap={8}>
|
|
62
|
+
<Flexbox align={'center'} gap={8} horizontal>
|
|
63
|
+
<Avatar
|
|
64
|
+
avatar={task.agent?.avatar || DEFAULT_AVATAR}
|
|
65
|
+
background={task.agent?.backgroundColor || theme.colorBgContainer}
|
|
66
|
+
shape={'square'}
|
|
67
|
+
size={20}
|
|
68
|
+
/>
|
|
69
|
+
<span className={styles.taskTitle}>{task.title || task.agent?.title || 'Task'}</span>
|
|
70
|
+
</Flexbox>
|
|
71
|
+
{task.instruction && (
|
|
72
|
+
<div className={styles.instruction}>
|
|
73
|
+
<Markdown animated variant={'chat'}>
|
|
74
|
+
{task.instruction}
|
|
75
|
+
</Markdown>
|
|
76
|
+
</div>
|
|
77
|
+
)}
|
|
78
|
+
</Flexbox>
|
|
79
|
+
</div>
|
|
80
|
+
))}
|
|
81
|
+
</div>
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
ExecuteTasksStreaming.displayName = 'ExecuteTasksStreaming';
|
|
86
|
+
|
|
87
|
+
export default ExecuteTasksStreaming;
|
|
@@ -2,6 +2,8 @@ import type { BuiltinStreaming } from '@lobechat/types';
|
|
|
2
2
|
|
|
3
3
|
import { GroupManagementApiName } from '../../types';
|
|
4
4
|
import { BroadcastStreaming } from './Broadcast';
|
|
5
|
+
import { ExecuteTaskStreaming } from './ExecuteTask';
|
|
6
|
+
import { ExecuteTasksStreaming } from './ExecuteTasks';
|
|
5
7
|
import { SpeakStreaming } from './Speak';
|
|
6
8
|
|
|
7
9
|
/**
|
|
@@ -12,5 +14,7 @@ import { SpeakStreaming } from './Speak';
|
|
|
12
14
|
*/
|
|
13
15
|
export const GroupManagementStreamings: Record<string, BuiltinStreaming> = {
|
|
14
16
|
[GroupManagementApiName.broadcast]: BroadcastStreaming as BuiltinStreaming,
|
|
17
|
+
[GroupManagementApiName.executeAgentTask]: ExecuteTaskStreaming as BuiltinStreaming,
|
|
18
|
+
[GroupManagementApiName.executeAgentTasks]: ExecuteTasksStreaming as BuiltinStreaming,
|
|
15
19
|
[GroupManagementApiName.speak]: SpeakStreaming as BuiltinStreaming,
|
|
16
20
|
};
|
|
@@ -8,29 +8,12 @@ import type {
|
|
|
8
8
|
import { groupManagementExecutor } from './executor';
|
|
9
9
|
|
|
10
10
|
// Mock agentGroupStore
|
|
11
|
-
const mockAddAgentsToGroup = vi.fn();
|
|
12
|
-
const mockRemoveAgentFromGroup = vi.fn();
|
|
13
|
-
|
|
14
11
|
vi.mock('@/store/agentGroup', () => ({
|
|
15
12
|
agentGroupSelectors: {
|
|
16
13
|
getAgentByIdFromGroup: vi.fn(() => () => undefined),
|
|
17
14
|
},
|
|
18
15
|
useAgentGroupStore: {
|
|
19
|
-
getState: () => ({
|
|
20
|
-
addAgentsToGroup: mockAddAgentsToGroup,
|
|
21
|
-
removeAgentFromGroup: mockRemoveAgentFromGroup,
|
|
22
|
-
}),
|
|
23
|
-
},
|
|
24
|
-
}));
|
|
25
|
-
|
|
26
|
-
// Mock agentService
|
|
27
|
-
const mockQueryAgents = vi.fn();
|
|
28
|
-
const mockCreateAgent = vi.fn();
|
|
29
|
-
|
|
30
|
-
vi.mock('@/services/agent', () => ({
|
|
31
|
-
agentService: {
|
|
32
|
-
createAgent: (...args: any[]) => mockCreateAgent(...args),
|
|
33
|
-
queryAgents: (...args: any[]) => mockQueryAgents(...args),
|
|
16
|
+
getState: () => ({}),
|
|
34
17
|
},
|
|
35
18
|
}));
|
|
36
19
|
|
|
@@ -288,78 +271,6 @@ describe('GroupManagementExecutor', () => {
|
|
|
288
271
|
});
|
|
289
272
|
});
|
|
290
273
|
|
|
291
|
-
describe('searchAgent', () => {
|
|
292
|
-
beforeEach(() => {
|
|
293
|
-
vi.clearAllMocks();
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
it('should return community not supported message when source is community', async () => {
|
|
297
|
-
const ctx = createMockContext();
|
|
298
|
-
|
|
299
|
-
const result = await groupManagementExecutor.searchAgent(
|
|
300
|
-
{ query: 'test', source: 'community' },
|
|
301
|
-
ctx,
|
|
302
|
-
);
|
|
303
|
-
|
|
304
|
-
expect(result.success).toBe(true);
|
|
305
|
-
expect(result.content).toContain('Community agent search is not yet supported');
|
|
306
|
-
expect(result.state).toEqual({ agents: [], source: 'community', total: 0 });
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
it('should search user agents and return formatted results', async () => {
|
|
310
|
-
mockQueryAgents.mockResolvedValue([
|
|
311
|
-
{ id: 'agent-1', title: 'Code Assistant', description: 'Helps with coding', avatar: '🤖' },
|
|
312
|
-
{ id: 'agent-2', title: 'Writer', description: null, avatar: null },
|
|
313
|
-
]);
|
|
314
|
-
|
|
315
|
-
const ctx = createMockContext();
|
|
316
|
-
const result = await groupManagementExecutor.searchAgent({ query: 'test' }, ctx);
|
|
317
|
-
|
|
318
|
-
expect(result.success).toBe(true);
|
|
319
|
-
expect(mockQueryAgents).toHaveBeenCalledWith({ keyword: 'test', limit: 10 });
|
|
320
|
-
expect(result.content).toContain('Found 2 agents');
|
|
321
|
-
expect(result.content).toContain('Code Assistant');
|
|
322
|
-
expect(result.content).toContain('Writer');
|
|
323
|
-
expect(result.state.agents).toHaveLength(2);
|
|
324
|
-
expect(result.state.total).toBe(2);
|
|
325
|
-
});
|
|
326
|
-
|
|
327
|
-
it('should return no results message when no agents found', async () => {
|
|
328
|
-
mockQueryAgents.mockResolvedValue([]);
|
|
329
|
-
|
|
330
|
-
const ctx = createMockContext();
|
|
331
|
-
const result = await groupManagementExecutor.searchAgent({ query: 'nonexistent' }, ctx);
|
|
332
|
-
|
|
333
|
-
expect(result.success).toBe(true);
|
|
334
|
-
expect(result.content).toContain('No agents found matching "nonexistent"');
|
|
335
|
-
expect(result.state).toEqual({ agents: [], query: 'nonexistent', total: 0 });
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
it('should pass limit to queryAgents', async () => {
|
|
339
|
-
mockQueryAgents.mockResolvedValue([
|
|
340
|
-
{ id: 'agent-1', title: 'Agent 1' },
|
|
341
|
-
{ id: 'agent-2', title: 'Agent 2' },
|
|
342
|
-
]);
|
|
343
|
-
|
|
344
|
-
const ctx = createMockContext();
|
|
345
|
-
const result = await groupManagementExecutor.searchAgent({ query: 'test', limit: 2 }, ctx);
|
|
346
|
-
|
|
347
|
-
expect(result.success).toBe(true);
|
|
348
|
-
expect(mockQueryAgents).toHaveBeenCalledWith({ keyword: 'test', limit: 2 });
|
|
349
|
-
expect(result.state.agents).toHaveLength(2);
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
it('should handle errors gracefully', async () => {
|
|
353
|
-
mockQueryAgents.mockRejectedValue(new Error('Network error'));
|
|
354
|
-
|
|
355
|
-
const ctx = createMockContext();
|
|
356
|
-
const result = await groupManagementExecutor.searchAgent({ query: 'test' }, ctx);
|
|
357
|
-
|
|
358
|
-
expect(result.success).toBe(false);
|
|
359
|
-
expect(result.content).toBe('Failed to search agents: Network error');
|
|
360
|
-
});
|
|
361
|
-
});
|
|
362
|
-
|
|
363
274
|
describe('getAgentInfo', () => {
|
|
364
275
|
it('should return error when no groupId in context', async () => {
|
|
365
276
|
const ctx = createMockContext();
|
|
@@ -372,221 +283,7 @@ describe('GroupManagementExecutor', () => {
|
|
|
372
283
|
});
|
|
373
284
|
});
|
|
374
285
|
|
|
375
|
-
describe('
|
|
376
|
-
beforeEach(() => {
|
|
377
|
-
vi.clearAllMocks();
|
|
378
|
-
});
|
|
379
|
-
|
|
380
|
-
it('should return error when no groupId in context', async () => {
|
|
381
|
-
const ctx = createMockContext();
|
|
382
|
-
|
|
383
|
-
const result = await groupManagementExecutor.inviteAgent({ agentId: 'agent-1' }, ctx);
|
|
384
|
-
|
|
385
|
-
expect(result.success).toBe(false);
|
|
386
|
-
expect(result.content).toBe('No group context available');
|
|
387
|
-
});
|
|
388
|
-
|
|
389
|
-
it('should successfully invite agent when groupId is available', async () => {
|
|
390
|
-
mockAddAgentsToGroup.mockResolvedValue(undefined);
|
|
391
|
-
|
|
392
|
-
const ctx: BuiltinToolContext = {
|
|
393
|
-
...createMockContext(),
|
|
394
|
-
groupId: 'test-group-id',
|
|
395
|
-
};
|
|
396
|
-
|
|
397
|
-
const result = await groupManagementExecutor.inviteAgent({ agentId: 'agent-1' }, ctx);
|
|
398
|
-
|
|
399
|
-
expect(result.success).toBe(true);
|
|
400
|
-
expect(mockAddAgentsToGroup).toHaveBeenCalledWith('test-group-id', ['agent-1']);
|
|
401
|
-
expect(result.content).toBe('Agent "agent-1" has been invited to the group.');
|
|
402
|
-
expect(result.state).toEqual({ agentId: 'agent-1', type: 'inviteAgent' });
|
|
403
|
-
});
|
|
404
|
-
|
|
405
|
-
it('should handle errors during invitation', async () => {
|
|
406
|
-
mockAddAgentsToGroup.mockRejectedValue(new Error('Database error'));
|
|
407
|
-
|
|
408
|
-
const ctx: BuiltinToolContext = {
|
|
409
|
-
...createMockContext(),
|
|
410
|
-
groupId: 'test-group-id',
|
|
411
|
-
};
|
|
412
|
-
|
|
413
|
-
const result = await groupManagementExecutor.inviteAgent({ agentId: 'agent-1' }, ctx);
|
|
414
|
-
|
|
415
|
-
expect(result.success).toBe(false);
|
|
416
|
-
expect(result.content).toBe('Failed to invite agent "agent-1": Database error');
|
|
417
|
-
});
|
|
418
|
-
});
|
|
419
|
-
|
|
420
|
-
describe('removeAgent', () => {
|
|
421
|
-
beforeEach(() => {
|
|
422
|
-
vi.clearAllMocks();
|
|
423
|
-
});
|
|
424
|
-
|
|
425
|
-
it('should return error when no groupId in context', async () => {
|
|
426
|
-
const ctx = createMockContext();
|
|
427
|
-
|
|
428
|
-
const result = await groupManagementExecutor.removeAgent({ agentId: 'agent-1' }, ctx);
|
|
429
|
-
|
|
430
|
-
expect(result.success).toBe(false);
|
|
431
|
-
expect(result.content).toBe('No group context available');
|
|
432
|
-
});
|
|
433
|
-
|
|
434
|
-
it('should successfully remove agent when groupId is available', async () => {
|
|
435
|
-
mockRemoveAgentFromGroup.mockResolvedValue(undefined);
|
|
436
|
-
|
|
437
|
-
const ctx: BuiltinToolContext = {
|
|
438
|
-
...createMockContext(),
|
|
439
|
-
groupId: 'test-group-id',
|
|
440
|
-
};
|
|
441
|
-
|
|
442
|
-
const result = await groupManagementExecutor.removeAgent({ agentId: 'agent-1' }, ctx);
|
|
443
|
-
|
|
444
|
-
expect(result.success).toBe(true);
|
|
445
|
-
expect(mockRemoveAgentFromGroup).toHaveBeenCalledWith('test-group-id', 'agent-1');
|
|
446
|
-
expect(result.content).toBe('Agent "agent-1" has been removed from the group.');
|
|
447
|
-
expect(result.state).toEqual({ agentId: 'agent-1', type: 'removeAgent' });
|
|
448
|
-
});
|
|
449
|
-
|
|
450
|
-
it('should handle errors during removal', async () => {
|
|
451
|
-
mockRemoveAgentFromGroup.mockRejectedValue(new Error('Agent not found'));
|
|
452
|
-
|
|
453
|
-
const ctx: BuiltinToolContext = {
|
|
454
|
-
...createMockContext(),
|
|
455
|
-
groupId: 'test-group-id',
|
|
456
|
-
};
|
|
457
|
-
|
|
458
|
-
const result = await groupManagementExecutor.removeAgent({ agentId: 'agent-1' }, ctx);
|
|
459
|
-
|
|
460
|
-
expect(result.success).toBe(false);
|
|
461
|
-
expect(result.content).toBe('Failed to remove agent "agent-1": Agent not found');
|
|
462
|
-
});
|
|
463
|
-
});
|
|
464
|
-
|
|
465
|
-
describe('createAgent', () => {
|
|
466
|
-
beforeEach(() => {
|
|
467
|
-
vi.clearAllMocks();
|
|
468
|
-
});
|
|
469
|
-
|
|
470
|
-
it('should return error when no groupId in context', async () => {
|
|
471
|
-
const ctx = createMockContext();
|
|
472
|
-
|
|
473
|
-
const result = await groupManagementExecutor.createAgent(
|
|
474
|
-
{ title: 'New Agent', systemRole: 'You are a helpful assistant' },
|
|
475
|
-
ctx,
|
|
476
|
-
);
|
|
477
|
-
|
|
478
|
-
expect(result.success).toBe(false);
|
|
479
|
-
expect(result.content).toBe('No group context available');
|
|
480
|
-
});
|
|
481
|
-
|
|
482
|
-
it('should successfully create a virtual agent and add to group', async () => {
|
|
483
|
-
mockCreateAgent.mockResolvedValue({
|
|
484
|
-
agentId: 'new-agent-id',
|
|
485
|
-
sessionId: 'new-session-id',
|
|
486
|
-
});
|
|
487
|
-
|
|
488
|
-
const ctx: BuiltinToolContext = {
|
|
489
|
-
...createMockContext(),
|
|
490
|
-
groupId: 'test-group-id',
|
|
491
|
-
};
|
|
492
|
-
|
|
493
|
-
const result = await groupManagementExecutor.createAgent(
|
|
494
|
-
{
|
|
495
|
-
avatar: '🤖',
|
|
496
|
-
description: 'A helpful coding assistant',
|
|
497
|
-
systemRole: 'You are a coding expert',
|
|
498
|
-
title: 'Code Helper',
|
|
499
|
-
},
|
|
500
|
-
ctx,
|
|
501
|
-
);
|
|
502
|
-
|
|
503
|
-
expect(result.success).toBe(true);
|
|
504
|
-
expect(mockCreateAgent).toHaveBeenCalledWith({
|
|
505
|
-
config: {
|
|
506
|
-
avatar: '🤖',
|
|
507
|
-
description: 'A helpful coding assistant',
|
|
508
|
-
systemRole: 'You are a coding expert',
|
|
509
|
-
title: 'Code Helper',
|
|
510
|
-
virtual: true,
|
|
511
|
-
},
|
|
512
|
-
groupId: 'test-group-id',
|
|
513
|
-
});
|
|
514
|
-
expect(result.content).toBe('Agent "Code Helper" has been created and added to the group.');
|
|
515
|
-
expect(result.state).toEqual({
|
|
516
|
-
agentId: 'new-agent-id',
|
|
517
|
-
title: 'Code Helper',
|
|
518
|
-
type: 'createAgent',
|
|
519
|
-
});
|
|
520
|
-
});
|
|
521
|
-
|
|
522
|
-
it('should handle case when agentId is not returned', async () => {
|
|
523
|
-
mockCreateAgent.mockResolvedValue({
|
|
524
|
-
sessionId: 'new-session-id',
|
|
525
|
-
// No agentId returned
|
|
526
|
-
});
|
|
527
|
-
|
|
528
|
-
const ctx: BuiltinToolContext = {
|
|
529
|
-
...createMockContext(),
|
|
530
|
-
groupId: 'test-group-id',
|
|
531
|
-
};
|
|
532
|
-
|
|
533
|
-
const result = await groupManagementExecutor.createAgent(
|
|
534
|
-
{ title: 'New Agent', systemRole: 'Test' },
|
|
535
|
-
ctx,
|
|
536
|
-
);
|
|
537
|
-
|
|
538
|
-
expect(result.success).toBe(false);
|
|
539
|
-
expect(result.content).toBe('Failed to create agent: No agent ID returned');
|
|
540
|
-
});
|
|
541
|
-
|
|
542
|
-
it('should handle errors during creation', async () => {
|
|
543
|
-
mockCreateAgent.mockRejectedValue(new Error('Database connection failed'));
|
|
544
|
-
|
|
545
|
-
const ctx: BuiltinToolContext = {
|
|
546
|
-
...createMockContext(),
|
|
547
|
-
groupId: 'test-group-id',
|
|
548
|
-
};
|
|
549
|
-
|
|
550
|
-
const result = await groupManagementExecutor.createAgent(
|
|
551
|
-
{ title: 'New Agent', systemRole: 'Test' },
|
|
552
|
-
ctx,
|
|
553
|
-
);
|
|
554
|
-
|
|
555
|
-
expect(result.success).toBe(false);
|
|
556
|
-
expect(result.content).toBe('Failed to create agent: Database connection failed');
|
|
557
|
-
});
|
|
558
|
-
|
|
559
|
-
it('should create agent with minimal params (only required fields)', async () => {
|
|
560
|
-
mockCreateAgent.mockResolvedValue({
|
|
561
|
-
agentId: 'minimal-agent-id',
|
|
562
|
-
sessionId: 'minimal-session-id',
|
|
563
|
-
});
|
|
564
|
-
|
|
565
|
-
const ctx: BuiltinToolContext = {
|
|
566
|
-
...createMockContext(),
|
|
567
|
-
groupId: 'test-group-id',
|
|
568
|
-
};
|
|
569
|
-
|
|
570
|
-
const result = await groupManagementExecutor.createAgent(
|
|
571
|
-
{ title: 'Minimal Agent', systemRole: 'Basic assistant' },
|
|
572
|
-
ctx,
|
|
573
|
-
);
|
|
574
|
-
|
|
575
|
-
expect(result.success).toBe(true);
|
|
576
|
-
expect(mockCreateAgent).toHaveBeenCalledWith({
|
|
577
|
-
config: {
|
|
578
|
-
avatar: undefined,
|
|
579
|
-
description: undefined,
|
|
580
|
-
systemRole: 'Basic assistant',
|
|
581
|
-
title: 'Minimal Agent',
|
|
582
|
-
virtual: true,
|
|
583
|
-
},
|
|
584
|
-
groupId: 'test-group-id',
|
|
585
|
-
});
|
|
586
|
-
});
|
|
587
|
-
});
|
|
588
|
-
|
|
589
|
-
describe('executeTask', () => {
|
|
286
|
+
describe('executeAgentTask', () => {
|
|
590
287
|
beforeEach(() => {
|
|
591
288
|
vi.clearAllMocks();
|
|
592
289
|
});
|
|
@@ -594,7 +291,7 @@ describe('GroupManagementExecutor', () => {
|
|
|
594
291
|
it('should return stop=true to terminate supervisor execution', async () => {
|
|
595
292
|
const ctx = createMockContext();
|
|
596
293
|
|
|
597
|
-
const result = await groupManagementExecutor.
|
|
294
|
+
const result = await groupManagementExecutor.executeAgentTask(
|
|
598
295
|
{ agentId: 'agent-1', task: 'Do something' },
|
|
599
296
|
ctx,
|
|
600
297
|
);
|
|
@@ -606,7 +303,7 @@ describe('GroupManagementExecutor', () => {
|
|
|
606
303
|
agentId: 'agent-1',
|
|
607
304
|
task: 'Do something',
|
|
608
305
|
timeout: undefined,
|
|
609
|
-
type: '
|
|
306
|
+
type: 'executeAgentTask',
|
|
610
307
|
});
|
|
611
308
|
});
|
|
612
309
|
|
|
@@ -628,7 +325,7 @@ describe('GroupManagementExecutor', () => {
|
|
|
628
325
|
registerAfterCompletion,
|
|
629
326
|
);
|
|
630
327
|
|
|
631
|
-
await groupManagementExecutor.
|
|
328
|
+
await groupManagementExecutor.executeAgentTask(
|
|
632
329
|
{ agentId: 'agent-1', task: 'Do something', timeout: 30000 },
|
|
633
330
|
ctx,
|
|
634
331
|
);
|
|
@@ -653,7 +350,7 @@ describe('GroupManagementExecutor', () => {
|
|
|
653
350
|
it('should not fail when groupOrchestration is not available', async () => {
|
|
654
351
|
const ctx = createMockContext();
|
|
655
352
|
|
|
656
|
-
const result = await groupManagementExecutor.
|
|
353
|
+
const result = await groupManagementExecutor.executeAgentTask(
|
|
657
354
|
{ agentId: 'agent-1', task: 'Do something' },
|
|
658
355
|
ctx,
|
|
659
356
|
);
|
|
@@ -665,7 +362,7 @@ describe('GroupManagementExecutor', () => {
|
|
|
665
362
|
it('should include timeout in state when provided', async () => {
|
|
666
363
|
const ctx = createMockContext();
|
|
667
364
|
|
|
668
|
-
const result = await groupManagementExecutor.
|
|
365
|
+
const result = await groupManagementExecutor.executeAgentTask(
|
|
669
366
|
{ agentId: 'agent-1', task: 'Do something', timeout: 60000 },
|
|
670
367
|
ctx,
|
|
671
368
|
);
|
|
@@ -675,7 +372,7 @@ describe('GroupManagementExecutor', () => {
|
|
|
675
372
|
agentId: 'agent-1',
|
|
676
373
|
task: 'Do something',
|
|
677
374
|
timeout: 60000,
|
|
678
|
-
type: '
|
|
375
|
+
type: 'executeAgentTask',
|
|
679
376
|
});
|
|
680
377
|
});
|
|
681
378
|
});
|
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
* Lobe Group Management Executor
|
|
4
4
|
*
|
|
5
5
|
* Handles all group management tool calls for multi-agent orchestration.
|
|
6
|
+
* Note: Member management (searchAgent, inviteAgent, createAgent, removeAgent)
|
|
7
|
+
* is handled by group-agent-builder. This executor focuses on orchestration.
|
|
6
8
|
*/
|
|
7
9
|
import {
|
|
8
10
|
BroadcastParams,
|
|
9
|
-
CreateAgentParams,
|
|
10
11
|
CreateWorkflowParams,
|
|
11
12
|
DelegateParams,
|
|
12
13
|
ExecuteTaskParams,
|
|
@@ -14,9 +15,6 @@ import {
|
|
|
14
15
|
GroupManagementApiName,
|
|
15
16
|
GroupManagementIdentifier,
|
|
16
17
|
InterruptParams,
|
|
17
|
-
InviteAgentParams,
|
|
18
|
-
RemoveAgentParams,
|
|
19
|
-
SearchAgentParams,
|
|
20
18
|
SpeakParams,
|
|
21
19
|
SummarizeParams,
|
|
22
20
|
VoteParams,
|
|
@@ -24,166 +22,13 @@ import {
|
|
|
24
22
|
import { formatAgentProfile } from '@lobechat/prompts';
|
|
25
23
|
import { BaseExecutor, type BuiltinToolContext, type BuiltinToolResult } from '@lobechat/types';
|
|
26
24
|
|
|
27
|
-
import { agentService } from '@/services/agent';
|
|
28
25
|
import { agentGroupSelectors, useAgentGroupStore } from '@/store/agentGroup';
|
|
29
26
|
|
|
30
27
|
class GroupManagementExecutor extends BaseExecutor<typeof GroupManagementApiName> {
|
|
31
28
|
readonly identifier = GroupManagementIdentifier;
|
|
32
29
|
protected readonly apiEnum = GroupManagementApiName;
|
|
33
30
|
|
|
34
|
-
// ====================
|
|
35
|
-
|
|
36
|
-
searchAgent = async (
|
|
37
|
-
params: SearchAgentParams,
|
|
38
|
-
_ctx: BuiltinToolContext,
|
|
39
|
-
): Promise<BuiltinToolResult> => {
|
|
40
|
-
const { query, limit = 10, source = 'user' } = params;
|
|
41
|
-
|
|
42
|
-
// Currently only support searching user's own agents
|
|
43
|
-
// Community search can be added in the future
|
|
44
|
-
if (source === 'community') {
|
|
45
|
-
return {
|
|
46
|
-
content:
|
|
47
|
-
'Community agent search is not yet supported. Please use source="user" to search your own agents.',
|
|
48
|
-
state: { agents: [], source, total: 0 },
|
|
49
|
-
success: true,
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
try {
|
|
54
|
-
const results = await agentService.queryAgents({ keyword: query, limit });
|
|
55
|
-
|
|
56
|
-
const agents = results.map((agent) => ({
|
|
57
|
-
avatar: agent.avatar,
|
|
58
|
-
description: agent.description,
|
|
59
|
-
id: agent.id,
|
|
60
|
-
title: agent.title,
|
|
61
|
-
}));
|
|
62
|
-
|
|
63
|
-
const total = agents.length;
|
|
64
|
-
|
|
65
|
-
if (total === 0) {
|
|
66
|
-
return {
|
|
67
|
-
content: query
|
|
68
|
-
? `No agents found matching "${query}".`
|
|
69
|
-
: 'No agents found. You can create a new agent or search with different keywords.',
|
|
70
|
-
state: { agents: [], query, total: 0 },
|
|
71
|
-
success: true,
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Format agents list for LLM consumption
|
|
76
|
-
const agentList = agents
|
|
77
|
-
.map(
|
|
78
|
-
(a, i) =>
|
|
79
|
-
`${i + 1}. ${a.title || 'Untitled'} (ID: ${a.id})${a.description ? ` - ${a.description}` : ''}`,
|
|
80
|
-
)
|
|
81
|
-
.join('\n');
|
|
82
|
-
|
|
83
|
-
return {
|
|
84
|
-
content: `Found ${total} agent${total > 1 ? 's' : ''} matching "${query}":\n${agentList}`,
|
|
85
|
-
state: { agents, query, total },
|
|
86
|
-
success: true,
|
|
87
|
-
};
|
|
88
|
-
} catch (error) {
|
|
89
|
-
return {
|
|
90
|
-
content: `Failed to search agents: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
91
|
-
success: false,
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
inviteAgent = async (
|
|
97
|
-
params: InviteAgentParams,
|
|
98
|
-
ctx: BuiltinToolContext,
|
|
99
|
-
): Promise<BuiltinToolResult> => {
|
|
100
|
-
const { groupId } = ctx;
|
|
101
|
-
|
|
102
|
-
if (!groupId) {
|
|
103
|
-
return { content: 'No group context available', success: false };
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
try {
|
|
107
|
-
await useAgentGroupStore.getState().addAgentsToGroup(groupId, [params.agentId]);
|
|
108
|
-
|
|
109
|
-
return {
|
|
110
|
-
content: `Agent "${params.agentId}" has been invited to the group.`,
|
|
111
|
-
state: { agentId: params.agentId, type: 'inviteAgent' },
|
|
112
|
-
success: true,
|
|
113
|
-
};
|
|
114
|
-
} catch (error) {
|
|
115
|
-
return {
|
|
116
|
-
content: `Failed to invite agent "${params.agentId}": ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
117
|
-
success: false,
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
createAgent = async (
|
|
123
|
-
params: CreateAgentParams,
|
|
124
|
-
ctx: BuiltinToolContext,
|
|
125
|
-
): Promise<BuiltinToolResult> => {
|
|
126
|
-
const { groupId } = ctx;
|
|
127
|
-
|
|
128
|
-
if (!groupId) {
|
|
129
|
-
return { content: 'No group context available', success: false };
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
try {
|
|
133
|
-
// Create a virtual agent (agents created by supervisor are virtual)
|
|
134
|
-
const result = await agentService.createAgent({
|
|
135
|
-
config: {
|
|
136
|
-
avatar: params.avatar,
|
|
137
|
-
description: params.description,
|
|
138
|
-
systemRole: params.systemRole,
|
|
139
|
-
title: params.title,
|
|
140
|
-
virtual: true,
|
|
141
|
-
},
|
|
142
|
-
groupId,
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
if (!result.agentId) {
|
|
146
|
-
return { content: 'Failed to create agent: No agent ID returned', success: false };
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return {
|
|
150
|
-
content: `Agent "${params.title}" has been created and added to the group.`,
|
|
151
|
-
state: { agentId: result.agentId, title: params.title, type: 'createAgent' },
|
|
152
|
-
success: true,
|
|
153
|
-
};
|
|
154
|
-
} catch (error) {
|
|
155
|
-
return {
|
|
156
|
-
content: `Failed to create agent: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
157
|
-
success: false,
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
removeAgent = async (
|
|
163
|
-
params: RemoveAgentParams,
|
|
164
|
-
ctx: BuiltinToolContext,
|
|
165
|
-
): Promise<BuiltinToolResult> => {
|
|
166
|
-
const { groupId } = ctx;
|
|
167
|
-
|
|
168
|
-
if (!groupId) {
|
|
169
|
-
return { content: 'No group context available', success: false };
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
try {
|
|
173
|
-
await useAgentGroupStore.getState().removeAgentFromGroup(groupId, params.agentId);
|
|
174
|
-
|
|
175
|
-
return {
|
|
176
|
-
content: `Agent "${params.agentId}" has been removed from the group.`,
|
|
177
|
-
state: { agentId: params.agentId, type: 'removeAgent' },
|
|
178
|
-
success: true,
|
|
179
|
-
};
|
|
180
|
-
} catch (error) {
|
|
181
|
-
return {
|
|
182
|
-
content: `Failed to remove agent "${params.agentId}": ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
183
|
-
success: false,
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
};
|
|
31
|
+
// ==================== Agent Info ====================
|
|
187
32
|
|
|
188
33
|
getAgentInfo = async (
|
|
189
34
|
params: GetAgentInfoParams,
|
|
@@ -306,7 +151,7 @@ class GroupManagementExecutor extends BaseExecutor<typeof GroupManagementApiName
|
|
|
306
151
|
|
|
307
152
|
// ==================== Task Execution ====================
|
|
308
153
|
|
|
309
|
-
|
|
154
|
+
executeAgentTask = async (
|
|
310
155
|
params: ExecuteTaskParams,
|
|
311
156
|
ctx: BuiltinToolContext,
|
|
312
157
|
): Promise<BuiltinToolResult> => {
|
|
@@ -333,7 +178,7 @@ class GroupManagementExecutor extends BaseExecutor<typeof GroupManagementApiName
|
|
|
333
178
|
skipCallSupervisor: params.skipCallSupervisor,
|
|
334
179
|
task: params.task,
|
|
335
180
|
timeout: params.timeout,
|
|
336
|
-
type: '
|
|
181
|
+
type: 'executeAgentTask',
|
|
337
182
|
},
|
|
338
183
|
stop: true,
|
|
339
184
|
success: true,
|