perf(qa): lazy-load runner catalog for lab ui

This commit is contained in:
Vincent Koc
2026-04-07 13:01:44 +01:00
committed by Peter Steinberger
parent f312d6c106
commit cde12e63e7
2 changed files with 71 additions and 12 deletions

View File

@@ -353,6 +353,58 @@ describe("qa-lab server", () => {
);
});
it("does not eagerly load the runner model catalog before bootstrap is requested", async () => {
const repoRoot = await mkdtemp(path.join(os.tmpdir(), "qa-lab-lazy-catalog-"));
cleanups.push(async () => {
await rm(repoRoot, { recursive: true, force: true });
});
const markerPath = path.join(repoRoot, "runner-catalog-hit.txt");
await mkdir(path.join(repoRoot, "dist"), { recursive: true });
await mkdir(path.join(repoRoot, "extensions/qa-lab/web/dist"), { recursive: true });
await writeFile(
path.join(repoRoot, "dist/index.js"),
[
'const fs = require("node:fs");',
`fs.writeFileSync(${JSON.stringify(markerPath)}, process.argv.slice(2).join(" "), "utf8");`,
"process.stdout.write(JSON.stringify({",
" models: [{",
' key: "openai/gpt-5.4",',
' name: "GPT-5.4",',
' input: "openai/gpt-5.4",',
" available: true,",
" missing: false,",
" }],",
"}));",
].join("\n"),
"utf8",
);
await writeFile(
path.join(repoRoot, "extensions/qa-lab/web/dist/index.html"),
"<!doctype html><html><body>lazy catalog</body></html>",
"utf8",
);
const lab = await startQaLabServer({
host: "127.0.0.1",
port: 0,
repoRoot,
});
cleanups.push(async () => {
await lab.stop();
});
await sleep(150);
await expect(readFile(markerPath, "utf8")).rejects.toThrow();
const bootstrapResponse = await fetchWithRetry(`${lab.baseUrl}/api/bootstrap`);
expect(bootstrapResponse.status).toBe(200);
const runnerCatalog = await waitForRunnerCatalog(lab.baseUrl);
expect(runnerCatalog.status).toBe("ready");
expect(await readFile(markerPath, "utf8")).toContain("models list --all --json");
});
it("can disable the embedded echo gateway for real-suite runs", async () => {
const lab = await startQaLabServer({
host: "127.0.0.1",

View File

@@ -515,18 +515,25 @@ export async function startQaLabServer(params?: {
} | null = null;
let publicBaseUrl = "";
const runnerModelCatalogPromise = (async () => {
try {
const { loadQaRunnerModelOptions } = await import("./model-catalog.runtime.js");
runnerModelOptions = await loadQaRunnerModelOptions({
repoRoot,
});
runnerModelCatalogStatus = "ready";
} catch {
runnerModelOptions = [];
runnerModelCatalogStatus = "failed";
let runnerModelCatalogPromise: Promise<void> | null = null;
const ensureRunnerModelCatalog = () => {
if (runnerModelCatalogPromise) {
return runnerModelCatalogPromise;
}
})();
runnerModelCatalogPromise = (async () => {
try {
const { loadQaRunnerModelOptions } = await import("./model-catalog.runtime.js");
runnerModelOptions = await loadQaRunnerModelOptions({
repoRoot,
});
runnerModelCatalogStatus = "ready";
} catch {
runnerModelOptions = [];
runnerModelCatalogStatus = "failed";
}
})();
return runnerModelCatalogPromise;
};
const server = createServer(async (req, res) => {
const url = new URL(req.url ?? "/", "http://127.0.0.1");
@@ -547,6 +554,7 @@ export async function startQaLabServer(params?: {
}
if (req.method === "GET" && url.pathname === "/api/bootstrap") {
void ensureRunnerModelCatalog();
const resolvedControlUiUrl = controlUiProxyTarget
? `${publicBaseUrl}/control-ui/`
: controlUiUrl;
@@ -801,7 +809,6 @@ export async function startQaLabServer(params?: {
kickoffTask: scenarioCatalog.kickoffTask,
});
}
void runnerModelCatalogPromise;
server.on("upgrade", (req, socket, head) => {
const url = new URL(req.url ?? "/", "http://127.0.0.1");