feat(web): Task 卡片差异显示子代理模型名 + 子代理完成状态转发修复
- Task/tool 卡片头部在子代理模型与主代理不同(model 或 provider 任一不同)时显示模型徽章 - useChat 放行带 subagent_task_id 的 execution_completed 消息,更新卡片 running→completed 状态
This commit is contained in:
parent
53e2fb6dc6
commit
73c25e5a20
@ -89,6 +89,8 @@ interface MessageBubbleProps {
|
|||||||
message: ChatMessage;
|
message: ChatMessage;
|
||||||
onNavigateToSubAgent?: (taskId: string, description: string, subagentType?: string) => void;
|
onNavigateToSubAgent?: (taskId: string, description: string, subagentType?: string) => void;
|
||||||
showThinking?: boolean;
|
showThinking?: boolean;
|
||||||
|
/** 主代理当前生效模型(Task 卡片差异显示:子代理模型不同才展示模型徽章) */
|
||||||
|
effectiveModel?: { provider: string; model: string } | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAttachmentIcon(mediaType: string) {
|
function getAttachmentIcon(mediaType: string) {
|
||||||
@ -352,6 +354,7 @@ export const MessageBubble = memo(function MessageBubble({
|
|||||||
message,
|
message,
|
||||||
onNavigateToSubAgent,
|
onNavigateToSubAgent,
|
||||||
showThinking = true,
|
showThinking = true,
|
||||||
|
effectiveModel,
|
||||||
}: MessageBubbleProps) {
|
}: MessageBubbleProps) {
|
||||||
const isUser = message.role === 'user';
|
const isUser = message.role === 'user';
|
||||||
const isTool = message.role === 'tool';
|
const isTool = message.role === 'tool';
|
||||||
@ -484,6 +487,19 @@ export const MessageBubble = memo(function MessageBubble({
|
|||||||
// 安全获取 task 状态配色,未知状态回退到默认(避免 undefined.borderColor 崩溃)
|
// 安全获取 task 状态配色,未知状态回退到默认(避免 undefined.borderColor 崩溃)
|
||||||
const taskStyle = taskResult ? (taskStatusConfig[taskResult.status] ?? taskStatusConfig.failed) : null;
|
const taskStyle = taskResult ? (taskStatusConfig[taskResult.status] ?? taskStatusConfig.failed) : null;
|
||||||
|
|
||||||
|
// 子代理模型徽章:结果携带模型信息,且(无主代理参照 或 model/provider 任一不同)时展示。
|
||||||
|
// provider 不同即使 model 同名也显示——不同 provider 的同名模型在路由/计费上已不同。
|
||||||
|
const modelDiffers =
|
||||||
|
!effectiveModel ||
|
||||||
|
effectiveModel.model !== taskResult?.model ||
|
||||||
|
effectiveModel.provider !== taskResult?.provider;
|
||||||
|
const subagentModelLabel =
|
||||||
|
taskResult?.model && modelDiffers
|
||||||
|
? effectiveModel && effectiveModel.provider === taskResult.provider
|
||||||
|
? taskResult.model
|
||||||
|
: `${taskResult.provider ?? '?'}/${taskResult.model}`
|
||||||
|
: null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-message-id={message.id} className="animate-slide-in">
|
<div data-message-id={message.id} className="animate-slide-in">
|
||||||
<div
|
<div
|
||||||
@ -504,6 +520,14 @@ export const MessageBubble = memo(function MessageBubble({
|
|||||||
? `${message.toolName || 'Tool'}${taskDescription ? ` · ${taskDescription}` : ''}`
|
? `${message.toolName || 'Tool'}${taskDescription ? ` · ${taskDescription}` : ''}`
|
||||||
: message.toolName || 'Tool'}
|
: message.toolName || 'Tool'}
|
||||||
</span>
|
</span>
|
||||||
|
{subagentModelLabel && (
|
||||||
|
<span
|
||||||
|
className="flex-shrink-0 rounded-full border border-[var(--border-color)] bg-[var(--bg-tertiary)] px-1.5 py-px text-[10px] leading-4 text-[var(--text-muted)] max-w-[160px] truncate"
|
||||||
|
title={`子代理模型:${taskResult?.provider ?? '?'} / ${taskResult?.model}`}
|
||||||
|
>
|
||||||
|
{subagentModelLabel}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
<span
|
<span
|
||||||
className={`flex-shrink-0 transition-all duration-300 ${
|
className={`flex-shrink-0 transition-all duration-300 ${
|
||||||
taskStyle ? taskStyle.iconColor : statusConfig.iconColor
|
taskStyle ? taskStyle.iconColor : statusConfig.iconColor
|
||||||
|
|||||||
@ -12,6 +12,8 @@ interface MessageListProps {
|
|||||||
viewKey?: string;
|
viewKey?: string;
|
||||||
/** 高亮的消息 ID,点击待办项后滚动并高亮显示 */
|
/** 高亮的消息 ID,点击待办项后滚动并高亮显示 */
|
||||||
highlightedMessageId?: string | null;
|
highlightedMessageId?: string | null;
|
||||||
|
/** 主代理当前生效模型(透传给 MessageBubble 做 Task 卡片差异显示) */
|
||||||
|
effectiveModel?: { provider: string; model: string } | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MessageList({
|
export function MessageList({
|
||||||
@ -20,6 +22,7 @@ export function MessageList({
|
|||||||
showThinking = true,
|
showThinking = true,
|
||||||
viewKey,
|
viewKey,
|
||||||
highlightedMessageId,
|
highlightedMessageId,
|
||||||
|
effectiveModel,
|
||||||
}: MessageListProps) {
|
}: MessageListProps) {
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
const isAtBottomRef = useRef(true);
|
const isAtBottomRef = useRef(true);
|
||||||
@ -274,6 +277,7 @@ export function MessageList({
|
|||||||
message={message}
|
message={message}
|
||||||
onNavigateToSubAgent={onNavigateToSubAgent}
|
onNavigateToSubAgent={onNavigateToSubAgent}
|
||||||
showThinking={showThinking}
|
showThinking={showThinking}
|
||||||
|
effectiveModel={effectiveModel}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -182,7 +182,9 @@ export function useChat(): UseChatReturn {
|
|||||||
|
|
||||||
// Tier 3: 主视图路由
|
// Tier 3: 主视图路由
|
||||||
// 3a: 带 subagent_task_id 的消息在主视图直接丢弃(已在 Tier 2 未命中)
|
// 3a: 带 subagent_task_id 的消息在主视图直接丢弃(已在 Tier 2 未命中)
|
||||||
if (getSubagentTaskId(message)) return;
|
// 例外:execution_completed 携带 subagent_status,需转发到 handleMainViewMessage
|
||||||
|
// 更新主视图 task 卡片占位状态(running → completed 等),否则卡片永远显示运行中
|
||||||
|
if (getSubagentTaskId(message) && message.type !== 'execution_completed') return;
|
||||||
|
|
||||||
// 3b: 非 chat 消息的 case 分发
|
// 3b: 非 chat 消息的 case 分发
|
||||||
switch (message.type) {
|
switch (message.type) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user