mirror of
https://github.com/modelstudioai/cli.git
synced 2026-09-14 19:49:23 +08:00
Merge pull request #2 from modelstudioai/fix/ts-lint
refactor(stress): simplify target definitions and enhance trace ID ha…
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* 压测 target 工厂函数:封装共享逻辑,每个 target 只需声明式配置。
|
||||
*/
|
||||
import { mkdirSync, existsSync, readFileSync } from "node:fs";
|
||||
import { mkdirSync, existsSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { execFile } from "node:child_process";
|
||||
import { promisify } from "node:util";
|
||||
|
||||
@@ -93,12 +93,12 @@ export async function runStressSuite({ globals, forwarded }) {
|
||||
|
||||
// Phase 0: 生成共享前置资源
|
||||
console.error("");
|
||||
console.error("========== [Phase 0] 生成共享前置资源 ==========");
|
||||
console.error("========== [Phase 0] 生成前置资源 ==========");
|
||||
let sharedFixturesDir;
|
||||
try {
|
||||
sharedFixturesDir = await generateCombinedFixtures({ suiteRoot, cliPackage: undefined });
|
||||
} catch (err) {
|
||||
console.error(`[全量压测] 共享前置资源生成失败: ${err.message}`);
|
||||
console.error(`[全量压测] 前置资源生成失败: ${err.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,16 @@ export function formatSuccessRate(rate) {
|
||||
return `${(rate * 100).toFixed(1)}%`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用例墙钟耗时:优先 wallClockMs,否则 finishedAt - startedAt,无效时 0。
|
||||
* @param {{ wallClockMs?: number, finishedAt?: number, startedAt?: number }} row
|
||||
*/
|
||||
function caseWallClockMs(row) {
|
||||
if (row.wallClockMs != null) return row.wallClockMs;
|
||||
const delta = row.finishedAt - row.startedAt;
|
||||
return Number.isFinite(delta) ? delta : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} suiteRoot
|
||||
* @param {object[]} rows 各用例 finishStressRun 返回的摘要
|
||||
@@ -55,7 +65,7 @@ function buildSuiteMarkdown(rows, meta) {
|
||||
lines.push("| 用例 | 执行时间 | 任务数 | 并发 | 成功 | 失败 | 成功率 | 详细报告 |");
|
||||
lines.push("|------|----------|--------|------|------|------|--------|--------|");
|
||||
for (const r of rows) {
|
||||
const dur = formatDuration(r.wallClockMs ?? r.finishedAt - r.startedAt ?? 0);
|
||||
const dur = formatDuration(caseWallClockMs(r));
|
||||
const sub = r.reportPath ? `\`${r.reportPath}\`` : "—";
|
||||
lines.push(
|
||||
`| ${escapeTableCell(r.displayName)} | ${dur} | ${r.count ?? "—"} | ${r.concurrency ?? "—"} | ${r.successCount ?? "—"} | ${r.failCount ?? "—"} | ${formatSuccessRate(r.successRate)} | ${sub} |`,
|
||||
@@ -71,7 +81,7 @@ function buildSuiteMarkdown(rows, meta) {
|
||||
function buildSuiteHtml(rows, meta) {
|
||||
const tableRows = rows
|
||||
.map((r) => {
|
||||
const dur = formatDuration(r.wallClockMs ?? r.finishedAt - r.startedAt ?? 0);
|
||||
const dur = formatDuration(caseWallClockMs(r));
|
||||
const sub = r.reportPath
|
||||
? `<a href="${escapeHtml(r.reportPath)}">${escapeHtml(r.reportPath)}</a>`
|
||||
: "—";
|
||||
|
||||
@@ -4,6 +4,33 @@
|
||||
import { extractJsonFromStdout } from "./parsers.mjs";
|
||||
import { fetchRequestIdByTaskId } from "./fetch-request-id.mjs";
|
||||
|
||||
/**
|
||||
* 将 trace id 字段转为非空字符串;对象等无法可靠转换时返回 undefined。
|
||||
* @param {unknown} value
|
||||
* @returns {string | undefined}
|
||||
*/
|
||||
function toTraceString(value) {
|
||||
if (value == null) return undefined;
|
||||
if (typeof value === "string") {
|
||||
const trimmed = value.trim();
|
||||
return trimmed || undefined;
|
||||
}
|
||||
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
||||
const trimmed = String(value).trim();
|
||||
return trimmed || undefined;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将日志字段转为字符串;非 string 时返回空字符串。
|
||||
* @param {unknown} value
|
||||
* @returns {string}
|
||||
*/
|
||||
function toLogText(value) {
|
||||
return typeof value === "string" ? value : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {unknown} data
|
||||
*/
|
||||
@@ -18,9 +45,9 @@ export function extractTraceIdsFromJson(data) {
|
||||
? /** @type {Record<string, unknown>} */ (obj.error)
|
||||
: undefined;
|
||||
|
||||
const requestIdRaw = obj.request_id ?? obj.requestId ?? err?.request_id ?? err?.requestId;
|
||||
const requestId =
|
||||
requestIdRaw != null && String(requestIdRaw).trim() ? String(requestIdRaw).trim() : undefined;
|
||||
const requestId = toTraceString(
|
||||
obj.request_id ?? obj.requestId ?? err?.request_id ?? err?.requestId,
|
||||
);
|
||||
|
||||
const taskId = formatTaskIdFromJson(obj);
|
||||
return { requestId, taskId };
|
||||
@@ -30,28 +57,29 @@ export function extractTraceIdsFromJson(data) {
|
||||
* @param {Record<string, unknown>} data
|
||||
*/
|
||||
function formatTaskIdFromJson(data) {
|
||||
if (data.task_id != null && String(data.task_id).trim()) {
|
||||
return String(data.task_id).trim();
|
||||
}
|
||||
const single = toTraceString(data.task_id);
|
||||
if (single) return single;
|
||||
if (data.task_ids != null) {
|
||||
return formatTaskIdsValue(data.task_ids);
|
||||
}
|
||||
if (Array.isArray(data.videos)) {
|
||||
const ids = data.videos
|
||||
.map((v) =>
|
||||
v && typeof v === "object" ? /** @type {{ task_id?: unknown }} */ (v).task_id : null,
|
||||
v && typeof v === "object"
|
||||
? toTraceString(/** @type {{ task_id?: unknown }} */ (v).task_id)
|
||||
: undefined,
|
||||
)
|
||||
.filter((id) => id != null && String(id).trim())
|
||||
.map((id) => String(id).trim());
|
||||
.filter((id) => id != null);
|
||||
if (ids.length > 0) return [...new Set(ids)].join(", ");
|
||||
}
|
||||
if (Array.isArray(data.images)) {
|
||||
const ids = data.images
|
||||
.map((v) =>
|
||||
v && typeof v === "object" ? /** @type {{ task_id?: unknown }} */ (v).task_id : null,
|
||||
v && typeof v === "object"
|
||||
? toTraceString(/** @type {{ task_id?: unknown }} */ (v).task_id)
|
||||
: undefined,
|
||||
)
|
||||
.filter((id) => id != null && String(id).trim())
|
||||
.map((id) => String(id).trim());
|
||||
.filter((id) => id != null);
|
||||
if (ids.length > 0) return [...new Set(ids)].join(", ");
|
||||
}
|
||||
return undefined;
|
||||
@@ -62,11 +90,10 @@ function formatTaskIdFromJson(data) {
|
||||
*/
|
||||
function formatTaskIdsValue(value) {
|
||||
if (Array.isArray(value)) {
|
||||
const ids = value.map((v) => String(v).trim()).filter(Boolean);
|
||||
const ids = value.map((v) => toTraceString(v)).filter((id) => id != null);
|
||||
return ids.length > 0 ? ids.join(", ") : undefined;
|
||||
}
|
||||
if (value != null && String(value).trim()) return String(value).trim();
|
||||
return undefined;
|
||||
return toTraceString(value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -186,9 +213,9 @@ export function extractTraceIdsFromLogs(stdout, stderr) {
|
||||
* @param {Record<string, unknown>} result
|
||||
*/
|
||||
export function mergeTraceIds(result) {
|
||||
const stdout = String(result.stdout ?? "");
|
||||
const stderr = String(result.stderr ?? "");
|
||||
const errorText = String(result.error ?? "");
|
||||
const stdout = toLogText(result.stdout);
|
||||
const stderr = toLogText(result.stderr);
|
||||
const errorText = toLogText(result.error);
|
||||
const combined = `${stdout}\n${stderr}\n${errorText}`;
|
||||
|
||||
const data = extractJsonFromStdout(stdout);
|
||||
@@ -230,7 +257,7 @@ export async function enrichTraceIdsAsync(result) {
|
||||
|
||||
if (merged.requestId) return merged;
|
||||
|
||||
const taskId = merged.taskId ? String(merged.taskId).split(",")[0].trim() : "";
|
||||
const taskId = toTraceString(merged.taskId)?.split(",")[0]?.trim() ?? "";
|
||||
if (!taskId) return merged;
|
||||
|
||||
if (process.env.STRESS_FETCH_REQUEST_ID === "0") return merged;
|
||||
|
||||
@@ -30,7 +30,7 @@ export const runStress = defineStressTarget({
|
||||
|
||||
generatePrompt: (idx) => `[ASR-${idx}-${Date.now().toString(36)}]`,
|
||||
|
||||
buildCliArgs: ({ MODEL, CLI_TIMEOUT_SEC, POLL_INTERVAL, fixtureRef, runDir, index }) => [
|
||||
buildCliArgs: ({ MODEL, CLI_TIMEOUT_SEC, POLL_INTERVAL, fixtureRef, runDir }) => [
|
||||
"speech",
|
||||
"recognize",
|
||||
"--model",
|
||||
|
||||
@@ -88,7 +88,7 @@ export const runStress = defineStressTarget({
|
||||
String(POLL_INTERVAL),
|
||||
],
|
||||
|
||||
buildBaseRecord: ({ runDir, index, extraParams }) => ({
|
||||
buildBaseRecord: ({ runDir, index }) => ({
|
||||
downloadPath: join(runDir, `video_${String(index + 1).padStart(3, "0")}.mp4`),
|
||||
}),
|
||||
|
||||
|
||||
Reference in New Issue
Block a user