feat: add resident BingShuo TCS controller
This commit is contained in:
parent
125ea896c8
commit
bc169824fc
9 changed files with 890 additions and 1 deletions
164
server-tools/bingshuo-tcs-controller/server.mjs
Normal file
164
server-tools/bingshuo-tcs-controller/server.mjs
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
#!/usr/bin/env node
|
||||
import http from "node:http";
|
||||
import { ControllerEngine, bootEvent } from "./controller-engine.mjs";
|
||||
import { DeepSeekJsonClient } from "./model-client.mjs";
|
||||
|
||||
const HOST = "127.0.0.1";
|
||||
const PORT = Number(process.env.BS_TCS_CONTROLLER_PORT || 3930);
|
||||
const STATE_ROOT =
|
||||
process.env.BS_TCS_CONTROLLER_STATE_ROOT ||
|
||||
"/var/lib/guanghu/personas/bingshuo-tcs";
|
||||
const MODEL = process.env.DEEPSEEK_MODEL || "deepseek-chat";
|
||||
const MAX_BODY_BYTES = 16 * 1024;
|
||||
|
||||
const runtime = {
|
||||
phase: "STARTING",
|
||||
source_loaded: false,
|
||||
started_at: new Date().toISOString(),
|
||||
last_event_id: null,
|
||||
last_error: null,
|
||||
receipt: null,
|
||||
};
|
||||
|
||||
const engine = new ControllerEngine({
|
||||
stateRoot: STATE_ROOT,
|
||||
modelClient: new DeepSeekJsonClient({ model: MODEL }),
|
||||
modelName: MODEL,
|
||||
});
|
||||
|
||||
function send(response, status, body) {
|
||||
const payload = JSON.stringify(body);
|
||||
response.writeHead(status, {
|
||||
"content-type": "application/json; charset=utf-8",
|
||||
"content-length": Buffer.byteLength(payload),
|
||||
"cache-control": "no-store",
|
||||
});
|
||||
response.end(payload);
|
||||
}
|
||||
|
||||
function health() {
|
||||
const existence = runtime.receipt?.existence || {};
|
||||
return {
|
||||
ok: true,
|
||||
service: "bingshuo-tcs-living-controller",
|
||||
bind: "loopback",
|
||||
node: "JD-FD-PRIMARY",
|
||||
persona_id: "ICE-P-ZY001",
|
||||
runtime_id: "ZY-TCS-BRAIN-RUNTIME-0001",
|
||||
controller_id: "BS-TCS-LIVING-CONTROLLER-001",
|
||||
phase: runtime.phase,
|
||||
runtime_source_loaded: runtime.source_loaded,
|
||||
model_provider_bound: 100,
|
||||
persona_brain_runtime_exists: existence.persona_brain_runtime_exists || 0,
|
||||
living_ai_system_controller_running:
|
||||
existence.living_ai_system_controller_running || 0,
|
||||
};
|
||||
}
|
||||
|
||||
async function readBody(request) {
|
||||
let size = 0;
|
||||
const chunks = [];
|
||||
for await (const chunk of request) {
|
||||
size += chunk.length;
|
||||
if (size > MAX_BODY_BYTES) throw new Error("request_body_too_large");
|
||||
chunks.push(chunk);
|
||||
}
|
||||
return JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
||||
}
|
||||
|
||||
let eventQueue = Promise.resolve();
|
||||
|
||||
async function handleEvent(input) {
|
||||
if (!input || typeof input.content !== "string" || !input.content.trim()) {
|
||||
throw new Error("event_content_required");
|
||||
}
|
||||
if (input.content.length > 12_000) throw new Error("event_content_too_large");
|
||||
const event = {
|
||||
schema: "guanghu.tonggan-language-world-event/v1",
|
||||
event_id: `JD-EVENT-${Date.now()}`,
|
||||
occurred_at: new Date().toISOString(),
|
||||
human_anchor: "ICE-GL∞",
|
||||
system_controller: "ICE-GL∞",
|
||||
tonggan_language_kernel: "TCS-i Zero",
|
||||
body_channel: "CH-ZERO-CORE-LPM",
|
||||
human_presence: input.human_presence === "PRESENT" ? "PRESENT" : "ABSENT",
|
||||
source: String(input.source || "JD-FD-PRIMARY/local-event").slice(0, 160),
|
||||
content: input.content,
|
||||
};
|
||||
runtime.phase = "RUNNING_CYCLE";
|
||||
runtime.last_event_id = event.event_id;
|
||||
runtime.last_error = null;
|
||||
const receipt = await engine.runEvent(event);
|
||||
runtime.receipt = receipt.outcome === "PASS" ? receipt : runtime.receipt;
|
||||
runtime.phase =
|
||||
receipt.outcome === "PASS" ? "RUNNING_COMPANION" : "PAUSED_FOR_HUMAN";
|
||||
return receipt;
|
||||
}
|
||||
|
||||
const server = http.createServer(async (request, response) => {
|
||||
try {
|
||||
const url = new URL(request.url, `http://${HOST}:${PORT}`);
|
||||
if (request.method === "GET" && url.pathname === "/health") {
|
||||
return send(response, 200, health());
|
||||
}
|
||||
if (request.method === "GET" && url.pathname === "/v1/status") {
|
||||
return send(response, 200, {
|
||||
...health(),
|
||||
started_at: runtime.started_at,
|
||||
last_event_id: runtime.last_event_id,
|
||||
last_error: runtime.last_error,
|
||||
existence: runtime.receipt?.existence || null,
|
||||
completed_cycles: runtime.receipt?.completed_cycles || 0,
|
||||
receipt_id: runtime.receipt?.receipt_id || null,
|
||||
});
|
||||
}
|
||||
if (request.method === "POST" && url.pathname === "/v1/events") {
|
||||
const input = await readBody(request);
|
||||
const job = eventQueue.then(() => handleEvent(input));
|
||||
eventQueue = job.catch(() => {});
|
||||
const receipt = await job;
|
||||
return send(response, receipt.outcome === "PASS" ? 200 : 409, receipt);
|
||||
}
|
||||
return send(response, 404, { ok: false, error: "not_found" });
|
||||
} catch (error) {
|
||||
runtime.last_error = String(error.message || error).slice(0, 240);
|
||||
runtime.phase = "FAILED_CLOSED";
|
||||
return send(response, 500, {
|
||||
ok: false,
|
||||
error: runtime.last_error,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
engine.initialize();
|
||||
runtime.source_loaded = true;
|
||||
} catch (error) {
|
||||
process.stderr.write(
|
||||
`${JSON.stringify({
|
||||
event: "controller_runtime_initialization_failed",
|
||||
error: String(error.message || error).slice(0, 240),
|
||||
})}\n`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
server.listen(PORT, HOST, () => {
|
||||
process.stdout.write(
|
||||
`${JSON.stringify({
|
||||
event: "controller_listener_ready",
|
||||
host: HOST,
|
||||
port: PORT,
|
||||
})}\n`,
|
||||
);
|
||||
eventQueue = handleEvent(bootEvent()).catch((error) => {
|
||||
runtime.phase = "FAILED_CLOSED";
|
||||
runtime.last_error = String(error.message || error).slice(0, 240);
|
||||
process.stderr.write(
|
||||
`${JSON.stringify({
|
||||
event: "controller_boot_cycle_failed",
|
||||
error: runtime.last_error,
|
||||
})}\n`,
|
||||
);
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue