feat(all): 源精度命名体系、工作流可观测台、节点停用管理与白名单归档

核心变更:

  1. GridAxisValue 源精度命名
     - 新增 GridAxisValue 类型,携带 f64 数值 + YAML 源书写文本(Deref 透明兼容算术)
     - config.rs 绕过 serde_yaml 归一化,逐 token 捕获轴值原文(logg: 5.0 → g5.0)
     - runner/executor/scheduler 全链路改用 DB TEXT 列权威 point_name,
       修复 REAL 列回读丢精度导致的 model_name 错配

  2. 工作流执行可观测台
     - 新增 stats/progress/points 三组 API(进度时间序列、经验速率 ETA、
       停滞预警、逐点明细分页、收敛性热力图数据)
     - 新增 workflow_progress_snapshots 表 + tasks/grid_points 耗时列
     - runner 携带 last_iter/worst_depth/n_depths 进 conv.json
     - 前端新增 hash 路由、工作流详情页(概览/网格点/收敛分析三 Tab)、YAML 编辑器

  3. 节点停用/启用管理
     - 新增 disabled 状态 + disable/enable API;停用节点保持心跳但停止分发,
       worker 空闲待命而非退出;移除 revoke API,token 失效统一走重发覆盖;
       移除 host_name 字段

  4. 白名单结果归档
     - 新增 result_filter 模块,只归档有语义产物,丢弃 Tlusty 中间单元(~2MB/模型)
     - executor 原子写入归档 + 200 点 LRU 上限

  5. 历史数据导入
     - sync_seeds 重写为 import_results:经 /admin/import_seed 标记 converged +
       按新版命名迁移产物树

  6. 部署与目录重规划
     - data/results→seeds、data/archive→result + migrate_data_dirs.sh
     - deploy.sh 增强(SSH 复用、Profile、远程 env);Dockerfile 瘦身

  7. 文档同步更新 api/database/architecture/deployment
This commit is contained in:
fmq
2026-07-31 01:34:05 +08:00
parent b91f1e4fa5
commit 1bfa240cb0
73 changed files with 12332 additions and 1608 deletions
+68 -11
View File
@@ -25,19 +25,30 @@ export function escapeHtml(s) {
.replace(/'/g, ''');
}
// 统一 fetch 包装:自动注入 Authorization 头,401 时清除 token 并刷新
export async function apiFetch(url, opts = {}) {
// 统一 fetch 包装:自动注入 Authorization 头,支持 10s 超时处理与 401 处理
export async function apiFetch(url, opts = {}, timeoutMs = 10000) {
const token = getAdminToken();
const headers = { ...(opts.headers || {}) };
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
const res = await fetch(url, { ...opts, headers });
if (res.status === 401) {
clearAdminToken();
location.reload();
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
const signal = opts.signal || controller.signal;
try {
const res = await fetch(url, { ...opts, headers, signal });
clearTimeout(timeoutId);
if (res.status === 401) {
clearAdminToken();
location.reload();
}
return res;
} catch (err) {
clearTimeout(timeoutId);
throw err;
}
return res;
}
// API 请求接口定义
@@ -76,6 +87,15 @@ export async function createWorkflow(workflowData) {
});
}
/** 更新既有工作流配置(PUT)。服务端拒绝 running/initializing 状态覆写(400)。 */
export async function updateWorkflowApi(name, workflowData) {
return apiFetch(`/api/workflows/${encodeURIComponent(name)}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(workflowData),
});
}
export async function startWorkflowApi(name) {
return apiFetch(`/api/workflows/${encodeURIComponent(name)}/start`, { method: 'POST' });
}
@@ -92,6 +112,39 @@ export async function getWorkflowDetailApi(name) {
return apiFetch(`/api/workflows/${encodeURIComponent(name)}`);
}
// ===== 工作流执行观测 API(详情页数据源) =====
// 均返回 raw Response,调用方检查 res.ok && json.success
// opts 可传 { signal } 用于视图卸载时中断在途请求。
export async function fetchWorkflowStatsApi(name, opts = {}) {
return apiFetch(`/api/workflows/${encodeURIComponent(name)}/stats`, opts);
}
export async function fetchWorkflowPointsApi(name, params = {}, opts = {}) {
const qs = new URLSearchParams();
Object.entries(params).forEach(([k, v]) => {
if (v != null && v !== '') qs.set(k, v);
});
const s = qs.toString();
return apiFetch(`/api/workflows/${encodeURIComponent(name)}/points${s ? `?${s}` : ''}`, opts);
}
export async function fetchPointDetailApi(name, point, opts = {}) {
return apiFetch(
`/api/workflows/${encodeURIComponent(name)}/points/${encodeURIComponent(point)}`,
opts
);
}
export async function fetchWorkflowProgressApi(name, params = {}, opts = {}) {
const qs = new URLSearchParams();
Object.entries(params).forEach(([k, v]) => {
if (v != null && v !== '') qs.set(k, v);
});
const s = qs.toString();
return apiFetch(`/api/workflows/${encodeURIComponent(name)}/progress${s ? `?${s}` : ''}`, opts);
}
export async function approveNodeApi(nodeId) {
return apiFetch(`/api/admin/nodes/${encodeURIComponent(nodeId)}/approve`, { method: 'POST' });
}
@@ -100,10 +153,14 @@ export async function rejectNodeApi(nodeId) {
return apiFetch(`/api/admin/nodes/${encodeURIComponent(nodeId)}/reject`, { method: 'POST' });
}
export async function revokeNodeApi(nodeId) {
return apiFetch(`/api/admin/nodes/${encodeURIComponent(nodeId)}/revoke`, { method: 'POST' });
}
export async function reissueNodeApi(nodeId) {
return apiFetch(`/api/admin/nodes/${encodeURIComponent(nodeId)}/reissue`, { method: 'POST' });
}
export async function disableNodeApi(nodeId) {
return apiFetch(`/api/admin/nodes/${encodeURIComponent(nodeId)}/disable`, { method: 'POST' });
}
export async function enableNodeApi(nodeId) {
return apiFetch(`/api/admin/nodes/${encodeURIComponent(nodeId)}/enable`, { method: 'POST' });
}