Files
jobtrackingapp/job-tracker-ui/scripts/cv-ai-provider-fixture.mjs
T

133 lines
5.9 KiB
JavaScript

// Test-only HTTP provider. Controlled wording/evidence exercises the API contract;
// this fixture does not measure a model's semantic accuracy and never calls a provider.
import http from "node:http";
const args = process.argv.slice(2);
if (args.length && (args.length !== 2 || args[0] !== "--port")) {
throw new Error("Usage: node cv-ai-provider-fixture.mjs [--port 18133]");
}
const port = args.length ? Number(args[1]) : 18133;
if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error("Invalid fixture port.");
const fabricated = "Managed 50 engineers at Microsoft and increased revenue 300%.";
let mode = "supported";
const counts = { rewrite: 0, evaluate: 0 };
const calls = [];
function supportedRewrite(value) {
return value.replace(/\bBuilt\b/g, "Developed").replace(/\bbuilt\b/g, "developed")
.replace(/\breliable\b/g, "dependable").replace(/\bUtviklet\b/g, "Bygget")
.replace(/\butviklet\b/g, "bygget");
}
function equivalent(value) {
return supportedRewrite(value).trim();
}
function parts(value) {
if (typeof value !== "string" || !value.trim()) throw new Error("Nonempty source/proposal required.");
if (!value.trimStart().startsWith("[")) return value.split("\n").map((line) => line.trim()).filter(Boolean);
const blocks = JSON.parse(value);
if (!Array.isArray(blocks)) throw new Error("CV blocks must be an array.");
const values = [];
for (const block of blocks) {
if (!block || typeof block !== "object" || Array.isArray(block)) throw new Error("Invalid CV block.");
for (const [key, field] of Object.entries(block)) {
if (key === "id") {
if (typeof field !== "string") throw new Error("Invalid block id.");
} else if (key === "title" || key === "subtitle") {
add(field);
} else if (key === "items" && Array.isArray(field)) {
field.forEach(add);
} else throw new Error("Invalid block field.");
}
}
return values;
function add(value) {
if (value === null) return;
if (typeof value !== "string") throw new Error("Invalid block value.");
if (value.trim()) values.push(value);
}
}
function rewrite(body) {
if (typeof body.text !== "string" || !body.text.trim()) throw new Error("Nonempty text required.");
const outputMode = body.output_mode ?? "text";
if (outputMode !== "text" && outputMode !== "cv_blocks_json") throw new Error("Invalid output_mode.");
if (outputMode === "text") return { rewritten_text: mode === "fabricated" ? fabricated : supportedRewrite(body.text) };
parts(body.text);
const blocks = JSON.parse(body.text).map((block) => Object.fromEntries(Object.entries(block).map(([key, value]) => [
key, key === "id" ? value : typeof value === "string" ? supportedRewrite(value)
: Array.isArray(value) ? value.map(supportedRewrite) : value,
])));
if (!blocks.length) throw new Error("Nonempty CV blocks required.");
if (mode === "fabricated") {
const target = blocks.find((block) => block.items?.length) ?? blocks[0];
target.items = [fabricated];
}
return { rewritten_text: JSON.stringify(blocks) };
}
function evaluate(body) {
const source = parts(body.candidate_source);
const proposed = parts(body.proposal);
const claims = proposed.map((text) => {
const quote = source.find((value) => equivalent(value) === equivalent(text));
return { text, verdict: quote ? "supported" : "unsupported", sourceQuotes: quote ? [quote] : [] };
});
return { assessment: {
verdict: claims.length && claims.every((claim) => claim.verdict === "supported") ? "supported" : "unsupported",
coverageComplete: true,
claims,
} };
}
function send(response, status, body) {
response.writeHead(status, {
"Content-Type": "application/json",
"X-Ai-Provider": "loopback-fixture",
"X-Ai-Model": "deterministic-cv-proof",
"X-Ai-Route-Reason": "test-fixture",
});
response.end(JSON.stringify(body));
}
const server = http.createServer(async (request, response) => {
const path = request.url?.split("?")[0];
if (request.method === "GET" && path === "/health") return send(response, 200, { status: "ok", fixture: true });
if (request.method === "GET" && path === "/fixture/state") return send(response, 200, { mode, counts, calls });
if (request.method !== "POST" || !["/fixture/mode", "/fixture/reset", "/cv/rewrite", "/cv/evaluate-rewrite"].includes(path)) {
return send(response, 404, { error: "unknown_fixture_route" });
}
try {
const chunks = [];
let size = 0;
for await (const chunk of request) {
size += chunk.length;
if (size > 256_000) return send(response, 413, { error: "fixture_body_too_large" });
chunks.push(chunk);
}
const raw = Buffer.concat(chunks).toString("utf8");
const body = raw.trim() ? JSON.parse(raw) : {};
if (!body || typeof body !== "object" || Array.isArray(body)) throw new Error("JSON object required.");
if (path === "/fixture/mode" || path === "/fixture/reset") {
const next = body.mode ?? (path === "/fixture/reset" ? "supported" : undefined);
if (next !== "supported" && next !== "fabricated") throw new Error("Invalid fixture mode.");
mode = next;
if (path === "/fixture/reset") { counts.rewrite = 0; counts.evaluate = 0; calls.length = 0; }
return send(response, 200, { mode, counts });
}
counts[path === "/cv/rewrite" ? "rewrite" : "evaluate"] += 1;
const result = path === "/cv/rewrite" ? rewrite(body) : evaluate(body);
calls.push({ path, mode, body, response: result });
return send(response, 200, result);
} catch (error) {
return send(response, 400, { error: error instanceof Error ? error.message : "Invalid fixture request." });
}
});
server.listen(port, "127.0.0.1", () => console.log(`CV AI provider fixture: http://127.0.0.1:${port}`));
server.on("error", (error) => { console.error(error.message); process.exitCode = 1; });
process.on("SIGTERM", () => server.close(() => process.exit(0)));
process.on("SIGINT", () => server.close(() => process.exit(0)));