Skip to content

Commit 0a2d2c6

Browse files
devlux76Copilot
andauthored
refactor: move core engine into lib/ and harness into ui/ (#85)
* refactor: move core engine code into lib/ and runtime harness into ui/ * fix: update test results to reflect failure status and record failed test IDs * fix: add unzip package to Dockerfile dependencies * fix: ensure WebGL→WASM fallback catches synchronous throws (#86) * Initial plan * fix: wrap WebGlVectorBackend.create() in .then() to capture synchronous throws for WASM fallback Co-authored-by: devlux76 <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: devlux76 <[email protected]> --------- Co-authored-by: Copilot <[email protected]>
1 parent 54f5272 commit 0a2d2c6

File tree

118 files changed

+8538
-8277
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

118 files changed

+8538
-8277
lines changed

BackendKind.ts

Lines changed: 1 addition & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1 @@
1-
export type BackendKind = "webnn" | "webgpu" | "webgl" | "wasm";
2-
3-
function hasWebGpuSupport(): boolean {
4-
return (
5-
typeof navigator !== "undefined" &&
6-
typeof (navigator as Navigator & { gpu?: unknown }).gpu !== "undefined"
7-
);
8-
}
9-
10-
function hasWebGl2Support(): boolean {
11-
if (typeof document === "undefined") {
12-
return false;
13-
}
14-
15-
const canvas = document.createElement("canvas");
16-
return canvas.getContext("webgl2") !== null;
17-
}
18-
19-
function hasWebNnSupport(): boolean {
20-
return (
21-
typeof navigator !== "undefined" &&
22-
typeof (navigator as Navigator & { ml?: unknown }).ml !== "undefined"
23-
);
24-
}
25-
26-
export function detectBackend(): BackendKind {
27-
if (hasWebGpuSupport()) {
28-
return "webgpu";
29-
}
30-
31-
if (hasWebGl2Support()) {
32-
return "webgl";
33-
}
34-
35-
if (hasWebNnSupport()) {
36-
return "webnn";
37-
}
38-
39-
return "wasm";
40-
}
1+
export * from "./lib/BackendKind";

CreateVectorBackend.ts

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1 @@
1-
import { detectBackend } from "./BackendKind";
2-
import type { VectorBackend } from "./VectorBackend";
3-
import { WasmVectorBackend } from "./WasmVectorBackend";
4-
import { WebGlVectorBackend } from "./WebGLVectorBackend";
5-
import { WebGpuVectorBackend } from "./WebGPUVectorBackend";
6-
import { WebNnVectorBackend } from "./WebNNVectorBackend";
7-
8-
export async function createVectorBackend(
9-
wasmBytes: ArrayBuffer
10-
): Promise<VectorBackend> {
11-
const kind = detectBackend();
12-
if (kind === "webgpu") {
13-
return WebGpuVectorBackend.create().catch(() =>
14-
WasmVectorBackend.create(wasmBytes)
15-
);
16-
}
17-
if (kind === "webgl") {
18-
return Promise.resolve(WebGlVectorBackend.create()).catch(() =>
19-
WasmVectorBackend.create(wasmBytes)
20-
);
21-
}
22-
if (kind === "webnn") {
23-
return WebNnVectorBackend.create(wasmBytes).catch(() =>
24-
WasmVectorBackend.create(wasmBytes)
25-
);
26-
}
27-
return WasmVectorBackend.create(wasmBytes);
28-
}
1+
export * from "./lib/CreateVectorBackend";

PLAN.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ This document tracks the implementation status of each major module in CORTEX. I
139139

140140
| Module | Status | Files | Notes |
141141
|--------|--------|-------|-------|
142-
| Browser Harness | ✅ Complete | `runtime/harness/index.html`, `scripts/runtime-harness-server.mjs` | Localhost-served HTML harness for browser testing |
142+
| Browser Harness | ✅ Complete | `ui/harness/index.html`, `scripts/runtime-harness-server.mjs` | Localhost-served HTML harness for browser testing |
143143
| Electron Wrapper | ✅ Complete | `scripts/electron-harness-main.mjs` | Thin Electron launcher for GPU-realism testing |
144144
| Playwright Tests | ✅ Complete | `tests/runtime/browser-harness.spec.mjs`, `tests/runtime/electron-harness.spec.mjs` | Browser lane passes; Electron context-sensitive |
145145
| Docker Debug Lane | ✅ Complete | `docker/electron-debug/*`, `docker-compose.electron-debug.yml` | Sandbox-isolated Electron debugging via VS Code attach |

Policy.ts

Lines changed: 1 addition & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -1,147 +1 @@
1-
import type { ModelProfile } from "./core/ModelProfile";
2-
import {
3-
ModelProfileResolver,
4-
type ModelProfileResolverOptions,
5-
type ResolveModelProfileInput,
6-
} from "./core/ModelProfileResolver";
7-
8-
export type QueryScope = "broad" | "normal" | "narrow" | "default";
9-
10-
export interface ProjectionHead {
11-
dimIn: number;
12-
dimOut: number;
13-
bits?: number;
14-
// Byte offset for the projection head in a flattened projection buffer.
15-
offset: number;
16-
}
17-
18-
export interface RoutingPolicy {
19-
broad: ProjectionHead;
20-
normal: ProjectionHead;
21-
narrow: ProjectionHead;
22-
}
23-
24-
export interface ResolvedRoutingPolicy {
25-
modelProfile: ModelProfile;
26-
routingPolicy: RoutingPolicy;
27-
}
28-
29-
export interface ResolveRoutingPolicyOptions {
30-
resolver?: ModelProfileResolver;
31-
resolverOptions?: ModelProfileResolverOptions;
32-
routingPolicyOverrides?: Partial<RoutingPolicyDerivation>;
33-
}
34-
35-
export interface RoutingPolicyDerivation {
36-
broadDimRatio: number;
37-
normalDimRatio: number;
38-
narrowDimRatio: number;
39-
broadHashBits: number;
40-
dimAlignment: number;
41-
minProjectionDim: number;
42-
}
43-
44-
export const DEFAULT_ROUTING_POLICY_DERIVATION: RoutingPolicyDerivation =
45-
Object.freeze({
46-
broadDimRatio: 1 / 8,
47-
normalDimRatio: 1 / 4,
48-
narrowDimRatio: 1 / 2,
49-
broadHashBits: 128,
50-
dimAlignment: 8,
51-
minProjectionDim: 8,
52-
});
53-
54-
function assertPositiveInteger(name: string, value: number): void {
55-
if (!Number.isInteger(value) || value <= 0) {
56-
throw new Error(`${name} must be a positive integer`);
57-
}
58-
}
59-
60-
function assertPositiveFinite(name: string, value: number): void {
61-
if (!Number.isFinite(value) || value <= 0) {
62-
throw new Error(`${name} must be positive and finite`);
63-
}
64-
}
65-
66-
function alignDown(value: number, alignment: number): number {
67-
return Math.floor(value / alignment) * alignment;
68-
}
69-
70-
function deriveProjectionDim(
71-
dimIn: number,
72-
ratio: number,
73-
derivation: RoutingPolicyDerivation,
74-
): number {
75-
const raw = Math.floor(dimIn * ratio);
76-
const aligned = alignDown(raw, derivation.dimAlignment);
77-
const bounded = Math.max(derivation.minProjectionDim, aligned);
78-
return Math.min(dimIn, bounded);
79-
}
80-
81-
function validateDerivation(derivation: RoutingPolicyDerivation): void {
82-
assertPositiveFinite("broadDimRatio", derivation.broadDimRatio);
83-
assertPositiveFinite("normalDimRatio", derivation.normalDimRatio);
84-
assertPositiveFinite("narrowDimRatio", derivation.narrowDimRatio);
85-
assertPositiveInteger("broadHashBits", derivation.broadHashBits);
86-
assertPositiveInteger("dimAlignment", derivation.dimAlignment);
87-
assertPositiveInteger("minProjectionDim", derivation.minProjectionDim);
88-
}
89-
90-
export function createRoutingPolicy(
91-
modelProfile: Pick<ModelProfile, "embeddingDimension">,
92-
overrides: Partial<RoutingPolicyDerivation> = {},
93-
): RoutingPolicy {
94-
assertPositiveInteger("embeddingDimension", modelProfile.embeddingDimension);
95-
96-
const derivation: RoutingPolicyDerivation = {
97-
...DEFAULT_ROUTING_POLICY_DERIVATION,
98-
...overrides,
99-
};
100-
101-
validateDerivation(derivation);
102-
103-
const dimIn = modelProfile.embeddingDimension;
104-
const broadDim = deriveProjectionDim(dimIn, derivation.broadDimRatio, derivation);
105-
const normalDim = deriveProjectionDim(dimIn, derivation.normalDimRatio, derivation);
106-
const narrowDim = deriveProjectionDim(dimIn, derivation.narrowDimRatio, derivation);
107-
108-
const broadOffset = 0;
109-
const normalOffset = broadOffset + broadDim * dimIn;
110-
const narrowOffset = normalOffset + normalDim * dimIn;
111-
112-
return {
113-
broad: {
114-
dimIn,
115-
dimOut: broadDim,
116-
bits: derivation.broadHashBits,
117-
offset: broadOffset,
118-
},
119-
normal: {
120-
dimIn,
121-
dimOut: normalDim,
122-
offset: normalOffset,
123-
},
124-
narrow: {
125-
dimIn,
126-
dimOut: narrowDim,
127-
offset: narrowOffset,
128-
},
129-
};
130-
}
131-
132-
export function resolveRoutingPolicyForModel(
133-
input: ResolveModelProfileInput,
134-
options: ResolveRoutingPolicyOptions = {},
135-
): ResolvedRoutingPolicy {
136-
const resolver =
137-
options.resolver ?? new ModelProfileResolver(options.resolverOptions);
138-
const modelProfile = resolver.resolve(input);
139-
140-
return {
141-
modelProfile,
142-
routingPolicy: createRoutingPolicy(
143-
modelProfile,
144-
options.routingPolicyOverrides,
145-
),
146-
};
147-
}
1+
export * from "./lib/Policy";

TopK.ts

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1 @@
1-
import type { DistanceResult, ScoreResult } from "./VectorBackend";
2-
3-
export function topKByScore(scores: Float32Array, k: number): ScoreResult[] {
4-
const limit = Math.max(0, Math.min(k, scores.length));
5-
const indices = Array.from({ length: scores.length }, (_, i) => i);
6-
7-
indices.sort((a, b) => scores[b] - scores[a]);
8-
9-
return indices.slice(0, limit).map((index) => ({
10-
index,
11-
score: scores[index]
12-
}));
13-
}
14-
15-
export function topKByDistance(
16-
distances: Uint32Array | Int32Array | Float32Array,
17-
k: number
18-
): DistanceResult[] {
19-
const limit = Math.max(0, Math.min(k, distances.length));
20-
const indices = Array.from({ length: distances.length }, (_, i) => i);
21-
22-
indices.sort((a, b) => Number(distances[a]) - Number(distances[b]));
23-
24-
return indices.slice(0, limit).map((index) => ({
25-
index,
26-
distance: Number(distances[index])
27-
}));
28-
}
1+
export * from "./lib/TopK";

VectorBackend.ts

Lines changed: 1 addition & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1 @@
1-
import type { BackendKind } from "./BackendKind";
2-
3-
export interface ScoreResult {
4-
index: number;
5-
score: number;
6-
}
7-
8-
export interface DistanceResult {
9-
index: number;
10-
distance: number;
11-
}
12-
13-
export interface VectorBackend {
14-
kind: BackendKind;
15-
16-
// Exact or high-precision dot-product scoring over row-major matrices.
17-
dotMany(
18-
query: Float32Array,
19-
matrix: Float32Array,
20-
dim: number,
21-
count: number
22-
): Promise<Float32Array>;
23-
24-
// Projection helper used to reduce dimensionality for routing tiers.
25-
project(
26-
vector: Float32Array,
27-
projectionMatrix: Float32Array,
28-
dimIn: number,
29-
dimOut: number
30-
): Promise<Float32Array>;
31-
32-
topKFromScores(scores: Float32Array, k: number): Promise<ScoreResult[]>;
33-
34-
// Random-hyperplane hash from projected vectors into packed binary codes.
35-
hashToBinary(
36-
vector: Float32Array,
37-
projectionMatrix: Float32Array,
38-
dimIn: number,
39-
bits: number
40-
): Promise<Uint32Array>;
41-
42-
hammingTopK(
43-
queryCode: Uint32Array,
44-
codes: Uint32Array,
45-
wordsPerCode: number,
46-
count: number,
47-
k: number
48-
): Promise<DistanceResult[]>;
49-
}
1+
export * from "./lib/VectorBackend";

0 commit comments

Comments
 (0)