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:
@ -47,6 +47,9 @@ type KernelStatus struct {
|
||||
|
||||
Tracker TrackerStatus `json:"tracker"`
|
||||
|
||||
// Residents 是驻留式子 agent 的运行时视图(数量 = len(Residents))。
|
||||
Residents []ResidentStatus `json:"residents"`
|
||||
|
||||
// Scheduler 是输入调度器的运行时快照(可观测性,设计文档 §11 O1/O2)。
|
||||
// M2 起输入不再直接排队在 channel 上,而是经 readyQueue/pendingInterrupts/
|
||||
// suspendStack 三集合按优先级调度;这里把这些状态暴露出来。
|
||||
@ -57,12 +60,30 @@ type KernelStatus struct {
|
||||
type SchedulerStatus struct {
|
||||
// Running 是当前执行的任务(空表示空闲)。
|
||||
Running *SchedulerTask `json:"running,omitempty"`
|
||||
// Immediate 是刚抢占成功、将在下一个安全点立即运行的中断(最多一个)。
|
||||
Immediate *SchedulerTask `json:"immediate,omitempty"`
|
||||
// ReadyQueueDepth / PendingInterrupts / SuspendStack 是三个集合的深度。
|
||||
ReadyQueueDepth int `json:"ready_queue_depth"`
|
||||
PendingInterrupts int `json:"pending_interrupts"`
|
||||
SuspendStack int `json:"suspend_stack"`
|
||||
MaxSuspendDepth int `json:"max_suspend_depth"`
|
||||
|
||||
// InterruptQueues 是**四条中断队列各自的深度**,下标即中断级别(1..4);
|
||||
// 下标 0 恒为 0,这样 level 可以直接当数组下标用,省掉调用方 ±1 的翻译。
|
||||
//
|
||||
// 为什么单列:PendingInterrupts 只是总数,看不清"堵在哪一级"——
|
||||
// 四级中断是抢占优先级,堵在 L1 还是 L4 是完全不同的运行状态。
|
||||
InterruptQueues [5]int `json:"interrupt_queues"`
|
||||
|
||||
// SuspendFrames 是中断栈的帧,**栈底 → 栈顶**(只暴露任务标识,不含帧内容)。
|
||||
// 深度见 SuspendStack;帧的顺序回答了"谁被谁打断"。
|
||||
SuspendFrames []SchedulerFrame `json:"suspend_frames,omitempty"`
|
||||
|
||||
// InterruptsByLevel / PreemptsByLevel 是各级中断的累计计数(下标 1..4):
|
||||
// 前者=被登记次数(含没抢成的),后者=判定可抢占并进入 immediate 的次数。
|
||||
InterruptsByLevel [5]uint64 `json:"interrupts_by_level"`
|
||||
PreemptsByLevel [5]uint64 `json:"preempts_by_level"`
|
||||
|
||||
Enqueued uint64 `json:"enqueued"`
|
||||
Executed uint64 `json:"executed"`
|
||||
Rejected uint64 `json:"rejected"`
|
||||
@ -71,6 +92,26 @@ type SchedulerStatus struct {
|
||||
Preempted uint64 `json:"preempted"`
|
||||
}
|
||||
|
||||
// SchedulerFrame 是中断栈里的一帧(供图形化展示"压了几层现场")。
|
||||
type SchedulerFrame struct {
|
||||
Task SchedulerTask `json:"task"`
|
||||
}
|
||||
|
||||
// ResidentStatus 是驻留式子 agent 的运行时视图。
|
||||
//
|
||||
// 为什么进状态面:驻留子是"常驻的独立 agent",它们的数量、轮次与上下文占用
|
||||
// 是运行态里最需要一眼看到的东西(此前只在日志里,WebUI 只能显示文字)。
|
||||
type ResidentStatus struct {
|
||||
ID string `json:"id"`
|
||||
State string `json:"state"`
|
||||
Rounds int `json:"rounds"`
|
||||
ContextFull bool `json:"context_full"`
|
||||
InputChs []string `json:"input_channels,omitempty"`
|
||||
AllowedOutputs []string `json:"allowed_outputs,omitempty"`
|
||||
InputChTable int `json:"input_ch_table"`
|
||||
CreatedAt string `json:"created_at,omitempty"`
|
||||
}
|
||||
|
||||
// SchedulerTask 是任务的最小标识(不暴露帧内容)。
|
||||
type SchedulerTask struct {
|
||||
ID uint64 `json:"id"`
|
||||
@ -97,9 +138,18 @@ type PluginInfo struct {
|
||||
}
|
||||
|
||||
type ChannelInfo struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Ready bool `json:"ready"`
|
||||
Name string `json:"name"`
|
||||
// Type 保持为 DeviceType 的数字字符串(历史字段,别改语义)。
|
||||
Type string `json:"type"`
|
||||
// Direction 是方向的可读名:in(只进)/ out(只出)/ io(双向)。
|
||||
Direction string `json:"direction"`
|
||||
Ready bool `json:"ready"`
|
||||
// 以下三项供通道拓扑展示:此前 collectKernelStatus 只透传了 Name/Type,
|
||||
// 把 Description/Tools/OutputCaps 全丢了,前端只能画出一排光秃秃的名字。
|
||||
Description string `json:"description,omitempty"`
|
||||
Tools []string `json:"tools,omitempty"`
|
||||
OutputCaps int `json:"output_caps"`
|
||||
CapsText string `json:"caps_text,omitempty"`
|
||||
}
|
||||
|
||||
type MemoryStatus struct {
|
||||
|
||||
Reference in New Issue
Block a user