@lobehub/lobehub 2.0.0-next.335 → 2.0.0-next.336
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +33 -0
- package/changelog/v1.json +12 -0
- package/package.json +1 -1
- package/packages/builtin-tool-group-management/src/manifest.ts +54 -53
- package/packages/builtin-tool-group-management/src/systemRole.ts +43 -111
- package/packages/context-engine/src/engine/tools/ToolArgumentsRepairer.ts +129 -0
- package/packages/context-engine/src/engine/tools/__tests__/ToolArgumentsRepairer.test.ts +186 -0
- package/packages/context-engine/src/engine/tools/index.ts +3 -0
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/tasks/index.ts +2 -0
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/tasks/with-assistant-group.json +156 -0
- package/packages/conversation-flow/src/__tests__/parse.test.ts +22 -0
- package/packages/conversation-flow/src/transformation/FlatListBuilder.ts +88 -11
- package/packages/types/src/openai/chat.ts +0 -4
- package/src/app/[variants]/(main)/community/(detail)/user/features/DetailProvider.tsx +5 -1
- package/src/app/[variants]/(main)/community/(detail)/user/features/UserAgentCard.tsx +8 -8
- package/src/app/[variants]/(main)/community/(detail)/user/features/UserGroupCard.tsx +142 -15
- package/src/app/[variants]/(main)/community/(detail)/user/features/useUserDetail.ts +45 -20
- package/src/server/routers/lambda/market/agentGroup.ts +179 -1
- package/src/server/services/discover/index.ts +4 -0
- package/src/services/chat/chat.test.ts +109 -104
- package/src/services/chat/index.ts +13 -32
- package/src/services/chat/mecha/agentConfigResolver.test.ts +113 -0
- package/src/services/chat/mecha/agentConfigResolver.ts +15 -5
- package/src/services/marketApi.ts +14 -0
- package/src/store/chat/agents/__tests__/createAgentExecutors/helpers/testExecutor.ts +13 -0
- package/src/store/chat/agents/createAgentExecutors.ts +13 -1
- package/src/store/chat/slices/aiChat/actions/__tests__/conversationControl.test.ts +5 -1
- package/src/store/chat/slices/aiChat/actions/__tests__/fixtures.ts +14 -0
- package/src/store/chat/slices/aiChat/actions/__tests__/streamingExecutor.test.ts +131 -7
- package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +61 -62
- package/src/store/chat/slices/plugin/action.test.ts +71 -0
- package/src/store/chat/slices/plugin/actions/internals.ts +14 -5
|
@@ -15,6 +15,35 @@ import { aiModelSelectors } from '@/store/aiInfra';
|
|
|
15
15
|
import { useToolStore } from '@/store/tool';
|
|
16
16
|
|
|
17
17
|
import { chatService } from './index';
|
|
18
|
+
import type { ResolvedAgentConfig } from './mecha';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Default mock resolvedAgentConfig for tests
|
|
22
|
+
*/
|
|
23
|
+
const createMockResolvedConfig = (overrides?: {
|
|
24
|
+
agentConfig?: Partial<ResolvedAgentConfig['agentConfig']>;
|
|
25
|
+
chatConfig?: Partial<ResolvedAgentConfig['chatConfig']>;
|
|
26
|
+
plugins?: string[];
|
|
27
|
+
isBuiltinAgent?: boolean;
|
|
28
|
+
}): ResolvedAgentConfig =>
|
|
29
|
+
({
|
|
30
|
+
agentConfig: {
|
|
31
|
+
model: DEFAULT_AGENT_CONFIG.model,
|
|
32
|
+
provider: 'openai',
|
|
33
|
+
systemRole: '',
|
|
34
|
+
chatConfig: {},
|
|
35
|
+
params: {},
|
|
36
|
+
tts: {},
|
|
37
|
+
...overrides?.agentConfig,
|
|
38
|
+
},
|
|
39
|
+
chatConfig: {
|
|
40
|
+
searchMode: 'off',
|
|
41
|
+
autoCreateTopicThreshold: 2,
|
|
42
|
+
...overrides?.chatConfig,
|
|
43
|
+
},
|
|
44
|
+
isBuiltinAgent: overrides?.isBuiltinAgent ?? false,
|
|
45
|
+
plugins: overrides?.plugins ?? [],
|
|
46
|
+
}) as ResolvedAgentConfig;
|
|
18
47
|
|
|
19
48
|
// Mocking external dependencies
|
|
20
49
|
vi.mock('i18next', () => ({
|
|
@@ -109,7 +138,10 @@ describe('ChatService', () => {
|
|
|
109
138
|
],
|
|
110
139
|
});
|
|
111
140
|
});
|
|
112
|
-
await chatService.createAssistantMessage({
|
|
141
|
+
await chatService.createAssistantMessage({
|
|
142
|
+
messages,
|
|
143
|
+
resolvedAgentConfig: createMockResolvedConfig({ plugins: enabledPlugins }),
|
|
144
|
+
});
|
|
113
145
|
|
|
114
146
|
expect(getChatCompletionSpy).toHaveBeenCalledWith(
|
|
115
147
|
expect.objectContaining({
|
|
@@ -136,21 +168,14 @@ describe('ChatService', () => {
|
|
|
136
168
|
vi.spyOn(aiModelSelectors, 'isModelHasExtendParams').mockReturnValue(() => true);
|
|
137
169
|
vi.spyOn(aiModelSelectors, 'modelExtendParams').mockReturnValue(() => ['enableReasoning']);
|
|
138
170
|
|
|
139
|
-
// Mock agent chat config with reasoning enabled
|
|
140
|
-
vi.spyOn(chatConfigByIdSelectors, 'getChatConfigById').mockReturnValue(
|
|
141
|
-
() =>
|
|
142
|
-
({
|
|
143
|
-
enableReasoning: true,
|
|
144
|
-
reasoningBudgetToken: 2048,
|
|
145
|
-
searchMode: 'off',
|
|
146
|
-
}) as any,
|
|
147
|
-
);
|
|
148
|
-
|
|
149
171
|
await chatService.createAssistantMessage({
|
|
150
172
|
messages,
|
|
151
173
|
model: 'deepseek-reasoner',
|
|
152
174
|
provider: 'deepseek',
|
|
153
|
-
|
|
175
|
+
resolvedAgentConfig: createMockResolvedConfig({
|
|
176
|
+
agentConfig: { model: 'deepseek-reasoner', provider: 'deepseek' },
|
|
177
|
+
chatConfig: { enableReasoning: true, reasoningBudgetToken: 2048 },
|
|
178
|
+
}),
|
|
154
179
|
});
|
|
155
180
|
|
|
156
181
|
expect(getChatCompletionSpy).toHaveBeenCalledWith(
|
|
@@ -172,20 +197,14 @@ describe('ChatService', () => {
|
|
|
172
197
|
vi.spyOn(aiModelSelectors, 'isModelHasExtendParams').mockReturnValue(() => true);
|
|
173
198
|
vi.spyOn(aiModelSelectors, 'modelExtendParams').mockReturnValue(() => ['enableReasoning']);
|
|
174
199
|
|
|
175
|
-
// Mock agent chat config with reasoning disabled
|
|
176
|
-
vi.spyOn(chatConfigByIdSelectors, 'getChatConfigById').mockReturnValue(
|
|
177
|
-
() =>
|
|
178
|
-
({
|
|
179
|
-
enableReasoning: false,
|
|
180
|
-
searchMode: 'off',
|
|
181
|
-
}) as any,
|
|
182
|
-
);
|
|
183
|
-
|
|
184
200
|
await chatService.createAssistantMessage({
|
|
185
201
|
messages,
|
|
186
202
|
model: 'deepseek-reasoner',
|
|
187
203
|
provider: 'deepseek',
|
|
188
|
-
|
|
204
|
+
resolvedAgentConfig: createMockResolvedConfig({
|
|
205
|
+
agentConfig: { model: 'deepseek-reasoner', provider: 'deepseek' },
|
|
206
|
+
chatConfig: { enableReasoning: false },
|
|
207
|
+
}),
|
|
189
208
|
});
|
|
190
209
|
|
|
191
210
|
expect(getChatCompletionSpy).toHaveBeenCalledWith(
|
|
@@ -207,21 +226,15 @@ describe('ChatService', () => {
|
|
|
207
226
|
vi.spyOn(aiModelSelectors, 'isModelHasExtendParams').mockReturnValue(() => true);
|
|
208
227
|
vi.spyOn(aiModelSelectors, 'modelExtendParams').mockReturnValue(() => ['enableReasoning']);
|
|
209
228
|
|
|
210
|
-
// Mock agent chat config with reasoning enabled but no custom budget
|
|
211
|
-
vi.spyOn(chatConfigByIdSelectors, 'getChatConfigById').mockReturnValue(
|
|
212
|
-
() =>
|
|
213
|
-
({
|
|
214
|
-
enableReasoning: true,
|
|
215
|
-
// reasoningBudgetToken is undefined
|
|
216
|
-
searchMode: 'off',
|
|
217
|
-
}) as any,
|
|
218
|
-
);
|
|
219
|
-
|
|
220
229
|
await chatService.createAssistantMessage({
|
|
221
230
|
messages,
|
|
222
231
|
model: 'deepseek-reasoner',
|
|
223
232
|
provider: 'deepseek',
|
|
224
|
-
|
|
233
|
+
resolvedAgentConfig: createMockResolvedConfig({
|
|
234
|
+
agentConfig: { model: 'deepseek-reasoner', provider: 'deepseek' },
|
|
235
|
+
// enableReasoning is true, but reasoningBudgetToken is undefined
|
|
236
|
+
chatConfig: { enableReasoning: true },
|
|
237
|
+
}),
|
|
225
238
|
});
|
|
226
239
|
|
|
227
240
|
expect(getChatCompletionSpy).toHaveBeenCalledWith(
|
|
@@ -243,20 +256,14 @@ describe('ChatService', () => {
|
|
|
243
256
|
vi.spyOn(aiModelSelectors, 'isModelHasExtendParams').mockReturnValue(() => true);
|
|
244
257
|
vi.spyOn(aiModelSelectors, 'modelExtendParams').mockReturnValue(() => ['reasoningEffort']);
|
|
245
258
|
|
|
246
|
-
// Mock agent chat config with reasoning effort set
|
|
247
|
-
vi.spyOn(chatConfigByIdSelectors, 'getChatConfigById').mockReturnValue(
|
|
248
|
-
() =>
|
|
249
|
-
({
|
|
250
|
-
reasoningEffort: 'high',
|
|
251
|
-
searchMode: 'off',
|
|
252
|
-
}) as any,
|
|
253
|
-
);
|
|
254
|
-
|
|
255
259
|
await chatService.createAssistantMessage({
|
|
256
260
|
messages,
|
|
257
261
|
model: 'test-model',
|
|
258
262
|
provider: 'test-provider',
|
|
259
|
-
|
|
263
|
+
resolvedAgentConfig: createMockResolvedConfig({
|
|
264
|
+
agentConfig: { model: 'test-model', provider: 'test-provider' },
|
|
265
|
+
chatConfig: { reasoningEffort: 'high' },
|
|
266
|
+
}),
|
|
260
267
|
});
|
|
261
268
|
|
|
262
269
|
expect(getChatCompletionSpy).toHaveBeenCalledWith(
|
|
@@ -275,20 +282,14 @@ describe('ChatService', () => {
|
|
|
275
282
|
vi.spyOn(aiModelSelectors, 'isModelHasExtendParams').mockReturnValue(() => true);
|
|
276
283
|
vi.spyOn(aiModelSelectors, 'modelExtendParams').mockReturnValue(() => ['thinkingBudget']);
|
|
277
284
|
|
|
278
|
-
// Mock agent chat config with thinking budget set
|
|
279
|
-
vi.spyOn(chatConfigByIdSelectors, 'getChatConfigById').mockReturnValue(
|
|
280
|
-
() =>
|
|
281
|
-
({
|
|
282
|
-
thinkingBudget: 5000,
|
|
283
|
-
searchMode: 'off',
|
|
284
|
-
}) as any,
|
|
285
|
-
);
|
|
286
|
-
|
|
287
285
|
await chatService.createAssistantMessage({
|
|
288
286
|
messages,
|
|
289
287
|
model: 'test-model',
|
|
290
288
|
provider: 'test-provider',
|
|
291
|
-
|
|
289
|
+
resolvedAgentConfig: createMockResolvedConfig({
|
|
290
|
+
agentConfig: { model: 'test-model', provider: 'test-provider' },
|
|
291
|
+
chatConfig: { thinkingBudget: 5000 },
|
|
292
|
+
}),
|
|
292
293
|
});
|
|
293
294
|
|
|
294
295
|
expect(getChatCompletionSpy).toHaveBeenCalledWith(
|
|
@@ -329,9 +330,11 @@ describe('ChatService', () => {
|
|
|
329
330
|
const getChatCompletionSpy = vi.spyOn(chatService, 'getChatCompletion');
|
|
330
331
|
await chatService.createAssistantMessage({
|
|
331
332
|
messages,
|
|
332
|
-
plugins: [],
|
|
333
333
|
model: 'gpt-4-vision-preview',
|
|
334
334
|
provider: 'openai',
|
|
335
|
+
resolvedAgentConfig: createMockResolvedConfig({
|
|
336
|
+
agentConfig: { model: 'gpt-4-vision-preview', provider: 'openai' },
|
|
337
|
+
}),
|
|
335
338
|
});
|
|
336
339
|
|
|
337
340
|
expect(getChatCompletionSpy).toHaveBeenCalledWith(
|
|
@@ -378,7 +381,10 @@ describe('ChatService', () => {
|
|
|
378
381
|
] as UIChatMessage[];
|
|
379
382
|
|
|
380
383
|
const getChatCompletionSpy = vi.spyOn(chatService, 'getChatCompletion');
|
|
381
|
-
await chatService.createAssistantMessage({
|
|
384
|
+
await chatService.createAssistantMessage({
|
|
385
|
+
messages,
|
|
386
|
+
resolvedAgentConfig: createMockResolvedConfig(),
|
|
387
|
+
});
|
|
382
388
|
|
|
383
389
|
expect(getChatCompletionSpy).toHaveBeenCalledWith(
|
|
384
390
|
{
|
|
@@ -435,8 +441,10 @@ describe('ChatService', () => {
|
|
|
435
441
|
|
|
436
442
|
await chatService.createAssistantMessage({
|
|
437
443
|
messages,
|
|
438
|
-
plugins: [],
|
|
439
444
|
model: 'gpt-4-vision-preview',
|
|
445
|
+
resolvedAgentConfig: createMockResolvedConfig({
|
|
446
|
+
agentConfig: { model: 'gpt-4-vision-preview' },
|
|
447
|
+
}),
|
|
440
448
|
});
|
|
441
449
|
|
|
442
450
|
// Verify the utility functions were called
|
|
@@ -525,8 +533,10 @@ describe('ChatService', () => {
|
|
|
525
533
|
|
|
526
534
|
await chatService.createAssistantMessage({
|
|
527
535
|
messages,
|
|
528
|
-
plugins: [],
|
|
529
536
|
model: 'gpt-4-vision-preview',
|
|
537
|
+
resolvedAgentConfig: createMockResolvedConfig({
|
|
538
|
+
agentConfig: { model: 'gpt-4-vision-preview' },
|
|
539
|
+
}),
|
|
530
540
|
});
|
|
531
541
|
|
|
532
542
|
// Verify the utility functions were called
|
|
@@ -630,8 +640,10 @@ describe('ChatService', () => {
|
|
|
630
640
|
|
|
631
641
|
await chatService.createAssistantMessage({
|
|
632
642
|
messages,
|
|
633
|
-
plugins: [],
|
|
634
643
|
model: 'gpt-4-vision-preview',
|
|
644
|
+
resolvedAgentConfig: createMockResolvedConfig({
|
|
645
|
+
agentConfig: { model: 'gpt-4-vision-preview' },
|
|
646
|
+
}),
|
|
635
647
|
});
|
|
636
648
|
|
|
637
649
|
// Verify isDesktopLocalStaticServerUrl was called for each image
|
|
@@ -730,7 +742,10 @@ describe('ChatService', () => {
|
|
|
730
742
|
messages,
|
|
731
743
|
model: 'gpt-3.5-turbo-1106',
|
|
732
744
|
top_p: 1,
|
|
733
|
-
|
|
745
|
+
resolvedAgentConfig: createMockResolvedConfig({
|
|
746
|
+
agentConfig: { model: 'gpt-3.5-turbo-1106' },
|
|
747
|
+
plugins: ['seo'],
|
|
748
|
+
}),
|
|
734
749
|
});
|
|
735
750
|
|
|
736
751
|
expect(getChatCompletionSpy).toHaveBeenCalledWith(
|
|
@@ -832,7 +847,10 @@ describe('ChatService', () => {
|
|
|
832
847
|
messages,
|
|
833
848
|
model: 'gpt-3.5-turbo-1106',
|
|
834
849
|
top_p: 1,
|
|
835
|
-
|
|
850
|
+
resolvedAgentConfig: createMockResolvedConfig({
|
|
851
|
+
agentConfig: { model: 'gpt-3.5-turbo-1106' },
|
|
852
|
+
plugins: ['seo'],
|
|
853
|
+
}),
|
|
836
854
|
});
|
|
837
855
|
|
|
838
856
|
expect(getChatCompletionSpy).toHaveBeenCalledWith(
|
|
@@ -888,7 +906,9 @@ describe('ChatService', () => {
|
|
|
888
906
|
messages,
|
|
889
907
|
model: 'gpt-3.5-turbo-1106',
|
|
890
908
|
top_p: 1,
|
|
891
|
-
|
|
909
|
+
resolvedAgentConfig: createMockResolvedConfig({
|
|
910
|
+
agentConfig: { model: 'gpt-3.5-turbo-1106' },
|
|
911
|
+
}),
|
|
892
912
|
});
|
|
893
913
|
|
|
894
914
|
expect(getChatCompletionSpy).toHaveBeenCalledWith(
|
|
@@ -949,7 +969,10 @@ describe('ChatService', () => {
|
|
|
949
969
|
mockToolsEngine as any,
|
|
950
970
|
);
|
|
951
971
|
|
|
952
|
-
await chatService.createAssistantMessage({
|
|
972
|
+
await chatService.createAssistantMessage({
|
|
973
|
+
messages,
|
|
974
|
+
resolvedAgentConfig: createMockResolvedConfig(),
|
|
975
|
+
});
|
|
953
976
|
|
|
954
977
|
// Verify tools were passed to getChatCompletion
|
|
955
978
|
expect(getChatCompletionSpy).toHaveBeenCalledWith(
|
|
@@ -1003,7 +1026,10 @@ describe('ChatService', () => {
|
|
|
1003
1026
|
mockToolsEngine as any,
|
|
1004
1027
|
);
|
|
1005
1028
|
|
|
1006
|
-
await chatService.createAssistantMessage({
|
|
1029
|
+
await chatService.createAssistantMessage({
|
|
1030
|
+
messages,
|
|
1031
|
+
resolvedAgentConfig: createMockResolvedConfig(),
|
|
1032
|
+
});
|
|
1007
1033
|
|
|
1008
1034
|
// Verify enabledSearch was set to true
|
|
1009
1035
|
expect(getChatCompletionSpy).toHaveBeenCalledWith(
|
|
@@ -1051,7 +1077,10 @@ describe('ChatService', () => {
|
|
|
1051
1077
|
mockToolsEngine as any,
|
|
1052
1078
|
);
|
|
1053
1079
|
|
|
1054
|
-
await chatService.createAssistantMessage({
|
|
1080
|
+
await chatService.createAssistantMessage({
|
|
1081
|
+
messages,
|
|
1082
|
+
resolvedAgentConfig: createMockResolvedConfig(),
|
|
1083
|
+
});
|
|
1055
1084
|
|
|
1056
1085
|
// Verify enabledSearch was not set
|
|
1057
1086
|
expect(getChatCompletionSpy).toHaveBeenCalledWith(
|
|
@@ -1342,20 +1371,14 @@ describe('ChatService private methods', () => {
|
|
|
1342
1371
|
'disableContextCaching',
|
|
1343
1372
|
]);
|
|
1344
1373
|
|
|
1345
|
-
// Mock agent chat config with context caching disabled
|
|
1346
|
-
vi.spyOn(chatConfigByIdSelectors, 'getChatConfigById').mockReturnValue(
|
|
1347
|
-
() =>
|
|
1348
|
-
({
|
|
1349
|
-
disableContextCaching: true,
|
|
1350
|
-
searchMode: 'off',
|
|
1351
|
-
}) as any,
|
|
1352
|
-
);
|
|
1353
|
-
|
|
1354
1374
|
await chatService.createAssistantMessage({
|
|
1355
1375
|
messages,
|
|
1356
1376
|
model: 'test-model',
|
|
1357
1377
|
provider: 'test-provider',
|
|
1358
|
-
|
|
1378
|
+
resolvedAgentConfig: createMockResolvedConfig({
|
|
1379
|
+
agentConfig: { model: 'test-model', provider: 'test-provider' },
|
|
1380
|
+
chatConfig: { disableContextCaching: true },
|
|
1381
|
+
}),
|
|
1359
1382
|
});
|
|
1360
1383
|
|
|
1361
1384
|
expect(getChatCompletionSpy).toHaveBeenCalledWith(
|
|
@@ -1378,20 +1401,14 @@ describe('ChatService private methods', () => {
|
|
|
1378
1401
|
'disableContextCaching',
|
|
1379
1402
|
]);
|
|
1380
1403
|
|
|
1381
|
-
// Mock agent chat config with context caching enabled (default)
|
|
1382
|
-
vi.spyOn(chatConfigByIdSelectors, 'getChatConfigById').mockReturnValue(
|
|
1383
|
-
() =>
|
|
1384
|
-
({
|
|
1385
|
-
disableContextCaching: false,
|
|
1386
|
-
searchMode: 'off',
|
|
1387
|
-
}) as any,
|
|
1388
|
-
);
|
|
1389
|
-
|
|
1390
1404
|
await chatService.createAssistantMessage({
|
|
1391
1405
|
messages,
|
|
1392
1406
|
model: 'test-model',
|
|
1393
1407
|
provider: 'test-provider',
|
|
1394
|
-
|
|
1408
|
+
resolvedAgentConfig: createMockResolvedConfig({
|
|
1409
|
+
agentConfig: { model: 'test-model', provider: 'test-provider' },
|
|
1410
|
+
chatConfig: { disableContextCaching: false },
|
|
1411
|
+
}),
|
|
1395
1412
|
});
|
|
1396
1413
|
|
|
1397
1414
|
// enabledContextCaching should not be present in the call
|
|
@@ -1407,20 +1424,14 @@ describe('ChatService private methods', () => {
|
|
|
1407
1424
|
vi.spyOn(aiModelSelectors, 'isModelHasExtendParams').mockReturnValue(() => true);
|
|
1408
1425
|
vi.spyOn(aiModelSelectors, 'modelExtendParams').mockReturnValue(() => ['reasoningEffort']);
|
|
1409
1426
|
|
|
1410
|
-
// Mock agent chat config with reasoning effort set
|
|
1411
|
-
vi.spyOn(chatConfigByIdSelectors, 'getChatConfigById').mockReturnValue(
|
|
1412
|
-
() =>
|
|
1413
|
-
({
|
|
1414
|
-
reasoningEffort: 'high',
|
|
1415
|
-
searchMode: 'off',
|
|
1416
|
-
}) as any,
|
|
1417
|
-
);
|
|
1418
|
-
|
|
1419
1427
|
await chatService.createAssistantMessage({
|
|
1420
1428
|
messages,
|
|
1421
1429
|
model: 'test-model',
|
|
1422
1430
|
provider: 'test-provider',
|
|
1423
|
-
|
|
1431
|
+
resolvedAgentConfig: createMockResolvedConfig({
|
|
1432
|
+
agentConfig: { model: 'test-model', provider: 'test-provider' },
|
|
1433
|
+
chatConfig: { reasoningEffort: 'high' },
|
|
1434
|
+
}),
|
|
1424
1435
|
});
|
|
1425
1436
|
|
|
1426
1437
|
expect(getChatCompletionSpy).toHaveBeenCalledWith(
|
|
@@ -1439,20 +1450,14 @@ describe('ChatService private methods', () => {
|
|
|
1439
1450
|
vi.spyOn(aiModelSelectors, 'isModelHasExtendParams').mockReturnValue(() => true);
|
|
1440
1451
|
vi.spyOn(aiModelSelectors, 'modelExtendParams').mockReturnValue(() => ['thinkingBudget']);
|
|
1441
1452
|
|
|
1442
|
-
// Mock agent chat config with thinking budget set
|
|
1443
|
-
vi.spyOn(chatConfigByIdSelectors, 'getChatConfigById').mockReturnValue(
|
|
1444
|
-
() =>
|
|
1445
|
-
({
|
|
1446
|
-
thinkingBudget: 5000,
|
|
1447
|
-
searchMode: 'off',
|
|
1448
|
-
}) as any,
|
|
1449
|
-
);
|
|
1450
|
-
|
|
1451
1453
|
await chatService.createAssistantMessage({
|
|
1452
1454
|
messages,
|
|
1453
1455
|
model: 'test-model',
|
|
1454
1456
|
provider: 'test-provider',
|
|
1455
|
-
|
|
1457
|
+
resolvedAgentConfig: createMockResolvedConfig({
|
|
1458
|
+
agentConfig: { model: 'test-model', provider: 'test-provider' },
|
|
1459
|
+
chatConfig: { thinkingBudget: 5000 },
|
|
1460
|
+
}),
|
|
1456
1461
|
});
|
|
1457
1462
|
|
|
1458
1463
|
expect(getChatCompletionSpy).toHaveBeenCalledWith(
|
|
@@ -27,7 +27,7 @@ import { ModelProvider } from 'model-bank';
|
|
|
27
27
|
import { DEFAULT_AGENT_CONFIG } from '@/const/settings';
|
|
28
28
|
import { enableAuth } from '@/envs/auth';
|
|
29
29
|
import { getSearchConfig } from '@/helpers/getSearchConfig';
|
|
30
|
-
import { createAgentToolsEngine
|
|
30
|
+
import { createAgentToolsEngine } from '@/helpers/toolEngineering';
|
|
31
31
|
import { getAgentStoreState } from '@/store/agent';
|
|
32
32
|
import {
|
|
33
33
|
agentByIdSelectors,
|
|
@@ -58,10 +58,10 @@ import { createHeaderWithAuth } from '../_auth';
|
|
|
58
58
|
import { API_ENDPOINTS } from '../_url';
|
|
59
59
|
import { findDeploymentName, isEnableFetchOnClient, resolveRuntimeProvider } from './helper';
|
|
60
60
|
import {
|
|
61
|
+
type ResolvedAgentConfig,
|
|
61
62
|
contextEngineering,
|
|
62
63
|
getTargetAgentId,
|
|
63
64
|
initializeWithClientStore,
|
|
64
|
-
resolveAgentConfig,
|
|
65
65
|
resolveModelExtendParams,
|
|
66
66
|
} from './mecha';
|
|
67
67
|
import { type FetchOptions } from './types';
|
|
@@ -70,6 +70,11 @@ interface GetChatCompletionPayload extends Partial<Omit<ChatStreamPayload, 'mess
|
|
|
70
70
|
agentId?: string;
|
|
71
71
|
groupId?: string;
|
|
72
72
|
messages: UIChatMessage[];
|
|
73
|
+
/**
|
|
74
|
+
* Pre-resolved agent config from AgentRuntime layer.
|
|
75
|
+
* Required to ensure config consistency and proper isSubTask filtering.
|
|
76
|
+
*/
|
|
77
|
+
resolvedAgentConfig: ResolvedAgentConfig;
|
|
73
78
|
scope?: MessageMapScope;
|
|
74
79
|
topicId?: string;
|
|
75
80
|
}
|
|
@@ -107,12 +112,11 @@ interface CreateAssistantMessageStream extends FetchSSEOptions {
|
|
|
107
112
|
class ChatService {
|
|
108
113
|
createAssistantMessage = async (
|
|
109
114
|
{
|
|
110
|
-
plugins: enabledPlugins,
|
|
111
115
|
messages,
|
|
112
116
|
agentId,
|
|
113
117
|
groupId,
|
|
114
|
-
scope,
|
|
115
118
|
topicId,
|
|
119
|
+
resolvedAgentConfig,
|
|
116
120
|
...params
|
|
117
121
|
}: GetChatCompletionPayload,
|
|
118
122
|
options?: FetchOptions,
|
|
@@ -126,24 +130,13 @@ class ChatService {
|
|
|
126
130
|
params,
|
|
127
131
|
);
|
|
128
132
|
|
|
129
|
-
// =================== 1.
|
|
133
|
+
// =================== 1. use pre-resolved agent config =================== //
|
|
134
|
+
// Config is resolved in AgentRuntime layer (internal_createAgentState)
|
|
135
|
+
// which handles isSubTask filtering and other runtime modifications
|
|
130
136
|
|
|
131
137
|
const targetAgentId = getTargetAgentId(agentId);
|
|
132
138
|
|
|
133
|
-
|
|
134
|
-
// plugins is already merged (runtime plugins > agent config plugins)
|
|
135
|
-
const {
|
|
136
|
-
agentConfig,
|
|
137
|
-
chatConfig,
|
|
138
|
-
plugins: pluginIds,
|
|
139
|
-
} = resolveAgentConfig({
|
|
140
|
-
agentId: targetAgentId,
|
|
141
|
-
groupId, // Pass groupId for supervisor detection
|
|
142
|
-
model: payload.model,
|
|
143
|
-
plugins: enabledPlugins,
|
|
144
|
-
provider: payload.provider,
|
|
145
|
-
scope, // Pass scope to preserve page-agent injection
|
|
146
|
-
});
|
|
139
|
+
const { agentConfig, chatConfig, plugins: pluginIds } = resolvedAgentConfig;
|
|
147
140
|
|
|
148
141
|
// Get search config with agentId for agent-specific settings
|
|
149
142
|
const searchConfig = getSearchConfig(payload.model, payload.provider!, targetAgentId);
|
|
@@ -495,26 +488,14 @@ class ChatService {
|
|
|
495
488
|
onLoadingChange?.(true);
|
|
496
489
|
|
|
497
490
|
try {
|
|
498
|
-
// Use simple tools engine without complex search logic
|
|
499
|
-
const toolsEngine = createToolsEngine();
|
|
500
|
-
const { tools, enabledManifests } = toolsEngine.generateToolsDetailed({
|
|
501
|
-
model: params.model!,
|
|
502
|
-
provider: params.provider!,
|
|
503
|
-
toolIds: params.plugins,
|
|
504
|
-
});
|
|
505
|
-
|
|
506
491
|
const llmMessages = await contextEngineering({
|
|
507
|
-
manifests: enabledManifests,
|
|
508
492
|
messages: params.messages as any,
|
|
509
493
|
model: params.model!,
|
|
510
494
|
provider: params.provider!,
|
|
511
|
-
tools: params.plugins,
|
|
512
495
|
});
|
|
513
496
|
|
|
514
|
-
// remove plugins
|
|
515
|
-
delete params.plugins;
|
|
516
497
|
await this.getChatCompletion(
|
|
517
|
-
{ ...params, messages: llmMessages
|
|
498
|
+
{ ...params, messages: llmMessages },
|
|
518
499
|
{
|
|
519
500
|
onErrorHandle: (error) => {
|
|
520
501
|
errorHandle(new Error(error.message), error);
|
|
@@ -800,4 +800,117 @@ describe('resolveAgentConfig', () => {
|
|
|
800
800
|
expect(result.agentConfig.systemRole).toBe('Supervisor system role');
|
|
801
801
|
});
|
|
802
802
|
});
|
|
803
|
+
|
|
804
|
+
describe('sub-task filtering (isSubTask)', () => {
|
|
805
|
+
beforeEach(() => {
|
|
806
|
+
vi.spyOn(agentSelectors.agentSelectors, 'getAgentSlugById').mockReturnValue(() => undefined);
|
|
807
|
+
});
|
|
808
|
+
|
|
809
|
+
it('should filter out lobe-gtd when isSubTask is true for regular agent', () => {
|
|
810
|
+
vi.spyOn(agentSelectors.agentSelectors, 'getAgentConfigById').mockReturnValue(
|
|
811
|
+
() =>
|
|
812
|
+
({
|
|
813
|
+
...mockAgentConfig,
|
|
814
|
+
plugins: ['lobe-gtd', 'plugin-a', 'plugin-b'],
|
|
815
|
+
}) as any,
|
|
816
|
+
);
|
|
817
|
+
|
|
818
|
+
const result = resolveAgentConfig({
|
|
819
|
+
agentId: 'test-agent',
|
|
820
|
+
isSubTask: true,
|
|
821
|
+
});
|
|
822
|
+
|
|
823
|
+
expect(result.plugins).not.toContain('lobe-gtd');
|
|
824
|
+
expect(result.plugins).toEqual(['plugin-a', 'plugin-b']);
|
|
825
|
+
});
|
|
826
|
+
|
|
827
|
+
it('should keep lobe-gtd when isSubTask is false', () => {
|
|
828
|
+
vi.spyOn(agentSelectors.agentSelectors, 'getAgentConfigById').mockReturnValue(
|
|
829
|
+
() =>
|
|
830
|
+
({
|
|
831
|
+
...mockAgentConfig,
|
|
832
|
+
plugins: ['lobe-gtd', 'plugin-a', 'plugin-b'],
|
|
833
|
+
}) as any,
|
|
834
|
+
);
|
|
835
|
+
|
|
836
|
+
const result = resolveAgentConfig({
|
|
837
|
+
agentId: 'test-agent',
|
|
838
|
+
isSubTask: false,
|
|
839
|
+
});
|
|
840
|
+
|
|
841
|
+
expect(result.plugins).toContain('lobe-gtd');
|
|
842
|
+
expect(result.plugins).toEqual(['lobe-gtd', 'plugin-a', 'plugin-b']);
|
|
843
|
+
});
|
|
844
|
+
|
|
845
|
+
it('should keep lobe-gtd when isSubTask is undefined', () => {
|
|
846
|
+
vi.spyOn(agentSelectors.agentSelectors, 'getAgentConfigById').mockReturnValue(
|
|
847
|
+
() =>
|
|
848
|
+
({
|
|
849
|
+
...mockAgentConfig,
|
|
850
|
+
plugins: ['lobe-gtd', 'plugin-a'],
|
|
851
|
+
}) as any,
|
|
852
|
+
);
|
|
853
|
+
|
|
854
|
+
const result = resolveAgentConfig({ agentId: 'test-agent' });
|
|
855
|
+
|
|
856
|
+
expect(result.plugins).toContain('lobe-gtd');
|
|
857
|
+
});
|
|
858
|
+
|
|
859
|
+
it('should filter lobe-gtd in page scope when isSubTask is true', () => {
|
|
860
|
+
vi.spyOn(agentSelectors.agentSelectors, 'getAgentConfigById').mockReturnValue(
|
|
861
|
+
() =>
|
|
862
|
+
({
|
|
863
|
+
...mockAgentConfig,
|
|
864
|
+
plugins: ['lobe-gtd', 'plugin-a'],
|
|
865
|
+
}) as any,
|
|
866
|
+
);
|
|
867
|
+
vi.spyOn(builtinAgents, 'getAgentRuntimeConfig').mockReturnValue({
|
|
868
|
+
systemRole: 'Page agent system role',
|
|
869
|
+
});
|
|
870
|
+
|
|
871
|
+
const result = resolveAgentConfig({
|
|
872
|
+
agentId: 'test-agent',
|
|
873
|
+
scope: 'page',
|
|
874
|
+
isSubTask: true,
|
|
875
|
+
});
|
|
876
|
+
|
|
877
|
+
expect(result.plugins).not.toContain('lobe-gtd');
|
|
878
|
+
expect(result.plugins).toContain(PageAgentIdentifier);
|
|
879
|
+
});
|
|
880
|
+
|
|
881
|
+
it('should filter lobe-gtd for builtin agent when isSubTask is true', () => {
|
|
882
|
+
vi.spyOn(agentSelectors.agentSelectors, 'getAgentSlugById').mockReturnValue(
|
|
883
|
+
() => 'some-builtin-slug',
|
|
884
|
+
);
|
|
885
|
+
vi.spyOn(builtinAgents, 'getAgentRuntimeConfig').mockReturnValue({
|
|
886
|
+
plugins: ['lobe-gtd', 'runtime-plugin'],
|
|
887
|
+
systemRole: 'Runtime system role',
|
|
888
|
+
});
|
|
889
|
+
|
|
890
|
+
const result = resolveAgentConfig({
|
|
891
|
+
agentId: 'builtin-agent',
|
|
892
|
+
isSubTask: true,
|
|
893
|
+
});
|
|
894
|
+
|
|
895
|
+
expect(result.plugins).not.toContain('lobe-gtd');
|
|
896
|
+
expect(result.plugins).toContain('runtime-plugin');
|
|
897
|
+
});
|
|
898
|
+
|
|
899
|
+
it('should keep lobe-gtd for builtin agent when isSubTask is false', () => {
|
|
900
|
+
vi.spyOn(agentSelectors.agentSelectors, 'getAgentSlugById').mockReturnValue(
|
|
901
|
+
() => 'some-builtin-slug',
|
|
902
|
+
);
|
|
903
|
+
vi.spyOn(builtinAgents, 'getAgentRuntimeConfig').mockReturnValue({
|
|
904
|
+
plugins: ['lobe-gtd', 'runtime-plugin'],
|
|
905
|
+
systemRole: 'Runtime system role',
|
|
906
|
+
});
|
|
907
|
+
|
|
908
|
+
const result = resolveAgentConfig({
|
|
909
|
+
agentId: 'builtin-agent',
|
|
910
|
+
isSubTask: false,
|
|
911
|
+
});
|
|
912
|
+
|
|
913
|
+
expect(result.plugins).toContain('lobe-gtd');
|
|
914
|
+
});
|
|
915
|
+
});
|
|
803
916
|
});
|
|
@@ -61,6 +61,12 @@ export interface AgentConfigResolverContext {
|
|
|
61
61
|
*/
|
|
62
62
|
groupId?: string;
|
|
63
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Whether this is a sub-task execution.
|
|
66
|
+
* When true, filters out lobe-gtd tools to prevent nested sub-task creation.
|
|
67
|
+
*/
|
|
68
|
+
isSubTask?: boolean;
|
|
69
|
+
|
|
64
70
|
/** Current model being used (for template variables) */
|
|
65
71
|
model?: string;
|
|
66
72
|
/** Plugins enabled for the agent */
|
|
@@ -106,9 +112,13 @@ export interface ResolvedAgentConfig {
|
|
|
106
112
|
* For regular agents, this simply returns the config from the store.
|
|
107
113
|
*/
|
|
108
114
|
export const resolveAgentConfig = (ctx: AgentConfigResolverContext): ResolvedAgentConfig => {
|
|
109
|
-
const { agentId, model, documentContent, plugins, targetAgentConfig } = ctx;
|
|
115
|
+
const { agentId, model, documentContent, plugins, targetAgentConfig, isSubTask } = ctx;
|
|
116
|
+
|
|
117
|
+
log('resolveAgentConfig called with agentId: %s, scope: %s, isSubTask: %s', agentId, ctx.scope, isSubTask);
|
|
110
118
|
|
|
111
|
-
|
|
119
|
+
// Helper to filter out lobe-gtd in sub-task context to prevent nested sub-task creation
|
|
120
|
+
const applySubTaskFilter = (pluginIds: string[]) =>
|
|
121
|
+
isSubTask ? pluginIds.filter((id) => id !== 'lobe-gtd') : pluginIds;
|
|
112
122
|
|
|
113
123
|
const agentStoreState = getAgentStoreState();
|
|
114
124
|
|
|
@@ -199,7 +209,7 @@ export const resolveAgentConfig = (ctx: AgentConfigResolverContext): ResolvedAge
|
|
|
199
209
|
agentConfig: finalAgentConfig,
|
|
200
210
|
chatConfig: finalChatConfig,
|
|
201
211
|
isBuiltinAgent: false,
|
|
202
|
-
plugins: pageAgentPlugins,
|
|
212
|
+
plugins: applySubTaskFilter(pageAgentPlugins),
|
|
203
213
|
};
|
|
204
214
|
}
|
|
205
215
|
|
|
@@ -208,7 +218,7 @@ export const resolveAgentConfig = (ctx: AgentConfigResolverContext): ResolvedAge
|
|
|
208
218
|
agentConfig: finalAgentConfig,
|
|
209
219
|
chatConfig: finalChatConfig,
|
|
210
220
|
isBuiltinAgent: false,
|
|
211
|
-
plugins: finalPlugins,
|
|
221
|
+
plugins: applySubTaskFilter(finalPlugins),
|
|
212
222
|
};
|
|
213
223
|
}
|
|
214
224
|
|
|
@@ -329,7 +339,7 @@ export const resolveAgentConfig = (ctx: AgentConfigResolverContext): ResolvedAge
|
|
|
329
339
|
agentConfig: finalAgentConfig,
|
|
330
340
|
chatConfig: resolvedChatConfig,
|
|
331
341
|
isBuiltinAgent: true,
|
|
332
|
-
plugins: finalPlugins,
|
|
342
|
+
plugins: applySubTaskFilter(finalPlugins),
|
|
333
343
|
slug,
|
|
334
344
|
};
|
|
335
345
|
};
|