Files
xiaobailing-turn-detection/labels/eot-studio/app/api/model-scores/route.ts
T

60 lines
3.0 KiB
TypeScript

import { promises as fs } from "node:fs";
import { createHash } from "node:crypto";
import path from "node:path";
import { parseScoreExport, type ModelId, type ScoreResponse } from "@/lib/model-scores";
export const runtime = "nodejs";
export const dynamic = "force-dynamic";
const root = path.resolve(process.cwd(), "..", "..");
const sources: { model: ModelId; directory: string; name: string }[] = [
{ model: "maai", directory: "maai-ch-kyoto", name: "MaAI" },
{ model: "smart_turn", directory: "smart-turn-v3.2", name: "Smart Turn" },
];
const json = (data: unknown, status = 200) => Response.json(data, { status, headers: { "Cache-Control": "no-store" } });
export async function GET(request: Request) {
const params = new URL(request.url).searchParams;
const callId = params.get("callId") || "";
const recordingPath = params.get("recordingPath") || "";
if (!callId || callId.length > 200 || !recordingPath || recordingPath.length > 1000) {
return json({ error: "请选择已关联项目录音的对话。" }, 400);
}
const payload: ScoreResponse = { callId, recordingPath, series: [], messages: [] };
try {
for (const source of sources) {
try {
// Fixed paths only: no request value is used to locate an export.
const file = path.join(root, "experiments", source.directory, "results", "score-timeline.json");
const data = parseScoreExport(JSON.parse(await fs.readFile(file, "utf8")), source.model);
const call = data.calls.find(item => item.call_id === callId && item.audio_path === recordingPath);
if (call) payload.series.push({ ...call, model: data.model, name: data.name,
score: data.score, sample_interval_ms: data.sample_interval_ms });
else payload.messages.push(`${source.name} 暂无这段录音的评分。`);
} catch (error) {
if (!(error instanceof Error && "code" in error && error.code === "ENOENT")) {
console.error(`Cannot read ${source.name} scores`, error);
}
payload.messages.push(`${source.name} 评分暂时不可用。`);
}
}
if (payload.series.length) {
const dataRoot = await fs.realpath(path.join(root, "data"));
const audio = await fs.realpath(path.resolve(dataRoot, payload.series[0].audio_path));
const relative = path.relative(dataRoot, audio);
if (relative.startsWith(`..${path.sep}`) || relative === ".." || path.isAbsolute(relative)) {
return json({ error: "评分对应的录音不在项目目录内。" }, 422);
}
const hash = createHash("sha256").update(await fs.readFile(audio)).digest("hex");
payload.series = payload.series.filter(series => {
if (series.audio_sha256 === hash) return true;
payload.messages.push(`${series.name} 的评分对应旧录音,请重新生成评分。`);
return false;
});
}
return json(payload);
} catch (error) {
console.error("Cannot verify score recording", error);
return json({ error: "无法核对评分对应的录音,请检查项目文件。" }, 500);
}
}