@octomil/browser 1.0.0
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/LICENSE +21 -0
- package/README.md +75 -0
- package/dist/cache.d.ts +25 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +202 -0
- package/dist/cache.js.map +1 -0
- package/dist/device-auth.d.ts +41 -0
- package/dist/device-auth.d.ts.map +1 -0
- package/dist/device-auth.js +203 -0
- package/dist/device-auth.js.map +1 -0
- package/dist/experiments.d.ts +44 -0
- package/dist/experiments.d.ts.map +1 -0
- package/dist/experiments.js +135 -0
- package/dist/experiments.js.map +1 -0
- package/dist/federated.d.ts +53 -0
- package/dist/federated.d.ts.map +1 -0
- package/dist/federated.js +180 -0
- package/dist/federated.js.map +1 -0
- package/dist/index.cjs +2148 -0
- package/dist/index.cjs.map +7 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +45 -0
- package/dist/index.js.map +1 -0
- package/dist/inference.d.ts +43 -0
- package/dist/inference.d.ts.map +1 -0
- package/dist/inference.js +213 -0
- package/dist/inference.js.map +1 -0
- package/dist/integrity.d.ts +19 -0
- package/dist/integrity.d.ts.map +1 -0
- package/dist/integrity.js +35 -0
- package/dist/integrity.js.map +1 -0
- package/dist/model-loader.d.ts +40 -0
- package/dist/model-loader.d.ts.map +1 -0
- package/dist/model-loader.js +232 -0
- package/dist/model-loader.js.map +1 -0
- package/dist/octomil.d.ts +92 -0
- package/dist/octomil.d.ts.map +1 -0
- package/dist/octomil.js +368 -0
- package/dist/octomil.js.map +1 -0
- package/dist/octomil.min.js +2849 -0
- package/dist/octomil.min.js.map +7 -0
- package/dist/privacy.d.ts +40 -0
- package/dist/privacy.d.ts.map +1 -0
- package/dist/privacy.js +118 -0
- package/dist/privacy.js.map +1 -0
- package/dist/rollouts.d.ts +43 -0
- package/dist/rollouts.d.ts.map +1 -0
- package/dist/rollouts.js +114 -0
- package/dist/rollouts.js.map +1 -0
- package/dist/secure-aggregation.d.ts +50 -0
- package/dist/secure-aggregation.d.ts.map +1 -0
- package/dist/secure-aggregation.js +174 -0
- package/dist/secure-aggregation.js.map +1 -0
- package/dist/streaming.d.ts +25 -0
- package/dist/streaming.d.ts.map +1 -0
- package/dist/streaming.js +148 -0
- package/dist/streaming.js.map +1 -0
- package/dist/telemetry.d.ts +41 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +130 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/types.d.ts +239 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +17 -0
- package/dist/types.js.map +1 -0
- package/package.json +62 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @octomil/browser — Inference engine
|
|
3
|
+
*
|
|
4
|
+
* Wraps ONNX Runtime Web to create an inference session from a model
|
|
5
|
+
* buffer, auto-detect the best execution provider (WebGPU > WASM),
|
|
6
|
+
* and run forward passes.
|
|
7
|
+
*/
|
|
8
|
+
import { OctomilError } from "./types.js";
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// InferenceEngine
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
export class InferenceEngine {
|
|
13
|
+
session = null;
|
|
14
|
+
ortModule = null;
|
|
15
|
+
resolvedBackend = null;
|
|
16
|
+
// -----------------------------------------------------------------------
|
|
17
|
+
// Public
|
|
18
|
+
// -----------------------------------------------------------------------
|
|
19
|
+
/**
|
|
20
|
+
* Create an ONNX Runtime session from the given model bytes.
|
|
21
|
+
*
|
|
22
|
+
* @param modelData Raw ONNX model ArrayBuffer.
|
|
23
|
+
* @param backend Requested backend (`"webgpu"`, `"wasm"`, or `undefined` for auto).
|
|
24
|
+
*/
|
|
25
|
+
async createSession(modelData, backend) {
|
|
26
|
+
const ortMod = await this.loadOrt();
|
|
27
|
+
const provider = await this.resolveProvider(ortMod, backend);
|
|
28
|
+
this.resolvedBackend = provider === "webgpu" ? "webgpu" : "wasm";
|
|
29
|
+
const sessionOptions = {
|
|
30
|
+
executionProviders: [provider],
|
|
31
|
+
graphOptimizationLevel: "all",
|
|
32
|
+
};
|
|
33
|
+
try {
|
|
34
|
+
this.session = await ortMod.InferenceSession.create(modelData, sessionOptions);
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
// If WebGPU failed, retry with WASM.
|
|
38
|
+
if (provider === "webgpu") {
|
|
39
|
+
try {
|
|
40
|
+
this.session = await ortMod.InferenceSession.create(modelData, {
|
|
41
|
+
executionProviders: ["wasm"],
|
|
42
|
+
graphOptimizationLevel: "all",
|
|
43
|
+
});
|
|
44
|
+
this.resolvedBackend = "wasm";
|
|
45
|
+
}
|
|
46
|
+
catch (wasmErr) {
|
|
47
|
+
throw new OctomilError("MODEL_LOAD_FAILED", "Failed to create ONNX session with both WebGPU and WASM backends.", wasmErr);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
throw new OctomilError("MODEL_LOAD_FAILED", `Failed to create ONNX session: ${String(err)}`, err);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Run inference and return the output tensors plus timing info.
|
|
57
|
+
*/
|
|
58
|
+
async run(inputs) {
|
|
59
|
+
this.ensureSession();
|
|
60
|
+
const ortMod = this.ortModule;
|
|
61
|
+
const session = this.session;
|
|
62
|
+
// Build ORT tensor feeds.
|
|
63
|
+
const feeds = {};
|
|
64
|
+
for (const [name, tensor] of Object.entries(inputs)) {
|
|
65
|
+
feeds[name] = new ortMod.Tensor(inferOrtType(tensor.data), tensor.data, tensor.dims);
|
|
66
|
+
}
|
|
67
|
+
const start = performance.now();
|
|
68
|
+
let results;
|
|
69
|
+
try {
|
|
70
|
+
results = await session.run(feeds);
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
throw new OctomilError("INFERENCE_FAILED", `Inference run failed: ${String(err)}`, err);
|
|
74
|
+
}
|
|
75
|
+
const latencyMs = performance.now() - start;
|
|
76
|
+
// Convert ORT outputs to NamedTensors.
|
|
77
|
+
const tensors = this.convertOutputs(results);
|
|
78
|
+
// Extract convenience fields from the first output tensor.
|
|
79
|
+
const convenience = this.extractConvenience(tensors);
|
|
80
|
+
return {
|
|
81
|
+
tensors,
|
|
82
|
+
latencyMs,
|
|
83
|
+
...convenience,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/** Names of the model's input tensors. */
|
|
87
|
+
get inputNames() {
|
|
88
|
+
this.ensureSession();
|
|
89
|
+
return this.session.inputNames;
|
|
90
|
+
}
|
|
91
|
+
/** Names of the model's output tensors. */
|
|
92
|
+
get outputNames() {
|
|
93
|
+
this.ensureSession();
|
|
94
|
+
return this.session.outputNames;
|
|
95
|
+
}
|
|
96
|
+
/** The backend that was actually used after negotiation. */
|
|
97
|
+
get activeBackend() {
|
|
98
|
+
return this.resolvedBackend;
|
|
99
|
+
}
|
|
100
|
+
/** Release WASM / WebGPU resources. */
|
|
101
|
+
dispose() {
|
|
102
|
+
if (this.session) {
|
|
103
|
+
// InferenceSession.release() returns a Promise but we fire-and-forget.
|
|
104
|
+
void this.session.release();
|
|
105
|
+
this.session = null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// -----------------------------------------------------------------------
|
|
109
|
+
// Internal
|
|
110
|
+
// -----------------------------------------------------------------------
|
|
111
|
+
async loadOrt() {
|
|
112
|
+
if (this.ortModule)
|
|
113
|
+
return this.ortModule;
|
|
114
|
+
try {
|
|
115
|
+
// Dynamic import so tree-shaking works and the dependency is optional
|
|
116
|
+
// at the type level.
|
|
117
|
+
this.ortModule = (await import("onnxruntime-web"));
|
|
118
|
+
return this.ortModule;
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
throw new OctomilError("BACKEND_UNAVAILABLE", 'Failed to import onnxruntime-web. Make sure the package is installed: npm i onnxruntime-web', err);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
async resolveProvider(_ortMod, backend) {
|
|
125
|
+
if (backend === "wasm")
|
|
126
|
+
return "wasm";
|
|
127
|
+
if (backend === "webgpu" || backend === undefined) {
|
|
128
|
+
const hasWebGPU = await this.detectWebGPU();
|
|
129
|
+
if (hasWebGPU)
|
|
130
|
+
return "webgpu";
|
|
131
|
+
if (backend === "webgpu") {
|
|
132
|
+
throw new OctomilError("BACKEND_UNAVAILABLE", "WebGPU was explicitly requested but is not available in this browser.");
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return "wasm";
|
|
136
|
+
}
|
|
137
|
+
async detectWebGPU() {
|
|
138
|
+
if (typeof navigator === "undefined")
|
|
139
|
+
return false;
|
|
140
|
+
try {
|
|
141
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
142
|
+
const gpu = navigator.gpu;
|
|
143
|
+
if (!gpu)
|
|
144
|
+
return false;
|
|
145
|
+
const adapter = await gpu.requestAdapter();
|
|
146
|
+
return adapter !== null;
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
ensureSession() {
|
|
153
|
+
if (!this.session) {
|
|
154
|
+
throw new OctomilError("SESSION_DISPOSED", "No active session. Call load() before running inference.");
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
convertOutputs(results) {
|
|
158
|
+
const tensors = {};
|
|
159
|
+
for (const name of Object.keys(results)) {
|
|
160
|
+
const ortTensor = results[name];
|
|
161
|
+
tensors[name] = {
|
|
162
|
+
data: ortTensor.data,
|
|
163
|
+
dims: Array.from(ortTensor.dims),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
return tensors;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Best-effort extraction of `label` / `score` / `scores` from the
|
|
170
|
+
* first output tensor — only if it looks like a classification head.
|
|
171
|
+
*/
|
|
172
|
+
extractConvenience(tensors) {
|
|
173
|
+
const names = Object.keys(tensors);
|
|
174
|
+
if (names.length === 0)
|
|
175
|
+
return {};
|
|
176
|
+
const first = tensors[names[0]];
|
|
177
|
+
const data = first.data;
|
|
178
|
+
if (!(data instanceof Float32Array))
|
|
179
|
+
return {};
|
|
180
|
+
if (data.length === 0)
|
|
181
|
+
return {};
|
|
182
|
+
// Treat as class probabilities / logits.
|
|
183
|
+
const scores = Array.from(data);
|
|
184
|
+
let maxIdx = 0;
|
|
185
|
+
let maxVal = -Infinity;
|
|
186
|
+
for (let i = 0; i < scores.length; i++) {
|
|
187
|
+
if (scores[i] > maxVal) {
|
|
188
|
+
maxVal = scores[i];
|
|
189
|
+
maxIdx = i;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return {
|
|
193
|
+
label: String(maxIdx),
|
|
194
|
+
score: maxVal,
|
|
195
|
+
scores,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// ---------------------------------------------------------------------------
|
|
200
|
+
// Helpers
|
|
201
|
+
// ---------------------------------------------------------------------------
|
|
202
|
+
function inferOrtType(data) {
|
|
203
|
+
if (data instanceof Float32Array)
|
|
204
|
+
return "float32";
|
|
205
|
+
if (data instanceof Int32Array)
|
|
206
|
+
return "int32";
|
|
207
|
+
if (data instanceof BigInt64Array)
|
|
208
|
+
return "int64";
|
|
209
|
+
if (data instanceof Uint8Array)
|
|
210
|
+
return "uint8";
|
|
211
|
+
return "float32";
|
|
212
|
+
}
|
|
213
|
+
//# sourceMappingURL=inference.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inference.js","sourceRoot":"","sources":["../src/inference.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @octomil/browser — Model integrity verification
|
|
3
|
+
*
|
|
4
|
+
* SHA-256 checksum computation and verification using the Web Crypto API.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Compute the SHA-256 hash of an ArrayBuffer, returned as a hex string.
|
|
8
|
+
*/
|
|
9
|
+
export declare function computeHash(data: ArrayBuffer): Promise<string>;
|
|
10
|
+
/**
|
|
11
|
+
* Verify that `data` matches the expected SHA-256 hex digest.
|
|
12
|
+
* Returns `true` if the hash matches, `false` otherwise.
|
|
13
|
+
*/
|
|
14
|
+
export declare function verifyModelIntegrity(data: ArrayBuffer, expectedHash: string): Promise<boolean>;
|
|
15
|
+
/**
|
|
16
|
+
* Same as `verifyModelIntegrity` but throws on mismatch.
|
|
17
|
+
*/
|
|
18
|
+
export declare function assertModelIntegrity(data: ArrayBuffer, expectedHash: string): Promise<void>;
|
|
19
|
+
//# sourceMappingURL=integrity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integrity.d.ts","sourceRoot":"","sources":["../src/integrity.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH;;GAEG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAMpE;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,WAAW,EACjB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,OAAO,CAAC,CAGlB;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,WAAW,EACjB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC,CASf"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @octomil/browser — Model integrity verification
|
|
3
|
+
*
|
|
4
|
+
* SHA-256 checksum computation and verification using the Web Crypto API.
|
|
5
|
+
*/
|
|
6
|
+
import { OctomilError } from "./types.js";
|
|
7
|
+
/**
|
|
8
|
+
* Compute the SHA-256 hash of an ArrayBuffer, returned as a hex string.
|
|
9
|
+
*/
|
|
10
|
+
export async function computeHash(data) {
|
|
11
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
12
|
+
const hashArray = new Uint8Array(hashBuffer);
|
|
13
|
+
return Array.from(hashArray)
|
|
14
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
15
|
+
.join("");
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Verify that `data` matches the expected SHA-256 hex digest.
|
|
19
|
+
* Returns `true` if the hash matches, `false` otherwise.
|
|
20
|
+
*/
|
|
21
|
+
export async function verifyModelIntegrity(data, expectedHash) {
|
|
22
|
+
const actual = await computeHash(data);
|
|
23
|
+
return actual === expectedHash.toLowerCase();
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Same as `verifyModelIntegrity` but throws on mismatch.
|
|
27
|
+
*/
|
|
28
|
+
export async function assertModelIntegrity(data, expectedHash) {
|
|
29
|
+
const match = await verifyModelIntegrity(data, expectedHash);
|
|
30
|
+
if (!match) {
|
|
31
|
+
throw new OctomilError("MODEL_LOAD_FAILED", "Model integrity check failed: SHA-256 hash mismatch. " +
|
|
32
|
+
"The downloaded model may be corrupted or tampered with.");
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=integrity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integrity.js","sourceRoot":"","sources":["../src/integrity.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAiB;IACjD,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC/D,MAAM,SAAS,GAAG,IAAI,UAAU,CAAC,UAAU,CAAC,CAAC;IAC7C,OAAO,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC;SACzB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;SAC3C,IAAI,CAAC,EAAE,CAAC,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,IAAiB,EACjB,YAAoB;IAEpB,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,CAAC;IACvC,OAAO,MAAM,KAAK,YAAY,CAAC,WAAW,EAAE,CAAC;AAC/C,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,IAAiB,EACjB,YAAoB;IAEpB,MAAM,KAAK,GAAG,MAAM,oBAAoB,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;IAC7D,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,YAAY,CACpB,mBAAmB,EACnB,uDAAuD;YACrD,yDAAyD,CAC5D,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @octomil/browser — Model loader
|
|
3
|
+
*
|
|
4
|
+
* Downloads an ONNX model from a URL or the Octomil model registry,
|
|
5
|
+
* caches it locally, and returns the raw `ArrayBuffer` for the
|
|
6
|
+
* inference engine to consume.
|
|
7
|
+
*/
|
|
8
|
+
import type { ModelCache } from "./cache.js";
|
|
9
|
+
import type { OctomilOptions } from "./types.js";
|
|
10
|
+
export declare class ModelLoader {
|
|
11
|
+
private readonly modelId;
|
|
12
|
+
private readonly serverUrl;
|
|
13
|
+
private readonly apiKey;
|
|
14
|
+
private readonly onProgress;
|
|
15
|
+
private readonly cache;
|
|
16
|
+
constructor(options: OctomilOptions, cache: ModelCache);
|
|
17
|
+
/**
|
|
18
|
+
* Resolve the model URL, check the cache, download if needed,
|
|
19
|
+
* and return the ONNX model bytes.
|
|
20
|
+
*/
|
|
21
|
+
load(): Promise<ArrayBuffer>;
|
|
22
|
+
/** Check whether the model is already cached. */
|
|
23
|
+
isCached(): Promise<boolean>;
|
|
24
|
+
/** Remove the cached model. */
|
|
25
|
+
clearCache(): Promise<void>;
|
|
26
|
+
/** Get cache info for the model. */
|
|
27
|
+
getCacheInfo(): Promise<import("./types.js").CacheInfo>;
|
|
28
|
+
/**
|
|
29
|
+
* If `modelId` looks like a URL (starts with http:// or https://) use it
|
|
30
|
+
* directly. Otherwise treat it as a registry model name and resolve via
|
|
31
|
+
* the Octomil server.
|
|
32
|
+
*/
|
|
33
|
+
resolveModelUrl(): Promise<string>;
|
|
34
|
+
private fetchRegistryUrl;
|
|
35
|
+
private buildHeaders;
|
|
36
|
+
private download;
|
|
37
|
+
private delay;
|
|
38
|
+
private validate;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=model-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-loader.d.ts","sourceRoot":"","sources":["../src/model-loader.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAEV,cAAc,EAEf,MAAM,YAAY,CAAC;AAcpB,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqB;IAC/C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA8C;IACzE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;gBAEvB,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,UAAU;IAYtD;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,WAAW,CAAC;IAqBlC,iDAAiD;IAC3C,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAC;IAKlC,+BAA+B;IACzB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAKjC,oCAAoC;IAC9B,YAAY;IASlB;;;;OAIG;IACG,eAAe,IAAI,OAAO,CAAC,MAAM,CAAC;YAkB1B,gBAAgB;IA0C9B,OAAO,CAAC,YAAY;YAWN,QAAQ;IAmHtB,OAAO,CAAC,KAAK;IAQb,OAAO,CAAC,QAAQ;CAkBjB"}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @octomil/browser — Model loader
|
|
3
|
+
*
|
|
4
|
+
* Downloads an ONNX model from a URL or the Octomil model registry,
|
|
5
|
+
* caches it locally, and returns the raw `ArrayBuffer` for the
|
|
6
|
+
* inference engine to consume.
|
|
7
|
+
*/
|
|
8
|
+
import { OctomilError } from "./types.js";
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// Constants
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
const MAX_RETRIES = 3;
|
|
13
|
+
const RETRY_DELAY_MS = 1_000;
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// ModelLoader
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
export class ModelLoader {
|
|
18
|
+
modelId;
|
|
19
|
+
serverUrl;
|
|
20
|
+
apiKey;
|
|
21
|
+
onProgress;
|
|
22
|
+
cache;
|
|
23
|
+
constructor(options, cache) {
|
|
24
|
+
this.modelId = options.model;
|
|
25
|
+
this.serverUrl = options.serverUrl;
|
|
26
|
+
this.apiKey = options.apiKey;
|
|
27
|
+
this.onProgress = options.onProgress;
|
|
28
|
+
this.cache = cache;
|
|
29
|
+
}
|
|
30
|
+
// -----------------------------------------------------------------------
|
|
31
|
+
// Public
|
|
32
|
+
// -----------------------------------------------------------------------
|
|
33
|
+
/**
|
|
34
|
+
* Resolve the model URL, check the cache, download if needed,
|
|
35
|
+
* and return the ONNX model bytes.
|
|
36
|
+
*/
|
|
37
|
+
async load() {
|
|
38
|
+
const url = await this.resolveModelUrl();
|
|
39
|
+
// Check cache first.
|
|
40
|
+
const cached = await this.cache.get(url);
|
|
41
|
+
if (cached) {
|
|
42
|
+
return cached;
|
|
43
|
+
}
|
|
44
|
+
// Download the model.
|
|
45
|
+
const data = await this.download(url);
|
|
46
|
+
// Validate — a minimal check that the buffer is non-empty.
|
|
47
|
+
this.validate(data);
|
|
48
|
+
// Cache for next time.
|
|
49
|
+
await this.cache.put(url, data);
|
|
50
|
+
return data;
|
|
51
|
+
}
|
|
52
|
+
/** Check whether the model is already cached. */
|
|
53
|
+
async isCached() {
|
|
54
|
+
const url = await this.resolveModelUrl();
|
|
55
|
+
return this.cache.has(url);
|
|
56
|
+
}
|
|
57
|
+
/** Remove the cached model. */
|
|
58
|
+
async clearCache() {
|
|
59
|
+
const url = await this.resolveModelUrl();
|
|
60
|
+
await this.cache.remove(url);
|
|
61
|
+
}
|
|
62
|
+
/** Get cache info for the model. */
|
|
63
|
+
async getCacheInfo() {
|
|
64
|
+
const url = await this.resolveModelUrl();
|
|
65
|
+
return this.cache.info(url);
|
|
66
|
+
}
|
|
67
|
+
// -----------------------------------------------------------------------
|
|
68
|
+
// Internal — URL resolution
|
|
69
|
+
// -----------------------------------------------------------------------
|
|
70
|
+
/**
|
|
71
|
+
* If `modelId` looks like a URL (starts with http:// or https://) use it
|
|
72
|
+
* directly. Otherwise treat it as a registry model name and resolve via
|
|
73
|
+
* the Octomil server.
|
|
74
|
+
*/
|
|
75
|
+
async resolveModelUrl() {
|
|
76
|
+
if (this.modelId.startsWith("http://") ||
|
|
77
|
+
this.modelId.startsWith("https://")) {
|
|
78
|
+
return this.modelId;
|
|
79
|
+
}
|
|
80
|
+
if (!this.serverUrl) {
|
|
81
|
+
throw new OctomilError("MODEL_NOT_FOUND", `Cannot resolve model "${this.modelId}": no serverUrl configured.`);
|
|
82
|
+
}
|
|
83
|
+
return this.fetchRegistryUrl(this.modelId);
|
|
84
|
+
}
|
|
85
|
+
async fetchRegistryUrl(name) {
|
|
86
|
+
const registryUrl = `${this.serverUrl}/api/v1/models/${encodeURIComponent(name)}/metadata`;
|
|
87
|
+
const headers = {
|
|
88
|
+
Accept: "application/json",
|
|
89
|
+
};
|
|
90
|
+
if (this.apiKey) {
|
|
91
|
+
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
92
|
+
}
|
|
93
|
+
let response;
|
|
94
|
+
try {
|
|
95
|
+
response = await fetch(registryUrl, { headers });
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
throw new OctomilError("NETWORK_ERROR", `Failed to reach model registry at ${registryUrl}`, err);
|
|
99
|
+
}
|
|
100
|
+
if (!response.ok) {
|
|
101
|
+
if (response.status === 404) {
|
|
102
|
+
throw new OctomilError("MODEL_NOT_FOUND", `Model "${name}" not found in registry.`);
|
|
103
|
+
}
|
|
104
|
+
throw new OctomilError("NETWORK_ERROR", `Registry returned HTTP ${response.status}: ${response.statusText}`);
|
|
105
|
+
}
|
|
106
|
+
const metadata = (await response.json());
|
|
107
|
+
return metadata.url;
|
|
108
|
+
}
|
|
109
|
+
// -----------------------------------------------------------------------
|
|
110
|
+
// Internal — download
|
|
111
|
+
// -----------------------------------------------------------------------
|
|
112
|
+
buildHeaders(extra = {}) {
|
|
113
|
+
const headers = {
|
|
114
|
+
"Accept-Encoding": "gzip, deflate, br",
|
|
115
|
+
...extra,
|
|
116
|
+
};
|
|
117
|
+
if (this.apiKey) {
|
|
118
|
+
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
119
|
+
}
|
|
120
|
+
return headers;
|
|
121
|
+
}
|
|
122
|
+
async download(url) {
|
|
123
|
+
const chunks = [];
|
|
124
|
+
let loaded = 0;
|
|
125
|
+
let totalSize = 0;
|
|
126
|
+
let attempt = 0;
|
|
127
|
+
while (attempt <= MAX_RETRIES) {
|
|
128
|
+
const headers = this.buildHeaders(loaded > 0 ? { Range: `bytes=${loaded}-` } : {});
|
|
129
|
+
let response;
|
|
130
|
+
try {
|
|
131
|
+
response = await fetch(url, { headers });
|
|
132
|
+
}
|
|
133
|
+
catch (err) {
|
|
134
|
+
attempt++;
|
|
135
|
+
if (attempt > MAX_RETRIES) {
|
|
136
|
+
throw new OctomilError("NETWORK_ERROR", `Failed to download model from ${url} after ${MAX_RETRIES} retries`, err);
|
|
137
|
+
}
|
|
138
|
+
await this.delay(RETRY_DELAY_MS * attempt);
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
// 416 Range Not Satisfiable — server doesn't support range requests
|
|
142
|
+
// or we already have the full file. Start fresh.
|
|
143
|
+
if (response.status === 416) {
|
|
144
|
+
chunks.length = 0;
|
|
145
|
+
loaded = 0;
|
|
146
|
+
attempt++;
|
|
147
|
+
if (attempt > MAX_RETRIES) {
|
|
148
|
+
throw new OctomilError("MODEL_LOAD_FAILED", `Model download failed: range request rejected after ${MAX_RETRIES} retries`);
|
|
149
|
+
}
|
|
150
|
+
await this.delay(RETRY_DELAY_MS * attempt);
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (!response.ok && response.status !== 206) {
|
|
154
|
+
throw new OctomilError("MODEL_LOAD_FAILED", `Model download failed: HTTP ${response.status} ${response.statusText}`);
|
|
155
|
+
}
|
|
156
|
+
// Determine total size from Content-Length or Content-Range.
|
|
157
|
+
if (totalSize === 0) {
|
|
158
|
+
const contentRange = response.headers.get("Content-Range");
|
|
159
|
+
if (contentRange) {
|
|
160
|
+
// Content-Range: bytes 0-999/5000
|
|
161
|
+
const match = contentRange.match(/\/(\d+)$/);
|
|
162
|
+
if (match)
|
|
163
|
+
totalSize = parseInt(match[1], 10);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
totalSize = parseInt(response.headers.get("Content-Length") ?? "0", 10);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// Stream the body, collecting chunks.
|
|
170
|
+
if (!response.body) {
|
|
171
|
+
const buf = await response.arrayBuffer();
|
|
172
|
+
return buf;
|
|
173
|
+
}
|
|
174
|
+
const reader = response.body.getReader();
|
|
175
|
+
let streamFailed = false;
|
|
176
|
+
try {
|
|
177
|
+
for (;;) {
|
|
178
|
+
const { done, value } = await reader.read();
|
|
179
|
+
if (done)
|
|
180
|
+
break;
|
|
181
|
+
chunks.push(value);
|
|
182
|
+
loaded += value.byteLength;
|
|
183
|
+
this.onProgress?.({
|
|
184
|
+
loaded,
|
|
185
|
+
total: totalSize,
|
|
186
|
+
percent: totalSize > 0 ? (loaded / totalSize) * 100 : NaN,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
catch (err) {
|
|
191
|
+
// Stream interrupted — retry with range request from where we left off.
|
|
192
|
+
streamFailed = true;
|
|
193
|
+
attempt++;
|
|
194
|
+
if (attempt > MAX_RETRIES) {
|
|
195
|
+
throw new OctomilError("NETWORK_ERROR", `Model download interrupted after ${MAX_RETRIES} retries`, err);
|
|
196
|
+
}
|
|
197
|
+
await this.delay(RETRY_DELAY_MS * attempt);
|
|
198
|
+
}
|
|
199
|
+
finally {
|
|
200
|
+
reader.releaseLock();
|
|
201
|
+
}
|
|
202
|
+
if (!streamFailed)
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
// Concatenate chunks into a single ArrayBuffer.
|
|
206
|
+
const combined = new Uint8Array(loaded);
|
|
207
|
+
let offset = 0;
|
|
208
|
+
for (const chunk of chunks) {
|
|
209
|
+
combined.set(chunk, offset);
|
|
210
|
+
offset += chunk.byteLength;
|
|
211
|
+
}
|
|
212
|
+
return combined.buffer;
|
|
213
|
+
}
|
|
214
|
+
delay(ms) {
|
|
215
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
216
|
+
}
|
|
217
|
+
// -----------------------------------------------------------------------
|
|
218
|
+
// Internal — validation
|
|
219
|
+
// -----------------------------------------------------------------------
|
|
220
|
+
validate(data) {
|
|
221
|
+
if (data.byteLength === 0) {
|
|
222
|
+
throw new OctomilError("MODEL_LOAD_FAILED", "Downloaded model is empty (0 bytes).");
|
|
223
|
+
}
|
|
224
|
+
// ONNX protobuf files start with 0x08 (field 1, varint type).
|
|
225
|
+
// This is a lightweight sanity check, not a full format validation.
|
|
226
|
+
const header = new Uint8Array(data, 0, Math.min(4, data.byteLength));
|
|
227
|
+
if (header[0] !== 0x08) {
|
|
228
|
+
throw new OctomilError("MODEL_LOAD_FAILED", "Downloaded file does not appear to be a valid ONNX model.");
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
//# sourceMappingURL=model-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-loader.js","sourceRoot":"","sources":["../src/model-loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @octomil/browser — Main SDK entry point
|
|
3
|
+
*
|
|
4
|
+
* The `Octomil` class is the primary public interface. It orchestrates
|
|
5
|
+
* model loading, caching, inference, and optional telemetry.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { Octomil } from '@octomil/browser';
|
|
10
|
+
*
|
|
11
|
+
* const ml = new Octomil({
|
|
12
|
+
* model: 'https://models.octomil.io/sentiment-v1.onnx',
|
|
13
|
+
* backend: 'webgpu',
|
|
14
|
+
* });
|
|
15
|
+
*
|
|
16
|
+
* await ml.load();
|
|
17
|
+
* const result = await ml.predict({ raw: inputData, dims: [1, 3, 224, 224] });
|
|
18
|
+
* console.log(result.label, result.score);
|
|
19
|
+
* ml.dispose();
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
import type { Backend, CacheInfo, ChatChunk, ChatMessage, ChatOptions, ChatResponse, OctomilOptions, PredictInput, PredictOutput } from "./types.js";
|
|
23
|
+
export declare class Octomil {
|
|
24
|
+
private readonly options;
|
|
25
|
+
private readonly cache;
|
|
26
|
+
private readonly loader;
|
|
27
|
+
private readonly engine;
|
|
28
|
+
private telemetry;
|
|
29
|
+
private loaded;
|
|
30
|
+
private disposed;
|
|
31
|
+
constructor(options: OctomilOptions);
|
|
32
|
+
/**
|
|
33
|
+
* Download (or load from cache) the ONNX model and create the
|
|
34
|
+
* inference session. Must be called before `predict()` or `chat()`.
|
|
35
|
+
*/
|
|
36
|
+
load(): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* Run a single inference pass.
|
|
39
|
+
*
|
|
40
|
+
* Accepts either raw named tensors or convenience payloads
|
|
41
|
+
* (`{ text }`, `{ image }`, `{ raw, dims }`).
|
|
42
|
+
*/
|
|
43
|
+
predict(input: PredictInput): Promise<PredictOutput>;
|
|
44
|
+
/**
|
|
45
|
+
* Run inference on multiple inputs sequentially.
|
|
46
|
+
* ONNX Runtime Web doesn't handle concurrent sessions well,
|
|
47
|
+
* so we process one at a time.
|
|
48
|
+
*/
|
|
49
|
+
predictBatch(inputs: PredictInput[]): Promise<PredictOutput[]>;
|
|
50
|
+
/**
|
|
51
|
+
* OpenAI-compatible chat completion.
|
|
52
|
+
* Requires a server with streaming endpoint. Uses StreamingInferenceEngine
|
|
53
|
+
* under the hood to collect the full response.
|
|
54
|
+
*/
|
|
55
|
+
chat(messages: ChatMessage[], options?: ChatOptions): Promise<ChatResponse>;
|
|
56
|
+
/**
|
|
57
|
+
* Streaming chat — yields chunks as they arrive.
|
|
58
|
+
*/
|
|
59
|
+
chatStream(messages: ChatMessage[], options?: ChatOptions): AsyncGenerator<ChatChunk, void, undefined>;
|
|
60
|
+
/** Check whether the model binary is currently cached locally. */
|
|
61
|
+
isCached(): Promise<boolean>;
|
|
62
|
+
/** Remove the cached model binary. */
|
|
63
|
+
clearCache(): Promise<void>;
|
|
64
|
+
/** Get cache metadata for the model. */
|
|
65
|
+
cacheInfo(): Promise<CacheInfo>;
|
|
66
|
+
/** The inference backend currently in use (after `load()`). */
|
|
67
|
+
get activeBackend(): Backend | null;
|
|
68
|
+
/** Input tensor names defined by the loaded model. */
|
|
69
|
+
get inputNames(): readonly string[];
|
|
70
|
+
/** Output tensor names defined by the loaded model. */
|
|
71
|
+
get outputNames(): readonly string[];
|
|
72
|
+
/** Whether `load()` has been called successfully. */
|
|
73
|
+
get isLoaded(): boolean;
|
|
74
|
+
/** Release all resources (WASM memory, WebGPU device, telemetry). */
|
|
75
|
+
dispose(): void;
|
|
76
|
+
private ensureNotDisposed;
|
|
77
|
+
private ensureReady;
|
|
78
|
+
/**
|
|
79
|
+
* Normalise the various `PredictInput` shapes into a flat
|
|
80
|
+
* `NamedTensors` map suitable for the inference engine.
|
|
81
|
+
*/
|
|
82
|
+
private prepareTensors;
|
|
83
|
+
/** Type guard for NamedTensors. */
|
|
84
|
+
private isNamedTensors;
|
|
85
|
+
/**
|
|
86
|
+
* Convert an image source to a Float32Array in NCHW format
|
|
87
|
+
* (batch=1, channels=3, H, W) normalised to [0, 1].
|
|
88
|
+
*/
|
|
89
|
+
private imageToTensors;
|
|
90
|
+
private trackEvent;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=octomil.d.ts.map
|