serve(runtime) listens first, writes the state root only once the runtime holds it, answers Idunn's route challenge ahead of the cross-site refusal, and fences on lease loss. --health is deleted; the pid file and /proc reclaim move behind the hand-run runtime. Adds scripts/idunn-entry.mjs, the kimodo and SOMA-X gitlinks, encode_texts for the child, and tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
112 lines
5 KiB
JavaScript
112 lines
5 KiB
JavaScript
// Stands in for kimodo-resident-worker.py in the daemon's tests. It binds port 0, prints the ready line the
|
|
// daemon waits for, and answers /health and /generate with what the daemon gave it. The --model value picks a
|
|
// misbehaviour: crash-before-ready, hang-before-ready, sick, slow-health, stubborn, fail-generate,
|
|
// refuse-generate, drop-generate, slow-generate, encoder (a grandchild, then no answer), graceful (exits 0.5 s after SIGTERM), die-after-ready. Any other value is just the model the worker serves.
|
|
import http from "node:http";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { spawn } from "node:child_process";
|
|
import { appendFileSync } from "node:fs";
|
|
|
|
const argv = process.argv.slice(2);
|
|
const flag = (name) => {
|
|
const index = argv.indexOf(name);
|
|
return index < 0 ? undefined : argv[index + 1];
|
|
};
|
|
const model = flag("--model");
|
|
const spawnLog = path.join(process.env.VILI_EMBEDDINGS_DIR, "stub-spawns.log");
|
|
let requests = 0;
|
|
let healthCalls = 0;
|
|
|
|
appendFileSync(spawnLog, `${process.pid}\n`);
|
|
console.log("stub-stdout-line");
|
|
console.error("stub-stderr-line");
|
|
// decoys: neither is the ready line
|
|
console.log(JSON.stringify({ event: "progress", port: 1 }));
|
|
console.log(JSON.stringify({ event: "kimodo-worker-ready", port: "not-a-port" }));
|
|
|
|
if (model === "crash-before-ready") process.exit(3);
|
|
|
|
if (model === "stubborn") {
|
|
process.on("SIGTERM", () => {});
|
|
// the grandchild reports itself once its handler is armed, so a test never reads a pid that would die of the default SIGTERM
|
|
spawn(process.execPath, ["-e", `process.on('SIGTERM', () => {}); require('fs').appendFileSync(${JSON.stringify(spawnLog)}, 'grandchild ' + process.pid + '\\n'); setInterval(() => {}, 1000)`], { stdio: "ignore" });
|
|
}
|
|
|
|
if (model === "graceful") {
|
|
process.on("SIGTERM", () => {
|
|
setTimeout(() => {
|
|
appendFileSync(spawnLog, "graceful-exit\n");
|
|
process.exit(0);
|
|
}, 500);
|
|
});
|
|
}
|
|
|
|
if (model === "hang-before-ready") {
|
|
setInterval(() => {}, 1000);
|
|
} else {
|
|
const server = http.createServer((request, response) => {
|
|
const chunks = [];
|
|
request.on("data", (chunk) => chunks.push(chunk));
|
|
request.on("end", () => {
|
|
const send = (status, body) => {
|
|
response.writeHead(status, { "content-type": "application/json" });
|
|
response.end(JSON.stringify(body));
|
|
};
|
|
if (request.method === "GET" && request.url === "/health") {
|
|
healthCalls += 1;
|
|
if (model === "sick" || model === "die-after-ready" || (model === "slow-health" && healthCalls <= 2)) return send(200, { ok: false });
|
|
send(200, { ok: true, model: model || "stub-model", displayModel: model || "Stub Model", pid: process.pid, requests });
|
|
} else if (request.method === "POST" && request.url === "/generate") {
|
|
requests += 1;
|
|
const payload = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
if (model === "fail-generate") return send(500, { ok: false });
|
|
if (model === "refuse-generate") return send(200, { ok: false });
|
|
if (model === "drop-generate") return request.socket.destroy();
|
|
if (model === "slow-generate") return;
|
|
if (model === "encoder") {
|
|
// an encoder child of the real worker: a grandchild in the worker's group, then a request that never answers
|
|
spawn(process.execPath, ["-e", `process.on('SIGTERM', () => {}); require('fs').appendFileSync(${JSON.stringify(spawnLog)}, 'grandchild ' + process.pid + '\\n'); setInterval(() => {}, 1000)`], { stdio: "ignore" });
|
|
return;
|
|
}
|
|
send(200, {
|
|
ok: true,
|
|
model: model || "stub-model",
|
|
pid: process.pid,
|
|
requests,
|
|
contentType: request.headers["content-type"],
|
|
output: payload.output,
|
|
prompt: payload.prompt,
|
|
constraints: payload.constraints,
|
|
priority: os.getPriority(),
|
|
env: {
|
|
OMP_NUM_THREADS: process.env.OMP_NUM_THREADS,
|
|
MKL_NUM_THREADS: process.env.MKL_NUM_THREADS,
|
|
VILI_THREADS: process.env.VILI_THREADS,
|
|
HF_HUB_OFFLINE: process.env.HF_HUB_OFFLINE,
|
|
TRANSFORMERS_OFFLINE: process.env.TRANSFORMERS_OFFLINE,
|
|
HF_HOME: process.env.HF_HOME,
|
|
PYTHONPATH: process.env.PYTHONPATH,
|
|
PYTHONDONTWRITEBYTECODE: process.env.PYTHONDONTWRITEBYTECODE,
|
|
PYTHONUNBUFFERED: process.env.PYTHONUNBUFFERED,
|
|
VILI_KIMODO_COMMIT: process.env.VILI_KIMODO_COMMIT,
|
|
VILI_PYTHON_ENV: process.env.VILI_PYTHON_ENV,
|
|
PATH: process.env.PATH,
|
|
HOME: process.env.HOME,
|
|
LANG: process.env.LANG,
|
|
LEAK_CHECK: process.env.LEAK_CHECK,
|
|
},
|
|
argv,
|
|
});
|
|
} else {
|
|
response.writeHead(404);
|
|
response.end();
|
|
}
|
|
});
|
|
});
|
|
|
|
server.listen(Number(flag("--port")), flag("--host"), () => {
|
|
console.log(JSON.stringify({ event: "kimodo-worker-ready", port: server.address().port }));
|
|
if (model === "die-after-ready") setTimeout(() => process.exit(4), 300);
|
|
});
|
|
}
|