feat(webui): 总览改版 —— 阶段管道滑块 / per-agent 负载环 / 通道→agent 拓扑与光点

总览页此前是一堆数字与文字块,看不出「这一轮走到哪、谁忙、消息从哪进哪出」。
本次把运行态面板改成以图形为主:

- 阶段管道:七阶段滑块,由 SSE stage 事件驱动,当前阶段高亮、滑块滑过去;
  一轮结束(after_output 或 2.5s 无事件)自动回到空闲,不做假动画。
- 队列与中断栈:沿用五条进度条(L1–L4 + 排队),中断栈补一条深度进度条。
- Agent 拓扑:改成「每 agent 一条横带」——左 inputch、中 agent 节点(圆环 = 负载)、
  右 outputch,连线即路由;删掉旧的「归属框 + 单个内核盒」画法(看得出哪个子接了哪条输入)。
- 光点动画:channel_input(新增轻量 SSE 事件)沿 inputch→agent 连线跑;
  agent_output 沿 agent→outputch 连线跑。用 SMIL animateMotion,不需要 rAF 循环。
- 负载:由该 agent **自己的**调度器积压(排队 / 四级中断 / 中断栈)按级别加权折算,
  环形图展示。为此把驻留子的调度器积压透出到状态面(SDK 纯追加字段)。

后端:sdk.ResidentStatus / core.ResidentInfo 增加子 agent 调度器积压四项;
WebUI SSE 增加 channel_input 轻量事件(只带通道名与 agent id,不带正文)。

顺带收口对话区视觉(页签改分段控件、消息间距/气泡区分、输入区分隔线)。
This commit is contained in:
JianFeeeee
2026-09-14 15:33:23 +08:00
parent 0fda210e8b
commit e4d69fa140
6 changed files with 454 additions and 141 deletions

View File

@ -771,6 +771,29 @@ func (h *Handler) handleChatEvents(w http.ResponseWriter, r *http.Request) {
})
unsubs = append(unsubs, unsub)
}
// channel_input:把「哪条输入通道刚进来一条消息、由哪个 agent 接手」单独推一条
// **轻量**事件,供总览页拓扑画「光点进入 agent」的动画。
//
// 为什么不直接把 raw_input 放进 subTypes:那条事件的 payload 带整条输入正文
// (用户消息,可能几 KB),而拓扑只需要 `source`(通道名)与发出者的 agent id。
// 全量转发会让每次用户说话都在 SSE 上多背一份正文。
unsubInput := h.sdk.Subscribe(sdk.EventRawInput, func(evt *sdk.Event) {
src, _ := evt.Payload["source"].(string)
if src == "" {
return
}
data, _ := json.Marshal(map[string]interface{}{
"type": "channel_input",
"source": src,
"agent": evt.Source,
"timestamp": evt.Timestamp,
})
seq++
id := fmt.Sprintf("%d-%d", evt.Timestamp, seq)
sendSSE(writeCh, id, "channel_input", string(data))
})
unsubs = append(unsubs, unsubInput)
defer func() {
for _, unsub := range unsubs {
unsub()