@plasius/gpu-model-runtime 0.2.5 → 0.3.1
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/README.md +47 -6
- package/dist/index.cjs +34 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -61
- package/dist/index.d.ts +12 -61
- package/dist/index.js +34 -30
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
|
+
import { GpuModelDiagnosticMode, GpuModelDocument, ModelSource as ModelSource$1, ModelProvenance as ModelProvenance$1 } from '@plasius/gpu-model-core';
|
|
2
|
+
export { GpuModelConversionDiagnostic, GpuModelDiagnosticMode, GpuModelDocument } from '@plasius/gpu-model-core';
|
|
1
3
|
import { ModelAssetRef, ModelLodLevel } from '@plasius/asset-contracts';
|
|
2
4
|
|
|
3
5
|
/** Default ceiling per entrypoint or related resource: 64 MiB. */
|
|
4
6
|
declare const DEFAULT_MODEL_MAX_BYTES: number;
|
|
5
|
-
|
|
6
|
-
type
|
|
7
|
-
|
|
8
|
-
sourceSystem?: string;
|
|
9
|
-
acquiredAt?: string;
|
|
10
|
-
licenseHint?: string;
|
|
11
|
-
}>;
|
|
7
|
+
/** Canonical source and policy contracts stay owned by the published core. */
|
|
8
|
+
type ModelSourceKind = ModelSource$1["kind"];
|
|
9
|
+
type ModelProvenance = ModelProvenance$1;
|
|
12
10
|
type ModelResourceInput = Readonly<{
|
|
13
11
|
bytes: ArrayBuffer | Blob | Uint8Array;
|
|
14
12
|
contentType?: string;
|
|
@@ -19,56 +17,9 @@ type ModelPackage = Readonly<{
|
|
|
19
17
|
baseUrl?: string;
|
|
20
18
|
resources?: Readonly<Record<string, ModelResourceInput>>;
|
|
21
19
|
}>;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
path: string;
|
|
25
|
-
mimeTypeHint?: string;
|
|
26
|
-
fileNameHint?: string;
|
|
27
|
-
package?: ModelPackage;
|
|
28
|
-
provenance?: ModelProvenance;
|
|
29
|
-
}> | Readonly<{
|
|
30
|
-
kind: "url";
|
|
31
|
-
url: string;
|
|
32
|
-
headers?: Readonly<Record<string, string>>;
|
|
33
|
-
mimeTypeHint?: string;
|
|
34
|
-
package?: ModelPackage;
|
|
35
|
-
provenance?: ModelProvenance;
|
|
36
|
-
}> | Readonly<{
|
|
37
|
-
kind: "blob-storage-url";
|
|
38
|
-
url: string;
|
|
39
|
-
credentialMode?: "anonymous" | "signed" | "runtime-resolved";
|
|
40
|
-
mimeTypeHint?: string;
|
|
41
|
-
package?: ModelPackage;
|
|
42
|
-
provenance?: ModelProvenance;
|
|
43
|
-
}> | Readonly<{
|
|
44
|
-
kind: "blob";
|
|
45
|
-
blob: Blob;
|
|
46
|
-
fileNameHint?: string;
|
|
47
|
-
mimeTypeHint?: string;
|
|
48
|
-
package?: ModelPackage;
|
|
49
|
-
provenance?: ModelProvenance;
|
|
50
|
-
}> | Readonly<{
|
|
51
|
-
kind: "array-buffer";
|
|
52
|
-
bytes: ArrayBuffer;
|
|
53
|
-
fileNameHint?: string;
|
|
54
|
-
mimeTypeHint?: string;
|
|
55
|
-
package?: ModelPackage;
|
|
56
|
-
provenance?: ModelProvenance;
|
|
57
|
-
}> | Readonly<{
|
|
58
|
-
kind: "uint8-array";
|
|
59
|
-
bytes: Uint8Array;
|
|
60
|
-
fileNameHint?: string;
|
|
61
|
-
mimeTypeHint?: string;
|
|
62
|
-
package?: ModelPackage;
|
|
63
|
-
provenance?: ModelProvenance;
|
|
64
|
-
}> | Readonly<{
|
|
65
|
-
kind: "stream";
|
|
66
|
-
stream: ReadableStream<Uint8Array> | AsyncIterable<Uint8Array>;
|
|
67
|
-
fileNameHint?: string;
|
|
68
|
-
mimeTypeHint?: string;
|
|
69
|
-
byteLengthHint?: number;
|
|
20
|
+
/** Runtime acquisition extends canonical IO with package-resource resolution. */
|
|
21
|
+
type ModelSource = ModelSource$1 & Readonly<{
|
|
70
22
|
package?: ModelPackage;
|
|
71
|
-
provenance?: ModelProvenance;
|
|
72
23
|
}>;
|
|
73
24
|
type ResourceReference = Readonly<{
|
|
74
25
|
path: string;
|
|
@@ -134,16 +85,16 @@ type SniffInput = Readonly<{
|
|
|
134
85
|
}>;
|
|
135
86
|
type AdapterLoadContext = Readonly<{
|
|
136
87
|
signal: AbortSignal;
|
|
137
|
-
mode:
|
|
88
|
+
mode: GpuModelDiagnosticMode;
|
|
138
89
|
execution: "main" | "worker";
|
|
139
90
|
adapterOptions: unknown;
|
|
140
91
|
}>;
|
|
141
|
-
type AdapterLoadResult<TCanonicalModel, TRendererReady = unknown> = Readonly<{
|
|
92
|
+
type AdapterLoadResult<TCanonicalModel = GpuModelDocument, TRendererReady = unknown> = Readonly<{
|
|
142
93
|
canonicalModel: TCanonicalModel;
|
|
143
94
|
rendererReady?: TRendererReady;
|
|
144
95
|
diagnostics?: readonly unknown[];
|
|
145
96
|
}>;
|
|
146
|
-
interface ModelAdapter<TCanonicalModel, TRendererReady = unknown> {
|
|
97
|
+
interface ModelAdapter<TCanonicalModel = GpuModelDocument, TRendererReady = unknown> {
|
|
147
98
|
readonly formatId: string;
|
|
148
99
|
readonly supportsWorker?: boolean;
|
|
149
100
|
sniff(input: SniffInput): number | boolean;
|
|
@@ -175,7 +126,7 @@ type ModelCache = Readonly<{
|
|
|
175
126
|
}>;
|
|
176
127
|
type LoadModelOptions = Readonly<{
|
|
177
128
|
signal?: AbortSignal;
|
|
178
|
-
mode?:
|
|
129
|
+
mode?: GpuModelDiagnosticMode;
|
|
179
130
|
adapterOptions?: unknown;
|
|
180
131
|
formatId?: string;
|
|
181
132
|
fetchPolicy?: FetchPolicy;
|
|
@@ -185,7 +136,7 @@ type LoadModelOptions = Readonly<{
|
|
|
185
136
|
mode?: "auto" | "never" | "always";
|
|
186
137
|
}>;
|
|
187
138
|
}>;
|
|
188
|
-
type ModelLoadResult<TCanonicalModel, TRendererReady = unknown> = Readonly<{
|
|
139
|
+
type ModelLoadResult<TCanonicalModel = GpuModelDocument, TRendererReady = unknown> = Readonly<{
|
|
189
140
|
canonicalModel: TCanonicalModel;
|
|
190
141
|
rendererReady?: TRendererReady;
|
|
191
142
|
diagnostics?: readonly unknown[];
|
package/dist/index.js
CHANGED
|
@@ -33,6 +33,10 @@ function createModelCacheKey(contentHash, formatId, adapterOptions) {
|
|
|
33
33
|
var DEFAULT_MODEL_MAX_BYTES = 64 * 1024 * 1024;
|
|
34
34
|
|
|
35
35
|
// src/bytes.ts
|
|
36
|
+
var typedArrayKind = Object.getOwnPropertyDescriptor(
|
|
37
|
+
Object.getPrototypeOf(Uint8Array.prototype),
|
|
38
|
+
Symbol.toStringTag
|
|
39
|
+
)?.get;
|
|
36
40
|
var ModelResourceTooLargeError = class extends Error {
|
|
37
41
|
code = "MODEL_RESOURCE_TOO_LARGE";
|
|
38
42
|
constructor() {
|
|
@@ -109,7 +113,7 @@ async function readBoundedStream(source, maxBytes, signal) {
|
|
|
109
113
|
const next = await abortable(reader ? reader.read() : iterator.next(), signal);
|
|
110
114
|
checkAbort(signal);
|
|
111
115
|
if (next.done) break;
|
|
112
|
-
if (!(next.value
|
|
116
|
+
if (!ArrayBuffer.isView(next.value) || typedArrayKind?.call(next.value) !== "Uint8Array") throw new TypeError("Model stream chunks must be Uint8Array");
|
|
113
117
|
const chunk = next.value;
|
|
114
118
|
if (chunk.byteLength > maxBytes - length) throw new ModelResourceTooLargeError();
|
|
115
119
|
const required = length + chunk.byteLength;
|
|
@@ -241,12 +245,15 @@ async function verifyIntegrity(bytes, integrity, hashBytes) {
|
|
|
241
245
|
if (!integrity) {
|
|
242
246
|
return;
|
|
243
247
|
}
|
|
244
|
-
const expected = integrity.replace(/^sha256-/iu, "")
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
248
|
+
const expected = integrity.replace(/^sha256-/iu, "");
|
|
249
|
+
if (/^[0-9a-f]{64}$/iu.test(expected)) {
|
|
250
|
+
const actualHex = await hashBytes(bytes);
|
|
251
|
+
if (expected.toLowerCase() === actualHex.toLowerCase()) return;
|
|
252
|
+
} else if (/^[A-Za-z0-9+/]{43}=$/u.test(expected)) {
|
|
253
|
+
const actualBase64 = bytesToBase64(await digestBytes(bytes, hashBytes));
|
|
254
|
+
if (expected === actualBase64) return;
|
|
249
255
|
}
|
|
256
|
+
throw new NonRetryableResourceError("Model resource integrity check failed");
|
|
250
257
|
}
|
|
251
258
|
async function digestBytes(bytes, hashBytes) {
|
|
252
259
|
if (globalThis.crypto?.subtle) {
|
|
@@ -336,7 +343,7 @@ var PackageResourceResolver = class {
|
|
|
336
343
|
const memoryResource = this.memoryResources[memoryKey] ?? this.memoryResources[`./${memoryKey}`];
|
|
337
344
|
if (memoryResource) {
|
|
338
345
|
const bytes = await boundedBytes(memoryResource.bytes, maxBytes, controller.signal);
|
|
339
|
-
await abortable(
|
|
346
|
+
await abortable(verifyIntegrity(bytes, requested.integrity ?? memoryResource.integrity, this.hashBytes), controller.signal);
|
|
340
347
|
return {
|
|
341
348
|
path: memoryKey,
|
|
342
349
|
bytes,
|
|
@@ -424,7 +431,23 @@ var ModelRuntime = class {
|
|
|
424
431
|
fileName: input.fileName,
|
|
425
432
|
sourceKind: source.kind
|
|
426
433
|
}, options.formatId);
|
|
427
|
-
const
|
|
434
|
+
const workerMode = options.worker?.mode ?? "auto";
|
|
435
|
+
const canUseWorker = workerMode !== "never" && Boolean(this.workerDispatcher) && Boolean(adapter.supportsWorker);
|
|
436
|
+
if (workerMode === "always" && !canUseWorker) {
|
|
437
|
+
throw new Error(`Adapter ${adapter.formatId} does not support the requested worker execution`);
|
|
438
|
+
}
|
|
439
|
+
const context = {
|
|
440
|
+
signal: options.signal ?? new AbortController().signal,
|
|
441
|
+
mode: options.mode ?? "strict",
|
|
442
|
+
execution: canUseWorker ? "worker" : "main",
|
|
443
|
+
adapterOptions: options.adapterOptions ?? null
|
|
444
|
+
};
|
|
445
|
+
const cacheKey = createModelCacheKey(input.contentHash, adapter.formatId, {
|
|
446
|
+
adapterOptions: context.adapterOptions,
|
|
447
|
+
maxBytes: byteLimit(options.fetchPolicy?.maxBytes),
|
|
448
|
+
mode: context.mode,
|
|
449
|
+
execution: context.execution
|
|
450
|
+
});
|
|
428
451
|
const useCache = options.useCache ?? true;
|
|
429
452
|
if (useCache) {
|
|
430
453
|
const cached = await this.cache.get(cacheKey);
|
|
@@ -432,13 +455,7 @@ var ModelRuntime = class {
|
|
|
432
455
|
return { ...cached, fromCache: true };
|
|
433
456
|
}
|
|
434
457
|
}
|
|
435
|
-
const
|
|
436
|
-
signal: options.signal ?? new AbortController().signal,
|
|
437
|
-
mode: options.mode ?? "strict",
|
|
438
|
-
execution: "main",
|
|
439
|
-
adapterOptions: options.adapterOptions ?? null
|
|
440
|
-
};
|
|
441
|
-
const adapterResult = await this.loadWithExecution(adapter, input, context, options);
|
|
458
|
+
const adapterResult = await this.loadWithExecution(adapter, input, context);
|
|
442
459
|
const result = {
|
|
443
460
|
...adapterResult,
|
|
444
461
|
contentHash: input.contentHash,
|
|
@@ -453,13 +470,8 @@ var ModelRuntime = class {
|
|
|
453
470
|
}
|
|
454
471
|
return result;
|
|
455
472
|
}
|
|
456
|
-
async loadWithExecution(adapter, input, context
|
|
457
|
-
|
|
458
|
-
const canUseWorker = workerMode !== "never" && Boolean(this.workerDispatcher) && Boolean(adapter.supportsWorker);
|
|
459
|
-
if (workerMode === "always" && !canUseWorker) {
|
|
460
|
-
throw new Error(`Adapter ${adapter.formatId} does not support the requested worker execution`);
|
|
461
|
-
}
|
|
462
|
-
if (canUseWorker && this.workerDispatcher) {
|
|
473
|
+
async loadWithExecution(adapter, input, context) {
|
|
474
|
+
if (context.execution === "worker" && this.workerDispatcher) {
|
|
463
475
|
const workerRequest = {
|
|
464
476
|
formatId: adapter.formatId,
|
|
465
477
|
input,
|
|
@@ -565,14 +577,6 @@ async function sourceToBytes(source, maxBytes, signal) {
|
|
|
565
577
|
if (source.byteLengthHint !== void 0) checkSize(source.byteLengthHint, maxBytes);
|
|
566
578
|
return readBoundedStream(source.stream, maxBytes, signal);
|
|
567
579
|
}
|
|
568
|
-
async function verifyResourceIntegrity(bytes, integrity, hashBytes) {
|
|
569
|
-
if (!integrity) return;
|
|
570
|
-
const expected = integrity.replace(/^sha256-/iu, "").toLowerCase();
|
|
571
|
-
const actual = (await hashBytes(bytes)).toLowerCase();
|
|
572
|
-
if (actual !== expected) {
|
|
573
|
-
throw new Error("Model resource integrity check failed");
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
580
|
var defaultFetch = (input, init) => {
|
|
577
581
|
if (!globalThis.fetch) {
|
|
578
582
|
throw new Error("URL sources require fetch or an injected fetch dependency");
|