feat(admin): 提示词控制面页 + 概览/状态页重做 + 接入 vitest

- 新增「提示词」页(/prompts):接 /api/v1/prompts,建版本/激活热下发/
  对比激活版行级 diff/撤销,挂到配置组
- 概览页重做为系统控制塔:吃 admin/overview(全平台口径)——平台任务/
  评测/用户规模/服务在线 + 模型路由态 + 提示词覆盖态 + 健康拓扑 + 30s自刷
- 服务状态页重做:中性克制风 + 请求链路做成深色实时数据管道(流动光点/
  节点辉光) + MCP 工具改能力域配色小卡片
- 接入 vitest(此前零测试):41 单测(api 控制面/lib diff/路由派生)
- api.ts 增 statsOverview/adminOverview/prompt 控制面四接口 + groupPrompts

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Blizzard
2026-07-02 08:59:40 +08:00
parent e2104efc64
commit 30e80c0eed
13 changed files with 3825 additions and 470 deletions
+54
View File
@@ -0,0 +1,54 @@
import { describe, it, expect } from "vitest";
import { lineDiff, diffStat } from "./diff";
describe("lineDiff", () => {
it("完全相同 → 全 eq", () => {
const d = lineDiff("a\nb\nc", "a\nb\nc");
expect(d.every((l) => l.op === "eq")).toBe(true);
expect(d.map((l) => l.text)).toEqual(["a", "b", "c"]);
});
it("纯新增行标 add", () => {
const d = lineDiff("a\nc", "a\nb\nc");
expect(d).toEqual([
{ op: "eq", text: "a" },
{ op: "add", text: "b" },
{ op: "eq", text: "c" },
]);
});
it("纯删除行标 del", () => {
const d = lineDiff("a\nb\nc", "a\nc");
expect(d).toEqual([
{ op: "eq", text: "a" },
{ op: "del", text: "b" },
{ op: "eq", text: "c" },
]);
});
it("替换 = del 旧 + add 新", () => {
const d = lineDiff("hello\nworld", "hello\nthere");
expect(d).toContainEqual({ op: "del", text: "world" });
expect(d).toContainEqual({ op: "add", text: "there" });
expect(d[0]).toEqual({ op: "eq", text: "hello" });
});
it("空 → 非空全是 add", () => {
const d = lineDiff("", "x\ny");
// 空串 split 出一个空行,故首行是 "" 的替换
expect(d.filter((l) => l.op === "add").map((l) => l.text)).toContain("x");
expect(d.filter((l) => l.op === "add").map((l) => l.text)).toContain("y");
});
it("保留公共子序列、只动差异块", () => {
const d = lineDiff("1\n2\n3\n4", "1\nX\n3\n4");
expect(d.filter((l) => l.op === "eq").map((l) => l.text)).toEqual(["1", "3", "4"]);
expect(diffStat(d)).toEqual({ add: 1, del: 1 });
});
});
describe("diffStat", () => {
it("统计 add/del 行数", () => {
expect(diffStat(lineDiff("a\nb", "a\nb\nc\nd"))).toEqual({ add: 2, del: 0 });
});
});
+56
View File
@@ -0,0 +1,56 @@
// 行级 diffLCS)—— 用于 prompt 控制面"选某版 vs 当前激活版"的差异预览。
// 纯函数、无依赖,便于单测。
export type DiffOp = "eq" | "add" | "del";
export interface DiffLine {
op: DiffOp; // eq=两侧相同 / add=仅在 b(新增)/ del=仅在 a(删除)
text: string;
}
// lineDiff(a, b):把 a → b 的逐行变化展开。基于最长公共子序列。
export function lineDiff(a: string, b: string): DiffLine[] {
const al = a.split("\n");
const bl = b.split("\n");
const n = al.length;
const m = bl.length;
// LCS 长度表:lcs[i][j] = al[i:] 与 bl[j:] 的最长公共子序列长度。
const lcs: number[][] = Array.from({ length: n + 1 }, () => new Array<number>(m + 1).fill(0));
for (let i = n - 1; i >= 0; i--) {
for (let j = m - 1; j >= 0; j--) {
lcs[i][j] = al[i] === bl[j] ? lcs[i + 1][j + 1] + 1 : Math.max(lcs[i + 1][j], lcs[i][j + 1]);
}
}
// 回溯:相同→eq;否则按 LCS 走向决定 del(消耗 a)还是 add(消耗 b)。
const out: DiffLine[] = [];
let i = 0;
let j = 0;
while (i < n && j < m) {
if (al[i] === bl[j]) {
out.push({ op: "eq", text: al[i] });
i++;
j++;
} else if (lcs[i + 1][j] >= lcs[i][j + 1]) {
out.push({ op: "del", text: al[i] });
i++;
} else {
out.push({ op: "add", text: bl[j] });
j++;
}
}
while (i < n) out.push({ op: "del", text: al[i++] });
while (j < m) out.push({ op: "add", text: bl[j++] });
return out;
}
// diffStat:统计新增/删除行数(给 UI 角标"+3 -1")。
export function diffStat(lines: DiffLine[]): { add: number; del: number } {
let add = 0;
let del = 0;
for (const l of lines) {
if (l.op === "add") add++;
else if (l.op === "del") del++;
}
return { add, del };
}