@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
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { ThreadStatus } from '@lobechat/types';
|
|
4
|
+
import { Avatar, Block, Flexbox, Icon, Text } from '@lobehub/ui';
|
|
5
|
+
import { cssVar } from 'antd-style';
|
|
6
|
+
import { Footprints, ListChecksIcon, Wrench, XIcon } from 'lucide-react';
|
|
7
|
+
import { memo, useEffect, useState } from 'react';
|
|
8
|
+
import { useTranslation } from 'react-i18next';
|
|
9
|
+
|
|
10
|
+
import NeuralNetworkLoading from '@/components/NeuralNetworkLoading';
|
|
11
|
+
import { DEFAULT_AVATAR } from '@/const/meta';
|
|
12
|
+
|
|
13
|
+
import { formatDuration, formatElapsedTime, isProcessingStatus } from '../../Tasks/shared';
|
|
14
|
+
|
|
15
|
+
export interface TaskMetrics {
|
|
16
|
+
/** Task duration in milliseconds (for completed tasks) */
|
|
17
|
+
duration?: number;
|
|
18
|
+
/** Whether metrics are still loading */
|
|
19
|
+
isLoading?: boolean;
|
|
20
|
+
/** Start time timestamp for elapsed time calculation */
|
|
21
|
+
startTime?: number;
|
|
22
|
+
/** Number of execution steps/blocks */
|
|
23
|
+
steps?: number;
|
|
24
|
+
/** Total tool calls count */
|
|
25
|
+
toolCalls?: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface TaskTitleProps {
|
|
29
|
+
/** Agent info for avatar display */
|
|
30
|
+
agent?: {
|
|
31
|
+
avatar?: string;
|
|
32
|
+
backgroundColor?: string | null;
|
|
33
|
+
};
|
|
34
|
+
/** Metrics to display (steps, tool calls, elapsed time) */
|
|
35
|
+
metrics?: TaskMetrics;
|
|
36
|
+
status?: ThreadStatus;
|
|
37
|
+
title?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const TaskStatusIndicator = memo<{ status?: ThreadStatus }>(({ status }) => {
|
|
41
|
+
const isCompleted = status === ThreadStatus.Completed;
|
|
42
|
+
const isError = status === ThreadStatus.Failed || status === ThreadStatus.Cancel;
|
|
43
|
+
const isProcessing = status ? isProcessingStatus(status) : false;
|
|
44
|
+
const isInitializing = !status;
|
|
45
|
+
|
|
46
|
+
let icon;
|
|
47
|
+
|
|
48
|
+
if (isCompleted) {
|
|
49
|
+
icon = <Icon color={cssVar.colorSuccess} icon={ListChecksIcon} />;
|
|
50
|
+
} else if (isError) {
|
|
51
|
+
icon = <Icon color={cssVar.colorError} icon={XIcon} />;
|
|
52
|
+
} else if (isProcessing || isInitializing) {
|
|
53
|
+
icon = <NeuralNetworkLoading size={16} />;
|
|
54
|
+
} else {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<Block
|
|
60
|
+
align={'center'}
|
|
61
|
+
flex={'none'}
|
|
62
|
+
gap={4}
|
|
63
|
+
height={24}
|
|
64
|
+
horizontal
|
|
65
|
+
justify={'center'}
|
|
66
|
+
style={{
|
|
67
|
+
fontSize: 12,
|
|
68
|
+
}}
|
|
69
|
+
variant={'outlined'}
|
|
70
|
+
width={24}
|
|
71
|
+
>
|
|
72
|
+
{icon}
|
|
73
|
+
</Block>
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
TaskStatusIndicator.displayName = 'TaskStatusIndicator';
|
|
78
|
+
|
|
79
|
+
interface MetricsDisplayProps {
|
|
80
|
+
metrics: TaskMetrics;
|
|
81
|
+
status?: ThreadStatus;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const MetricsDisplay = memo<MetricsDisplayProps>(({ metrics, status }) => {
|
|
85
|
+
const { t } = useTranslation('chat');
|
|
86
|
+
const { steps, toolCalls, startTime, duration, isLoading } = metrics;
|
|
87
|
+
const [elapsedTime, setElapsedTime] = useState(0);
|
|
88
|
+
|
|
89
|
+
const isProcessing = status ? isProcessingStatus(status) : false;
|
|
90
|
+
|
|
91
|
+
// Calculate initial elapsed time
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
if (startTime && isProcessing) {
|
|
94
|
+
setElapsedTime(Math.max(0, Date.now() - startTime));
|
|
95
|
+
}
|
|
96
|
+
}, [startTime, isProcessing]);
|
|
97
|
+
|
|
98
|
+
// Timer for updating elapsed time every second (only when processing)
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
if (!startTime || !isProcessing) return;
|
|
101
|
+
|
|
102
|
+
const timer = setInterval(() => {
|
|
103
|
+
setElapsedTime(Math.max(0, Date.now() - startTime));
|
|
104
|
+
}, 1000);
|
|
105
|
+
|
|
106
|
+
return () => clearInterval(timer);
|
|
107
|
+
}, [startTime, isProcessing]);
|
|
108
|
+
|
|
109
|
+
// Don't show metrics if loading or no data
|
|
110
|
+
if (isLoading) return null;
|
|
111
|
+
|
|
112
|
+
const hasSteps = steps !== undefined && steps > 0;
|
|
113
|
+
const hasToolCalls = toolCalls !== undefined && toolCalls > 0;
|
|
114
|
+
const hasTime = isProcessing ? startTime !== undefined : duration !== undefined;
|
|
115
|
+
|
|
116
|
+
// Don't render if no metrics to show
|
|
117
|
+
if (!hasSteps && !hasToolCalls && !hasTime) return null;
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
<Flexbox align="center" gap={8} horizontal>
|
|
121
|
+
{/* Steps */}
|
|
122
|
+
{hasSteps && (
|
|
123
|
+
<Flexbox align="center" gap={2} horizontal>
|
|
124
|
+
<Icon color={cssVar.colorTextTertiary} icon={Footprints} size={12} />
|
|
125
|
+
<Text fontSize={12} type="secondary">
|
|
126
|
+
{steps}
|
|
127
|
+
</Text>
|
|
128
|
+
</Flexbox>
|
|
129
|
+
)}
|
|
130
|
+
{/* Tool calls */}
|
|
131
|
+
{hasToolCalls && (
|
|
132
|
+
<Flexbox align="center" gap={2} horizontal>
|
|
133
|
+
<Icon color={cssVar.colorTextTertiary} icon={Wrench} size={12} />
|
|
134
|
+
<Text fontSize={12} type="secondary">
|
|
135
|
+
{toolCalls}
|
|
136
|
+
</Text>
|
|
137
|
+
</Flexbox>
|
|
138
|
+
)}
|
|
139
|
+
{/* Time */}
|
|
140
|
+
{hasTime && (
|
|
141
|
+
<Text fontSize={12} type="secondary">
|
|
142
|
+
{isProcessing
|
|
143
|
+
? formatElapsedTime(elapsedTime)
|
|
144
|
+
: duration
|
|
145
|
+
? t('task.metrics.duration', { duration: formatDuration(duration) })
|
|
146
|
+
: null}
|
|
147
|
+
</Text>
|
|
148
|
+
)}
|
|
149
|
+
</Flexbox>
|
|
150
|
+
);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
MetricsDisplay.displayName = 'MetricsDisplay';
|
|
154
|
+
|
|
155
|
+
const TaskTitle = memo<TaskTitleProps>(({ title, status, metrics, agent }) => {
|
|
156
|
+
return (
|
|
157
|
+
<Flexbox align="center" gap={6} horizontal>
|
|
158
|
+
<TaskStatusIndicator status={status} />
|
|
159
|
+
{agent && (
|
|
160
|
+
<Avatar
|
|
161
|
+
avatar={agent.avatar || DEFAULT_AVATAR}
|
|
162
|
+
background={agent.backgroundColor || undefined}
|
|
163
|
+
shape={'circle'}
|
|
164
|
+
size={20}
|
|
165
|
+
/>
|
|
166
|
+
)}
|
|
167
|
+
<Text ellipsis fontSize={14}>
|
|
168
|
+
{title}
|
|
169
|
+
</Text>
|
|
170
|
+
{metrics && <MetricsDisplay metrics={metrics} status={status} />}
|
|
171
|
+
</Flexbox>
|
|
172
|
+
);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
TaskTitle.displayName = 'TaskTitle';
|
|
176
|
+
|
|
177
|
+
export default TaskTitle;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type { UIChatMessage } from '@lobechat/types';
|
|
4
|
+
import isEqual from 'fast-deep-equal';
|
|
5
|
+
import { memo } from 'react';
|
|
6
|
+
|
|
7
|
+
import ClientTaskItem from './ClientTaskItem';
|
|
8
|
+
import ServerTaskItem from './ServerTaskItem';
|
|
9
|
+
|
|
10
|
+
interface TaskItemProps {
|
|
11
|
+
item: UIChatMessage;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const TaskItem = memo<TaskItemProps>(({ item }) => {
|
|
15
|
+
const isClientMode = item.taskDetail?.clientMode;
|
|
16
|
+
|
|
17
|
+
if (isClientMode) {
|
|
18
|
+
return <ClientTaskItem item={item} />;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return <ServerTaskItem item={item} />;
|
|
22
|
+
}, isEqual);
|
|
23
|
+
|
|
24
|
+
TaskItem.displayName = 'TaskItem';
|
|
25
|
+
|
|
26
|
+
export default TaskItem;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useMemo } from 'react';
|
|
4
|
+
|
|
5
|
+
import { useChatStore } from '@/store/chat';
|
|
6
|
+
import { displayMessageSelectors } from '@/store/chat/selectors';
|
|
7
|
+
import { messageMapKey } from '@/store/chat/utils/messageMapKey';
|
|
8
|
+
|
|
9
|
+
export interface ClientTaskStats {
|
|
10
|
+
isLoading: boolean;
|
|
11
|
+
startTime?: number;
|
|
12
|
+
steps: number;
|
|
13
|
+
toolCalls: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface UseClientTaskStatsOptions {
|
|
17
|
+
/** Agent ID from the task message (use task's agentId, not activeAgentId) */
|
|
18
|
+
agentId?: string;
|
|
19
|
+
enabled?: boolean;
|
|
20
|
+
/** Group ID from the task message (use task's groupId, not activeGroupId) */
|
|
21
|
+
groupId?: string;
|
|
22
|
+
threadId?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Hook to fetch thread messages and compute task statistics for client mode tasks.
|
|
27
|
+
* Used in TaskItem to display progress metrics (steps, tool calls, elapsed time).
|
|
28
|
+
*/
|
|
29
|
+
export const useClientTaskStats = ({
|
|
30
|
+
agentId: propAgentId,
|
|
31
|
+
groupId,
|
|
32
|
+
threadId,
|
|
33
|
+
enabled = true,
|
|
34
|
+
}: UseClientTaskStatsOptions): ClientTaskStats => {
|
|
35
|
+
// Use task message's agentId to query with the correct SubAgent ID that created the thread
|
|
36
|
+
// Fall back to activeAgentId if not provided
|
|
37
|
+
const [activeAgentId, activeTopicId, useFetchMessages] = useChatStore((s) => [
|
|
38
|
+
s.activeAgentId,
|
|
39
|
+
s.activeTopicId,
|
|
40
|
+
s.useFetchMessages,
|
|
41
|
+
]);
|
|
42
|
+
|
|
43
|
+
const agentId = propAgentId || activeAgentId;
|
|
44
|
+
|
|
45
|
+
const threadContext = useMemo(
|
|
46
|
+
() => ({
|
|
47
|
+
agentId,
|
|
48
|
+
groupId,
|
|
49
|
+
scope: 'thread' as const,
|
|
50
|
+
threadId,
|
|
51
|
+
topicId: activeTopicId,
|
|
52
|
+
}),
|
|
53
|
+
[agentId, groupId, activeTopicId, threadId],
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
const threadMessageKey = useMemo(
|
|
57
|
+
() => (threadId ? messageMapKey(threadContext) : null),
|
|
58
|
+
[threadId, threadContext],
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
// Fetch thread messages (skip when disabled or no threadId)
|
|
62
|
+
useFetchMessages(threadContext, !enabled || !threadId);
|
|
63
|
+
|
|
64
|
+
// Get thread messages from store using selector
|
|
65
|
+
const threadMessages = useChatStore((s) =>
|
|
66
|
+
threadMessageKey
|
|
67
|
+
? displayMessageSelectors.getDisplayMessagesByKey(threadMessageKey)(s)
|
|
68
|
+
: undefined,
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
// Compute stats from thread messages
|
|
72
|
+
return useMemo(() => {
|
|
73
|
+
if (!threadMessages || !enabled) {
|
|
74
|
+
return { isLoading: true, steps: 0, toolCalls: 0 };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Find the assistantGroup message which contains the children blocks
|
|
78
|
+
const assistantGroupMessage = threadMessages.find((item) => item.role === 'assistantGroup');
|
|
79
|
+
const blocks = assistantGroupMessage?.children ?? [];
|
|
80
|
+
|
|
81
|
+
// Calculate stats
|
|
82
|
+
const steps = blocks.length;
|
|
83
|
+
const toolCalls = blocks.reduce((sum, block) => sum + (block.tools?.length || 0), 0);
|
|
84
|
+
const startTime = assistantGroupMessage?.createdAt;
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
isLoading: false,
|
|
88
|
+
startTime,
|
|
89
|
+
steps,
|
|
90
|
+
toolCalls,
|
|
91
|
+
};
|
|
92
|
+
}, [threadMessages, enabled]);
|
|
93
|
+
};
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { type UIChatMessage } from '@lobechat/types';
|
|
4
|
+
import { Block, Flexbox, GroupAvatar, Icon, Tag } from '@lobehub/ui';
|
|
5
|
+
import { cssVar } from 'antd-style';
|
|
6
|
+
import isEqual from 'fast-deep-equal';
|
|
7
|
+
import { ListTodo } from 'lucide-react';
|
|
8
|
+
import { memo, useMemo } from 'react';
|
|
9
|
+
import { useTranslation } from 'react-i18next';
|
|
10
|
+
|
|
11
|
+
import { DEFAULT_AVATAR } from '@/const/meta';
|
|
12
|
+
import { useAgentGroupStore } from '@/store/agentGroup';
|
|
13
|
+
import { agentGroupSelectors } from '@/store/agentGroup/selectors';
|
|
14
|
+
|
|
15
|
+
import { ChatItem } from '../../ChatItem';
|
|
16
|
+
import { dataSelectors, useConversationStore } from '../../store';
|
|
17
|
+
import { AssistantActionsBar } from '../Task/Actions';
|
|
18
|
+
import TaskItem from './TaskItem';
|
|
19
|
+
|
|
20
|
+
interface GroupTasksMessageProps {
|
|
21
|
+
id: string;
|
|
22
|
+
index: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Custom avatar component for GroupTasks
|
|
27
|
+
* Shows GroupAvatar (only task agents, no user) with a ListTodo badge
|
|
28
|
+
*/
|
|
29
|
+
const GroupTasksAvatar = memo<{ avatars: { avatar?: string; background?: string }[] }>(
|
|
30
|
+
({ avatars }) => {
|
|
31
|
+
return (
|
|
32
|
+
<Flexbox flex={'none'} height={28} style={{ position: 'relative' }} width={28}>
|
|
33
|
+
<GroupAvatar
|
|
34
|
+
avatarShape={'square'}
|
|
35
|
+
avatars={avatars.map((a) => ({
|
|
36
|
+
avatar: a.avatar || DEFAULT_AVATAR,
|
|
37
|
+
background: a.background,
|
|
38
|
+
}))}
|
|
39
|
+
cornerShape={'square'}
|
|
40
|
+
size={28}
|
|
41
|
+
/>
|
|
42
|
+
<Block
|
|
43
|
+
align={'center'}
|
|
44
|
+
flex={'none'}
|
|
45
|
+
height={16}
|
|
46
|
+
justify={'center'}
|
|
47
|
+
style={{
|
|
48
|
+
borderRadius: 4,
|
|
49
|
+
position: 'absolute',
|
|
50
|
+
right: -4,
|
|
51
|
+
top: -4,
|
|
52
|
+
}}
|
|
53
|
+
variant={'outlined'}
|
|
54
|
+
width={16}
|
|
55
|
+
>
|
|
56
|
+
<Icon color={cssVar.colorTextDescription} icon={ListTodo} size={10} />
|
|
57
|
+
</Block>
|
|
58
|
+
</Flexbox>
|
|
59
|
+
);
|
|
60
|
+
},
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
GroupTasksAvatar.displayName = 'GroupTasksAvatar';
|
|
64
|
+
|
|
65
|
+
const GroupTasksMessage = memo<GroupTasksMessageProps>(({ id, index }) => {
|
|
66
|
+
const { t } = useTranslation('chat');
|
|
67
|
+
const item = useConversationStore(dataSelectors.getDisplayMessageById(id), isEqual)!;
|
|
68
|
+
const actionsConfig = useConversationStore((s) => s.actionsBar?.assistant);
|
|
69
|
+
const tasks = (item as UIChatMessage)?.tasks?.filter(Boolean) as UIChatMessage[] | undefined;
|
|
70
|
+
|
|
71
|
+
// Get unique agent IDs from tasks
|
|
72
|
+
const taskAgentIds = useMemo(() => {
|
|
73
|
+
if (!tasks) return [];
|
|
74
|
+
const ids = tasks.map((task) => task.agentId).filter(Boolean) as string[];
|
|
75
|
+
return [...new Set(ids)];
|
|
76
|
+
}, [tasks]);
|
|
77
|
+
|
|
78
|
+
// Get active group ID
|
|
79
|
+
const activeGroupId = useAgentGroupStore(agentGroupSelectors.activeGroupId);
|
|
80
|
+
|
|
81
|
+
// Get agent info (avatars and names) for all unique agents in tasks
|
|
82
|
+
const taskAgents = useAgentGroupStore((s) => {
|
|
83
|
+
if (!activeGroupId || taskAgentIds.length === 0) return [];
|
|
84
|
+
return taskAgentIds
|
|
85
|
+
.map((agentId) => {
|
|
86
|
+
const agent = agentGroupSelectors.getAgentByIdFromGroup(activeGroupId, agentId)(s);
|
|
87
|
+
return agent
|
|
88
|
+
? { avatar: agent.avatar, background: agent.backgroundColor, title: agent.title }
|
|
89
|
+
: null;
|
|
90
|
+
})
|
|
91
|
+
.filter(Boolean) as { avatar?: string; background?: string; title?: string }[];
|
|
92
|
+
}, isEqual);
|
|
93
|
+
|
|
94
|
+
// Build title: "Agent1 / Agent2 等 N 个 agents tasks" (show max 2 agents)
|
|
95
|
+
const title = useMemo(() => {
|
|
96
|
+
const agentNames = taskAgents.map((a) => a.title).filter(Boolean);
|
|
97
|
+
if (agentNames.length === 0) return '';
|
|
98
|
+
|
|
99
|
+
const totalAgents = agentNames.length;
|
|
100
|
+
// Show at most 2 agent names
|
|
101
|
+
const displayedAgents = agentNames.slice(0, 2).join(' / ');
|
|
102
|
+
|
|
103
|
+
if (totalAgents <= 2) {
|
|
104
|
+
// Show all agent names when 2 or fewer
|
|
105
|
+
return t('task.groupTasksTitleSimple', {
|
|
106
|
+
agents: displayedAgents,
|
|
107
|
+
count: tasks?.length || 0,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Show "Agent1 / Agent2 等 X 个 agents tasks" when more than 2
|
|
112
|
+
return t('task.groupTasksTitle', {
|
|
113
|
+
agents: displayedAgents,
|
|
114
|
+
count: totalAgents,
|
|
115
|
+
taskCount: tasks?.length || 0,
|
|
116
|
+
});
|
|
117
|
+
}, [taskAgents, tasks?.length, t]);
|
|
118
|
+
|
|
119
|
+
if (!tasks || tasks.length === 0) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const { createdAt } = item;
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<ChatItem
|
|
127
|
+
aboveMessage={null}
|
|
128
|
+
actions={
|
|
129
|
+
<AssistantActionsBar actionsConfig={actionsConfig} data={item} id={id} index={index} />
|
|
130
|
+
}
|
|
131
|
+
avatar={{ title }}
|
|
132
|
+
customAvatarRender={() => <GroupTasksAvatar avatars={taskAgents} />}
|
|
133
|
+
id={id}
|
|
134
|
+
message=""
|
|
135
|
+
placement="left"
|
|
136
|
+
showTitle
|
|
137
|
+
time={createdAt}
|
|
138
|
+
titleAddon={<Tag>{t('task.groupTasks', { count: tasks.length })}</Tag>}
|
|
139
|
+
>
|
|
140
|
+
<Flexbox gap={8} width={'100%'}>
|
|
141
|
+
{tasks.map((task) => (
|
|
142
|
+
<TaskItem item={task} key={task.id} />
|
|
143
|
+
))}
|
|
144
|
+
</Flexbox>
|
|
145
|
+
</ChatItem>
|
|
146
|
+
);
|
|
147
|
+
}, isEqual);
|
|
148
|
+
|
|
149
|
+
GroupTasksMessage.displayName = 'GroupTasksMessage';
|
|
150
|
+
|
|
151
|
+
export default GroupTasksMessage;
|
|
@@ -110,7 +110,13 @@ const GroupMessage = memo<GroupMessageProps>(({ id, index, disableEditing, isLat
|
|
|
110
110
|
titleAddon={<Tag>{t('supervisor.label')}</Tag>}
|
|
111
111
|
>
|
|
112
112
|
{children && children.length > 0 && (
|
|
113
|
-
<Group
|
|
113
|
+
<Group
|
|
114
|
+
blocks={children}
|
|
115
|
+
content={item.content}
|
|
116
|
+
disableEditing={disableEditing}
|
|
117
|
+
id={id}
|
|
118
|
+
messageIndex={index}
|
|
119
|
+
/>
|
|
114
120
|
)}
|
|
115
121
|
{model && (
|
|
116
122
|
<Usage model={model} performance={performance} provider={provider!} usage={usage} />
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { type TaskDetail, ThreadStatus } from '@lobechat/types';
|
|
4
|
-
import { Flexbox } from '@lobehub/ui';
|
|
5
4
|
import { memo, useMemo } from 'react';
|
|
6
5
|
|
|
7
6
|
import BubblesLoading from '@/components/BubblesLoading';
|
|
@@ -9,10 +8,8 @@ import { useChatStore } from '@/store/chat';
|
|
|
9
8
|
import { displayMessageSelectors } from '@/store/chat/selectors';
|
|
10
9
|
import { messageMapKey } from '@/store/chat/utils/messageMapKey';
|
|
11
10
|
|
|
12
|
-
import
|
|
11
|
+
import { TaskMessages } from '../../Tasks/shared';
|
|
13
12
|
import InitializingState from './InitializingState';
|
|
14
|
-
import InstructionAccordion from './InstructionAccordion';
|
|
15
|
-
import ProcessingState from './ProcessingState';
|
|
16
13
|
|
|
17
14
|
interface ClientTaskDetailProps {
|
|
18
15
|
/** Agent ID from the task message (use task's agentId, not activeAgentId) */
|
|
@@ -24,92 +21,76 @@ interface ClientTaskDetailProps {
|
|
|
24
21
|
taskDetail?: TaskDetail;
|
|
25
22
|
}
|
|
26
23
|
|
|
27
|
-
const ClientTaskDetail = memo<ClientTaskDetailProps>(
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
<CompletedState
|
|
99
|
-
assistantId={assistantGroupMessage.id}
|
|
100
|
-
blocks={assistantGroupMessage.children}
|
|
101
|
-
duration={taskDetail?.duration}
|
|
102
|
-
model={model ?? undefined}
|
|
103
|
-
provider={provider ?? undefined}
|
|
104
|
-
totalCost={taskDetail?.totalCost}
|
|
105
|
-
totalTokens={taskDetail?.totalTokens}
|
|
106
|
-
totalToolCalls={taskDetail?.totalToolCalls}
|
|
107
|
-
/>
|
|
108
|
-
)}
|
|
109
|
-
</Flexbox>
|
|
110
|
-
);
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
ClientTaskDetail.displayName = 'ClientClientTaskDetail';
|
|
24
|
+
const ClientTaskDetail = memo<ClientTaskDetailProps>(
|
|
25
|
+
({ agentId: propAgentId, groupId, taskDetail }) => {
|
|
26
|
+
const threadId = taskDetail?.threadId;
|
|
27
|
+
const isExecuting = taskDetail?.status === ThreadStatus.Processing;
|
|
28
|
+
|
|
29
|
+
// Use task message's agentId to query with the correct SubAgent ID that created the thread
|
|
30
|
+
// Fall back to activeAgentId if task message doesn't have agentId (shouldn't happen normally)
|
|
31
|
+
const [activeAgentId, activeTopicId, useFetchMessages] = useChatStore((s) => [
|
|
32
|
+
s.activeAgentId,
|
|
33
|
+
s.activeTopicId,
|
|
34
|
+
s.useFetchMessages,
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
const agentId = propAgentId || activeAgentId;
|
|
38
|
+
|
|
39
|
+
const threadContext = useMemo(
|
|
40
|
+
() => ({
|
|
41
|
+
agentId,
|
|
42
|
+
groupId,
|
|
43
|
+
scope: 'thread' as const,
|
|
44
|
+
threadId,
|
|
45
|
+
topicId: activeTopicId,
|
|
46
|
+
}),
|
|
47
|
+
[agentId, groupId, activeTopicId, threadId],
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
const threadMessageKey = useMemo(
|
|
51
|
+
() => (threadId ? messageMapKey(threadContext) : null),
|
|
52
|
+
[threadId],
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
// Fetch thread messages (skip when executing - messages come from real-time updates)
|
|
56
|
+
useFetchMessages(threadContext, isExecuting);
|
|
57
|
+
|
|
58
|
+
// Get thread messages from store using selector
|
|
59
|
+
const threadMessages = useChatStore((s) =>
|
|
60
|
+
threadMessageKey
|
|
61
|
+
? displayMessageSelectors.getDisplayMessagesByKey(threadMessageKey)(s)
|
|
62
|
+
: undefined,
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
if (!threadMessages) return <BubblesLoading />;
|
|
66
|
+
|
|
67
|
+
// Find the assistantGroup message which contains the children blocks
|
|
68
|
+
const assistantGroupMessage = threadMessages.find((item) => item.role === 'assistantGroup');
|
|
69
|
+
const childrenCount = assistantGroupMessage?.children?.length ?? 0;
|
|
70
|
+
|
|
71
|
+
// Get model/provider from assistantGroup message
|
|
72
|
+
const model = assistantGroupMessage?.model;
|
|
73
|
+
const provider = assistantGroupMessage?.provider;
|
|
74
|
+
|
|
75
|
+
// Initializing state: no status yet (task just created, waiting for client execution)
|
|
76
|
+
if (threadMessages.length === 0 || !assistantGroupMessage?.children || childrenCount === 0) {
|
|
77
|
+
return <InitializingState />;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<TaskMessages
|
|
82
|
+
duration={taskDetail?.duration}
|
|
83
|
+
isProcessing={isExecuting}
|
|
84
|
+
messages={threadMessages}
|
|
85
|
+
model={model ?? undefined}
|
|
86
|
+
provider={provider ?? undefined}
|
|
87
|
+
startTime={assistantGroupMessage.createdAt}
|
|
88
|
+
totalCost={taskDetail?.totalCost}
|
|
89
|
+
/>
|
|
90
|
+
);
|
|
91
|
+
},
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
ClientTaskDetail.displayName = 'ClientTaskDetail';
|
|
114
95
|
|
|
115
96
|
export default ClientTaskDetail;
|