@lobehub/lobehub 2.0.0-next.305 → 2.0.0-next.307
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/.vscode/settings.json +18 -3
- package/CHANGELOG.md +53 -0
- package/changelog/v1.json +18 -0
- package/e2e/src/steps/community/detail-pages.steps.ts +3 -1
- package/e2e/src/steps/community/interactions.steps.ts +4 -4
- package/package.json +2 -2
- package/packages/builtin-agents/src/agents/group-supervisor/index.ts +1 -7
- package/packages/builtin-tool-group-agent-builder/src/ExecutionRuntime/index.ts +29 -0
- package/packages/builtin-tool-group-agent-builder/src/executor.ts +18 -0
- package/packages/builtin-tool-group-agent-builder/src/manifest.ts +17 -0
- package/packages/builtin-tool-group-agent-builder/src/types.ts +10 -0
- package/packages/builtin-tool-group-management/src/executor.test.ts +0 -12
- package/packages/builtin-tool-group-management/src/executor.ts +8 -47
- package/packages/builtin-tool-group-management/src/manifest.ts +0 -17
- package/packages/builtin-tool-group-management/src/systemRole.ts +1 -8
- package/packages/builtin-tool-group-management/src/types.ts +0 -10
- package/packages/builtin-tool-local-system/src/ExecutionRuntime/index.ts +70 -31
- package/packages/builtin-tool-local-system/src/executor/index.ts +94 -60
- package/packages/context-engine/src/processors/GroupMessageFlatten.ts +9 -6
- package/packages/context-engine/src/processors/__tests__/GroupMessageFlatten.test.ts +103 -0
- package/packages/context-engine/src/providers/GroupAgentBuilderContextInjector.ts +18 -31
- package/packages/context-engine/src/providers/__tests__/GroupAgentBuilderContextInjector.test.ts +307 -0
- package/packages/database/src/repositories/agentGroup/index.ts +23 -0
- package/packages/prompts/src/prompts/fileSystem/formatCommandOutput.test.ts +61 -0
- package/packages/prompts/src/prompts/fileSystem/formatCommandOutput.ts +21 -0
- package/packages/prompts/src/prompts/fileSystem/formatCommandResult.test.ts +87 -0
- package/packages/prompts/src/prompts/fileSystem/formatCommandResult.ts +35 -0
- package/packages/prompts/src/prompts/fileSystem/formatEditResult.test.ts +57 -0
- package/packages/prompts/src/prompts/fileSystem/formatEditResult.ts +17 -0
- package/packages/prompts/src/prompts/fileSystem/formatFileContent.test.ts +59 -0
- package/packages/prompts/src/prompts/fileSystem/formatFileContent.ts +14 -0
- package/packages/prompts/src/prompts/fileSystem/formatFileList.test.ts +62 -0
- package/packages/prompts/src/prompts/fileSystem/formatFileList.ts +13 -0
- package/packages/prompts/src/prompts/fileSystem/formatFileSearchResults.test.ts +34 -0
- package/packages/prompts/src/prompts/fileSystem/formatFileSearchResults.ts +12 -0
- package/packages/prompts/src/prompts/fileSystem/formatGlobResults.test.ts +64 -0
- package/packages/prompts/src/prompts/fileSystem/formatGlobResults.ts +23 -0
- package/packages/prompts/src/prompts/fileSystem/formatGrepResults.test.ts +85 -0
- package/packages/prompts/src/prompts/fileSystem/formatGrepResults.ts +24 -0
- package/packages/prompts/src/prompts/fileSystem/formatKillResult.test.ts +30 -0
- package/packages/prompts/src/prompts/fileSystem/formatKillResult.ts +9 -0
- package/packages/prompts/src/prompts/fileSystem/formatMoveResults.test.ts +37 -0
- package/packages/prompts/src/prompts/fileSystem/formatMoveResults.ts +20 -0
- package/packages/prompts/src/prompts/fileSystem/formatMultipleFiles.test.ts +54 -0
- package/packages/prompts/src/prompts/fileSystem/formatMultipleFiles.ts +9 -0
- package/packages/prompts/src/prompts/fileSystem/formatRenameResult.test.ts +35 -0
- package/packages/prompts/src/prompts/fileSystem/formatRenameResult.ts +17 -0
- package/packages/prompts/src/prompts/fileSystem/formatWriteResult.test.ts +30 -0
- package/packages/prompts/src/prompts/fileSystem/formatWriteResult.ts +11 -0
- package/packages/prompts/src/prompts/fileSystem/index.ts +13 -0
- package/packages/prompts/src/prompts/index.ts +1 -0
- package/packages/prompts/src/prompts/userMemory/__snapshots__/index.test.ts.snap +14 -38
- package/packages/prompts/src/prompts/userMemory/index.ts +5 -24
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/Topic/Actions.tsx +4 -3
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/Topic/useDropdownMenu.tsx +12 -2
- package/src/app/[variants]/(main)/community/(detail)/assistant/index.tsx +1 -1
- package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Sidebar/ActionButton/AddGroupAgent.tsx +69 -17
- package/src/app/[variants]/(main)/community/(detail)/mcp/index.tsx +1 -1
- package/src/app/[variants]/(main)/group/_layout/Sidebar/Topic/Actions.tsx +4 -3
- package/src/app/[variants]/(main)/group/_layout/Sidebar/Topic/useDropdownMenu.tsx +12 -2
- package/src/app/[variants]/(main)/group/features/Conversation/MainChatInput/index.tsx +2 -2
- package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/AgentItem/index.tsx +2 -2
- package/src/features/ChatInput/ActionBar/Upload/ServerMode.tsx +13 -3
- package/src/features/ChatInput/ActionBar/components/ActionDropdown.tsx +26 -3
- package/src/features/Conversation/Messages/Supervisor/index.tsx +2 -1
- package/src/features/Conversation/Messages/components/ContentLoading.tsx +8 -2
- package/src/features/ResourceManager/components/Header/AddButton.tsx +20 -3
- package/src/server/routers/lambda/__tests__/agentGroup.test.ts +1 -0
- package/src/server/routers/lambda/agentGroup.ts +22 -0
- package/src/services/chat/index.ts +1 -0
- package/src/services/chat/mecha/agentConfigResolver.test.ts +62 -45
- package/src/services/chat/mecha/agentConfigResolver.ts +77 -10
- package/src/services/chat/mecha/modelParamsResolver.test.ts +211 -0
- package/src/services/chatGroup/index.ts +14 -0
- package/src/store/agentGroup/action.ts +30 -0
- package/src/store/agentGroup/slices/lifecycle.test.ts +77 -18
- package/src/store/agentGroup/slices/lifecycle.ts +7 -9
- package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +6 -2
- package/src/store/chat/slices/operation/__tests__/selectors.test.ts +124 -0
- package/src/store/chat/slices/operation/selectors.ts +22 -0
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
type DropdownMenuProps,
|
|
9
9
|
DropdownMenuRoot,
|
|
10
10
|
DropdownMenuTrigger,
|
|
11
|
+
type MenuItemType,
|
|
11
12
|
type MenuProps,
|
|
12
13
|
type PopoverTrigger,
|
|
13
14
|
renderDropdownMenuItems,
|
|
@@ -16,6 +17,7 @@ import { createStaticStyles, cx } from 'antd-style';
|
|
|
16
17
|
import {
|
|
17
18
|
type CSSProperties,
|
|
18
19
|
type ReactNode,
|
|
20
|
+
isValidElement,
|
|
19
21
|
memo,
|
|
20
22
|
useCallback,
|
|
21
23
|
useEffect,
|
|
@@ -34,8 +36,15 @@ const styles = createStaticStyles(({ css }) => ({
|
|
|
34
36
|
`,
|
|
35
37
|
}));
|
|
36
38
|
|
|
37
|
-
type
|
|
38
|
-
|
|
39
|
+
export type ActionDropdownMenuItem = MenuItemType;
|
|
40
|
+
|
|
41
|
+
export type ActionDropdownMenuItems = MenuProps<ActionDropdownMenuItem>['items'];
|
|
42
|
+
|
|
43
|
+
type ActionDropdownMenu = Omit<
|
|
44
|
+
Pick<MenuProps<ActionDropdownMenuItem>, 'className' | 'onClick' | 'style'>,
|
|
45
|
+
'items'
|
|
46
|
+
> & {
|
|
47
|
+
items: ActionDropdownMenuItems | (() => ActionDropdownMenuItems);
|
|
39
48
|
};
|
|
40
49
|
|
|
41
50
|
export interface ActionDropdownProps extends Omit<DropdownMenuProps, 'items'> {
|
|
@@ -116,7 +125,7 @@ const ActionDropdown = memo<ActionDropdownProps>(
|
|
|
116
125
|
}, [openOnHover, triggerProps]);
|
|
117
126
|
|
|
118
127
|
const decorateMenuItems = useCallback(
|
|
119
|
-
(items:
|
|
128
|
+
(items: ActionDropdownMenuItems): ActionDropdownMenuItems => {
|
|
120
129
|
if (!items) return items;
|
|
121
130
|
|
|
122
131
|
return items.map((item) => {
|
|
@@ -136,10 +145,24 @@ const ActionDropdown = memo<ActionDropdownProps>(
|
|
|
136
145
|
};
|
|
137
146
|
}
|
|
138
147
|
const itemOnClick = 'onClick' in item ? item.onClick : undefined;
|
|
148
|
+
const closeOnClick = 'closeOnClick' in item ? item.closeOnClick : undefined;
|
|
149
|
+
const keepOpenOnClick = closeOnClick === false;
|
|
150
|
+
const itemLabel = 'label' in item ? item.label : undefined;
|
|
151
|
+
const shouldKeepOpen = isValidElement(itemLabel);
|
|
152
|
+
|
|
153
|
+
const resolvedCloseOnClick = closeOnClick ?? (shouldKeepOpen ? false : undefined);
|
|
139
154
|
|
|
140
155
|
return {
|
|
141
156
|
...item,
|
|
157
|
+
...(resolvedCloseOnClick !== undefined ? { closeOnClick: resolvedCloseOnClick } : null),
|
|
142
158
|
onClick: (info) => {
|
|
159
|
+
if (keepOpenOnClick) {
|
|
160
|
+
info.domEvent.stopPropagation();
|
|
161
|
+
menu.onClick?.(info);
|
|
162
|
+
itemOnClick?.(info);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
143
166
|
info.domEvent.preventDefault();
|
|
144
167
|
menu.onClick?.(info);
|
|
145
168
|
itemOnClick?.(info);
|
|
@@ -55,7 +55,8 @@ const GroupMessage = memo<GroupMessageProps>(({ id, index, disableEditing, isLat
|
|
|
55
55
|
|
|
56
56
|
// Get editing state from ConversationStore
|
|
57
57
|
const creating = useConversationStore(messageStateSelectors.isMessageCreating(id));
|
|
58
|
-
const
|
|
58
|
+
const generating = useConversationStore(messageStateSelectors.isMessageGenerating(id));
|
|
59
|
+
const newScreen = useNewScreen({ creating: creating || generating, isLatestItem });
|
|
59
60
|
|
|
60
61
|
const setMessageItemActionElementPortialContext = useSetMessageItemActionElementPortialContext();
|
|
61
62
|
const setMessageItemActionTypeContext = useSetMessageItemActionTypeContext();
|
|
@@ -18,10 +18,11 @@ interface ContentLoadingProps {
|
|
|
18
18
|
const ContentLoading = memo<ContentLoadingProps>(({ id }) => {
|
|
19
19
|
const { t } = useTranslation('chat');
|
|
20
20
|
const runningOp = useChatStore(operationSelectors.getDeepestRunningOperationByMessage(id));
|
|
21
|
+
console.log('runningOp', runningOp);
|
|
21
22
|
const [elapsedSeconds, setElapsedSeconds] = useState(0);
|
|
23
|
+
const [startTime, setStartTime] = useState(runningOp?.metadata?.startTime);
|
|
22
24
|
|
|
23
25
|
const operationType = runningOp?.type as OperationType | undefined;
|
|
24
|
-
const startTime = runningOp?.metadata?.startTime;
|
|
25
26
|
|
|
26
27
|
// Track elapsed time, reset when operation type changes
|
|
27
28
|
useEffect(() => {
|
|
@@ -39,7 +40,12 @@ const ContentLoading = memo<ContentLoadingProps>(({ id }) => {
|
|
|
39
40
|
const interval = setInterval(updateElapsed, 1000);
|
|
40
41
|
|
|
41
42
|
return () => clearInterval(interval);
|
|
42
|
-
}, [startTime
|
|
43
|
+
}, [startTime]);
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
setElapsedSeconds(0);
|
|
47
|
+
setStartTime(Date.now());
|
|
48
|
+
}, [operationType, id]);
|
|
43
49
|
|
|
44
50
|
// Get localized label based on operation type
|
|
45
51
|
const operationLabel = operationType
|
|
@@ -5,7 +5,7 @@ import { Notion } from '@lobehub/icons';
|
|
|
5
5
|
import { Button, DropdownMenu, Icon, type MenuProps } from '@lobehub/ui';
|
|
6
6
|
import { Upload } from 'antd';
|
|
7
7
|
import { FilePenLine, FileUp, FolderIcon, FolderUp, Link, Plus } from 'lucide-react';
|
|
8
|
-
import { useCallback, useMemo } from 'react';
|
|
8
|
+
import { type ChangeEvent, useCallback, useMemo, useState } from 'react';
|
|
9
9
|
import { useTranslation } from 'react-i18next';
|
|
10
10
|
|
|
11
11
|
import { useResourceManagerStore } from '@/app/[variants]/(main)/resource/features/store';
|
|
@@ -23,6 +23,7 @@ const AddButton = () => {
|
|
|
23
23
|
const pushDockFileList = useFileStore((s) => s.pushDockFileList);
|
|
24
24
|
const uploadFolderWithStructure = useFileStore((s) => s.uploadFolderWithStructure);
|
|
25
25
|
const createResourceAndSync = useFileStore((s) => s.createResourceAndSync);
|
|
26
|
+
const [menuOpen, setMenuOpen] = useState(false);
|
|
26
27
|
|
|
27
28
|
// TODO: Migrate Notion import to use createResource
|
|
28
29
|
// Keep old functions temporarily for components not yet migrated
|
|
@@ -121,6 +122,13 @@ const AddButton = () => {
|
|
|
121
122
|
t,
|
|
122
123
|
uploadFolderWithStructure,
|
|
123
124
|
});
|
|
125
|
+
const handleFolderUploadWithClose = useCallback(
|
|
126
|
+
(event: ChangeEvent<HTMLInputElement>) => {
|
|
127
|
+
setMenuOpen(false);
|
|
128
|
+
return handleFolderUpload(event);
|
|
129
|
+
},
|
|
130
|
+
[handleFolderUpload],
|
|
131
|
+
);
|
|
124
132
|
|
|
125
133
|
const items = useMemo<MenuProps['items']>(
|
|
126
134
|
() => [
|
|
@@ -144,11 +152,13 @@ const AddButton = () => {
|
|
|
144
152
|
type: 'divider',
|
|
145
153
|
},
|
|
146
154
|
{
|
|
155
|
+
closeOnClick: false,
|
|
147
156
|
icon: <Icon icon={FileUp} />,
|
|
148
157
|
key: 'upload-file',
|
|
149
158
|
label: (
|
|
150
159
|
<Upload
|
|
151
160
|
beforeUpload={async (file) => {
|
|
161
|
+
setMenuOpen(false);
|
|
152
162
|
await pushDockFileList([file], libraryId, currentFolderId ?? undefined);
|
|
153
163
|
|
|
154
164
|
return false;
|
|
@@ -161,6 +171,7 @@ const AddButton = () => {
|
|
|
161
171
|
),
|
|
162
172
|
},
|
|
163
173
|
{
|
|
174
|
+
closeOnClick: false,
|
|
164
175
|
icon: <Icon icon={FolderUp} />,
|
|
165
176
|
key: 'upload-folder',
|
|
166
177
|
label: <label htmlFor="folder-upload-input">{t('header.actions.uploadFolder')}</label>,
|
|
@@ -211,7 +222,13 @@ const AddButton = () => {
|
|
|
211
222
|
|
|
212
223
|
return (
|
|
213
224
|
<>
|
|
214
|
-
<DropdownMenu
|
|
225
|
+
<DropdownMenu
|
|
226
|
+
items={items}
|
|
227
|
+
onOpenChange={setMenuOpen}
|
|
228
|
+
open={menuOpen}
|
|
229
|
+
placement="bottomRight"
|
|
230
|
+
trigger="both"
|
|
231
|
+
>
|
|
215
232
|
<Button data-no-highlight icon={Plus} type="primary">
|
|
216
233
|
{t('addLibrary')}
|
|
217
234
|
</Button>
|
|
@@ -233,7 +250,7 @@ const AddButton = () => {
|
|
|
233
250
|
<input
|
|
234
251
|
id="folder-upload-input"
|
|
235
252
|
multiple
|
|
236
|
-
onChange={
|
|
253
|
+
onChange={handleFolderUploadWithClose}
|
|
237
254
|
style={{ display: 'none' }}
|
|
238
255
|
type="file"
|
|
239
256
|
// @ts-expect-error - webkitdirectory is not in the React types
|
|
@@ -129,6 +129,19 @@ export const agentGroupRouter = router({
|
|
|
129
129
|
})
|
|
130
130
|
.partial(),
|
|
131
131
|
),
|
|
132
|
+
supervisorConfig: z
|
|
133
|
+
.object({
|
|
134
|
+
avatar: z.string().nullish(),
|
|
135
|
+
backgroundColor: z.string().nullish(),
|
|
136
|
+
description: z.string().nullish(),
|
|
137
|
+
model: z.string().nullish(),
|
|
138
|
+
params: z.any().nullish(),
|
|
139
|
+
provider: z.string().nullish(),
|
|
140
|
+
systemRole: z.string().nullish(),
|
|
141
|
+
tags: z.array(z.string()).nullish(),
|
|
142
|
+
title: z.string().nullish(),
|
|
143
|
+
})
|
|
144
|
+
.optional(),
|
|
132
145
|
}),
|
|
133
146
|
)
|
|
134
147
|
.mutation(async ({ input, ctx }) => {
|
|
@@ -144,6 +157,14 @@ export const agentGroupRouter = router({
|
|
|
144
157
|
const memberAgentIds = createdAgents.map((agent) => agent.id);
|
|
145
158
|
|
|
146
159
|
// 2. Create group with supervisor and member agents
|
|
160
|
+
// Filter out null/undefined values from supervisorConfig
|
|
161
|
+
const supervisorConfig = input.supervisorConfig
|
|
162
|
+
? Object.fromEntries(
|
|
163
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, eqeqeq
|
|
164
|
+
Object.entries(input.supervisorConfig).filter(([_, v]) => v != null),
|
|
165
|
+
)
|
|
166
|
+
: undefined;
|
|
167
|
+
|
|
147
168
|
const { group, supervisorAgentId } = await ctx.agentGroupRepo.createGroupWithSupervisor(
|
|
148
169
|
{
|
|
149
170
|
...input.groupConfig,
|
|
@@ -152,6 +173,7 @@ export const agentGroupRouter = router({
|
|
|
152
173
|
),
|
|
153
174
|
},
|
|
154
175
|
memberAgentIds,
|
|
176
|
+
supervisorConfig as any,
|
|
155
177
|
);
|
|
156
178
|
|
|
157
179
|
return { agentIds: memberAgentIds, groupId: group.id, supervisorAgentId };
|
|
@@ -627,7 +627,7 @@ describe('resolveAgentConfig', () => {
|
|
|
627
627
|
});
|
|
628
628
|
});
|
|
629
629
|
|
|
630
|
-
describe('supervisor agent (
|
|
630
|
+
describe('supervisor agent (detected via groupId)', () => {
|
|
631
631
|
const mockGroupStoreState = { groupMap: {} };
|
|
632
632
|
const mockGroupWithSupervisor = {
|
|
633
633
|
agents: [
|
|
@@ -651,18 +651,11 @@ describe('resolveAgentConfig', () => {
|
|
|
651
651
|
);
|
|
652
652
|
});
|
|
653
653
|
|
|
654
|
-
it('should detect supervisor agent
|
|
655
|
-
// Mock:
|
|
656
|
-
vi.spyOn(
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
).mockReturnValue(() => mockGroupWithSupervisor as any);
|
|
660
|
-
|
|
661
|
-
// Mock: getGroupBySupervisorAgentId for building groupSupervisorContext
|
|
662
|
-
vi.spyOn(
|
|
663
|
-
agentGroupSelectors.agentGroupSelectors,
|
|
664
|
-
'getGroupBySupervisorAgentId',
|
|
665
|
-
).mockReturnValue(() => mockGroupWithSupervisor as any);
|
|
654
|
+
it('should detect supervisor agent using groupId for direct lookup', () => {
|
|
655
|
+
// Mock: groupById returns the group
|
|
656
|
+
vi.spyOn(agentGroupSelectors.agentGroupByIdSelectors, 'groupById').mockReturnValue(
|
|
657
|
+
() => mockGroupWithSupervisor as any,
|
|
658
|
+
);
|
|
666
659
|
|
|
667
660
|
// Mock: getGroupMembers returns non-supervisor agents
|
|
668
661
|
vi.spyOn(agentGroupSelectors.agentGroupSelectors, 'getGroupMembers').mockReturnValue(
|
|
@@ -680,7 +673,10 @@ describe('resolveAgentConfig', () => {
|
|
|
680
673
|
systemRole: 'You are a group supervisor...',
|
|
681
674
|
});
|
|
682
675
|
|
|
683
|
-
const result = resolveAgentConfig({
|
|
676
|
+
const result = resolveAgentConfig({
|
|
677
|
+
agentId: 'supervisor-agent-id',
|
|
678
|
+
groupId: 'group-123',
|
|
679
|
+
});
|
|
684
680
|
|
|
685
681
|
expect(result.isBuiltinAgent).toBe(true);
|
|
686
682
|
expect(result.slug).toBe('group-supervisor');
|
|
@@ -689,17 +685,10 @@ describe('resolveAgentConfig', () => {
|
|
|
689
685
|
});
|
|
690
686
|
|
|
691
687
|
it('should pass groupSupervisorContext to getAgentRuntimeConfig', () => {
|
|
692
|
-
// Mock:
|
|
693
|
-
vi.spyOn(
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
).mockReturnValue(() => mockGroupWithSupervisor as any);
|
|
697
|
-
|
|
698
|
-
// Mock: getGroupBySupervisorAgentId for building groupSupervisorContext
|
|
699
|
-
vi.spyOn(
|
|
700
|
-
agentGroupSelectors.agentGroupSelectors,
|
|
701
|
-
'getGroupBySupervisorAgentId',
|
|
702
|
-
).mockReturnValue(() => mockGroupWithSupervisor as any);
|
|
688
|
+
// Mock: groupById returns the group
|
|
689
|
+
vi.spyOn(agentGroupSelectors.agentGroupByIdSelectors, 'groupById').mockReturnValue(
|
|
690
|
+
() => mockGroupWithSupervisor as any,
|
|
691
|
+
);
|
|
703
692
|
|
|
704
693
|
// Mock: getGroupMembers returns non-supervisor agents
|
|
705
694
|
vi.spyOn(agentGroupSelectors.agentGroupSelectors, 'getGroupMembers').mockReturnValue(
|
|
@@ -718,7 +707,10 @@ describe('resolveAgentConfig', () => {
|
|
|
718
707
|
systemRole: 'You are a group supervisor...',
|
|
719
708
|
});
|
|
720
709
|
|
|
721
|
-
resolveAgentConfig({
|
|
710
|
+
resolveAgentConfig({
|
|
711
|
+
agentId: 'supervisor-agent-id',
|
|
712
|
+
groupId: 'group-123',
|
|
713
|
+
});
|
|
722
714
|
|
|
723
715
|
expect(getAgentRuntimeConfigSpy).toHaveBeenCalledWith(
|
|
724
716
|
'group-supervisor',
|
|
@@ -736,31 +728,53 @@ describe('resolveAgentConfig', () => {
|
|
|
736
728
|
);
|
|
737
729
|
});
|
|
738
730
|
|
|
739
|
-
it('should treat as regular agent when
|
|
740
|
-
//
|
|
741
|
-
|
|
742
|
-
agentGroupSelectors.agentGroupByIdSelectors,
|
|
743
|
-
'groupBySupervisorAgentId',
|
|
744
|
-
).mockReturnValue(() => undefined);
|
|
745
|
-
|
|
746
|
-
const result = resolveAgentConfig({ agentId: 'some-other-agent' });
|
|
731
|
+
it('should treat as regular agent when groupId is not provided', () => {
|
|
732
|
+
// Without groupId, cannot detect supervisor
|
|
733
|
+
const result = resolveAgentConfig({ agentId: 'supervisor-agent-id' });
|
|
747
734
|
|
|
748
735
|
expect(result.isBuiltinAgent).toBe(false);
|
|
749
736
|
expect(result.slug).toBeUndefined();
|
|
750
737
|
expect(result.plugins).toEqual(['plugin-a', 'plugin-b']); // Falls back to agent config plugins
|
|
751
738
|
});
|
|
752
739
|
|
|
753
|
-
it('should
|
|
754
|
-
//
|
|
755
|
-
vi.spyOn(
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
).mockReturnValue(() => mockGroupWithSupervisor as any);
|
|
740
|
+
it('should treat as regular agent when agentId does not match group supervisorAgentId', () => {
|
|
741
|
+
// Mock: groupById returns the group
|
|
742
|
+
vi.spyOn(agentGroupSelectors.agentGroupByIdSelectors, 'groupById').mockReturnValue(
|
|
743
|
+
() => mockGroupWithSupervisor as any,
|
|
744
|
+
);
|
|
759
745
|
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
'
|
|
763
|
-
|
|
746
|
+
// Pass a different agentId that is not the supervisor
|
|
747
|
+
const result = resolveAgentConfig({
|
|
748
|
+
agentId: 'some-other-agent',
|
|
749
|
+
groupId: 'group-123',
|
|
750
|
+
});
|
|
751
|
+
|
|
752
|
+
expect(result.isBuiltinAgent).toBe(false);
|
|
753
|
+
expect(result.slug).toBeUndefined();
|
|
754
|
+
expect(result.plugins).toEqual(['plugin-a', 'plugin-b']);
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
it('should treat as regular agent when group is not found', () => {
|
|
758
|
+
// Mock: groupById returns undefined
|
|
759
|
+
vi.spyOn(agentGroupSelectors.agentGroupByIdSelectors, 'groupById').mockReturnValue(
|
|
760
|
+
() => undefined,
|
|
761
|
+
);
|
|
762
|
+
|
|
763
|
+
const result = resolveAgentConfig({
|
|
764
|
+
agentId: 'supervisor-agent-id',
|
|
765
|
+
groupId: 'non-existent-group',
|
|
766
|
+
});
|
|
767
|
+
|
|
768
|
+
expect(result.isBuiltinAgent).toBe(false);
|
|
769
|
+
expect(result.slug).toBeUndefined();
|
|
770
|
+
expect(result.plugins).toEqual(['plugin-a', 'plugin-b']);
|
|
771
|
+
});
|
|
772
|
+
|
|
773
|
+
it('should work correctly when regenerating supervisor message with groupId', () => {
|
|
774
|
+
// This simulates the regenerate flow where both agentId and groupId are provided
|
|
775
|
+
vi.spyOn(agentGroupSelectors.agentGroupByIdSelectors, 'groupById').mockReturnValue(
|
|
776
|
+
() => mockGroupWithSupervisor as any,
|
|
777
|
+
);
|
|
764
778
|
|
|
765
779
|
vi.spyOn(agentGroupSelectors.agentGroupSelectors, 'getGroupMembers').mockReturnValue(
|
|
766
780
|
() => [{ id: 'member-agent-1', title: 'Agent 1' }] as any,
|
|
@@ -772,7 +786,10 @@ describe('resolveAgentConfig', () => {
|
|
|
772
786
|
systemRole: 'Supervisor system role',
|
|
773
787
|
});
|
|
774
788
|
|
|
775
|
-
const result = resolveAgentConfig({
|
|
789
|
+
const result = resolveAgentConfig({
|
|
790
|
+
agentId: 'supervisor-agent-id',
|
|
791
|
+
groupId: 'group-123',
|
|
792
|
+
});
|
|
776
793
|
|
|
777
794
|
// Should correctly identify as builtin supervisor agent
|
|
778
795
|
expect(result.isBuiltinAgent).toBe(true);
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
type LobeAgentConfig,
|
|
6
6
|
type MessageMapScope,
|
|
7
7
|
} from '@lobechat/types';
|
|
8
|
+
import debug from 'debug';
|
|
8
9
|
import { produce } from 'immer';
|
|
9
10
|
|
|
10
11
|
import { getAgentStoreState } from '@/store/agent';
|
|
@@ -12,6 +13,8 @@ import { agentSelectors, chatConfigByIdSelectors } from '@/store/agent/selectors
|
|
|
12
13
|
import { getChatGroupStoreState } from '@/store/agentGroup';
|
|
13
14
|
import { agentGroupByIdSelectors, agentGroupSelectors } from '@/store/agentGroup/selectors';
|
|
14
15
|
|
|
16
|
+
const log = debug('mecha:agentConfigResolver');
|
|
17
|
+
|
|
15
18
|
/**
|
|
16
19
|
* Applies params adjustments based on chatConfig settings.
|
|
17
20
|
*
|
|
@@ -52,6 +55,12 @@ export interface AgentConfigResolverContext {
|
|
|
52
55
|
/** Document content for page-agent */
|
|
53
56
|
documentContent?: string;
|
|
54
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Group ID for supervisor detection.
|
|
60
|
+
* When provided, used for direct lookup instead of iterating all groups.
|
|
61
|
+
*/
|
|
62
|
+
groupId?: string;
|
|
63
|
+
|
|
55
64
|
/** Current model being used (for template variables) */
|
|
56
65
|
model?: string;
|
|
57
66
|
/** Plugins enabled for the agent */
|
|
@@ -99,6 +108,8 @@ export interface ResolvedAgentConfig {
|
|
|
99
108
|
export const resolveAgentConfig = (ctx: AgentConfigResolverContext): ResolvedAgentConfig => {
|
|
100
109
|
const { agentId, model, documentContent, plugins, targetAgentConfig } = ctx;
|
|
101
110
|
|
|
111
|
+
log('resolveAgentConfig called with agentId: %s, scope: %s', agentId, ctx.scope);
|
|
112
|
+
|
|
102
113
|
const agentStoreState = getAgentStoreState();
|
|
103
114
|
|
|
104
115
|
// Get base config from store
|
|
@@ -109,21 +120,42 @@ export const resolveAgentConfig = (ctx: AgentConfigResolverContext): ResolvedAge
|
|
|
109
120
|
const basePlugins = agentConfig.plugins ?? [];
|
|
110
121
|
|
|
111
122
|
// Check if this is a builtin agent
|
|
112
|
-
// First check agent store, then check if this is a supervisor agent
|
|
123
|
+
// First check agent store, then check if this is a supervisor agent via groupId
|
|
113
124
|
let slug = agentSelectors.getAgentSlugById(agentId)(agentStoreState);
|
|
125
|
+
log('slug from agentStore: %s (agentId: %s)', slug, agentId);
|
|
114
126
|
|
|
115
|
-
// If not found in agent store, check if this is a supervisor agent
|
|
116
|
-
//
|
|
117
|
-
if (!slug) {
|
|
127
|
+
// If not found in agent store, check if this is a supervisor agent using groupId
|
|
128
|
+
// This is more reliable than iterating all groups to find a match
|
|
129
|
+
if (!slug && ctx.groupId) {
|
|
118
130
|
const groupStoreState = getChatGroupStoreState();
|
|
119
|
-
const group = agentGroupByIdSelectors.
|
|
120
|
-
|
|
121
|
-
|
|
131
|
+
const group = agentGroupByIdSelectors.groupById(ctx.groupId)(groupStoreState);
|
|
132
|
+
|
|
133
|
+
log(
|
|
134
|
+
'checking supervisor via groupId %s: group=%o',
|
|
135
|
+
ctx.groupId,
|
|
136
|
+
group
|
|
137
|
+
? {
|
|
138
|
+
groupId: group.id,
|
|
139
|
+
supervisorAgentId: group.supervisorAgentId,
|
|
140
|
+
title: group.title,
|
|
141
|
+
}
|
|
142
|
+
: null,
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
// Check if this agent is the supervisor of the specified group
|
|
146
|
+
if (group?.supervisorAgentId === agentId) {
|
|
122
147
|
slug = BUILTIN_AGENT_SLUGS.groupSupervisor;
|
|
148
|
+
log(
|
|
149
|
+
'agentId %s identified as group supervisor for group %s, assigned slug: %s',
|
|
150
|
+
agentId,
|
|
151
|
+
ctx.groupId,
|
|
152
|
+
slug,
|
|
153
|
+
);
|
|
123
154
|
}
|
|
124
155
|
}
|
|
125
156
|
|
|
126
157
|
if (!slug) {
|
|
158
|
+
log('agentId %s is not a builtin agent (no slug found)', agentId);
|
|
127
159
|
// Regular agent - use provided plugins if available, fallback to agent's plugins
|
|
128
160
|
const finalPlugins = plugins && plugins.length > 0 ? plugins : basePlugins;
|
|
129
161
|
|
|
@@ -181,20 +213,49 @@ export const resolveAgentConfig = (ctx: AgentConfigResolverContext): ResolvedAge
|
|
|
181
213
|
}
|
|
182
214
|
|
|
183
215
|
// Build groupSupervisorContext if this is a group-supervisor agent
|
|
216
|
+
// Use groupId for direct lookup instead of reverse lookup by supervisorAgentId
|
|
184
217
|
let groupSupervisorContext;
|
|
185
|
-
if (slug === BUILTIN_AGENT_SLUGS.groupSupervisor) {
|
|
218
|
+
if (slug === BUILTIN_AGENT_SLUGS.groupSupervisor && ctx.groupId) {
|
|
219
|
+
log('building groupSupervisorContext for agentId: %s, groupId: %s', agentId, ctx.groupId);
|
|
186
220
|
const groupStoreState = getChatGroupStoreState();
|
|
187
|
-
//
|
|
188
|
-
const group =
|
|
221
|
+
// Direct lookup using groupId
|
|
222
|
+
const group = agentGroupByIdSelectors.groupById(ctx.groupId)(groupStoreState);
|
|
223
|
+
|
|
224
|
+
log(
|
|
225
|
+
'groupById result for %s: %o',
|
|
226
|
+
ctx.groupId,
|
|
227
|
+
group
|
|
228
|
+
? {
|
|
229
|
+
agentsCount: group.agents?.length,
|
|
230
|
+
groupId: group.id,
|
|
231
|
+
supervisorAgentId: group.supervisorAgentId,
|
|
232
|
+
title: group.title,
|
|
233
|
+
}
|
|
234
|
+
: null,
|
|
235
|
+
);
|
|
189
236
|
|
|
190
237
|
if (group) {
|
|
191
238
|
const groupMembers = agentGroupSelectors.getGroupMembers(group.id)(groupStoreState);
|
|
239
|
+
log(
|
|
240
|
+
'groupMembers for groupId %s: %o',
|
|
241
|
+
group.id,
|
|
242
|
+
groupMembers.map((m) => ({ id: m.id, isSupervisor: m.isSupervisor, title: m.title })),
|
|
243
|
+
);
|
|
244
|
+
|
|
192
245
|
groupSupervisorContext = {
|
|
193
246
|
availableAgents: groupMembers.map((agent) => ({ id: agent.id, title: agent.title })),
|
|
194
247
|
groupId: group.id,
|
|
195
248
|
groupTitle: group.title || 'Group Chat',
|
|
196
249
|
systemPrompt: agentConfig.systemRole,
|
|
197
250
|
};
|
|
251
|
+
log('groupSupervisorContext built: %o', {
|
|
252
|
+
availableAgentsCount: groupSupervisorContext.availableAgents.length,
|
|
253
|
+
groupId: groupSupervisorContext.groupId,
|
|
254
|
+
groupTitle: groupSupervisorContext.groupTitle,
|
|
255
|
+
hasSystemPrompt: !!groupSupervisorContext.systemPrompt,
|
|
256
|
+
});
|
|
257
|
+
} else {
|
|
258
|
+
log('WARNING: group not found for groupId: %s', ctx.groupId);
|
|
198
259
|
}
|
|
199
260
|
}
|
|
200
261
|
|
|
@@ -258,6 +319,12 @@ export const resolveAgentConfig = (ctx: AgentConfigResolverContext): ResolvedAge
|
|
|
258
319
|
// Apply params adjustments based on chatConfig
|
|
259
320
|
const finalAgentConfig = applyParamsFromChatConfig(resolvedAgentConfig, resolvedChatConfig);
|
|
260
321
|
|
|
322
|
+
log('resolveAgentConfig completed for agentId: %s, result: %o', agentId, {
|
|
323
|
+
isBuiltinAgent: true,
|
|
324
|
+
pluginsCount: finalPlugins.length,
|
|
325
|
+
slug,
|
|
326
|
+
});
|
|
327
|
+
|
|
261
328
|
return {
|
|
262
329
|
agentConfig: finalAgentConfig,
|
|
263
330
|
chatConfig: resolvedChatConfig,
|