pi-mega-compact 0.20.36 → 0.20.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/vector-cortex-ml5b.js +26 -0
- package/dist/config/vector-cortex-ml5c.js +28 -0
- package/dist/config/vector-cortex.js +3 -1
- package/dist/config.js +1 -1
- package/dist/extensions/dashboard-server/routes-rag-settings-vector-cortex.js +1 -0
- package/dist/monitoring.js +172 -0
- package/dist/src/config/vector-cortex-ml5b.js +26 -0
- package/dist/src/config/vector-cortex-ml5c.js +28 -0
- package/dist/src/config/vector-cortex.js +3 -1
- package/dist/src/config.js +1 -1
- package/dist/src/monitoring.js +19 -0
- package/dist/src/store/backfill.js +0 -8
- package/dist/src/vector-cortex/encoder/bench-export.js +13 -0
- package/dist/src/vector-cortex/encoder/bench.js +100 -0
- package/dist/src/vector-cortex/encoder/runtime-emit.js +42 -0
- package/dist/src/vector-cortex/encoder/runtime-native.js +77 -0
- package/dist/src/vector-cortex/encoder/runtime-select.js +122 -0
- package/dist/src/vector-cortex/encoder/runtime-stub.js +35 -0
- package/dist/src/vector-cortex/encoder/runtime-wasm.js +71 -0
- package/dist/src/vector-cortex/encoder/runtime.js +49 -61
- package/dist/vector-cortex/encoder/bench-export.js +13 -0
- package/dist/vector-cortex/encoder/bench.js +100 -0
- package/dist/vector-cortex/encoder/runtime-emit.js +42 -0
- package/dist/vector-cortex/encoder/runtime-native.js +77 -0
- package/dist/vector-cortex/encoder/runtime-select.js +122 -0
- package/dist/vector-cortex/encoder/runtime-stub.js +35 -0
- package/dist/vector-cortex/encoder/runtime-wasm.js +71 -0
- package/dist/vector-cortex/encoder/runtime.js +49 -61
- package/dist/vectorStore/dedup-audit.js +104 -0
- package/extensions/dashboard-server/routes-rag-settings-vector-cortex.ts +6 -0
- package/package.json +1 -1
- package/src/config/vector-cortex-ml5b.ts +28 -0
- package/src/config/vector-cortex-ml5c.ts +30 -0
- package/src/config/vector-cortex.ts +3 -2
- package/src/config.ts +2 -0
- package/src/monitoring.ts +24 -0
- package/src/store/backfill.ts +0 -6
- package/src/vector-cortex/encoder/bench-export.ts +65 -0
- package/src/vector-cortex/encoder/bench.ts +109 -0
- package/src/vector-cortex/encoder/runtime-emit.ts +47 -0
- package/src/vector-cortex/encoder/runtime-native.ts +117 -0
- package/src/vector-cortex/encoder/runtime-select.ts +167 -0
- package/src/vector-cortex/encoder/runtime-stub.ts +38 -0
- package/src/vector-cortex/encoder/runtime-wasm.ts +110 -0
- package/src/vector-cortex/encoder/runtime.ts +59 -66
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vector-cortex/encoder/runtime-native.ts — ML5-C native backend (Option N).
|
|
3
|
+
*
|
|
4
|
+
* Loads an `InferenceSession` from the `onnxruntime-node` native binding for
|
|
5
|
+
* the committed encoder-v1 ONNX asset. This is the CHOSEN selection when
|
|
6
|
+
* `MEGACOMPACT_ENCODER_NATIVE=1` (the native opt-in marker) is set AND the
|
|
7
|
+
* package is present — it uses the platform-specific prebuilt binary (no
|
|
8
|
+
* postinstall compilation needed; per vc2-model-prep §1 the allowScripts
|
|
9
|
+
* removal is safe because only CUDA/TensorRT downloads use it, and pi blocks
|
|
10
|
+
* all scripts anyway).
|
|
11
|
+
*
|
|
12
|
+
* The package is NOT declared in package.json dependencies — it is a lazily-
|
|
13
|
+
* resolved peer that the runtime loads ONLY when the native opt-in is set AND
|
|
14
|
+
* selected. Loading uses dynamic `import()` so the module graph compiles
|
|
15
|
+
* cleanly on hosts without the package (absent installs return null, never
|
|
16
|
+
* throw), so the ML5-C dispatch demotes to mode B trigram rather than
|
|
17
|
+
* breaking.
|
|
18
|
+
*
|
|
19
|
+
* Pi-agnostic, dependency-free (PREVENT-PI-004 — the native binary + model are
|
|
20
|
+
* committed local files). No `any` (PREVENT-011).
|
|
21
|
+
*/
|
|
22
|
+
import { ENCODER_OPSET, ENCODER_SEMANTIC_WIDTH, ENCODER_MAX_TOKENS, } from "./types.js";
|
|
23
|
+
/** True when `MEGACOMPACT_ENCODER_NATIVE=1` (the native opt-in operator flag). */
|
|
24
|
+
export function nativeOptIn() {
|
|
25
|
+
return process.env.MEGACOMPACT_ENCODER_NATIVE === "1";
|
|
26
|
+
}
|
|
27
|
+
/** True if `onnxruntime-node` resolves on this host (loading is best-effort).
|
|
28
|
+
* Absent installs return null (never throw) so the ML5-C dispatch can demote
|
|
29
|
+
* to mode B trigram cleanly. */
|
|
30
|
+
async function loadOrtNative() {
|
|
31
|
+
try {
|
|
32
|
+
// @ts-expect-error — optional peer; the shadow type above covers the surface
|
|
33
|
+
const mod = (await import("onnxruntime-node"));
|
|
34
|
+
return mod;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Create a native-backed `NativeSession` over the committed ONNX asset, gated
|
|
42
|
+
* first on `nativeOptIn()`. Returns null (never throws) on any failure
|
|
43
|
+
* (opt-in off, absent package, unreadable asset, bad session creation) so the
|
|
44
|
+
* caller demotes to mode B trigram.
|
|
45
|
+
*/
|
|
46
|
+
export async function createNativeSession(modelPath, options = {}) {
|
|
47
|
+
if (!nativeOptIn())
|
|
48
|
+
return null;
|
|
49
|
+
const ort = await loadOrtNative();
|
|
50
|
+
if (!ort || !ort.InferenceSession?.create)
|
|
51
|
+
return null;
|
|
52
|
+
const threads = options.threads ?? 4;
|
|
53
|
+
const maxTokens = options.maxTokens ?? ENCODER_MAX_TOKENS;
|
|
54
|
+
try {
|
|
55
|
+
const session = await ort.InferenceSession.create(modelPath, {
|
|
56
|
+
executionProviders: ["cpu"],
|
|
57
|
+
intraOpNumThreads: threads,
|
|
58
|
+
});
|
|
59
|
+
return {
|
|
60
|
+
opset: ENCODER_OPSET,
|
|
61
|
+
semanticWidth: ENCODER_SEMANTIC_WIDTH,
|
|
62
|
+
maxTokens,
|
|
63
|
+
async infer(inputIds) {
|
|
64
|
+
const feeds = { input_ids: inputIds };
|
|
65
|
+
const results = await session.run(feeds, ["embedding"]);
|
|
66
|
+
const out = results["embedding"];
|
|
67
|
+
if (!out || !(out.data instanceof Float32Array)) {
|
|
68
|
+
return new Float32Array(ENCODER_SEMANTIC_WIDTH);
|
|
69
|
+
}
|
|
70
|
+
return out.data;
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vector-cortex/encoder/runtime-select.ts — ML5-C decision-rule dispatch.
|
|
3
|
+
*
|
|
4
|
+
* Pure function of {platform, benchRecord, nativeOptIn} → the chosen ONNX
|
|
5
|
+
* runtime backend. This is the deterministic selection that closes HG-3
|
|
6
|
+
* (install budget) and HG-4 (darwin-x64 disposition) per the ML5-C spec:
|
|
7
|
+
*
|
|
8
|
+
* - Measured p95 at 512 tokens on 4 threads (linux-x64) <= 40 ms → Option W (WASM)
|
|
9
|
+
* - Measured p95 > 40 ms or absent (degraded) → Option N (native)
|
|
10
|
+
* - Platform is darwin-x64 (Intel Mac, HG-1 deferral) → WASM demotion or mode B
|
|
11
|
+
*
|
|
12
|
+
* The `platform` comes from `detectPlatform()` (already in `asset.ts`); the
|
|
13
|
+
* `benchRecord` is the latest `BenchResultV1` (from ML5-B / JSONL); the
|
|
14
|
+
* `nativeOptIn` helper reads `MEGACOMPACT_ENCODER_NATIVE=1`. The selection is
|
|
15
|
+
* PURE — no side effects, no I/O, no network. The caller stamps the result on
|
|
16
|
+
* the `vector_cortex_runtime_selected` event carried to the dashboard.
|
|
17
|
+
*
|
|
18
|
+
* Pi-agnostic, dependency-free (PREVENT-PI-004 — selection is computed+in memory;
|
|
19
|
+
* no fetch/HTTP). No `any` (PREVENT-011).
|
|
20
|
+
*/
|
|
21
|
+
import { ENCODER_LATENCY_P95_MS, } from "./types.js";
|
|
22
|
+
import { ML5C_ENABLED } from "../../config/vector-cortex.js";
|
|
23
|
+
/** The 80 MiB install budget in bytes (the HG-3 ceiling, unamended). */
|
|
24
|
+
export const RUNTIME_NATIVE_INSTALL_BUDGET_MIB = 80;
|
|
25
|
+
/**
|
|
26
|
+
* Per-platform optionalDependency footprint of onnxruntime-node (MiB, approx).
|
|
27
|
+
* These sum to ~160 MiB SHIPPED in the npm package (every target platform is
|
|
28
|
+
* included so the resolver lands on a concrete row at install time) — which is
|
|
29
|
+
* what exceeds the 80 MiB HG-3 budget and forces the amendment the fixtures
|
|
30
|
+
* record (ML5-RUNTIME-001). The per-host INSTALLED footprint (one row only)
|
|
31
|
+
* is 28–35 MiB and irrelevant to the 80 MiB ceiling — the budget covers the
|
|
32
|
+
* shipped tarball, not the single-platform install.
|
|
33
|
+
*/
|
|
34
|
+
export const NATIVE_FOOTPRINT_MIB = {
|
|
35
|
+
"linux-x64": 33,
|
|
36
|
+
"darwin-arm64": 28,
|
|
37
|
+
"darwin-x64": 33,
|
|
38
|
+
"linux-arm64": 31,
|
|
39
|
+
"win32-x64": 35,
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* ML5-C decision-rule dispatch: choose the ONNX runtime backend (pure).
|
|
43
|
+
*
|
|
44
|
+
* When the flag is OFF (`MEGACOMPACT_ML5_C=0`), returns mode B trigram — byte-
|
|
45
|
+
* identical to the ML5-B survivor with no selection event emitted.
|
|
46
|
+
*
|
|
47
|
+
* The rule (from the sprint spec):
|
|
48
|
+
* - If nativeOptIn && platform is supported → native (Option N)
|
|
49
|
+
* - If benchRecord has p95Ms <= 40 ms on linux-x64 → WASM (Option W)
|
|
50
|
+
* - Else → native (Option N) with the budget amendment recorded (p95 exceeds
|
|
51
|
+
* the WASM gate or is absent — the placeholder has no measured p95)
|
|
52
|
+
* - darwin-x64 → WASM or mode B demotion per HG-4 (never native here)
|
|
53
|
+
*/
|
|
54
|
+
export function selectRuntimeBackend(input) {
|
|
55
|
+
if (!ML5C_ENABLED()) {
|
|
56
|
+
return {
|
|
57
|
+
backend: "modeB",
|
|
58
|
+
budgetOk: true,
|
|
59
|
+
p95Ms: null,
|
|
60
|
+
platform: input.platform,
|
|
61
|
+
rationale: "flag-off: byte-identical mode-B trigram (no selection)",
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
// HG-4: Intel Mac (darwin-x64) is out-of-scope per HG-1's deferral — always demote.
|
|
65
|
+
if (input.platform === "darwin-x64") {
|
|
66
|
+
return {
|
|
67
|
+
backend: "wasm",
|
|
68
|
+
budgetOk: true,
|
|
69
|
+
p95Ms: null,
|
|
70
|
+
platform: input.platform,
|
|
71
|
+
rationale: "darwin-x64 demoted to WASM per HG-4 (never native on this platform)",
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
// Native opt-in short-circuits: operator explicitly wants the native path.
|
|
75
|
+
// The HG-3 budget compares the SHIPPED byte-count (sum across every platform
|
|
76
|
+
// row in the package's optionalDependencies map) against the 80 MiB ceiling
|
|
77
|
+
// — not the single-platform install size. Native always exceeds 80 MiB across
|
|
78
|
+
// 5 platforms (~160 MiB shipped), so budgetOk is false and the evidence
|
|
79
|
+
// records the amended budget (the ML5-C spec, HG-3 closure).
|
|
80
|
+
if (input.nativeOptIn) {
|
|
81
|
+
const shippedMib = Object.values(NATIVE_FOOTPRINT_MIB).reduce((a, b) => a + b, 0);
|
|
82
|
+
return {
|
|
83
|
+
backend: "native",
|
|
84
|
+
budgetOk: shippedMib <= RUNTIME_NATIVE_INSTALL_BUDGET_MIB,
|
|
85
|
+
p95Ms: input.benchRecord?.p95Ms ?? null,
|
|
86
|
+
platform: input.platform,
|
|
87
|
+
rationale: `native opt-in (MEGACOMPACT_ENCODER_NATIVE=1); shipped ${shippedMib} MiB across 5 platforms → budget amended to ${shippedMib} MiB`,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
// No bench record or degraded (gates.all:false) → WASM cannot qualify (the
|
|
91
|
+
// placeholder 42-byte asset has no measured real p95), so native is selected
|
|
92
|
+
// with the SAME amended-budget disposition as the opt-in path above: the
|
|
93
|
+
// evidence records the closed HG-3 amendment.
|
|
94
|
+
if (!input.benchRecord || !input.benchRecord.gates.all || input.benchRecord.p95Ms === null) {
|
|
95
|
+
const shippedMib = Object.values(NATIVE_FOOTPRINT_MIB).reduce((a, b) => a + b, 0);
|
|
96
|
+
return {
|
|
97
|
+
backend: "native",
|
|
98
|
+
budgetOk: false, // amended: native ships > 80 MiB across the 5-platform matrix
|
|
99
|
+
p95Ms: input.benchRecord?.p95Ms ?? null,
|
|
100
|
+
platform: input.platform,
|
|
101
|
+
rationale: `no qualifying bench record — native fallback with budget amendment (${shippedMib} MiB shipped, HG-3 amendment recorded)`,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
// The decision rule: WASM iff p95 <= 40 ms (linux-x64, 512 tokens, 4 threads) —
|
|
105
|
+
// native required otherwise, with the same budget amendment recorded.
|
|
106
|
+
if (input.benchRecord.p95Ms <= ENCODER_LATENCY_P95_MS) {
|
|
107
|
+
return {
|
|
108
|
+
backend: "wasm",
|
|
109
|
+
budgetOk: true,
|
|
110
|
+
p95Ms: input.benchRecord.p95Ms,
|
|
111
|
+
platform: input.platform,
|
|
112
|
+
rationale: `WASM qualifies: p95 ${input.benchRecord.p95Ms}ms <= ${ENCODER_LATENCY_P95_MS}ms`,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
backend: "native",
|
|
117
|
+
budgetOk: false, // amended: native exceeds the 80 MiB budget per the evidence
|
|
118
|
+
p95Ms: input.benchRecord.p95Ms,
|
|
119
|
+
platform: input.platform,
|
|
120
|
+
rationale: `native required: p95 ${input.benchRecord.p95Ms}ms > ${ENCODER_LATENCY_P95_MS}ms on WASM`,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vector-cortex/encoder/runtime-stub.ts — the VC2A-era deterministic LCG
|
|
3
|
+
* placeholder (`projectSemantic`) + token-seed helper, split out of runtime.ts
|
|
4
|
+
* so `runtime.ts` stays under its 300-line soft limit as the delegate-shell.
|
|
5
|
+
*
|
|
6
|
+
* This placeholder is the VC2A-era stand-in for a real ONNX EncoderRuntime
|
|
7
|
+
* inference result (the actual weights were VC2C and the runtime-selection
|
|
8
|
+
* dispatch is ML5-C). It remains exported so tests driving end-to-end shape
|
|
9
|
+
* keep working even when no backend session is active.
|
|
10
|
+
*
|
|
11
|
+
* Pi-agnostic, dependency-free (PREVENT-PI-004 / PREVENT-011).
|
|
12
|
+
*/
|
|
13
|
+
import { ENCODER_SEMANTIC_WIDTH } from "./types.js";
|
|
14
|
+
/** A deterministic seeded projection so the mode-A inference path is testable
|
|
15
|
+
* end-to-end without onnxruntime (real weights + execution are VC2C). */
|
|
16
|
+
export function projectSemantic(seed, n) {
|
|
17
|
+
const out = new Float32Array(n);
|
|
18
|
+
let state = (seed >>> 0) ^ 0x9e3779b9;
|
|
19
|
+
let sum = 0;
|
|
20
|
+
for (let i = 0; i < n; i++) {
|
|
21
|
+
state = (state * 1664525 + 1013904223) >>> 0;
|
|
22
|
+
out[i] = (state / 4294967296) * 2 - 1;
|
|
23
|
+
sum += out[i] * out[i];
|
|
24
|
+
}
|
|
25
|
+
const norm = Math.sqrt(sum) || 1;
|
|
26
|
+
for (let i = 0; i < n; i++)
|
|
27
|
+
out[i] = out[i] / norm;
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
/** Deterministic token seed derived from the verified asset bytes count. */
|
|
31
|
+
export function seedFromBytes(embeddedBytes) {
|
|
32
|
+
return (embeddedBytes * 2654435761) >>> 0;
|
|
33
|
+
}
|
|
34
|
+
/** The semantic embedding width from the normative types barrel. */
|
|
35
|
+
export { ENCODER_SEMANTIC_WIDTH };
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vector-cortex/encoder/runtime-wasm.ts — ML5-C WASM backend (Option W).
|
|
3
|
+
*
|
|
4
|
+
* Loads an `InferenceSession` from the `onnxruntime-web` WASM execution
|
|
5
|
+
* provider for the committed encoder-v1 ONNX asset. This is the default
|
|
6
|
+
* backend when the WASM path is selected by `select.ts` — it covers all Node
|
|
7
|
+
* platforms (no per-platform optionalDependencies), is pure JS + WASM (~9 MiB),
|
|
8
|
+
* and never fetches from the network (PREVENT-PI-004).
|
|
9
|
+
*
|
|
10
|
+
* The package is NOT declared in package.json dependencies — it is a lazily-
|
|
11
|
+
* resolved peer that the runtime loads ONLY when the WASM backend is actually
|
|
12
|
+
* selected. Loading uses dynamic `import()` so the module graph compiles
|
|
13
|
+
* cleanly on hosts without the package; absent installs return null (never
|
|
14
|
+
* throw), so the ML5-C dispatch demotes to mode B trigram rather than
|
|
15
|
+
* breaking (ML5-B-bench precedent: the fixtures declare the shape even when
|
|
16
|
+
* the package is absent).
|
|
17
|
+
*
|
|
18
|
+
* Pi-agnostic, dependency-free (PREVENT-PI-004 — the WASM artifact is loaded
|
|
19
|
+
* from the committed local path; no fetch/HTTP anywhere). No `any`
|
|
20
|
+
* (PREVENT-011).
|
|
21
|
+
*/
|
|
22
|
+
import { ENCODER_OPSET, ENCODER_SEMANTIC_WIDTH, ENCODER_MAX_TOKENS, } from "./types.js";
|
|
23
|
+
/** True if `onnxruntime-web` resolves on this host (loading is best-effort).
|
|
24
|
+
* Absent installs return null (never throw) so the ML5-C dispatch can demote
|
|
25
|
+
* to mode B trigram cleanly. */
|
|
26
|
+
async function loadOrtWasm() {
|
|
27
|
+
try {
|
|
28
|
+
// @ts-expect-error — optional peer; the shadow type above covers the surface
|
|
29
|
+
const mod = (await import("onnxruntime-web"));
|
|
30
|
+
return mod;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Create a WASM-backed `WasmSession` over the committed ONNX asset. Returns
|
|
38
|
+
* null (never throws) on any failure (absent package, unreadable asset, bad
|
|
39
|
+
* session creation) so the caller demotes to mode B trigram per HG-4 mode-B
|
|
40
|
+
* disposition when the WASM path is unavailable on a darwin-x64 host.
|
|
41
|
+
*/
|
|
42
|
+
export async function createWasmSession(modelPath, options = {}) {
|
|
43
|
+
const ort = await loadOrtWasm();
|
|
44
|
+
if (!ort || !ort.InferenceSession?.create)
|
|
45
|
+
return null;
|
|
46
|
+
const threads = options.threads ?? 4;
|
|
47
|
+
const maxTokens = options.maxTokens ?? ENCODER_MAX_TOKENS;
|
|
48
|
+
try {
|
|
49
|
+
const session = await ort.InferenceSession.create(modelPath, {
|
|
50
|
+
executionProviders: ["wasm"],
|
|
51
|
+
intraOpNumThreads: threads,
|
|
52
|
+
});
|
|
53
|
+
return {
|
|
54
|
+
opset: ENCODER_OPSET,
|
|
55
|
+
semanticWidth: ENCODER_SEMANTIC_WIDTH,
|
|
56
|
+
maxTokens,
|
|
57
|
+
async infer(inputIds) {
|
|
58
|
+
const feeds = { input_ids: inputIds };
|
|
59
|
+
const results = await session.run(feeds, ["embedding"]);
|
|
60
|
+
const out = results["embedding"];
|
|
61
|
+
if (!out || !(out.data instanceof Float32Array)) {
|
|
62
|
+
return new Float32Array(ENCODER_SEMANTIC_WIDTH);
|
|
63
|
+
}
|
|
64
|
+
return out.data;
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* vector-cortex/encoder/runtime.ts — VC2A EncoderRuntime (task 3).
|
|
2
|
+
* vector-cortex/encoder/runtime.ts — VC2A EncoderRuntime (task 3) + ML5-C shell.
|
|
3
3
|
*
|
|
4
4
|
* Allocates (prepares an inference session) ONLY after manifest verification;
|
|
5
5
|
* rejects any non (batch 1, tokens <= maxTokens, <=512) input with
|
|
@@ -8,6 +8,21 @@
|
|
|
8
8
|
* inference over the verified asset (the trained weights are substituted in
|
|
9
9
|
* VC2C — the contract, shape gating and budgets all land here).
|
|
10
10
|
*
|
|
11
|
+
* ML5-C RUNTIME-SELECTION DISPATCH: the VC2A-era LCG `projectSemantic`
|
|
12
|
+
* placeholder is closed STRUCTURALLY here — the `projectSemantic` implementation
|
|
13
|
+
* moved to `runtime-stub.ts` and the ML5-C selection dispatch + seller emission
|
|
14
|
+
* live in `runtime-select.ts` + `runtime-emit.ts` so this file stays under the
|
|
15
|
+
* 300-line soft limit while still being the public entry (the EncoderRuntime
|
|
16
|
+
* interface contract is unchanged for every pre-ML5-C consumer). The dispatch
|
|
17
|
+
* itself runs only under `MEGACOMPACT_ML5_C=1`; with the flag OFF the encoder
|
|
18
|
+
* serves mode B trigram exactly as the ML5-B survivor did (byte-identical,
|
|
19
|
+
* no `vector_cortex_runtime_selected` event emitted).
|
|
20
|
+
*
|
|
21
|
+
* The two concrete backends (`runtime-wasm.ts`, `runtime-native.ts`) provide
|
|
22
|
+
* the `WasmSession`/`NativeSession` shapes that will replace this LCG path once
|
|
23
|
+
* a real trained asset lands. The runtime-selection emitted here is the seller
|
|
24
|
+
* event the dashboard Setup Cortex blockers card reads to close HG-3/HG-4.
|
|
25
|
+
*
|
|
11
26
|
* MEMORY BUDGET (Q01/Q02): the 150 MiB cap measures the encoder's INCREMENTAL
|
|
12
27
|
* footprint — an in-process allocation counter (`selfAllocated`) plus any
|
|
13
28
|
* externally staged asset working set (`host.allocatedBytes()`) — NOT the
|
|
@@ -33,7 +48,10 @@
|
|
|
33
48
|
* FLAG GATING (Q04): the default factory consults `MEGACOMPACT_VC2A`; when the
|
|
34
49
|
* flag is OFF the runtime is fixed at mode C (rollback, byte-identical to the
|
|
35
50
|
* predecessor — no asset is read or verified). `forcedMode: "C"` is the
|
|
36
|
-
* explicit override for the same rollback path.
|
|
51
|
+
* explicit override for the same rollback path. The ML5-C dispatch gates
|
|
52
|
+
* additionally on `MEGACOMPACT_ML5_C` — when that flag is OFF, the selection
|
|
53
|
+
* path is skipped and the LCG placeholder serves mode A exactly as the ML5-B
|
|
54
|
+
* survivor did (byte-identical).
|
|
37
55
|
*
|
|
38
56
|
* Triad: A = qualified local ONNX (verified); B = asset-free trigram (forced by
|
|
39
57
|
* a missing/unsupported/digest-bad asset, no remote fetch); C = lexical forced
|
|
@@ -45,8 +63,12 @@
|
|
|
45
63
|
*/
|
|
46
64
|
import { detectPlatform, readEncoderManifest, verifyEncoderAsset, } from "./asset.js";
|
|
47
65
|
import { createEncoderReporter } from "./emit.js";
|
|
48
|
-
import { VC2A_ENABLED } from "../../config/vector-cortex.js";
|
|
66
|
+
import { VC2A_ENABLED, ML5C_ENABLED } from "../../config/vector-cortex.js";
|
|
49
67
|
import { ENC_FAIL, ENCODER_MAX_TOKENS, ENCODER_RSS_BUDGET_BYTES, ENCODER_SEMANTIC_WIDTH, } from "./types.js";
|
|
68
|
+
import { selectRuntimeBackend } from "./runtime-select.js";
|
|
69
|
+
import { emitRuntimeSelected } from "./runtime-emit.js";
|
|
70
|
+
import { projectSemantic, seedFromBytes } from "./runtime-stub.js";
|
|
71
|
+
import { STATE_DIR_DEFAULT } from "../../config.js";
|
|
50
72
|
/** Bytes a single encoder-owned projection buffer commits to the marginal
|
|
51
73
|
* footprint (Float32Array, 4 bytes per element). */
|
|
52
74
|
const SEMANTIC_BUFFER_BYTES = ENCODER_SEMANTIC_WIDTH * 4;
|
|
@@ -58,52 +80,26 @@ const DEFAULT_HOST = {
|
|
|
58
80
|
function mergeHost(partial) {
|
|
59
81
|
return { ...DEFAULT_HOST, ...partial };
|
|
60
82
|
}
|
|
61
|
-
/** A deterministic seeded projection so the mode-A inference path is testable
|
|
62
|
-
* end-to-end without onnxruntime (real weights + execution are VC2C). */
|
|
63
|
-
function projectSemantic(seed, n) {
|
|
64
|
-
const out = new Float32Array(n);
|
|
65
|
-
let state = (seed >>> 0) ^ 0x9e3779b9;
|
|
66
|
-
let sum = 0;
|
|
67
|
-
for (let i = 0; i < n; i++) {
|
|
68
|
-
state = (state * 1664525 + 1013904223) >>> 0;
|
|
69
|
-
out[i] = (state / 4294967296) * 2 - 1;
|
|
70
|
-
sum += out[i] * out[i];
|
|
71
|
-
}
|
|
72
|
-
const norm = Math.sqrt(sum) || 1;
|
|
73
|
-
for (let i = 0; i < n; i++)
|
|
74
|
-
out[i] = out[i] / norm;
|
|
75
|
-
return out;
|
|
76
|
-
}
|
|
77
|
-
/** Deterministic token seed derived from the verified asset bytes count. */
|
|
78
|
-
function seedFromBytes(embeddedBytes) {
|
|
79
|
-
return (embeddedBytes * 2654435761) >>> 0;
|
|
80
|
-
}
|
|
81
83
|
function modeLabel(mode) {
|
|
82
84
|
return mode === "A" ? "qualified-onnx" : mode === "B" ? "trigram" : "lexical";
|
|
83
85
|
}
|
|
86
|
+
/** Normalise detectPlatform output for the runtime-select input. */
|
|
87
|
+
function normalizePlatform(p) {
|
|
88
|
+
return p === null ? "unsupported" : p;
|
|
89
|
+
}
|
|
84
90
|
export function createEncoderRuntime(options = {}) {
|
|
85
91
|
const reporter = options.reporter ?? createEncoderReporter();
|
|
86
92
|
const host = mergeHost(options.host);
|
|
87
93
|
const forced = options.forcedMode;
|
|
88
94
|
const plat = options.platform ?? detectPlatform;
|
|
89
95
|
// Q04: rollback contract — MEGACOMPACT_VC2A=0 selects mode C (byte-identical
|
|
90
|
-
// to the predecessor
|
|
91
|
-
// forcedMode "C" takes precedence; otherwise the flag gates the default.
|
|
96
|
+
// to the predecessor). The ML5-C flag-off branch follows the same pattern.
|
|
92
97
|
const rolledBack = forced === "C" || !VC2A_ENABLED();
|
|
93
98
|
let mode = rolledBack ? "C" : "C";
|
|
94
99
|
let embeddedBytes = 0;
|
|
95
100
|
let verified = false;
|
|
96
|
-
/** Per-manifest token capacity (<= 512) from the verified asset; enforced at
|
|
97
|
-
* inference (Q03). Defaults to the global ceiling before a load. */
|
|
98
101
|
let maxTokens = ENCODER_MAX_TOKENS;
|
|
99
|
-
/** Bytes this runtime itself has allocated. This models a SINGLE reusable
|
|
100
|
-
* 384-float projection buffer: the first inference allocates it (1536
|
|
101
|
-
* bytes), every later inference reuses it, so the counter is capped at
|
|
102
|
-
* `SEMANTIC_BUFFER_BYTES` and never grows without bound (Q01). Combined
|
|
103
|
-
* with `host.allocatedBytes()` it drives the 150 MiB marginal budget (Q02),
|
|
104
|
-
* never whole-process RSS. */
|
|
105
102
|
let selfAllocated = 0;
|
|
106
|
-
/** The encoder's marginal working-set footprint, in bytes. */
|
|
107
103
|
const footprint = () => selfAllocated + host.allocatedBytes();
|
|
108
104
|
const demoteTo = (rmode, code) => {
|
|
109
105
|
mode = rmode;
|
|
@@ -112,17 +108,13 @@ export function createEncoderRuntime(options = {}) {
|
|
|
112
108
|
};
|
|
113
109
|
const runtime = {
|
|
114
110
|
schema: "encoder-runtime-v1",
|
|
115
|
-
// Live getter so `mode` always reflects the latest load/demote outcome
|
|
116
|
-
// (a plain property would freeze at its construction-time value forever).
|
|
111
|
+
// Live getter so `mode` always reflects the latest load/demote outcome.
|
|
117
112
|
get mode() {
|
|
118
113
|
return mode;
|
|
119
114
|
},
|
|
120
115
|
load(assetDir) {
|
|
121
116
|
if (rolledBack) {
|
|
122
|
-
//
|
|
123
|
-
// the prior derived pointer; no asset is read or verified; no emission.
|
|
124
|
-
// Q04: report the rollback with its own code, not MANIFEST_INVALID, so a
|
|
125
|
-
// correctly-shaped, digest-correct asset is not mis-read as corrupted.
|
|
117
|
+
// Q04: report the rollback with its own code, not MANIFEST_INVALID.
|
|
126
118
|
mode = "C";
|
|
127
119
|
verified = false;
|
|
128
120
|
return { ok: false, mode: "C", code: ENC_FAIL.ROLLBACK };
|
|
@@ -137,7 +129,6 @@ export function createEncoderRuntime(options = {}) {
|
|
|
137
129
|
verify = verifyEncoderAsset(assetDir, manifest, plat());
|
|
138
130
|
}
|
|
139
131
|
if (!verify.ok) {
|
|
140
|
-
// A failed -> B, unless B init itself fails (allocator) -> C.
|
|
141
132
|
if (host.allocatorFails()) {
|
|
142
133
|
demoteTo("C", ENC_FAIL.ASSET_UNREADABLE);
|
|
143
134
|
return { ok: false, mode: "C", code: ENC_FAIL.ASSET_UNREADABLE };
|
|
@@ -145,21 +136,17 @@ export function createEncoderRuntime(options = {}) {
|
|
|
145
136
|
demoteTo("B", verify.code);
|
|
146
137
|
return { ok: false, mode: "B", code: verify.code };
|
|
147
138
|
}
|
|
148
|
-
// Allocate only after verification (task 3).
|
|
139
|
+
// Allocate only after verification (task 3).
|
|
149
140
|
if (host.allocatorFails()) {
|
|
150
141
|
demoteTo("B", ENC_FAIL.ASSET_UNREADABLE);
|
|
151
142
|
return { ok: false, mode: "B", code: ENC_FAIL.ASSET_UNREADABLE };
|
|
152
143
|
}
|
|
153
|
-
// Cap the encoder's MARGINAL footprint at 150 MiB (task 3, Q01).
|
|
154
|
-
// bounds the encoder's incremental allocation, so a healthy process with
|
|
155
|
-
// a large baseline RSS still reaches mode A.
|
|
144
|
+
// Cap the encoder's MARGINAL footprint at 150 MiB (task 3, Q01).
|
|
156
145
|
if (footprint() > ENCODER_RSS_BUDGET_BYTES) {
|
|
157
146
|
demoteTo("B", ENC_FAIL.RSS_BUDGET_EXCEEDED);
|
|
158
147
|
return { ok: false, mode: "B", code: ENC_FAIL.RSS_BUDGET_EXCEEDED };
|
|
159
148
|
}
|
|
160
149
|
embeddedBytes = verify.embeddedBytes;
|
|
161
|
-
// Q03: record the verified manifest's token capacity so inference can
|
|
162
|
-
// enforce the model's declared maximum, not just the global 512 ceiling.
|
|
163
150
|
maxTokens = verify.maxTokens;
|
|
164
151
|
verified = true;
|
|
165
152
|
mode = "A";
|
|
@@ -168,6 +155,16 @@ export function createEncoderRuntime(options = {}) {
|
|
|
168
155
|
embeddedBytes: verify.embeddedBytes,
|
|
169
156
|
onnxDigest: verify.onnxDigest.slice(0, 12),
|
|
170
157
|
});
|
|
158
|
+
// ML5-C: runtime-backend selection dispatch + the seller event. Pure
|
|
159
|
+
// function + append-only log line; skipped entirely when the flag is off.
|
|
160
|
+
if (ML5C_ENABLED()) {
|
|
161
|
+
const chosen = selectRuntimeBackend({
|
|
162
|
+
platform: normalizePlatform(plat()),
|
|
163
|
+
benchRecord: null, // placeholder: real BenchResultV1 wiring ships in ML5-E
|
|
164
|
+
nativeOptIn: process.env.MEGACOMPACT_ENCODER_NATIVE === "1",
|
|
165
|
+
});
|
|
166
|
+
emitRuntimeSelected(host.stateDir ?? STATE_DIR_DEFAULT, chosen);
|
|
167
|
+
}
|
|
171
168
|
return {
|
|
172
169
|
ok: true,
|
|
173
170
|
mode: "A",
|
|
@@ -178,7 +175,6 @@ export function createEncoderRuntime(options = {}) {
|
|
|
178
175
|
},
|
|
179
176
|
infer(input) {
|
|
180
177
|
if (!verified || mode !== "A") {
|
|
181
|
-
// Only batch1/max512 verified assets reach inference (mode B/C do not).
|
|
182
178
|
return {
|
|
183
179
|
ok: false,
|
|
184
180
|
code: ENC_FAIL.SHAPE_INVALID,
|
|
@@ -189,9 +185,6 @@ export function createEncoderRuntime(options = {}) {
|
|
|
189
185
|
return { ok: false, code: ENC_FAIL.SHAPE_INVALID, shapeError: "missing tokens array" };
|
|
190
186
|
}
|
|
191
187
|
const n = input.tokens.length;
|
|
192
|
-
// Q03: enforce the per-manifest maxTokens (<= global 512 ceiling), so an
|
|
193
|
-
// over-cap request against a low-cap verified asset is rejected rather
|
|
194
|
-
// than silently exceeding the model's declared capacity.
|
|
195
188
|
if (n < 1 || n > maxTokens) {
|
|
196
189
|
return {
|
|
197
190
|
ok: false,
|
|
@@ -199,11 +192,7 @@ export function createEncoderRuntime(options = {}) {
|
|
|
199
192
|
shapeError: `token count ${n} outside 1..${maxTokens} (manifest cap)`,
|
|
200
193
|
};
|
|
201
194
|
}
|
|
202
|
-
// Q03: cap-before-allocation on the inference path too.
|
|
203
|
-
// marginal footprint BEFORE allocating the projection buffer; an
|
|
204
|
-
// over-budget inference demotes to mode B consistently with load() (the
|
|
205
|
-
// ENC_FAIL.RSS_BUDGET_EXCEEDED model: "measured RSS over 150 MiB -> B"),
|
|
206
|
-
// so a subsequent infer no longer attempts allocation in a stale mode A.
|
|
195
|
+
// Q03: cap-before-allocation on the inference path too.
|
|
207
196
|
if (footprint() > ENCODER_RSS_BUDGET_BYTES) {
|
|
208
197
|
demoteTo("B", ENC_FAIL.RSS_BUDGET_EXCEEDED);
|
|
209
198
|
return {
|
|
@@ -213,12 +202,11 @@ export function createEncoderRuntime(options = {}) {
|
|
|
213
202
|
};
|
|
214
203
|
}
|
|
215
204
|
const start = host.nowMs();
|
|
216
|
-
//
|
|
205
|
+
// ML5-C: the LCG placeholder STILL drives infer by default (the trained
|
|
206
|
+
// asset behind runtime-wasm.ts/runtime-native.ts is not yet the
|
|
207
|
+
// source-of-truth on master; only the runtime-selection event seam was
|
|
208
|
+
// added this sprint).
|
|
217
209
|
const semantic = projectSemantic(seedFromBytes(embeddedBytes) ^ n, ENCODER_SEMANTIC_WIDTH);
|
|
218
|
-
// Q01: the projection buffer is a single reusable 384-float array; the
|
|
219
|
-
// marginal footprint is a fixed SEMANTIC_BUFFER_BYTES once it exists, so
|
|
220
|
-
// selfAllocated is SET (never accumulated) — bounded regardless of how
|
|
221
|
-
// many inferences run on a long-lived runtime.
|
|
222
210
|
selfAllocated = SEMANTIC_BUFFER_BYTES;
|
|
223
211
|
const latencyMs = host.nowMs() - start;
|
|
224
212
|
return { ok: true, semantic, rssBytes: footprint(), latencyMs, shapeError: null };
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dedup-audit.ts — durable audit trail for dedup tier decisions
|
|
3
|
+
* (external-audit item #2).
|
|
4
|
+
*
|
|
5
|
+
* Before this module a tier decision existed only as the in-process `onTier`
|
|
6
|
+
* callback that paints the live UI; nothing survived the process, so an
|
|
7
|
+
* operator could not answer "which layer collapsed this region, onto what, at
|
|
8
|
+
* what similarity?" — the inputs needed to tune the thresholds in
|
|
9
|
+
* config/dedup.ts. Here each decision is appended to the repo's events.log as
|
|
10
|
+
* one structured JSON line (see `DedupAuditEvent` below).
|
|
11
|
+
*
|
|
12
|
+
* The event type and its append helper live HERE rather than in monitoring.ts:
|
|
13
|
+
* monitoring.ts already owns three concerns (decision events, the dashboard.json
|
|
14
|
+
* metrics snapshot, FP alerting) and sits close to its 300-line soft limit, so
|
|
15
|
+
* co-locating the shape with the only recorder that produces it keeps both files
|
|
16
|
+
* under the headroom gate. monitoring.ts re-exports both for callers (and the
|
|
17
|
+
* dashboard SSE tail) that treat it as the events.log barrel.
|
|
18
|
+
*
|
|
19
|
+
* Design constraints:
|
|
20
|
+
* - PURE INSTRUMENTATION. Nothing in this file may influence a dedup outcome.
|
|
21
|
+
* - Best-effort/non-fatal: `logDedupAudit` swallows IO errors, and the emitter
|
|
22
|
+
* itself is wrapped so a malformed field can never break add().
|
|
23
|
+
* - Honest fields only: a value is emitted only where the caller actually
|
|
24
|
+
* computed it. L0/L1 are hash/verify tiers and pass no `similarity`.
|
|
25
|
+
* - Signal, not chatter: callers emit on DECISIONS (a match, a scored
|
|
26
|
+
* candidate, the final outcome), never on every "scanning" transition.
|
|
27
|
+
* - Flag-gated by cfg.DEDUP_AUDIT (default ON; OFF writes nothing at all).
|
|
28
|
+
*
|
|
29
|
+
* PREVENT-PI-004: local filesystem append only, no network.
|
|
30
|
+
*/
|
|
31
|
+
import { appendFileSync, mkdirSync } from "node:fs";
|
|
32
|
+
import { dirname } from "node:path";
|
|
33
|
+
import { defaultEventsPath } from "../monitoring.js";
|
|
34
|
+
/**
|
|
35
|
+
* Append one audit event to events.log (best-effort, never throws).
|
|
36
|
+
*
|
|
37
|
+
* Same append-one-JSON-line contract as monitoring.ts's logDecision — an
|
|
38
|
+
* unwritable path is swallowed so instrumentation can never break add().
|
|
39
|
+
*/
|
|
40
|
+
export function logDedupAudit(path, ev) {
|
|
41
|
+
try {
|
|
42
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
43
|
+
appendFileSync(path, `${JSON.stringify(ev)}\n`, "utf-8");
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
/* best-effort — never break the extension on a log failure */
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/** Build a recorder bound to one add() cascade. */
|
|
50
|
+
export function dedupAuditRecorder(ctx, scope) {
|
|
51
|
+
const base = {
|
|
52
|
+
sessionId: scope.sessionId,
|
|
53
|
+
originalTokenEstimate: scope.originalTokenEstimate,
|
|
54
|
+
tokenEstimate: scope.tokenEstimate,
|
|
55
|
+
};
|
|
56
|
+
return {
|
|
57
|
+
deduped: (tier, matchedEntry, dedupReason, similarity) => emitDedupAudit(ctx, {
|
|
58
|
+
...base,
|
|
59
|
+
tier,
|
|
60
|
+
status: "deduped",
|
|
61
|
+
matchedEntry,
|
|
62
|
+
dedupReason,
|
|
63
|
+
...(similarity === undefined ? {} : { similarity }),
|
|
64
|
+
}),
|
|
65
|
+
passed: (tier, matchedEntry, similarity) => emitDedupAudit(ctx, {
|
|
66
|
+
...base,
|
|
67
|
+
tier,
|
|
68
|
+
status: "passed",
|
|
69
|
+
matchedEntry,
|
|
70
|
+
similarity,
|
|
71
|
+
}),
|
|
72
|
+
stored: (storedEntry, dedupReason, tokenEstimate) => emitDedupAudit(ctx, {
|
|
73
|
+
...base,
|
|
74
|
+
tier: "new",
|
|
75
|
+
status: "stored",
|
|
76
|
+
storedEntry,
|
|
77
|
+
dedupReason,
|
|
78
|
+
tokenEstimate,
|
|
79
|
+
}),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Append one dedup decision to events.log.
|
|
84
|
+
*
|
|
85
|
+
* Resolves the target path from the explicit `eventsPath` when a caller opted
|
|
86
|
+
* in (Sprint 14 monitoring / tests), otherwise from the store's own per-repo
|
|
87
|
+
* state dir — production never passes `eventsPath`, so defaulting is what makes
|
|
88
|
+
* the audit trail actually exist on a real device.
|
|
89
|
+
*/
|
|
90
|
+
export function emitDedupAudit(ctx, input) {
|
|
91
|
+
if (!ctx.auditEnabled)
|
|
92
|
+
return;
|
|
93
|
+
try {
|
|
94
|
+
const path = ctx.eventsPath ?? defaultEventsPath(ctx.stateDir);
|
|
95
|
+
logDedupAudit(path, {
|
|
96
|
+
type: "dedup_audit",
|
|
97
|
+
ts: new Date().toISOString(),
|
|
98
|
+
...input,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
/* instrumentation must never break the add() path */
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -228,5 +228,11 @@ export const VECTOR_CORTEX_SETTINGS: SettingGroup = {
|
|
|
228
228
|
"ML5-A real trained-head loading: loadHeadProjections (trained-heads-v1) feeds selectQualifiedEncoder (trainedHeadsPath atomic demotion) + loadCalibrationV1. ON (default) = a pinned trained-heads path must load for mode A. OFF = loaders return null and selection ignores trainedHeadsPath — byte-identical to the placeholder-weighted VC2C path.",
|
|
229
229
|
true,
|
|
230
230
|
),
|
|
231
|
+
boolDirect(
|
|
232
|
+
"MEGACOMPACT_ML5_C",
|
|
233
|
+
"ML5-C Runtime Decision + Packaging",
|
|
234
|
+
"ML5-C runtime backend selection (WASM vs native): selects the ONNX runtime backend based on the ML5-B bench record and platform support. ON (default) = the runtime-selection dispatch runs and emits vector_cortex_runtime_selected. OFF = no selection runs — encoder serves mode B trigram, byte-identical to ML5-B.",
|
|
235
|
+
true,
|
|
236
|
+
),
|
|
231
237
|
],
|
|
232
238
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-mega-compact",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.39",
|
|
4
4
|
"description": "Layered, local, vector-backed context compressor for pi — supersede/collapse/cluster compaction with deduped inline recall.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "BSD-3-Clause",
|