mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 16:12:01 +00:00
03c18c4d78
一、文案(前端约 1600 处,后端传到界面的约 880 处) - 术语统一:工作流层面用「运行」(继续运行、重新运行、试运行),节点和工具层面用「执行」,不再用「跑」; 总时长 / 执行时长 / 等待审批;服务端;助手;Agent;token;工作流;固定版本;模型接入;系统提示;批准 / 驳回;受管级别 - 口语改书面:取不到、没取回来、读不懂、连不上、多半、照样、兜底、揉成这类说法改成「无法 / 未能 / 失败 / 请」;去掉拟人写法 - 不再露出实现细节:界面上不写 checkpoint、同一拍、哈希(凭证处除外),不写改版历史(「以前…」「老数据」); 报错、校验、门禁、自动修复里裸写的配置键名和枚举值换成界面上的中文标签,由新的 backend/app/engine/labels.py 统一提供; 发布弹窗里结论句的选项不再是 off / require_citation - 同一件事只留一种说法:生成阶段文案、出具档位说明、画布锁定提示各合成一份 - 去掉界面上的工业场景示例(MES、ERP、「张工」「王工」)和真实产品名,换成通用示例 二、说错的提示(都有测试证明) - 数据库测连接失败时的专用提示以前从不出现(按报错文字全等比较,永远比不上):改为按错误类型判断,不再拼出重复的前缀 - 「设置 → 供应商」改指「设置 → 模型接入」;「在设置里建数据源」改指「数据」页 - 缺少 API Key、模型接入已停用的报错不再夹英文,写明去哪里填 - 两处原因说错(「这种引用在后续版本支持」「这一版还不支持」)改成真实原因 - 节点说明里不准确的说法改正:代码节点在 microVM 或系统沙箱中执行;报告撰写、口径卡的说明不再说过头 - 导出的证据 CSV 里两列同名,第三列改为「片段编号」 三、前后端对齐 - 前端按后端原文识别日志、报错的地方,新旧两种写法都认得(历史运行里存的是旧文字),逐条补了新旧原文的核对 - 后端交回模型的原指令放进单独的 for_model 字段,前端在解析响应时去掉,不会显示到界面上 四、检查 - 检查脚本里按原文匹配的断言改成新文案;改完会变成永远成立的反向断言逐条修正,并做了变异验证 - 新增 check-copy(静态扫描前端界面字符串和后端上界面的文案):再出现「跑」「后端」「多半」「令牌」、 界面上的 Copilot、裸写的配置键名、半角标点等就报错;确需保留的写进 scripts/copy-allow.json 并注明理由
201 lines
9.1 KiB
TypeScript
201 lines
9.1 KiB
TypeScript
/**
|
||
* 运行状态与节点状态的唯一一份文案和外观。
|
||
*
|
||
* 之前有五张映射表:StatusDot 写「成功 / 等待人工」,运行页轮次头写「完成 /
|
||
* 等待人工介入」,问数据写「完成 / 出错了」,运行条写「等你确认」……同一屏上
|
||
* 「成功」和「完成」并存,人会以为是两个状态。加一个状态就得去五处各补一遍。
|
||
*
|
||
* 每个状态同时有剪影(shape)、颜色和文字三条通道:去掉颜色还能靠剪影认出来,
|
||
* 去掉剪影还能读字。颜色走 index.css 的状态令牌 --st-*;令牌还没定义时回落到
|
||
* ok / warn / err / accent 这几个基础色。
|
||
*/
|
||
|
||
import type { RunStatus } from '../types'
|
||
|
||
/** 所有能被显示的状态。运行层和节点层共用一张表,其中几个只出现在一层 */
|
||
export type StatusCode =
|
||
| 'idle' // 节点:还没跑过
|
||
| 'queued' // 运行:排队;节点:上游都交付了、自己还没开始(推导)
|
||
| 'running'
|
||
| 'waiting' // 等人审批
|
||
| 'done' // 节点跑完
|
||
| 'succeeded' // 运行跑完
|
||
| 'failed'
|
||
| 'cancelled'
|
||
| 'suspended' // 服务重启后挂起,可续跑
|
||
| 'held' // 后端的 interrupted 但没有待审批:已挂起 · 可续跑
|
||
| 'skipped' // 被 skip_if 跳过
|
||
| 'blocked' // 失败节点的下游,这次再也跑不到了(推导)
|
||
| 'unreached' // 运行结束时一次都没轮到,比如分支没走的那一侧(推导)
|
||
|
||
/**
|
||
* 九种剪影,外加挂起用的暂停符和未运行用的小空心点。
|
||
* 形状是给"去掉颜色也能认"准备的:灰度打印、色弱、投影仪偏色时都要分得开。
|
||
*/
|
||
export type StatusShape =
|
||
| 'ring' // 圆环:运行中
|
||
| 'dashed-ring' // 虚线环:排队
|
||
| 'square' // 实心圆角方(带勾):完成
|
||
| 'diamond' // 菱形:等人
|
||
| 'triangle' // 三角:失败
|
||
| 'slashed' // 斜杠圆:跳过
|
||
| 'stop' // 实心小方:取消
|
||
| 'dashed-x' // 虚线方加 ×:阻断
|
||
| 'bar' // 横杠:未到达
|
||
| 'pause' // 两竖条:挂起
|
||
| 'dot' // 小空心点:未运行
|
||
|
||
export interface StatusMeta {
|
||
code: StatusCode
|
||
/** 完整叫法,状态标签和 tooltip 用 */
|
||
label: string
|
||
/** 窄处用的短叫法(节点卡的运行槽、HUD 计数) */
|
||
short: string
|
||
shape: StatusShape
|
||
/** 前景色,CSS 颜色表达式 */
|
||
color: string
|
||
/** 淡底色,CSS 颜色表达式 */
|
||
soft: string
|
||
/** 是不是异常态。正常态安静、异常态醒目:只有它们该用底色和强调 */
|
||
alert: boolean
|
||
/** 是不是终态(这次运行里不会再变了) */
|
||
terminal: boolean
|
||
/** 推导出来的状态:tooltip 里要写明"推导",别让人以为是后端说的 */
|
||
derived?: boolean
|
||
/** 一句话解释,放 title */
|
||
hint?: string
|
||
}
|
||
|
||
const tok = (name: string, fallback: string) => ({
|
||
color: `var(--st-${name}, ${fallback})`,
|
||
soft: `var(--st-${name}-soft, color-mix(in srgb, ${fallback} 14%, transparent))`,
|
||
})
|
||
|
||
export const STATUS: Record<StatusCode, StatusMeta> = {
|
||
idle: {
|
||
code: 'idle', label: '未运行', short: '未运行', shape: 'dot', alert: false, terminal: false,
|
||
...tok('idle', 'var(--text-faint)'),
|
||
},
|
||
queued: {
|
||
code: 'queued', label: '排队中', short: '排队', shape: 'dashed-ring', alert: false, terminal: false,
|
||
hint: '上游已完成,等待同一步中的其他节点完成', ...tok('queued', 'var(--text-dim)'),
|
||
},
|
||
running: {
|
||
code: 'running', label: '运行中', short: '运行中', shape: 'ring', alert: false, terminal: false,
|
||
...tok('running', 'var(--accent)'),
|
||
},
|
||
waiting: {
|
||
code: 'waiting', label: '等待审批', short: '待审批', shape: 'diamond', alert: true, terminal: false,
|
||
hint: '停在人工审批处,处理后继续运行', ...tok('waiting', 'var(--warn)'),
|
||
},
|
||
done: {
|
||
code: 'done', label: '已完成', short: '完成', shape: 'square', alert: false, terminal: true,
|
||
...tok('done', 'var(--ok)'),
|
||
},
|
||
succeeded: {
|
||
code: 'succeeded', label: '已完成', short: '完成', shape: 'square', alert: false, terminal: true,
|
||
hint: '执行已完成。结论是否可用,请查看出具档位和复核结果', ...tok('done', 'var(--ok)'),
|
||
},
|
||
failed: {
|
||
code: 'failed', label: '失败', short: '失败', shape: 'triangle', alert: true, terminal: true,
|
||
...tok('failed', 'var(--err)'),
|
||
},
|
||
cancelled: {
|
||
code: 'cancelled', label: '已取消', short: '已取消', shape: 'stop', alert: false, terminal: true,
|
||
...tok('cancelled', 'var(--text-faint)'),
|
||
},
|
||
suspended: {
|
||
code: 'suspended', label: '已中断(服务重启)', short: '已中断', shape: 'pause', alert: true, terminal: true,
|
||
hint: '服务重启时本次运行尚未结束,已在断点处挂起,可继续运行', ...tok('suspended', 'var(--warn)'),
|
||
},
|
||
held: {
|
||
code: 'held', label: '已挂起 · 可继续运行', short: '已挂起', shape: 'pause', alert: true, terminal: true,
|
||
hint: '运行停在断点处,且没有待处理的审批,通常是服务重启所致,可继续运行',
|
||
...tok('suspended', 'var(--warn)'),
|
||
},
|
||
skipped: {
|
||
code: 'skipped', label: '已跳过', short: '跳过', shape: 'slashed', alert: false, terminal: true,
|
||
hint: '跳过条件成立,本步骤未执行', ...tok('skipped', 'var(--text-faint)'),
|
||
},
|
||
blocked: {
|
||
code: 'blocked', label: '已阻断', short: '阻断', shape: 'dashed-x', alert: true, terminal: true,
|
||
derived: true, hint: '推导:上游节点失败,本次运行不会执行到此处', ...tok('blocked', 'var(--err)'),
|
||
},
|
||
unreached: {
|
||
code: 'unreached', label: '未到达', short: '未到达', shape: 'bar', alert: false, terminal: true,
|
||
derived: true, hint: '推导:运行结束时未执行到此节点,例如未命中的条件分支', ...tok('unreached', 'var(--text-faint)'),
|
||
},
|
||
}
|
||
|
||
/** 旧代码和后端里的别名。未知状态码原样落到 idle 的外观上,文字照写原码 */
|
||
const ALIAS: Record<string, StatusCode> = {
|
||
success: 'succeeded', ok: 'succeeded', completed: 'succeeded', finished: 'succeeded',
|
||
error: 'failed', canceled: 'cancelled', pending: 'queued', interrupted: 'waiting',
|
||
}
|
||
|
||
/**
|
||
* 把后端的运行状态落到显示用的状态上。
|
||
*
|
||
* 后端的 interrupted 有两种:停在审批上(等待审批),和服务重启打断后没有
|
||
* 待审批(已挂起 · 可续跑)。二者要看审批列表里有没有这条运行的 pending
|
||
* 才分得开——传 pendingApproval。不知道时(没传)按等待审批处理,这是
|
||
* interrupted 最常见的来源,也是改造前的说法,不会比原来更错。
|
||
*/
|
||
export function resolveStatus(status: string | null | undefined, opts?: { pendingApproval?: boolean }): StatusCode {
|
||
if (!status) return 'idle'
|
||
if (status === 'interrupted') return opts?.pendingApproval === false ? 'held' : 'waiting'
|
||
if (status in STATUS) return status as StatusCode
|
||
return ALIAS[status] ?? 'idle'
|
||
}
|
||
|
||
/** 状态的完整外观。未知状态码回落到 idle 的外观,文字写原码,免得静默吞掉 */
|
||
export function statusMeta(status: string | null | undefined, opts?: { pendingApproval?: boolean }): StatusMeta {
|
||
const code = resolveStatus(status, opts)
|
||
const meta = STATUS[code]
|
||
if (status && code === 'idle' && status !== 'idle' && !(status in ALIAS)) {
|
||
return { ...meta, label: status, short: status }
|
||
}
|
||
return meta
|
||
}
|
||
|
||
/** 只要文字 */
|
||
export function statusLabel(status: string | null | undefined, opts?: { pendingApproval?: boolean; short?: boolean }): string {
|
||
const meta = statusMeta(status, opts)
|
||
return opts?.short ? meta.short : meta.label
|
||
}
|
||
|
||
/**
|
||
* 运行列表的状态筛选用:按显示顺序排好的运行状态。
|
||
* 这是显示用的码,不能直接当查询参数——waiting / held 在后端都是 interrupted,
|
||
* 查询前用 serverStatusOf 换回去。
|
||
*/
|
||
export const RUN_STATUS_ORDER: StatusCode[] = [
|
||
'running', 'waiting', 'held', 'failed', 'succeeded', 'cancelled', 'queued',
|
||
]
|
||
|
||
/**
|
||
* 显示用的状态码换回后端 runs.status 认的值,给 api.runs.list({status}) 用。
|
||
*
|
||
* waiting、held、suspended 在后端都是 interrupted:按它查回来的是两种混在一起,
|
||
* 要再用 resolveStatus(run.status, {pendingApproval: hasPendingApproval(approvals,
|
||
* run.id)}) 在前端分开。节点才有的状态(idle / skipped / blocked / unreached)
|
||
* 不是运行状态,返回 null。
|
||
*/
|
||
export function serverStatusOf(code: StatusCode): RunStatus | null {
|
||
switch (code) {
|
||
case 'waiting': case 'held': case 'suspended': return 'interrupted'
|
||
case 'done': case 'succeeded': return 'succeeded'
|
||
case 'queued': case 'running': case 'failed': case 'cancelled': return code
|
||
default: return null
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 运行是不是还"活着"(会有新事件)。held / suspended 虽然后端写的是
|
||
* interrupted,但不会自己动,不算。
|
||
*/
|
||
export function isLiveStatus(status: string | null | undefined, opts?: { pendingApproval?: boolean }): boolean {
|
||
const code = resolveStatus(status, opts)
|
||
return code === 'queued' || code === 'running' || code === 'waiting'
|
||
}
|