mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-10-04 00:03:59 +00:00
feat(status+webui): 运行态图形化 —— 排队/四级中断队列/中断栈/驻留子/通道拓扑
需求:首页不该只有文字,要能一眼看出内核在忙什么——排队消息数、各级中断
排队与中断栈、驻留子 agent 数量;这些要向**内部 SDK 暴露接口**,供 WebUI 等应用
展示;通道划分也要能画出来。
## 一、内核状态面(internal/sdk,内部 SDK,不受公开 SDK 冻结约束)
* SchedulerStatus 补:
- interrupt_queues[5]:**四级中断队列各自的深度**(下标即级别 1..4,下标 0 恒 0,
这样 level 能直接当数组下标用)。此前只有 pending_interrupts 总数,
看不出"堵在 L1 还是 L4"——四级是抢占优先级,堵在哪级是完全不同的运行状态。
- immediate:刚抢占成功、下一个安全点立即运行的那个中断(此前完全不可见)。
- suspend_frames:中断栈的帧(栈底→栈顶,只给任务标识),depth 之外还能看出
"谁被谁打断"。
- interrupts_by_level / preempts_by_level:各级累计登记数与抢占成功数。
* KernelStatus 补 residents(驻留子运行时视图:状态/轮次/上下文已满/输入通道/允许输出)。
刻意**不带**每个驻留子的 inputch 登记明细——状态面会被反复轮询,明细会让
每次 /status 背上几十 KB;只给表大小,要明细走专门接口。
* ChannelInfo 补 direction(in/out/io)、description、tools、output_caps、caps_text。
此前 collectKernelStatus 只透传 Name/Type,把描述/工具/能力**全丢了**,
前端只能画出一排光秃秃的名字。
## 二、WebUI
* 新增只读 `/api/v1/runtime`:只回运行态三件事(scheduler/residents/channels),
实测 **1.0KB**(/kernel 是 30KB 级)——所以能 3 秒轮询做"实时"感,
而不必反复拉全量状态。
* 首页新增「运行态」面板(纯 CSS + 内联 SVG,前端仍无构建链):
- 四个数字块:排队任务 / 待处理中断 / 中断栈(深度/上限) / 驻留子 Agent,带占比条;
- 四级中断队列条形图:每级"深度 · 登记/抢占",四级语义**照抄内核**
(L4 内核独占 / L3 交互 / L2 消息 / L1 后台),不自己起名字;
- 中断栈层叠图(栈顶在上)+ ⚡立即运行项;
- 通道拓扑:输入通道 → 内核 → 输出通道,双向通道两侧都出现,能力以胶囊标签显示。
* 3 秒轮询只在总览页可见时才发请求;切回总览时 renderAll 会立刻补一次。
## 验证
* 新增 TestSchedulerStatusExposesLevelsAndStack(四级队列/立即项/栈帧/各级计数映射,
并断言"未使用的级别必须为 0"与"下标 0 恒 0")、TestChannelInfoCarriesTopology、
TestRuntimeEndpoint(形状 + 不携带 tools/plugins + 无状态源时 503)。
* go build / vet / agent+core+sdk+plugin+webui 全量测试绿。
* 真实浏览器实测(CDP 驱动,注入运行态样本走真实渲染路径):
数字块 [3, 5, 2/4, 2];四级条 L4=1/L3=2/L2=1/L1=1 与数据一致;
栈帧按"栈底→栈顶"渲染且标出栈顶;通道左右分列、io 通道两侧都出现。
This commit is contained in:
@ -25,6 +25,9 @@ type StatusProvider interface {
|
||||
type KernelStatus = sdk.KernelStatus
|
||||
type PluginInfo = sdk.PluginInfo
|
||||
type ChannelInfo = sdk.ChannelInfo
|
||||
|
||||
// ResidentStatus 是驻留式子 agent 的运行时视图(见 internal/sdk/status.go)。
|
||||
type ResidentStatus = sdk.ResidentStatus
|
||||
type MemoryStatus = sdk.MemoryStatus
|
||||
type KnowledgeStatus = sdk.KnowledgeStatus
|
||||
type DocumentStatus = sdk.DocumentStatus
|
||||
@ -37,10 +40,32 @@ type TrackerStatus = sdk.TrackerStatus
|
||||
type BuildStatus = sdk.BuildStatus
|
||||
|
||||
func channelInfoFromIO(ch agentIO.ChannelInfo) ChannelInfo {
|
||||
tools := make([]string, 0, len(ch.Tools))
|
||||
for _, t := range ch.Tools {
|
||||
tools = append(tools, t.Name)
|
||||
}
|
||||
return ChannelInfo{
|
||||
Name: ch.Name,
|
||||
Type: fmt.Sprintf("%d", ch.Type),
|
||||
Ready: true,
|
||||
Name: ch.Name,
|
||||
Type: fmt.Sprintf("%d", ch.Type),
|
||||
Direction: channelDirection(ch.Type),
|
||||
Ready: true,
|
||||
Description: ch.Description,
|
||||
Tools: tools,
|
||||
OutputCaps: int(ch.OutputCaps),
|
||||
CapsText: ch.OutputCaps.String(),
|
||||
}
|
||||
}
|
||||
|
||||
// channelDirection 把 DeviceType 翻成可读方向(前端画拓扑用)。
|
||||
// 未知取值落到 "io":宁可当作双向,也不要把它画成只进或只出。
|
||||
func channelDirection(t agentIO.DeviceType) string {
|
||||
switch t {
|
||||
case agentIO.DeviceInput:
|
||||
return "in"
|
||||
case agentIO.DeviceOutput:
|
||||
return "out"
|
||||
default:
|
||||
return "io"
|
||||
}
|
||||
}
|
||||
|
||||
@ -221,6 +246,7 @@ func (a *Agent) GetKernelStatus() *KernelStatus {
|
||||
)
|
||||
ks.ONNX = a.onnxStatus()
|
||||
ks.Scheduler = a.schedulerStatus()
|
||||
ks.Residents = residentStatuses(a.Residents())
|
||||
|
||||
return ks
|
||||
}
|
||||
@ -254,3 +280,28 @@ func (a *Agent) onnxStatus() sdk.ONNXStatus {
|
||||
|
||||
var _ StatusProvider = (*Agent)(nil)
|
||||
var _ sdk.StatusAPI = (*Agent)(nil)
|
||||
|
||||
// residentStatuses 把内核的驻留子视图映射成 SDK DTO。
|
||||
//
|
||||
// 刻意**不带** ResidentInfo.Table(那是每个驻留子的 inputch 登记明细):状态面
|
||||
// 是给所有应用轮询的,把它塞进来会让每次 /status 都背上几十 KB。
|
||||
// 只给表的大小(InputChTable),要看明细走专门的接口。
|
||||
func residentStatuses(list []ResidentInfo) []ResidentStatus {
|
||||
out := make([]ResidentStatus, 0, len(list))
|
||||
for _, r := range list {
|
||||
st := ResidentStatus{
|
||||
ID: r.ID,
|
||||
State: r.State,
|
||||
Rounds: r.Rounds,
|
||||
ContextFull: r.ContextFull,
|
||||
InputChs: r.InputChs,
|
||||
AllowedOutputs: r.AllowedOutputs,
|
||||
InputChTable: r.TableSize,
|
||||
}
|
||||
if !r.CreatedAt.IsZero() {
|
||||
st.CreatedAt = r.CreatedAt.Format(time.RFC3339)
|
||||
}
|
||||
out = append(out, st)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user