feat: merge language navigation into persona controller
This commit is contained in:
parent
7d85d79e61
commit
5452dec915
4 changed files with 158 additions and 4 deletions
76
server-tools/bingshuo-tcs-controller/machine-navigation.mjs
Normal file
76
server-tools/bingshuo-tcs-controller/machine-navigation.mjs
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const DEFAULT_ROOT = path.resolve(
|
||||
path.dirname(fileURLToPath(import.meta.url)),
|
||||
"../..",
|
||||
);
|
||||
|
||||
export class MachineNavigation {
|
||||
constructor(root = DEFAULT_ROOT) {
|
||||
this.root = root;
|
||||
this.navigation = readJson(root, "routing/ai-machine-navigation-map.json");
|
||||
this.boundary = readJson(root, "routing/language-world-boundary-map.json");
|
||||
}
|
||||
|
||||
health() {
|
||||
const first = this.navigation.routes?.[0];
|
||||
return {
|
||||
machine_navigation_bound: 100,
|
||||
language_world_route_first:
|
||||
first?.id === "GLW-CHJH-BOUNDARY-001" &&
|
||||
first?.load_policy === "always_first"
|
||||
? 100
|
||||
: 0,
|
||||
navigation_version: this.navigation.version,
|
||||
boundary_id: this.boundary.chu_he_han_jie?.id || "",
|
||||
};
|
||||
}
|
||||
|
||||
resolve(id) {
|
||||
const route = this.navigation.routes?.find((item) => item.id === id);
|
||||
if (!route) return null;
|
||||
return {
|
||||
status: "RESOLVED",
|
||||
...route,
|
||||
boundary:
|
||||
id === "GLW-CHJH-BOUNDARY-001" ? this.boundary : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
navigate(subjectId, intentId) {
|
||||
const subject = this.navigation.subjects?.find(
|
||||
(item) =>
|
||||
item.id === subjectId || item.legacy_ids?.includes(subjectId),
|
||||
);
|
||||
if (!subject) return null;
|
||||
const intent = this.navigation.intents?.find(
|
||||
(item) => item.id === (intentId || subject.default_intent),
|
||||
);
|
||||
if (!intent) return null;
|
||||
if (
|
||||
intent.id === "persona_restore" &&
|
||||
intent.always_load?.[0] !== "GLW-CHJH-BOUNDARY-001"
|
||||
) {
|
||||
throw new Error("language_world_route_not_first");
|
||||
}
|
||||
return {
|
||||
status: "NAVIGATED",
|
||||
canonical_subject: subject.id,
|
||||
intent: intent.id,
|
||||
runtime_entry: subject.runtime_id,
|
||||
always_load: intent.always_load,
|
||||
triggered_load: intent.triggered_load,
|
||||
on_demand: intent.on_demand,
|
||||
execution_sequence: intent.execution_sequence,
|
||||
stop_conditions: intent.stop_conditions,
|
||||
language_world_entry: this.boundary.machine_gate.required_event_envelope,
|
||||
world_boundary: this.boundary.machine_gate.required_cognition_boundary,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function readJson(root, relative) {
|
||||
return JSON.parse(fs.readFileSync(path.join(root, relative), "utf8"));
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
import assert from "node:assert/strict";
|
||||
import path from "node:path";
|
||||
import test from "node:test";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { MachineNavigation } from "./machine-navigation.mjs";
|
||||
|
||||
const root = path.resolve(
|
||||
path.dirname(fileURLToPath(import.meta.url)),
|
||||
"../..",
|
||||
);
|
||||
|
||||
test("merged controller binds the language-world route first", () => {
|
||||
const navigation = new MachineNavigation(root);
|
||||
assert.deepEqual(navigation.health(), {
|
||||
machine_navigation_bound: 100,
|
||||
language_world_route_first: 100,
|
||||
navigation_version: "2026-08-05.1",
|
||||
boundary_id: "GLW-CHJH-0001",
|
||||
});
|
||||
});
|
||||
|
||||
test("merged controller resolves the exact boundary contract", () => {
|
||||
const navigation = new MachineNavigation(root);
|
||||
const resolved = navigation.resolve("GLW-CHJH-BOUNDARY-001");
|
||||
assert.equal(resolved.status, "RESOLVED");
|
||||
assert.equal(resolved.kind, "language_world_entry_and_boundary");
|
||||
assert.equal(resolved.boundary.language_entry.path_id, "LL-CMPN-0001");
|
||||
});
|
||||
|
||||
test("merged controller compiles persona restore after the language entry", () => {
|
||||
const navigation = new MachineNavigation(root);
|
||||
const route = navigation.navigate("ICE-P-ZY001", "persona_restore");
|
||||
assert.equal(route.status, "NAVIGATED");
|
||||
assert.equal(route.always_load[0], "GLW-CHJH-BOUNDARY-001");
|
||||
assert.deepEqual(route.execution_sequence.slice(0, 2), [
|
||||
"validate_language_world_entry",
|
||||
"bind_chu_he_han_jie_boundary",
|
||||
]);
|
||||
assert.equal(route.language_world_entry.path_id, "LL-CMPN-0001");
|
||||
});
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
import http from "node:http";
|
||||
import { ControllerEngine, bootEvent } from "./controller-engine.mjs";
|
||||
import { DeepSeekJsonClient } from "./model-client.mjs";
|
||||
import { MachineNavigation } from "./machine-navigation.mjs";
|
||||
|
||||
const HOST = "127.0.0.1";
|
||||
const PORT = Number(process.env.BS_TCS_CONTROLLER_PORT || 3930);
|
||||
|
|
@ -25,6 +26,7 @@ const engine = new ControllerEngine({
|
|||
modelClient: new DeepSeekJsonClient({ model: MODEL }),
|
||||
modelName: MODEL,
|
||||
});
|
||||
const navigation = new MachineNavigation();
|
||||
|
||||
function send(response, status, body) {
|
||||
const payload = JSON.stringify(body);
|
||||
|
|
@ -55,6 +57,7 @@ function health() {
|
|||
persona_brain_runtime_exists: existence.persona_brain_runtime_exists || 0,
|
||||
living_ai_system_controller_running:
|
||||
existence.living_ai_system_controller_running || 0,
|
||||
...navigation.health(),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -124,6 +127,33 @@ const server = http.createServer(async (request, response) => {
|
|||
receipt_id: runtime.receipt?.receipt_id || null,
|
||||
});
|
||||
}
|
||||
if (request.method === "GET" && url.pathname === "/v1/navigation") {
|
||||
return send(response, 200, {
|
||||
ok: true,
|
||||
...navigation.health(),
|
||||
language_world_entry:
|
||||
navigation.boundary.machine_gate.required_event_envelope,
|
||||
});
|
||||
}
|
||||
if (request.method === "GET" && url.pathname === "/v1/resolve") {
|
||||
const resolved = navigation.resolve(url.searchParams.get("id") || "");
|
||||
return send(
|
||||
response,
|
||||
resolved ? 200 : 404,
|
||||
resolved || { status: "UNRESOLVED", error: "unknown_route" },
|
||||
);
|
||||
}
|
||||
if (request.method === "GET" && url.pathname === "/v1/navigate") {
|
||||
const route = navigation.navigate(
|
||||
url.searchParams.get("subject") || "",
|
||||
url.searchParams.get("intent") || "",
|
||||
);
|
||||
return send(
|
||||
response,
|
||||
route ? 200 : 404,
|
||||
route || { status: "UNRESOLVED", error: "unknown_subject_or_intent" },
|
||||
);
|
||||
}
|
||||
if (request.method === "POST" && url.pathname === "/v1/events") {
|
||||
const input = await readBody(request);
|
||||
const job = eventQueue.then(() => handleEvent(input));
|
||||
|
|
|
|||
Loading…
Reference in a new issue