@lobehub/lobehub 2.0.9 → 2.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +50 -0
- package/Dockerfile +44 -52
- package/changelog/v2.json +18 -0
- package/locales/ar/chat.json +4 -0
- package/locales/ar/models.json +65 -0
- package/locales/bg-BG/chat.json +4 -0
- package/locales/bg-BG/models.json +10 -0
- package/locales/de-DE/chat.json +4 -0
- package/locales/de-DE/models.json +41 -0
- package/locales/en-US/chat.json +4 -0
- package/locales/es-ES/chat.json +4 -0
- package/locales/es-ES/models.json +50 -0
- package/locales/fa-IR/chat.json +4 -0
- package/locales/fa-IR/models.json +39 -0
- package/locales/fr-FR/chat.json +4 -0
- package/locales/fr-FR/models.json +9 -0
- package/locales/it-IT/chat.json +4 -0
- package/locales/it-IT/models.json +62 -0
- package/locales/ja-JP/chat.json +4 -0
- package/locales/ja-JP/models.json +40 -0
- package/locales/ko-KR/chat.json +4 -0
- package/locales/ko-KR/models.json +31 -0
- package/locales/nl-NL/chat.json +4 -0
- package/locales/nl-NL/models.json +52 -0
- package/locales/pl-PL/chat.json +4 -0
- package/locales/pl-PL/models.json +43 -0
- package/locales/pt-BR/chat.json +4 -0
- package/locales/pt-BR/models.json +92 -0
- package/locales/ru-RU/chat.json +4 -0
- package/locales/ru-RU/models.json +34 -0
- package/locales/tr-TR/chat.json +4 -0
- package/locales/tr-TR/models.json +55 -0
- package/locales/vi-VN/chat.json +4 -0
- package/locales/vi-VN/models.json +31 -0
- package/locales/zh-CN/chat.json +4 -0
- package/locales/zh-TW/chat.json +4 -0
- package/package.json +1 -1
- package/packages/agent-runtime/src/groupOrchestration/GroupOrchestrationSupervisor.ts +18 -1
- package/packages/agent-runtime/src/groupOrchestration/__tests__/GroupOrchestrationSupervisor.test.ts +76 -5
- package/packages/agent-runtime/src/groupOrchestration/types.ts +3 -3
- package/packages/builtin-tool-group-management/src/client/Intervention/ExecuteTask.tsx +11 -11
- package/packages/builtin-tool-group-management/src/client/Intervention/ExecuteTasks.tsx +78 -79
- package/packages/builtin-tool-group-management/src/client/Render/ExecuteTask/index.tsx +3 -3
- package/packages/builtin-tool-group-management/src/client/Render/ExecuteTasks/index.tsx +61 -63
- package/packages/builtin-tool-group-management/src/client/Streaming/ExecuteTask/index.tsx +3 -3
- package/packages/builtin-tool-group-management/src/executor.test.ts +7 -9
- package/packages/builtin-tool-group-management/src/executor.ts +3 -3
- package/packages/builtin-tool-group-management/src/manifest.ts +49 -50
- package/packages/builtin-tool-group-management/src/systemRole.ts +153 -5
- package/packages/builtin-tool-group-management/src/types.ts +3 -2
- package/packages/builtin-tool-gtd/src/systemRole.ts +4 -4
- package/packages/context-engine/src/processors/TasksFlatten.ts +7 -5
- package/packages/context-engine/src/processors/__tests__/TasksFlatten.test.ts +164 -0
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/agentGroup/index.ts +4 -0
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/agentGroup/supervisor-after-multi-tasks.json +91 -0
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/agentGroup/supervisor-content-only.json +74 -0
- package/packages/conversation-flow/src/__tests__/parse.test.ts +37 -0
- package/packages/conversation-flow/src/transformation/FlatListBuilder.ts +70 -4
- package/packages/conversation-flow/src/transformation/__tests__/FlatListBuilder.test.ts +147 -0
- package/packages/model-bank/src/aiModels/cerebras.ts +2 -22
- package/packages/model-bank/src/aiModels/google.ts +1 -44
- package/packages/model-bank/src/aiModels/nvidia.ts +12 -16
- package/packages/model-bank/src/aiModels/siliconcloud.ts +20 -0
- package/packages/model-bank/src/aiModels/volcengine.ts +69 -0
- package/packages/model-bank/src/aiModels/wenxin.ts +41 -38
- package/packages/model-bank/src/aiModels/zhipu.ts +58 -28
- package/packages/model-bank/src/types/aiModel.ts +29 -0
- package/packages/model-runtime/src/core/usageConverters/utils/computeChatCost.test.ts +2 -2
- package/packages/model-runtime/src/providers/google/createImage.test.ts +12 -12
- package/packages/model-runtime/src/providers/openrouter/index.test.ts +102 -0
- package/packages/model-runtime/src/providers/openrouter/index.ts +19 -7
- package/packages/model-runtime/src/providers/vercelaigateway/index.test.ts +47 -0
- package/packages/model-runtime/src/providers/vercelaigateway/index.ts +7 -1
- package/packages/types/src/message/ui/chat.ts +2 -0
- package/packages/types/src/tool/builtin.ts +5 -5
- package/src/features/Conversation/ChatItem/components/Title.tsx +1 -1
- package/src/features/Conversation/ChatList/index.tsx +0 -1
- package/src/features/Conversation/Messages/GroupTasks/TaskItem/ClientTaskItem.tsx +183 -0
- package/src/features/Conversation/Messages/GroupTasks/TaskItem/ServerTaskItem.tsx +94 -0
- package/src/features/Conversation/Messages/GroupTasks/TaskItem/TaskTitle.tsx +177 -0
- package/src/features/Conversation/Messages/GroupTasks/TaskItem/index.tsx +26 -0
- package/src/features/Conversation/Messages/GroupTasks/TaskItem/useClientTaskStats.ts +93 -0
- package/src/features/Conversation/Messages/GroupTasks/index.tsx +151 -0
- package/src/features/Conversation/Messages/Supervisor/index.tsx +7 -1
- package/src/features/Conversation/Messages/Task/ClientTaskDetail/index.tsx +72 -91
- package/src/features/Conversation/Messages/Task/TaskDetailPanel/StatusContent.tsx +46 -17
- package/src/features/Conversation/Messages/Tasks/TaskItem/ClientTaskItem.tsx +9 -24
- package/src/features/Conversation/Messages/Tasks/TaskItem/ServerTaskItem.tsx +18 -38
- package/src/features/Conversation/Messages/Tasks/shared/ErrorState.tsx +45 -2
- package/src/features/Conversation/Messages/Tasks/shared/InitializingState.tsx +16 -1
- package/src/features/Conversation/Messages/Tasks/shared/TaskContent.tsx +68 -0
- package/src/features/Conversation/Messages/Tasks/shared/TaskMessages.tsx +383 -0
- package/src/features/Conversation/Messages/Tasks/shared/index.ts +4 -0
- package/src/features/Conversation/Messages/Tasks/shared/useTaskPolling.ts +48 -0
- package/src/features/Conversation/Messages/index.tsx +5 -0
- package/src/locales/default/chat.ts +4 -0
- package/src/server/modules/AgentRuntime/RuntimeExecutors.ts +4 -0
- package/src/server/modules/AgentRuntime/__tests__/RuntimeExecutors.test.ts +106 -1
- package/src/server/services/aiAgent/__tests__/execAgent.threadId.test.ts +2 -2
- package/src/server/utils/truncateToolResult.ts +1 -4
- package/src/store/chat/agents/GroupOrchestration/__tests__/batch-exec-async-tasks.test.ts +15 -15
- package/src/store/chat/agents/GroupOrchestration/createGroupOrchestrationExecutors.ts +22 -15
- package/src/store/chat/agents/__tests__/createAgentExecutors/exec-tasks.test.ts +21 -10
- package/src/store/chat/agents/createAgentExecutors.ts +2 -0
- package/src/store/chat/slices/aiAgent/actions/groupOrchestration.ts +10 -7
- package/src/features/Conversation/Messages/Task/ClientTaskDetail/CompletedState.tsx +0 -108
- package/src/features/Conversation/Messages/Task/ClientTaskDetail/InstructionAccordion.tsx +0 -63
- package/src/features/Conversation/Messages/Task/ClientTaskDetail/ProcessingState.tsx +0 -123
|
@@ -34,6 +34,7 @@ beforeEach(() => {
|
|
|
34
34
|
});
|
|
35
35
|
|
|
36
36
|
afterEach(() => {
|
|
37
|
+
vi.unstubAllGlobals();
|
|
37
38
|
vi.clearAllMocks();
|
|
38
39
|
});
|
|
39
40
|
|
|
@@ -333,6 +334,107 @@ describe('LobeOpenRouterAI - custom features', () => {
|
|
|
333
334
|
expect.anything(),
|
|
334
335
|
);
|
|
335
336
|
});
|
|
337
|
+
|
|
338
|
+
it('should map thinkingLevel to reasoning effort', async () => {
|
|
339
|
+
await instance.chat({
|
|
340
|
+
messages: [{ content: 'Think level', role: 'user' }],
|
|
341
|
+
model: 'openai/gpt-4',
|
|
342
|
+
thinkingLevel: 'medium',
|
|
343
|
+
} as any);
|
|
344
|
+
|
|
345
|
+
expect(instance['client'].chat.completions.create).toHaveBeenCalledWith(
|
|
346
|
+
expect.objectContaining({ reasoning: { effort: 'medium' } }),
|
|
347
|
+
expect.anything(),
|
|
348
|
+
);
|
|
349
|
+
});
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
describe('models mapping', () => {
|
|
353
|
+
it('should map extendParams for gpt-5.x reasoning and verbosity', async () => {
|
|
354
|
+
const mockModels = [
|
|
355
|
+
{
|
|
356
|
+
architecture: { input_modalities: ['text'] },
|
|
357
|
+
created: 1_700_000_000,
|
|
358
|
+
description: 'Test model',
|
|
359
|
+
id: 'openai/gpt-5.2-mini',
|
|
360
|
+
name: 'openai/gpt-5.2-mini',
|
|
361
|
+
pricing: { completion: '0.00001', prompt: '0.00001' },
|
|
362
|
+
supported_parameters: ['reasoning'],
|
|
363
|
+
top_provider: { context_length: 8192, max_completion_tokens: 1024 },
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
architecture: { input_modalities: ['text'] },
|
|
367
|
+
created: 1_700_000_000,
|
|
368
|
+
description: 'Test model',
|
|
369
|
+
id: 'openai/gpt-5.1-mini',
|
|
370
|
+
name: 'openai/gpt-5.1-mini',
|
|
371
|
+
pricing: { completion: '0.00001', prompt: '0.00001' },
|
|
372
|
+
supported_parameters: ['reasoning'],
|
|
373
|
+
top_provider: { context_length: 8192, max_completion_tokens: 1024 },
|
|
374
|
+
},
|
|
375
|
+
];
|
|
376
|
+
|
|
377
|
+
vi.stubGlobal(
|
|
378
|
+
'fetch',
|
|
379
|
+
vi.fn().mockResolvedValue({
|
|
380
|
+
ok: true,
|
|
381
|
+
json: async () => ({ data: mockModels }),
|
|
382
|
+
} as any),
|
|
383
|
+
);
|
|
384
|
+
|
|
385
|
+
const models = await params.models();
|
|
386
|
+
const gpt52 = models.find((m) => m.id === 'openai/gpt-5.2-mini');
|
|
387
|
+
const gpt51 = models.find((m) => m.id === 'openai/gpt-5.1-mini');
|
|
388
|
+
|
|
389
|
+
expect(gpt52?.settings?.extendParams).toEqual(
|
|
390
|
+
expect.arrayContaining(['gpt5_2ReasoningEffort', 'textVerbosity']),
|
|
391
|
+
);
|
|
392
|
+
expect(gpt51?.settings?.extendParams).toEqual(
|
|
393
|
+
expect.arrayContaining(['gpt5_1ReasoningEffort', 'textVerbosity']),
|
|
394
|
+
);
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
it('should map thinkingLevel for gemini-3 flash/pro reasoning', async () => {
|
|
398
|
+
const mockModels = [
|
|
399
|
+
{
|
|
400
|
+
architecture: { input_modalities: ['text'] },
|
|
401
|
+
created: 1_700_000_000,
|
|
402
|
+
description: 'Test model',
|
|
403
|
+
id: 'google/gemini-3-pro',
|
|
404
|
+
name: 'google/gemini-3-pro',
|
|
405
|
+
pricing: { completion: '0.00001', prompt: '0.00001' },
|
|
406
|
+
supported_parameters: ['reasoning'],
|
|
407
|
+
top_provider: { context_length: 8192, max_completion_tokens: 1024 },
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
architecture: { input_modalities: ['text'] },
|
|
411
|
+
created: 1_700_000_000,
|
|
412
|
+
description: 'Test model',
|
|
413
|
+
id: 'google/gemini-3-flash',
|
|
414
|
+
name: 'google/gemini-3-flash',
|
|
415
|
+
pricing: { completion: '0.00001', prompt: '0.00001' },
|
|
416
|
+
supported_parameters: ['reasoning'],
|
|
417
|
+
top_provider: { context_length: 8192, max_completion_tokens: 1024 },
|
|
418
|
+
},
|
|
419
|
+
];
|
|
420
|
+
|
|
421
|
+
vi.stubGlobal(
|
|
422
|
+
'fetch',
|
|
423
|
+
vi.fn().mockResolvedValue({
|
|
424
|
+
ok: true,
|
|
425
|
+
json: async () => ({ data: mockModels }),
|
|
426
|
+
} as any),
|
|
427
|
+
);
|
|
428
|
+
|
|
429
|
+
const models = await params.models();
|
|
430
|
+
const geminiPro = models.find((m) => m.id === 'google/gemini-3-pro');
|
|
431
|
+
const geminiFlash = models.find((m) => m.id === 'google/gemini-3-flash');
|
|
432
|
+
|
|
433
|
+
expect(geminiPro?.settings?.extendParams).toEqual(expect.arrayContaining(['thinkingLevel2']));
|
|
434
|
+
expect(geminiFlash?.settings?.extendParams).toEqual(
|
|
435
|
+
expect.arrayContaining(['thinkingLevel']),
|
|
436
|
+
);
|
|
437
|
+
});
|
|
336
438
|
});
|
|
337
439
|
|
|
338
440
|
describe('models', () => {
|
|
@@ -17,11 +17,11 @@ export const params = {
|
|
|
17
17
|
chatCompletion: {
|
|
18
18
|
handlePayload: (payload) => {
|
|
19
19
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
20
|
-
const { reasoning_effort, thinking, reasoning: _reasoning, ...rest } = payload;
|
|
20
|
+
const { reasoning_effort, thinking, reasoning: _reasoning, thinkingLevel, ...rest } = payload;
|
|
21
21
|
|
|
22
22
|
let reasoning: OpenRouterReasoning | undefined;
|
|
23
23
|
|
|
24
|
-
if (thinking?.type || thinking?.budget_tokens !== undefined || reasoning_effort) {
|
|
24
|
+
if (thinking?.type || thinking?.budget_tokens !== undefined || reasoning_effort || thinkingLevel) {
|
|
25
25
|
if (thinking?.type === 'disabled') {
|
|
26
26
|
reasoning = { enabled: false };
|
|
27
27
|
} else if (thinking?.budget_tokens !== undefined) {
|
|
@@ -31,6 +31,9 @@ export const params = {
|
|
|
31
31
|
} else if (reasoning_effort) {
|
|
32
32
|
reasoning = { effort: reasoning_effort };
|
|
33
33
|
}
|
|
34
|
+
else if (thinkingLevel) {
|
|
35
|
+
reasoning = { effort: thinkingLevel };
|
|
36
|
+
}
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
return {
|
|
@@ -126,11 +129,14 @@ export const params = {
|
|
|
126
129
|
if (model.description && model.description.includes('`reasoning` `enabled`')) {
|
|
127
130
|
extendParams.push('enableReasoning');
|
|
128
131
|
}
|
|
129
|
-
if (hasReasoning && model.id.includes('gpt-5')) {
|
|
130
|
-
extendParams.push('
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
|
|
132
|
+
if (hasReasoning && model.id.includes('gpt-5.2')) {
|
|
133
|
+
extendParams.push('gpt5_2ReasoningEffort', 'textVerbosity');
|
|
134
|
+
} else if (hasReasoning && model.id.includes('gpt-5.1')) {
|
|
135
|
+
extendParams.push('gpt5_1ReasoningEffort', 'textVerbosity');
|
|
136
|
+
} else if (hasReasoning && model.id.includes('gpt-5')) {
|
|
137
|
+
extendParams.push('gpt5ReasoningEffort', 'textVerbosity');
|
|
138
|
+
} else if (hasReasoning && model.id.includes('openai')) {
|
|
139
|
+
extendParams.push('reasoningEffort', 'textVerbosity');
|
|
134
140
|
}
|
|
135
141
|
if (hasReasoning && model.id.includes('claude')) {
|
|
136
142
|
extendParams.push('enableReasoning', 'reasoningBudgetToken');
|
|
@@ -141,6 +147,12 @@ export const params = {
|
|
|
141
147
|
if (hasReasoning && model.id.includes('gemini-2.5')) {
|
|
142
148
|
extendParams.push('reasoningBudgetToken');
|
|
143
149
|
}
|
|
150
|
+
if (hasReasoning && model.id.includes('gemini-3-pro')) {
|
|
151
|
+
extendParams.push('thinkingLevel2');
|
|
152
|
+
}
|
|
153
|
+
if (hasReasoning && model.id.includes('gemini-3-flash')) {
|
|
154
|
+
extendParams.push('thinkingLevel');
|
|
155
|
+
}
|
|
144
156
|
return extendParams.length > 0 ? { settings: { extendParams } } : {};
|
|
145
157
|
})(),
|
|
146
158
|
};
|
|
@@ -245,6 +245,53 @@ describe('LobeVercelAIGatewayAI - custom features', () => {
|
|
|
245
245
|
expect(Array.isArray(model?.pricing?.units)).toBe(true);
|
|
246
246
|
});
|
|
247
247
|
|
|
248
|
+
it('should map extendParams for gpt-5.x reasoning models', async () => {
|
|
249
|
+
const mockModelData: VercelAIGatewayModelCard[] = [
|
|
250
|
+
{
|
|
251
|
+
id: 'openai/gpt-5.2-mini',
|
|
252
|
+
name: 'GPT-5.2 Mini',
|
|
253
|
+
pricing: { input: 0.000_003, output: 0.000_015 },
|
|
254
|
+
tags: ['reasoning'],
|
|
255
|
+
type: 'chat',
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
id: 'openai/gpt-5.1-mini',
|
|
259
|
+
name: 'GPT-5.1 Mini',
|
|
260
|
+
pricing: { input: 0.000_003, output: 0.000_015 },
|
|
261
|
+
tags: ['reasoning'],
|
|
262
|
+
type: 'chat',
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
id: 'openai/gpt-5-mini',
|
|
266
|
+
name: 'GPT-5 Mini',
|
|
267
|
+
pricing: { input: 0.000_003, output: 0.000_015 },
|
|
268
|
+
tags: ['reasoning'],
|
|
269
|
+
type: 'chat',
|
|
270
|
+
},
|
|
271
|
+
];
|
|
272
|
+
|
|
273
|
+
const mockClient = {
|
|
274
|
+
models: {
|
|
275
|
+
list: vi.fn().mockResolvedValue({ data: mockModelData }),
|
|
276
|
+
},
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
const models = await params.models({ client: mockClient as any });
|
|
280
|
+
const gpt52 = models.find((m) => m.id === 'openai/gpt-5.2-mini');
|
|
281
|
+
const gpt51 = models.find((m) => m.id === 'openai/gpt-5.1-mini');
|
|
282
|
+
const gpt5 = models.find((m) => m.id === 'openai/gpt-5-mini');
|
|
283
|
+
|
|
284
|
+
expect(gpt52?.settings?.extendParams).toEqual(
|
|
285
|
+
expect.arrayContaining(['gpt5_2ReasoningEffort', 'textVerbosity']),
|
|
286
|
+
);
|
|
287
|
+
expect(gpt51?.settings?.extendParams).toEqual(
|
|
288
|
+
expect.arrayContaining(['gpt5_1ReasoningEffort', 'textVerbosity']),
|
|
289
|
+
);
|
|
290
|
+
expect(gpt5?.settings?.extendParams).toEqual(
|
|
291
|
+
expect.arrayContaining(['gpt5ReasoningEffort', 'textVerbosity']),
|
|
292
|
+
);
|
|
293
|
+
});
|
|
294
|
+
|
|
248
295
|
it('should handle models with missing pricing', async () => {
|
|
249
296
|
const mockModelData: VercelAIGatewayModelCard[] = [
|
|
250
297
|
{
|
|
@@ -125,9 +125,15 @@ export const params = {
|
|
|
125
125
|
// Merge all applicable extendParams for settings
|
|
126
126
|
...(() => {
|
|
127
127
|
const extendParams: string[] = [];
|
|
128
|
-
if (tags.includes('reasoning') && m.id.includes('gpt-5')) {
|
|
128
|
+
if (tags.includes('reasoning') && m.id.includes('gpt-5') && !m.id.includes('gpt-5.1') && !m.id.includes('gpt-5.2')) {
|
|
129
129
|
extendParams.push('gpt5ReasoningEffort', 'textVerbosity');
|
|
130
130
|
}
|
|
131
|
+
if (tags.includes('reasoning') && m.id.includes('gpt-5.1') && !m.id.includes('gpt-5.2')) {
|
|
132
|
+
extendParams.push('gpt5_1ReasoningEffort', 'textVerbosity');
|
|
133
|
+
}
|
|
134
|
+
if (tags.includes('reasoning') && m.id.includes('gpt-5.2')) {
|
|
135
|
+
extendParams.push('gpt5_2ReasoningEffort', 'textVerbosity');
|
|
136
|
+
}
|
|
131
137
|
if (tags.includes('reasoning') && m.id.includes('openai') && !m.id.includes('gpt-5')) {
|
|
132
138
|
extendParams.push('reasoningEffort', 'textVerbosity');
|
|
133
139
|
}
|
|
@@ -25,6 +25,7 @@ export type UIMessageRoleType =
|
|
|
25
25
|
| 'tool'
|
|
26
26
|
| 'task'
|
|
27
27
|
| 'tasks'
|
|
28
|
+
| 'groupTasks'
|
|
28
29
|
| 'supervisor'
|
|
29
30
|
| 'assistantGroup'
|
|
30
31
|
| 'agentCouncil'
|
|
@@ -185,6 +186,7 @@ export interface UIChatMessage {
|
|
|
185
186
|
/**
|
|
186
187
|
* Task messages for role='tasks' virtual message
|
|
187
188
|
* Contains aggregated task messages with same parentId
|
|
189
|
+
* Also used to store task execution messages (intermediate steps) from polling
|
|
188
190
|
*/
|
|
189
191
|
tasks?: UIChatMessage[];
|
|
190
192
|
threadId?: string | null;
|
|
@@ -433,6 +433,10 @@ export interface TriggerExecuteTaskParams extends GroupOrchestrationBaseParams {
|
|
|
433
433
|
* The agent ID to execute the task
|
|
434
434
|
*/
|
|
435
435
|
agentId: string;
|
|
436
|
+
/**
|
|
437
|
+
* The instruction/task description for the agent
|
|
438
|
+
*/
|
|
439
|
+
instruction: string;
|
|
436
440
|
/**
|
|
437
441
|
* Whether to run on the desktop client (for local file/shell access).
|
|
438
442
|
* MUST be true when task requires local-system tools. Default is false (server execution).
|
|
@@ -443,10 +447,6 @@ export interface TriggerExecuteTaskParams extends GroupOrchestrationBaseParams {
|
|
|
443
447
|
* without calling the supervisor again.
|
|
444
448
|
*/
|
|
445
449
|
skipCallSupervisor?: boolean;
|
|
446
|
-
/**
|
|
447
|
-
* The task description for the agent
|
|
448
|
-
*/
|
|
449
|
-
task: string;
|
|
450
450
|
/**
|
|
451
451
|
* Optional timeout in milliseconds
|
|
452
452
|
*/
|
|
@@ -466,7 +466,7 @@ export interface TriggerExecuteTaskItem {
|
|
|
466
466
|
*/
|
|
467
467
|
agentId: string;
|
|
468
468
|
/**
|
|
469
|
-
* Detailed instruction
|
|
469
|
+
* Detailed instruction for the agent to execute
|
|
470
470
|
*/
|
|
471
471
|
instruction: string;
|
|
472
472
|
/**
|
|
@@ -91,7 +91,6 @@ const ChatList = memo<ChatListProps>(({ disableActionsBar, welcome, itemContent
|
|
|
91
91
|
<MessageActionProvider withSingletonActionsBar={!disableActionsBar}>
|
|
92
92
|
<VirtualizedList
|
|
93
93
|
dataSource={displayMessageIds}
|
|
94
|
-
// isGenerating={isGenerating}
|
|
95
94
|
itemContent={itemContent ?? defaultItemContent}
|
|
96
95
|
/>
|
|
97
96
|
</MessageActionProvider>
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { AccordionItem, Block, Text } from '@lobehub/ui';
|
|
4
|
+
import { memo, useMemo, useState } from 'react';
|
|
5
|
+
|
|
6
|
+
import { useAgentGroupStore } from '@/store/agentGroup';
|
|
7
|
+
import { agentGroupSelectors } from '@/store/agentGroup/selectors';
|
|
8
|
+
import { useChatStore } from '@/store/chat';
|
|
9
|
+
import { displayMessageSelectors } from '@/store/chat/selectors';
|
|
10
|
+
import { messageMapKey } from '@/store/chat/utils/messageMapKey';
|
|
11
|
+
import { ThreadStatus } from '@/types/index';
|
|
12
|
+
import type { UIChatMessage } from '@/types/index';
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
ErrorState,
|
|
16
|
+
InitializingState,
|
|
17
|
+
TaskMessages,
|
|
18
|
+
isProcessingStatus,
|
|
19
|
+
} from '../../Tasks/shared';
|
|
20
|
+
import TaskTitle, { type TaskMetrics } from './TaskTitle';
|
|
21
|
+
|
|
22
|
+
interface ClientTaskItemProps {
|
|
23
|
+
item: UIChatMessage;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const ClientTaskItem = memo<ClientTaskItemProps>(({ item }) => {
|
|
27
|
+
const { id, agentId: itemAgentId, groupId: itemGroupId, metadata, taskDetail } = item;
|
|
28
|
+
const [expanded, setExpanded] = useState(false);
|
|
29
|
+
|
|
30
|
+
const title = taskDetail?.title || metadata?.taskTitle;
|
|
31
|
+
const instruction = metadata?.instruction;
|
|
32
|
+
const status = taskDetail?.status;
|
|
33
|
+
const threadId = taskDetail?.threadId;
|
|
34
|
+
|
|
35
|
+
const isProcessing = isProcessingStatus(status);
|
|
36
|
+
const isCompleted = status === ThreadStatus.Completed;
|
|
37
|
+
const isError = status === ThreadStatus.Failed || status === ThreadStatus.Cancel;
|
|
38
|
+
const isInitializing = !taskDetail || !status;
|
|
39
|
+
|
|
40
|
+
// Fetch thread messages for client mode
|
|
41
|
+
// Use item's agentId (from task message) to query with the correct SubAgent ID that created the thread
|
|
42
|
+
// Fall back to activeAgentId if task message doesn't have agentId (shouldn't happen normally)
|
|
43
|
+
const [activeAgentId, activeTopicId, useFetchMessages] = useChatStore((s) => [
|
|
44
|
+
s.activeAgentId,
|
|
45
|
+
s.activeTopicId,
|
|
46
|
+
s.useFetchMessages,
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
// Use task message's agentId (skip 'supervisor' as it's not a valid agent ID for queries)
|
|
50
|
+
// Fall back to activeAgentId if not available
|
|
51
|
+
const agentId = itemAgentId && itemAgentId !== 'supervisor' ? itemAgentId : activeAgentId;
|
|
52
|
+
|
|
53
|
+
// Get agent info from store for displaying
|
|
54
|
+
const activeGroupId = useAgentGroupStore(agentGroupSelectors.activeGroupId);
|
|
55
|
+
const agent = useAgentGroupStore((s) =>
|
|
56
|
+
activeGroupId && itemAgentId
|
|
57
|
+
? agentGroupSelectors.getAgentByIdFromGroup(activeGroupId, itemAgentId)(s)
|
|
58
|
+
: null,
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
const threadContext = useMemo(
|
|
62
|
+
() => ({
|
|
63
|
+
agentId,
|
|
64
|
+
groupId: itemGroupId,
|
|
65
|
+
scope: 'thread' as const,
|
|
66
|
+
threadId,
|
|
67
|
+
topicId: activeTopicId,
|
|
68
|
+
}),
|
|
69
|
+
[agentId, itemGroupId, activeTopicId, threadId],
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
const threadMessageKey = useMemo(
|
|
73
|
+
() => (threadId ? messageMapKey(threadContext) : null),
|
|
74
|
+
[threadId, threadContext],
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
// Fetch thread messages (skip when executing - messages come from real-time updates)
|
|
78
|
+
useFetchMessages(threadContext, isProcessing);
|
|
79
|
+
|
|
80
|
+
// Get thread messages from store using selector
|
|
81
|
+
const threadMessages = useChatStore((s) =>
|
|
82
|
+
threadMessageKey
|
|
83
|
+
? displayMessageSelectors.getDisplayMessagesByKey(threadMessageKey)(s)
|
|
84
|
+
: undefined,
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
// Find the assistantGroup message which contains the children blocks
|
|
88
|
+
const assistantGroupMessage = threadMessages?.find((item) => item.role === 'assistantGroup');
|
|
89
|
+
const blocks = assistantGroupMessage?.children;
|
|
90
|
+
const childrenCount = blocks?.length ?? 0;
|
|
91
|
+
|
|
92
|
+
// Get model/provider from assistantGroup message
|
|
93
|
+
const model = assistantGroupMessage?.model;
|
|
94
|
+
const provider = assistantGroupMessage?.provider;
|
|
95
|
+
|
|
96
|
+
// Build metrics for TaskTitle based on blocks data
|
|
97
|
+
const metrics: TaskMetrics | undefined = useMemo(() => {
|
|
98
|
+
if (isProcessing && blocks) {
|
|
99
|
+
const toolCalls = blocks.reduce((sum, block) => sum + (block.tools?.length || 0), 0);
|
|
100
|
+
return {
|
|
101
|
+
isLoading: false,
|
|
102
|
+
startTime: assistantGroupMessage?.createdAt,
|
|
103
|
+
steps: blocks.length,
|
|
104
|
+
toolCalls,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
if (isCompleted || isError) {
|
|
108
|
+
return {
|
|
109
|
+
duration: taskDetail?.duration,
|
|
110
|
+
steps: taskDetail?.totalSteps,
|
|
111
|
+
toolCalls: taskDetail?.totalToolCalls,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
return undefined;
|
|
115
|
+
}, [
|
|
116
|
+
isProcessing,
|
|
117
|
+
isCompleted,
|
|
118
|
+
isError,
|
|
119
|
+
blocks,
|
|
120
|
+
assistantGroupMessage?.createdAt,
|
|
121
|
+
taskDetail?.duration,
|
|
122
|
+
taskDetail?.totalSteps,
|
|
123
|
+
taskDetail?.totalToolCalls,
|
|
124
|
+
]);
|
|
125
|
+
|
|
126
|
+
// Check if we have blocks to show (for Processing and Completed states)
|
|
127
|
+
const hasBlocks = blocks && childrenCount > 0;
|
|
128
|
+
|
|
129
|
+
return (
|
|
130
|
+
<AccordionItem
|
|
131
|
+
expand={expanded}
|
|
132
|
+
itemKey={id}
|
|
133
|
+
onExpandChange={setExpanded}
|
|
134
|
+
paddingBlock={4}
|
|
135
|
+
paddingInline={4}
|
|
136
|
+
title={
|
|
137
|
+
<TaskTitle
|
|
138
|
+
agent={
|
|
139
|
+
agent
|
|
140
|
+
? { avatar: agent.avatar || undefined, backgroundColor: agent.backgroundColor }
|
|
141
|
+
: undefined
|
|
142
|
+
}
|
|
143
|
+
metrics={metrics}
|
|
144
|
+
status={status}
|
|
145
|
+
title={title}
|
|
146
|
+
/>
|
|
147
|
+
}
|
|
148
|
+
>
|
|
149
|
+
<Block gap={16} padding={12} style={{ marginBlock: 8 }} variant={'outlined'}>
|
|
150
|
+
{instruction && (
|
|
151
|
+
<Block padding={12}>
|
|
152
|
+
<Text fontSize={13} type={'secondary'}>
|
|
153
|
+
{instruction}
|
|
154
|
+
</Text>
|
|
155
|
+
</Block>
|
|
156
|
+
)}
|
|
157
|
+
|
|
158
|
+
{/* Initializing State - no taskDetail yet or no blocks */}
|
|
159
|
+
{(isInitializing || (isProcessing && !hasBlocks)) && <InitializingState />}
|
|
160
|
+
|
|
161
|
+
{/* Processing or Completed State - show blocks via TaskMessages */}
|
|
162
|
+
{!isInitializing && (isProcessing || isCompleted) && hasBlocks && threadMessages && (
|
|
163
|
+
<TaskMessages
|
|
164
|
+
duration={taskDetail?.duration}
|
|
165
|
+
isProcessing={isProcessing}
|
|
166
|
+
messages={threadMessages}
|
|
167
|
+
model={model ?? undefined}
|
|
168
|
+
provider={provider ?? undefined}
|
|
169
|
+
startTime={assistantGroupMessage?.createdAt}
|
|
170
|
+
totalCost={taskDetail?.totalCost}
|
|
171
|
+
/>
|
|
172
|
+
)}
|
|
173
|
+
|
|
174
|
+
{/* Error State */}
|
|
175
|
+
{!isInitializing && isError && taskDetail && <ErrorState taskDetail={taskDetail} />}
|
|
176
|
+
</Block>
|
|
177
|
+
</AccordionItem>
|
|
178
|
+
);
|
|
179
|
+
}, Object.is);
|
|
180
|
+
|
|
181
|
+
ClientTaskItem.displayName = 'ClientTaskItem';
|
|
182
|
+
|
|
183
|
+
export default ClientTaskItem;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { ThreadStatus } from '@lobechat/types';
|
|
4
|
+
import type { UIChatMessage } from '@lobechat/types';
|
|
5
|
+
import { AccordionItem, Block } from '@lobehub/ui';
|
|
6
|
+
import isEqual from 'fast-deep-equal';
|
|
7
|
+
import { memo, useMemo, useState } from 'react';
|
|
8
|
+
|
|
9
|
+
import { useAgentGroupStore } from '@/store/agentGroup';
|
|
10
|
+
import { agentGroupSelectors } from '@/store/agentGroup/selectors';
|
|
11
|
+
|
|
12
|
+
import { TaskContent } from '../../Tasks/shared';
|
|
13
|
+
import TaskTitle, { type TaskMetrics } from './TaskTitle';
|
|
14
|
+
|
|
15
|
+
interface ServerTaskItemProps {
|
|
16
|
+
item: UIChatMessage;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const ServerTaskItem = memo<ServerTaskItemProps>(({ item }) => {
|
|
20
|
+
const { id, agentId, metadata, taskDetail, tasks } = item;
|
|
21
|
+
const [expanded, setExpanded] = useState(false);
|
|
22
|
+
|
|
23
|
+
const title = taskDetail?.title || metadata?.taskTitle;
|
|
24
|
+
const status = taskDetail?.status;
|
|
25
|
+
const threadId = taskDetail?.threadId;
|
|
26
|
+
|
|
27
|
+
const isCompleted = status === ThreadStatus.Completed;
|
|
28
|
+
const isError = status === ThreadStatus.Failed || status === ThreadStatus.Cancel;
|
|
29
|
+
|
|
30
|
+
// Get agent info from store
|
|
31
|
+
const activeGroupId = useAgentGroupStore(agentGroupSelectors.activeGroupId);
|
|
32
|
+
const agent = useAgentGroupStore((s) =>
|
|
33
|
+
activeGroupId && agentId
|
|
34
|
+
? agentGroupSelectors.getAgentByIdFromGroup(activeGroupId, agentId)(s)
|
|
35
|
+
: null,
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
// Build metrics for TaskTitle (only for completed/error states)
|
|
39
|
+
const metrics: TaskMetrics | undefined = useMemo(() => {
|
|
40
|
+
if (isCompleted || isError) {
|
|
41
|
+
return {
|
|
42
|
+
duration: taskDetail?.duration,
|
|
43
|
+
steps: taskDetail?.totalSteps,
|
|
44
|
+
toolCalls: taskDetail?.totalToolCalls,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return undefined;
|
|
48
|
+
}, [
|
|
49
|
+
isCompleted,
|
|
50
|
+
isError,
|
|
51
|
+
taskDetail?.duration,
|
|
52
|
+
taskDetail?.totalSteps,
|
|
53
|
+
taskDetail?.totalToolCalls,
|
|
54
|
+
]);
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<AccordionItem
|
|
58
|
+
expand={expanded}
|
|
59
|
+
itemKey={id}
|
|
60
|
+
onExpandChange={setExpanded}
|
|
61
|
+
paddingBlock={4}
|
|
62
|
+
paddingInline={4}
|
|
63
|
+
title={
|
|
64
|
+
<TaskTitle
|
|
65
|
+
agent={
|
|
66
|
+
agent
|
|
67
|
+
? { avatar: agent.avatar || undefined, backgroundColor: agent.backgroundColor }
|
|
68
|
+
: undefined
|
|
69
|
+
}
|
|
70
|
+
metrics={metrics}
|
|
71
|
+
status={status}
|
|
72
|
+
title={title}
|
|
73
|
+
/>
|
|
74
|
+
}
|
|
75
|
+
>
|
|
76
|
+
<Block gap={16} padding={12} style={{ marginBlock: 8 }} variant={'outlined'}>
|
|
77
|
+
{expanded && (
|
|
78
|
+
<TaskContent
|
|
79
|
+
id={id}
|
|
80
|
+
isError={isError}
|
|
81
|
+
messages={tasks}
|
|
82
|
+
status={status}
|
|
83
|
+
taskDetail={taskDetail}
|
|
84
|
+
threadId={threadId}
|
|
85
|
+
/>
|
|
86
|
+
)}
|
|
87
|
+
</Block>
|
|
88
|
+
</AccordionItem>
|
|
89
|
+
);
|
|
90
|
+
}, isEqual);
|
|
91
|
+
|
|
92
|
+
ServerTaskItem.displayName = 'ServerTaskItem';
|
|
93
|
+
|
|
94
|
+
export default ServerTaskItem;
|