feat(desktop): 运行·观测 重做为运行历史 + 复盘(Tier2,用上 exec Redis 回放)
RunsView 原本只能看「最近一次」实时运行;现做成完整的运行历史复盘: 后端 GET /api/v1/runs?limit=(store.RecentRuns):任务 LEFT JOIN 评测,返回 task_id/status/time + eval level/overall,供历史列表。 前端 RunsView:左侧运行历史列表(状态点 + 相对时间 + 评测分级徽标),点选任一历史运行 → 经 streamExec/streamTokens 从 Redis 回放该次执行轨迹 + 模型输出(对已完成任务流即回放), 并取 /tasks/:id/eval 显示评测(分级/忠实度/纠偏/评语/flags)。「当前运行」固定置顶沿用实时订阅。 api 补 listRuns + taskEval。 这正是之前 exec 轨迹 Redis 回放的消费场景。live(vite + 预览):提一新任务 → 历史列表出现 → 点选 → 完整回放轨迹(2 节点/2518ms/含推理过程)+ 答案全文 + 评测 ok·1.00,无控制台报错。 tsc+vite 构建通过,gateway 全绿。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -421,6 +421,17 @@ func (h *Handler) StatsOverview(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// Runs: GET /api/v1/runs?limit= —— 运行历史列表(任务 + 评测分级),供「运行 · 观测」复盘。
|
||||
func (h *Handler) Runs(c *gin.Context) {
|
||||
limit := 30
|
||||
if v := c.Query("limit"); v != "" {
|
||||
if n, err := strconv.Atoi(v); err == nil && n > 0 && n <= 200 {
|
||||
limit = n
|
||||
}
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"runs": h.db.RecentRuns(c.Request.Context(), limit)})
|
||||
}
|
||||
|
||||
// ListMemory: GET /api/v1/memory —— 列出当前用户的全部偏好(结构化,供记忆面板)。
|
||||
func (h *Handler) ListMemory(c *gin.Context) {
|
||||
res, err := h.bus.CallTool(c.Request.Context(), contract.ToolSubjectGo("memory_list"),
|
||||
|
||||
@@ -73,6 +73,7 @@ func New(db *store.Postgres, cache *store.Redis, bus *nats.Bus, blobStore *blob.
|
||||
p.POST("/reports", h.GenerateReport) // 报告生成
|
||||
p.GET("/billing", h.Billing)
|
||||
p.GET("/stats/overview", h.StatsOverview) // 工作台仪表盘聚合
|
||||
p.GET("/runs", h.Runs) // 运行历史(复盘)
|
||||
}
|
||||
|
||||
// 运维控制面:LLM 模型配置(含 API 密钥管理)—— 必须管理员(RequireAdmin)。
|
||||
|
||||
@@ -250,6 +250,31 @@ func (p *Postgres) RecentTasks(ctx context.Context, n int) []Task {
|
||||
return out
|
||||
}
|
||||
|
||||
// RunRow 是「运行历史」一行:任务 + 其评测(LEFT JOIN,未评则 level 空)。
|
||||
type RunRow struct {
|
||||
TaskID string `json:"task_id"`
|
||||
Status string `json:"status"`
|
||||
Detail string `json:"detail"`
|
||||
At time.Time `json:"at"`
|
||||
EvalLevel string `json:"eval_level"`
|
||||
EvalOverall float64 `json:"eval_overall"`
|
||||
}
|
||||
|
||||
// RecentRuns 返回最近 n 条运行(含评测分级,供「运行历史」列表)。
|
||||
func (p *Postgres) RecentRuns(ctx context.Context, n int) []RunRow {
|
||||
if p.db == nil {
|
||||
return nil
|
||||
}
|
||||
var out []RunRow
|
||||
p.db.WithContext(ctx).Table("sundynix_task as t").
|
||||
Select("t.task_id, t.status, t.detail, t.created_at as at, " +
|
||||
"coalesce(e.level,'') as eval_level, coalesce(e.overall,0) as eval_overall").
|
||||
Joins("left join sundynix_eval e on e.task_id = t.task_id").
|
||||
Where("t.deleted_at is null").
|
||||
Order("t.created_at desc").Limit(n).Scan(&out)
|
||||
return out
|
||||
}
|
||||
|
||||
// Close 释放底层连接。
|
||||
func (p *Postgres) Close() {
|
||||
if p.db == nil {
|
||||
|
||||
Reference in New Issue
Block a user