fix: Display aggregated findings for the orchestrator and task, and show sub-agent duration/status in UI.
This commit is contained in:
parent
2a332d6eda
commit
a9a22b91c7
|
|
@ -1919,6 +1919,9 @@ async def get_agent_tree(
|
||||||
logger.debug(f"[AgentTree API] tree nodes={len(tree.get('nodes', {}))}, root={tree.get('root_agent_id')}")
|
logger.debug(f"[AgentTree API] tree nodes={len(tree.get('nodes', {}))}, root={tree.get('root_agent_id')}")
|
||||||
logger.debug(f"[AgentTree API] 节点详情: {list(tree.get('nodes', {}).keys())}")
|
logger.debug(f"[AgentTree API] 节点详情: {list(tree.get('nodes', {}).keys())}")
|
||||||
|
|
||||||
|
# 🔥 获取 root agent ID,用于判断是否是 Orchestrator
|
||||||
|
root_agent_id = tree.get("root_agent_id")
|
||||||
|
|
||||||
# 构建节点列表
|
# 构建节点列表
|
||||||
nodes = []
|
nodes = []
|
||||||
for agent_id, node_data in tree.get("nodes", {}).items():
|
for agent_id, node_data in tree.get("nodes", {}).items():
|
||||||
|
|
@ -1935,10 +1938,15 @@ async def get_agent_tree(
|
||||||
tool_calls = agent_stats.get("tool_calls", 0)
|
tool_calls = agent_stats.get("tool_calls", 0)
|
||||||
tokens_used = agent_stats.get("tokens_used", 0)
|
tokens_used = agent_stats.get("tokens_used", 0)
|
||||||
|
|
||||||
# 从结果中获取发现数量
|
# 🔥 FIX: 对于 Orchestrator (root agent),使用 task 的 findings_count
|
||||||
if node_data.get("result"):
|
# 这确保了正确显示聚合的 findings 总数
|
||||||
result = node_data.get("result", {})
|
if agent_id == root_agent_id:
|
||||||
findings_count = len(result.get("findings", []))
|
findings_count = task.findings_count or 0
|
||||||
|
else:
|
||||||
|
# 从结果中获取发现数量(对于子 agent)
|
||||||
|
if node_data.get("result"):
|
||||||
|
result = node_data.get("result", {})
|
||||||
|
findings_count = len(result.get("findings", []))
|
||||||
|
|
||||||
nodes.append(AgentTreeNodeResponse(
|
nodes.append(AgentTreeNodeResponse(
|
||||||
id=node_data.get("id", agent_id),
|
id=node_data.get("id", agent_id),
|
||||||
|
|
@ -1956,14 +1964,15 @@ async def get_agent_tree(
|
||||||
children=[],
|
children=[],
|
||||||
))
|
))
|
||||||
|
|
||||||
|
# 🔥 使用 task.findings_count 作为 total_findings,确保一致性
|
||||||
return AgentTreeResponse(
|
return AgentTreeResponse(
|
||||||
task_id=task_id,
|
task_id=task_id,
|
||||||
root_agent_id=tree.get("root_agent_id"),
|
root_agent_id=root_agent_id,
|
||||||
total_agents=stats.get("total", 0),
|
total_agents=stats.get("total", 0),
|
||||||
running_agents=stats.get("running", 0),
|
running_agents=stats.get("running", 0),
|
||||||
completed_agents=stats.get("completed", 0),
|
completed_agents=stats.get("completed", 0),
|
||||||
failed_agents=stats.get("failed", 0),
|
failed_agents=stats.get("failed", 0),
|
||||||
total_findings=sum(n.findings_count for n in nodes),
|
total_findings=task.findings_count or 0,
|
||||||
nodes=nodes,
|
nodes=nodes,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -1989,7 +1998,6 @@ async def get_agent_tree(
|
||||||
running = 0
|
running = 0
|
||||||
completed = 0
|
completed = 0
|
||||||
failed = 0
|
failed = 0
|
||||||
total_findings = 0
|
|
||||||
|
|
||||||
for node in db_nodes:
|
for node in db_nodes:
|
||||||
if node.parent_agent_id is None:
|
if node.parent_agent_id is None:
|
||||||
|
|
@ -2002,7 +2010,13 @@ async def get_agent_tree(
|
||||||
elif node.status == "failed":
|
elif node.status == "failed":
|
||||||
failed += 1
|
failed += 1
|
||||||
|
|
||||||
total_findings += node.findings_count or 0
|
# 🔥 FIX: 对于 Orchestrator (root agent),使用 task 的 findings_count
|
||||||
|
# 这确保了正确显示聚合的 findings 总数
|
||||||
|
if node.parent_agent_id is None:
|
||||||
|
# Root agent uses task's total findings
|
||||||
|
node_findings_count = task.findings_count or 0
|
||||||
|
else:
|
||||||
|
node_findings_count = node.findings_count or 0
|
||||||
|
|
||||||
nodes.append(AgentTreeNodeResponse(
|
nodes.append(AgentTreeNodeResponse(
|
||||||
id=node.id,
|
id=node.id,
|
||||||
|
|
@ -2015,7 +2029,7 @@ async def get_agent_tree(
|
||||||
knowledge_modules=node.knowledge_modules,
|
knowledge_modules=node.knowledge_modules,
|
||||||
status=node.status,
|
status=node.status,
|
||||||
result_summary=node.result_summary,
|
result_summary=node.result_summary,
|
||||||
findings_count=node.findings_count or 0,
|
findings_count=node_findings_count,
|
||||||
iterations=node.iterations or 0,
|
iterations=node.iterations or 0,
|
||||||
tokens_used=node.tokens_used or 0,
|
tokens_used=node.tokens_used or 0,
|
||||||
tool_calls=node.tool_calls or 0,
|
tool_calls=node.tool_calls or 0,
|
||||||
|
|
@ -2023,6 +2037,7 @@ async def get_agent_tree(
|
||||||
children=[],
|
children=[],
|
||||||
))
|
))
|
||||||
|
|
||||||
|
# 🔥 使用 task.findings_count 作为 total_findings,确保一致性
|
||||||
return AgentTreeResponse(
|
return AgentTreeResponse(
|
||||||
task_id=task_id,
|
task_id=task_id,
|
||||||
root_agent_id=root_id,
|
root_agent_id=root_id,
|
||||||
|
|
@ -2030,7 +2045,7 @@ async def get_agent_tree(
|
||||||
running_agents=running,
|
running_agents=running,
|
||||||
completed_agents=completed,
|
completed_agents=completed,
|
||||||
failed_agents=failed,
|
failed_agents=failed,
|
||||||
total_findings=total_findings,
|
total_findings=task.findings_count or 0,
|
||||||
nodes=nodes,
|
nodes=nodes,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@
|
||||||
|
|
||||||
import { memo } from "react";
|
import { memo } from "react";
|
||||||
import { X, Cpu, Scan, FileSearch, ShieldCheck, Bot, Repeat, Zap, Bug, FileCode, Clock, Network } from "lucide-react";
|
import { X, Cpu, Scan, FileSearch, ShieldCheck, Bot, Repeat, Zap, Bug, FileCode, Clock, Network } from "lucide-react";
|
||||||
import { Badge } from "@/components/ui/badge";
|
|
||||||
import { AGENT_STATUS_CONFIG } from "../constants";
|
import { AGENT_STATUS_CONFIG } from "../constants";
|
||||||
import { findAgentInTree } from "../utils";
|
import { findAgentInTree } from "../utils";
|
||||||
import type { AgentDetailPanelProps } from "../types";
|
import type { AgentDetailPanelProps } from "../types";
|
||||||
|
|
@ -119,16 +118,36 @@ export const AgentDetailPanel = memo(function AgentDetailPanel({ agentId, treeNo
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Findings */}
|
{/* Findings - Only show for Orchestrator (root agent with no parent) */}
|
||||||
<div className="flex items-center gap-2 p-2 rounded bg-gray-900/30 border border-gray-800/30">
|
{!agent.parent_agent_id && (
|
||||||
<Bug className={`w-3.5 h-3.5 ${agent.findings_count > 0 ? 'text-red-400/70' : 'text-gray-500/70'}`} />
|
<div className="flex items-center gap-2 p-2 rounded bg-gray-900/30 border border-gray-800/30">
|
||||||
<div>
|
<Bug className={`w-3.5 h-3.5 ${agent.findings_count > 0 ? 'text-red-400/70' : 'text-gray-500/70'}`} />
|
||||||
<div className="text-[9px] text-gray-600 uppercase">Findings</div>
|
<div>
|
||||||
<div className={`text-sm font-mono ${agent.findings_count > 0 ? 'text-red-400' : 'text-white'}`}>
|
<div className="text-[9px] text-gray-600 uppercase">Findings</div>
|
||||||
{agent.findings_count}
|
<div className={`text-sm font-mono ${agent.findings_count > 0 ? 'text-red-400' : 'text-white'}`}>
|
||||||
|
{agent.findings_count}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
|
|
||||||
|
{/* Duration/Status - Show for sub-agents instead of Findings */}
|
||||||
|
{agent.parent_agent_id && (
|
||||||
|
<div className="flex items-center gap-2 p-2 rounded bg-gray-900/30 border border-gray-800/30">
|
||||||
|
<Clock className="w-3.5 h-3.5 text-slate-400/70" />
|
||||||
|
<div>
|
||||||
|
<div className="text-[9px] text-gray-600 uppercase">
|
||||||
|
{agent.duration_ms ? "Duration" : "Status"}
|
||||||
|
</div>
|
||||||
|
<div className="text-sm text-white font-mono">
|
||||||
|
{agent.duration_ms
|
||||||
|
? `${(agent.duration_ms / 1000).toFixed(1)}s`
|
||||||
|
: (AGENT_STATUS_CONFIG[agent.status]?.text || agent.status)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Tokens */}
|
{/* Tokens */}
|
||||||
<div className="flex items-center gap-2 p-2 rounded bg-gray-900/30 border border-gray-800/30">
|
<div className="flex items-center gap-2 p-2 rounded bg-gray-900/30 border border-gray-800/30">
|
||||||
|
|
|
||||||
|
|
@ -133,8 +133,8 @@ export const AgentTreeNodeItem = memo(function AgentTreeNodeItem({
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Findings count */}
|
{/* Findings count - Only show for Orchestrator (root agent) */}
|
||||||
{node.findings_count > 0 && (
|
{!node.parent_agent_id && node.findings_count > 0 && (
|
||||||
<Badge className="h-4 px-1.5 text-[9px] bg-rose-500/25 text-rose-300 border border-rose-500/40 font-mono font-semibold">
|
<Badge className="h-4 px-1.5 text-[9px] bg-rose-500/25 text-rose-300 border border-rose-500/40 font-mono font-semibold">
|
||||||
{node.findings_count}
|
{node.findings_count}
|
||||||
</Badge>
|
</Badge>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue