feat(gateway,admin-web): 可观测面从轮询改为 WS 帧推送(裁决 05.4.3 + 16.4.3-WS)
- 新增三个下行帧(纯新增,既有帧名/形状零改动):observability_log / observability_trace /
observability_metrics,各带 ≤1s 节拍、无新数据不发帧、单帧上限(日志 200 / span 100)
- 帧壳 {type,data} 不带 sessionId;**不进 frameReachesAll 白名单** ⇒ fail-closed 只发 owner+admin
(变异验证两次:加进白名单 → viewer 用例必红)
- 推送开启条件=owner/admin 凭据「读过一次」对应 REST(引导数据仍由该次读取返回),
连接全断即退订并停循环(无常驻 goroutine);既有 REST 端点路径/RBAC/形状完全不变
- 前端:删除 logs/metrics/trace 三处 useAutoReload 轮询;新增 observabilityStream(引导+去重合并+窗口)
与 observabilityViews(三个面板 + WS 徽标),长列表统一走已落地的 VirtualList
- 复核:build/vet 绿 + gateway 全包测试绿(含真 bus→真 /ws/chat 三角色帧断言)+ `-race`;
前端 tsc 0 错 + vite build 成功
- 待登记契约:api-client 需新增「可观测实时推送」一节
This commit is contained in:
1 parent
74b12d87ca
commit
0e1f163ef9
10 files changed
+1449
-312
No files matched your search
@@ -33,6 +33,40 @@ function wsUrl(): string {
|
||||
return `${proto}://${window.location.host}/ws/chat${qs}`;
|
||||
}
|
||||
|
||||
/** 可观测推送帧(裁定 05.4.3 + 16.4.3-WS):日志 / 指标 / trace 由服务端 WS 帧
|
||||
* 推送替代轮询。它们不属任何会话、也不进应用 store,因此单独一条订阅通道:
|
||||
* 只有打开着可观测面板的视图在听,其余时候帧被安静丢弃 —— 不会每秒把监控
|
||||
* 数据灌进设备事件日志。 */
|
||||
export interface ObservabilityFrame {
|
||||
type: string;
|
||||
data: unknown;
|
||||
}
|
||||
|
||||
const OBSERVABILITY_FRAME_TYPES = new Set([
|
||||
"observability_log",
|
||||
"observability_trace",
|
||||
"observability_metrics",
|
||||
]);
|
||||
|
||||
type ObservabilityListener = (frame: ObservabilityFrame) => void;
|
||||
|
||||
const observabilityListeners = new Set<ObservabilityListener>();
|
||||
|
||||
/** 订阅可观测推送帧,返回退订函数。 */
|
||||
export function subscribeObservabilityFrames(listener: ObservabilityListener): () => void {
|
||||
observabilityListeners.add(listener);
|
||||
return () => {
|
||||
observabilityListeners.delete(listener);
|
||||
};
|
||||
}
|
||||
|
||||
/** 把一帧可观测推送派发给所有监听者(没有监听者时即丢弃)。 */
|
||||
function notifyObservability(type: string, data: unknown): void {
|
||||
for (const listener of observabilityListeners) {
|
||||
listener({ type, data });
|
||||
}
|
||||
}
|
||||
|
||||
/** Route one decoded downstream frame into the store. Exported (with a `frame`
|
||||
* shaped first argument) so the frame→store mapping can be exercised as a pure
|
||||
* logic test without a browser: this project has no jsdom/test runner, so the
|
||||
@@ -43,6 +77,14 @@ export function dispatchFrame(ev: { type: string; sessionId?: string; data: unkn
|
||||
const s = useAppStore.getState();
|
||||
const r = toRecord(ev.data);
|
||||
|
||||
// 可观测推送帧(裁定 05.4.3 + 16.4.3-WS:日志/指标/trace 由 WS 推送替代轮询)
|
||||
// 走订阅通道。它们不属任何会话、也不进应用 store:只有打开着可观测面板的
|
||||
// 视图在听,其余时候帧被安静丢弃,不会每秒往设备事件日志里灌监控数据。
|
||||
if (OBSERVABILITY_FRAME_TYPES.has(ev.type)) {
|
||||
notifyObservability(ev.type, ev.data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (handleStreamEvent(ev, r, s)) return;
|
||||
if (handleThinkingEvent(ev, r, s)) return;
|
||||
if (handleToolEvent(ev, r, s)) return;
|
||||
|
||||
Reference in new issue
Block a user