@plasius/gpu-model-runtime 0.1.1 → 0.2.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/dist/index.cjs CHANGED
@@ -21,10 +21,15 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  AdapterRegistry: () => AdapterRegistry,
24
+ DEFAULT_MODEL_MAX_BYTES: () => DEFAULT_MODEL_MAX_BYTES,
24
25
  MemoryModelCache: () => MemoryModelCache,
26
+ ModelResidencyError: () => ModelResidencyError,
27
+ ModelResidencyManager: () => ModelResidencyManager,
28
+ ModelResourceTooLargeError: () => ModelResourceTooLargeError,
25
29
  ModelRuntime: () => ModelRuntime,
26
30
  PackageResourceResolver: () => PackageResourceResolver,
27
31
  createModelCacheKey: () => createModelCacheKey,
32
+ createModelResidencyKey: () => createModelResidencyKey,
28
33
  defaultSleep: () => defaultSleep,
29
34
  fetchResource: () => fetchResource,
30
35
  packageBootstrap: () => packageBootstrap,
@@ -65,8 +70,117 @@ function createModelCacheKey(contentHash, formatId, adapterOptions) {
65
70
  return `${formatId}:${contentHash}:${JSON.stringify(stableValue(adapterOptions ?? null))}`;
66
71
  }
67
72
 
73
+ // src/types.ts
74
+ var DEFAULT_MODEL_MAX_BYTES = 64 * 1024 * 1024;
75
+
76
+ // src/bytes.ts
77
+ var ModelResourceTooLargeError = class extends Error {
78
+ code = "MODEL_RESOURCE_TOO_LARGE";
79
+ constructor() {
80
+ super("Model resource exceeds maxBytes");
81
+ this.name = "ModelResourceTooLargeError";
82
+ }
83
+ };
84
+ function byteLimit(value = DEFAULT_MODEL_MAX_BYTES) {
85
+ if (!Number.isSafeInteger(value) || value <= 0) throw new RangeError("maxBytes must be a positive safe integer");
86
+ return value;
87
+ }
88
+ function checkSize(size, maxBytes) {
89
+ if (size > maxBytes) throw new ModelResourceTooLargeError();
90
+ }
91
+ function checkAbort(signal) {
92
+ if (signal?.aborted) throw new DOMException("The model load was aborted", "AbortError");
93
+ }
94
+ function acquisitionController(signal, timeoutMs = 3e4, parent) {
95
+ if (!Number.isSafeInteger(timeoutMs) || timeoutMs <= 0 || timeoutMs > 2147483647) {
96
+ throw new RangeError("timeoutMs must be a positive timer-safe integer");
97
+ }
98
+ const controller = new AbortController();
99
+ const abort = () => controller.abort();
100
+ signal?.addEventListener("abort", abort, { once: true });
101
+ parent?.addEventListener("abort", abort, { once: true });
102
+ if (signal?.aborted || parent?.aborted) controller.abort();
103
+ const timer = setTimeout(abort, timeoutMs);
104
+ return {
105
+ signal: controller.signal,
106
+ dispose() {
107
+ clearTimeout(timer);
108
+ signal?.removeEventListener("abort", abort);
109
+ parent?.removeEventListener("abort", abort);
110
+ }
111
+ };
112
+ }
113
+ async function abortable(work, signal) {
114
+ return new Promise((resolve, reject) => {
115
+ const abort = () => {
116
+ signal.removeEventListener("abort", abort);
117
+ reject(new DOMException("The model load was aborted", "AbortError"));
118
+ };
119
+ signal.addEventListener("abort", abort, { once: true });
120
+ if (signal.aborted) abort();
121
+ work.then(
122
+ (value) => {
123
+ signal.removeEventListener("abort", abort);
124
+ resolve(value);
125
+ },
126
+ (error) => {
127
+ signal.removeEventListener("abort", abort);
128
+ reject(error);
129
+ }
130
+ );
131
+ });
132
+ }
133
+ function cleanup(work) {
134
+ try {
135
+ void Promise.resolve(work()).catch(() => {
136
+ });
137
+ } catch {
138
+ }
139
+ }
140
+ async function readBoundedStream(source, maxBytes, signal) {
141
+ let bytes = new Uint8Array(0);
142
+ let length = 0;
143
+ let chunks = 0;
144
+ const reader = "getReader" in source ? source.getReader() : void 0;
145
+ const iterator = reader ? void 0 : source[Symbol.asyncIterator]();
146
+ try {
147
+ while (true) {
148
+ checkAbort(signal);
149
+ if (++chunks % 256 === 0) await abortable(new Promise((resolve) => setTimeout(resolve, 0)), signal);
150
+ const next = await abortable(reader ? reader.read() : iterator.next(), signal);
151
+ checkAbort(signal);
152
+ if (next.done) break;
153
+ if (!(next.value instanceof Uint8Array)) throw new TypeError("Model stream chunks must be Uint8Array");
154
+ const chunk = next.value;
155
+ if (chunk.byteLength > maxBytes - length) throw new ModelResourceTooLargeError();
156
+ const required = length + chunk.byteLength;
157
+ if (required > bytes.length) {
158
+ const grown = new Uint8Array(Math.min(maxBytes, Math.max(required, bytes.length * 2, 4096)));
159
+ grown.set(bytes.subarray(0, length));
160
+ bytes = grown;
161
+ }
162
+ bytes.set(chunk, length);
163
+ length = required;
164
+ }
165
+ return bytes.length === length ? bytes : bytes.slice(0, length);
166
+ } catch (error) {
167
+ if (reader) cleanup(() => reader.cancel());
168
+ else cleanup(() => iterator?.return?.());
169
+ throw error;
170
+ } finally {
171
+ reader?.releaseLock();
172
+ }
173
+ }
174
+ async function boundedBytes(input, maxBytes, signal) {
175
+ checkAbort(signal);
176
+ checkSize(input instanceof Blob ? input.size : input.byteLength, maxBytes);
177
+ if (input instanceof Blob) return readBoundedStream(input.stream(), maxBytes, signal);
178
+ return new Uint8Array(input);
179
+ }
180
+
68
181
  // src/fetch.ts
69
182
  var DEFAULT_POLICY = {
183
+ maxBytes: DEFAULT_MODEL_MAX_BYTES,
70
184
  maxAttempts: 3,
71
185
  baseDelayMs: 100,
72
186
  maxDelayMs: 1e3,
@@ -97,6 +211,7 @@ async function sha256Hex(bytes) {
97
211
  }
98
212
  async function fetchResource(url, options) {
99
213
  const policy = { ...DEFAULT_POLICY, ...options.policy };
214
+ const maxBytes = Math.min(byteLimit(policy.maxBytes), byteLimit(options.resolveOptions?.maxBytes ?? policy.maxBytes));
100
215
  const headers = new Headers(options.headers);
101
216
  if (options.resolveOptions?.range) {
102
217
  const { start, end } = options.resolveOptions.range;
@@ -104,13 +219,21 @@ async function fetchResource(url, options) {
104
219
  }
105
220
  let lastError;
106
221
  for (let attempt = 1; attempt <= policy.maxAttempts; attempt += 1) {
107
- const controller = createRequestController(options.resolveOptions?.signal, policy.timeoutMs);
222
+ const controller = acquisitionController(options.resolveOptions?.signal, policy.timeoutMs);
108
223
  try {
109
- const response = await options.fetch(url, {
224
+ checkAbort(controller.signal);
225
+ const response = await abortable(options.fetch(url, {
110
226
  headers,
111
227
  signal: controller.signal
112
- });
228
+ }).then((response2) => {
229
+ if (controller.signal.aborted) {
230
+ cleanup(() => response2.body?.cancel());
231
+ checkAbort(controller.signal);
232
+ }
233
+ return response2;
234
+ }), controller.signal);
113
235
  if (!response.ok) {
236
+ cleanup(() => response.body?.cancel());
114
237
  if (!RETRYABLE_STATUSES.has(response.status)) {
115
238
  throw new NonRetryableResourceError(`Model resource request failed with HTTP ${response.status}`);
116
239
  }
@@ -118,11 +241,20 @@ async function fetchResource(url, options) {
118
241
  throw new NonRetryableResourceError(`Model resource request failed with HTTP ${response.status}`);
119
242
  }
120
243
  lastError = new Error(`Retryable model resource response: HTTP ${response.status}`);
121
- await waitForRetry(options.sleep, attempt, policy, options.resolveOptions?.signal);
244
+ await waitForRetry(options.sleep, attempt, policy, controller.signal);
122
245
  continue;
123
246
  }
124
- const bytes = new Uint8Array(await response.arrayBuffer());
125
- await verifyIntegrity(bytes, options.integrity, options.hashBytes);
247
+ let bytes;
248
+ try {
249
+ const declared = response.headers.get("content-length");
250
+ if (declared !== null && /^\d+$/u.test(declared)) checkSize(Number(declared), maxBytes);
251
+ bytes = response.body ? await readBoundedStream(response.body, maxBytes, controller.signal) : new Uint8Array(0);
252
+ checkAbort(controller.signal);
253
+ } catch (error) {
254
+ if (!response.body?.locked) cleanup(() => response.body?.cancel());
255
+ throw error;
256
+ }
257
+ await abortable(verifyIntegrity(bytes, options.integrity, options.hashBytes), controller.signal);
126
258
  return {
127
259
  bytes,
128
260
  contentType: response.headers.get("content-type") ?? void 0,
@@ -132,14 +264,14 @@ async function fetchResource(url, options) {
132
264
  if (isAbortError(error) || options.resolveOptions?.signal?.aborted) {
133
265
  throw createAbortError();
134
266
  }
135
- if (error instanceof NonRetryableResourceError) {
267
+ if (error instanceof NonRetryableResourceError || error instanceof ModelResourceTooLargeError) {
136
268
  throw error;
137
269
  }
138
270
  lastError = error;
139
271
  if (attempt === policy.maxAttempts) {
140
272
  throw error;
141
273
  }
142
- await waitForRetry(options.sleep, attempt, policy, options.resolveOptions?.signal);
274
+ await waitForRetry(options.sleep, attempt, policy, controller.signal);
143
275
  } finally {
144
276
  controller.dispose();
145
277
  }
@@ -176,20 +308,7 @@ function bytesToBase64(bytes) {
176
308
  }
177
309
  async function waitForRetry(sleep, attempt, policy, signal) {
178
310
  const delay = Math.min(policy.maxDelayMs, policy.baseDelayMs * 2 ** (attempt - 1));
179
- await sleep(delay, signal);
180
- }
181
- function createRequestController(signal, timeoutMs) {
182
- const controller = new AbortController();
183
- const onAbort = () => controller.abort();
184
- signal?.addEventListener("abort", onAbort, { once: true });
185
- const timer = setTimeout(() => controller.abort(), timeoutMs);
186
- return {
187
- signal: controller.signal,
188
- dispose: () => {
189
- clearTimeout(timer);
190
- signal?.removeEventListener("abort", onAbort);
191
- }
192
- };
311
+ await abortable(sleep(delay, signal), signal);
193
312
  }
194
313
  function createAbortError() {
195
314
  return new DOMException("The model load was aborted", "AbortError");
@@ -225,6 +344,7 @@ var AdapterRegistry = class {
225
344
  }
226
345
  };
227
346
  var PackageResourceResolver = class {
347
+ parentSignal;
228
348
  memoryResources;
229
349
  baseUrl;
230
350
  sourceKind;
@@ -235,48 +355,58 @@ var PackageResourceResolver = class {
235
355
  headers;
236
356
  blobStorageUrlResolver;
237
357
  constructor(options) {
358
+ this.parentSignal = options.signal;
238
359
  this.sourceKind = options.sourceKind;
239
360
  this.baseUrl = options.package?.baseUrl ?? options.baseUrl;
240
361
  this.memoryResources = options.package?.resources ?? {};
241
362
  this.fetch = options.fetch;
242
363
  this.hashBytes = options.hashBytes;
243
364
  this.sleep = options.sleep;
244
- this.fetchPolicy = options.fetchPolicy;
365
+ this.fetchPolicy = { ...options.fetchPolicy };
245
366
  this.headers = options.headers;
246
367
  this.blobStorageUrlResolver = options.blobStorageUrlResolver;
247
368
  }
248
369
  async resolve(reference, options = {}) {
249
- const requested = typeof reference === "string" ? { path: reference } : reference;
250
- const memoryKey = normalizePackagePath(requested.path);
251
- const memoryResource = this.memoryResources[memoryKey] ?? this.memoryResources[`./${memoryKey}`];
252
- if (memoryResource) {
253
- const bytes = await toBytes(memoryResource.bytes);
254
- await verifyResourceIntegrity(bytes, requested.integrity ?? memoryResource.integrity, this.hashBytes);
370
+ const inheritedLimit = byteLimit(this.fetchPolicy?.maxBytes);
371
+ const maxBytes = Math.min(inheritedLimit, byteLimit(options.maxBytes ?? inheritedLimit));
372
+ const controller = acquisitionController(options.signal, this.fetchPolicy?.timeoutMs, this.parentSignal);
373
+ try {
374
+ checkAbort(controller.signal);
375
+ const requested = typeof reference === "string" ? { path: reference } : reference;
376
+ const memoryKey = normalizePackagePath(requested.path);
377
+ const memoryResource = this.memoryResources[memoryKey] ?? this.memoryResources[`./${memoryKey}`];
378
+ if (memoryResource) {
379
+ const bytes = await boundedBytes(memoryResource.bytes, maxBytes, controller.signal);
380
+ await abortable(verifyResourceIntegrity(bytes, requested.integrity ?? memoryResource.integrity, this.hashBytes), controller.signal);
381
+ return {
382
+ path: memoryKey,
383
+ bytes,
384
+ contentType: requested.contentType ?? memoryResource.contentType,
385
+ contentHash: await abortable(this.hashBytes(bytes), controller.signal),
386
+ rangeSupported: false
387
+ };
388
+ }
389
+ const url = await abortable(this.resolveUrl(requested.path), controller.signal);
390
+ checkAbort(controller.signal);
391
+ const result = await fetchResource(url, {
392
+ fetch: this.fetch,
393
+ hashBytes: this.hashBytes,
394
+ sleep: this.sleep,
395
+ headers: this.headers,
396
+ integrity: requested.integrity,
397
+ policy: this.fetchPolicy,
398
+ resolveOptions: { ...options, maxBytes, signal: controller.signal }
399
+ });
255
400
  return {
256
401
  path: memoryKey,
257
- bytes,
258
- contentType: requested.contentType ?? memoryResource.contentType,
259
- contentHash: await this.hashBytes(bytes),
260
- rangeSupported: false
402
+ bytes: result.bytes,
403
+ contentType: requested.contentType ?? result.contentType,
404
+ contentHash: await abortable(this.hashBytes(result.bytes), controller.signal),
405
+ rangeSupported: result.rangeSupported
261
406
  };
407
+ } finally {
408
+ controller.dispose();
262
409
  }
263
- const url = await this.resolveUrl(requested.path);
264
- const result = await fetchResource(url, {
265
- fetch: this.fetch,
266
- hashBytes: this.hashBytes,
267
- sleep: this.sleep,
268
- headers: this.headers,
269
- integrity: requested.integrity,
270
- policy: this.fetchPolicy,
271
- resolveOptions: options
272
- });
273
- return {
274
- path: memoryKey,
275
- bytes: result.bytes,
276
- contentType: requested.contentType ?? result.contentType,
277
- contentHash: await this.hashBytes(result.bytes),
278
- rangeSupported: result.rangeSupported
279
- };
280
410
  }
281
411
  async resolveUrl(reference) {
282
412
  if (/^[a-z][a-z\d+.-]*:/iu.test(reference)) {
@@ -335,7 +465,7 @@ var ModelRuntime = class {
335
465
  fileName: input.fileName,
336
466
  sourceKind: source.kind
337
467
  }, options.formatId);
338
- const cacheKey = createModelCacheKey(input.contentHash, adapter.formatId, options.adapterOptions);
468
+ const cacheKey = createModelCacheKey(input.contentHash, adapter.formatId, { adapterOptions: options.adapterOptions ?? null, maxBytes: byteLimit(options.fetchPolicy?.maxBytes) });
339
469
  const useCache = options.useCache ?? true;
340
470
  if (useCache) {
341
471
  const cached = await this.cache.get(cacheKey);
@@ -381,54 +511,64 @@ var ModelRuntime = class {
381
511
  return adapter.load(input, context);
382
512
  }
383
513
  async resolveSource(source, options) {
384
- const fileName = sourceFileName(source);
385
- const baseUrl = "url" in source ? source.url : void 0;
386
- const headers = "headers" in source ? source.headers : void 0;
387
- let bytes;
388
- let contentType = "mimeTypeHint" in source ? source.mimeTypeHint : void 0;
389
- let rangeSupported = false;
390
- if (source.kind === "file-path") {
391
- if (!this.readFile) {
392
- throw new Error("file-path sources require a readFile dependency");
514
+ const maxBytes = byteLimit(options.fetchPolicy?.maxBytes);
515
+ const controller = acquisitionController(options.signal, options.fetchPolicy?.timeoutMs);
516
+ try {
517
+ checkAbort(controller.signal);
518
+ const fileName = sourceFileName(source);
519
+ const baseUrl = "url" in source ? source.url : void 0;
520
+ const headers = "headers" in source ? source.headers : void 0;
521
+ let bytes;
522
+ let contentType = "mimeTypeHint" in source ? source.mimeTypeHint : void 0;
523
+ let rangeSupported = false;
524
+ if (source.kind === "file-path") {
525
+ if (!this.readFile) {
526
+ throw new Error("file-path sources require a readFile dependency");
527
+ }
528
+ bytes = await abortable(this.readFile(source.path, controller.signal, { maxBytes }), controller.signal);
529
+ checkSize(bytes.byteLength, maxBytes);
530
+ } else if (source.kind === "url" || source.kind === "blob-storage-url") {
531
+ const result = await fetchResource(ensureHttpUrl(source.url), {
532
+ fetch: this.fetch,
533
+ hashBytes: this.hashBytes,
534
+ sleep: this.sleep,
535
+ headers,
536
+ integrity: options.integrity,
537
+ policy: options.fetchPolicy,
538
+ resolveOptions: { signal: controller.signal }
539
+ });
540
+ bytes = result.bytes;
541
+ contentType ??= result.contentType;
542
+ rangeSupported = result.rangeSupported;
543
+ } else {
544
+ bytes = await sourceToBytes(source, maxBytes, controller.signal);
393
545
  }
394
- bytes = await this.readFile(source.path, options.signal);
395
- } else if (source.kind === "url" || source.kind === "blob-storage-url") {
396
- const result = await fetchResource(ensureHttpUrl(source.url), {
546
+ checkAbort(controller.signal);
547
+ const contentHash = await abortable(this.hashBytes(bytes), controller.signal);
548
+ const resourceResolver = new PackageResourceResolver({
549
+ sourceKind: source.kind,
550
+ signal: options.signal,
551
+ baseUrl,
552
+ package: source.package,
397
553
  fetch: this.fetch,
398
554
  hashBytes: this.hashBytes,
399
555
  sleep: this.sleep,
556
+ fetchPolicy: options.fetchPolicy,
400
557
  headers,
401
- integrity: options.integrity,
402
- policy: options.fetchPolicy,
403
- resolveOptions: { signal: options.signal }
558
+ blobStorageUrlResolver: this.blobStorageUrlResolver
404
559
  });
405
- bytes = result.bytes;
406
- contentType ??= result.contentType;
407
- rangeSupported = result.rangeSupported;
408
- } else {
409
- bytes = await sourceToBytes(source);
410
- }
411
- const contentHash = await this.hashBytes(bytes);
412
- const resourceResolver = new PackageResourceResolver({
413
- sourceKind: source.kind,
414
- baseUrl,
415
- package: source.package,
416
- fetch: this.fetch,
417
- hashBytes: this.hashBytes,
418
- sleep: this.sleep,
419
- fetchPolicy: options.fetchPolicy,
420
- headers,
421
- blobStorageUrlResolver: this.blobStorageUrlResolver
422
- });
423
- return {
424
- source,
425
- bytes,
426
- contentType,
427
- fileName,
428
- contentHash,
429
- rangeSupported,
430
- resourceResolver
431
- };
560
+ return {
561
+ source,
562
+ bytes,
563
+ contentType,
564
+ fileName,
565
+ contentHash,
566
+ rangeSupported,
567
+ resourceResolver
568
+ };
569
+ } finally {
570
+ controller.dispose();
571
+ }
432
572
  }
433
573
  };
434
574
  function scoreRegistration(registration, input) {
@@ -460,50 +600,11 @@ function normalizePackagePath(path) {
460
600
  }
461
601
  return segments.join("/");
462
602
  }
463
- async function sourceToBytes(source) {
464
- if (source.kind === "blob") {
465
- return toBytes(source.blob);
466
- }
467
- if (source.kind === "array-buffer") {
468
- return new Uint8Array(source.bytes);
469
- }
470
- if (source.kind === "uint8-array") {
471
- return new Uint8Array(source.bytes);
472
- }
473
- const chunks = [];
474
- if (Symbol.asyncIterator in Object(source.stream)) {
475
- for await (const chunk of source.stream) {
476
- chunks.push(new Uint8Array(chunk));
477
- }
478
- } else {
479
- const reader = source.stream.getReader();
480
- try {
481
- while (true) {
482
- const next = await reader.read();
483
- if (next.done) break;
484
- chunks.push(new Uint8Array(next.value));
485
- }
486
- } finally {
487
- reader.releaseLock();
488
- }
489
- }
490
- const length = chunks.reduce((total, chunk) => total + chunk.byteLength, 0);
491
- const bytes = new Uint8Array(length);
492
- let offset = 0;
493
- for (const chunk of chunks) {
494
- bytes.set(chunk, offset);
495
- offset += chunk.byteLength;
496
- }
497
- return bytes;
498
- }
499
- async function toBytes(input) {
500
- if (input instanceof Blob) {
501
- return new Uint8Array(await input.arrayBuffer());
502
- }
503
- if (input instanceof Uint8Array) {
504
- return new Uint8Array(input);
505
- }
506
- return new Uint8Array(input);
603
+ async function sourceToBytes(source, maxBytes, signal) {
604
+ if (source.kind === "blob") return boundedBytes(source.blob, maxBytes, signal);
605
+ if (source.kind === "array-buffer" || source.kind === "uint8-array") return boundedBytes(source.bytes, maxBytes, signal);
606
+ if (source.byteLengthHint !== void 0) checkSize(source.byteLengthHint, maxBytes);
607
+ return readBoundedStream(source.stream, maxBytes, signal);
507
608
  }
508
609
  async function verifyResourceIntegrity(bytes, integrity, hashBytes) {
509
610
  if (!integrity) return;
@@ -543,6 +644,636 @@ function ensureHttpUrl(value) {
543
644
  return url.href;
544
645
  }
545
646
 
647
+ // src/model-residency.ts
648
+ var SHA256_PATTERN = /^[a-f0-9]{64}$/u;
649
+ var PARTITION_ID_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/u;
650
+ var ModelResidencyError = class extends Error {
651
+ code;
652
+ constructor(code, message) {
653
+ super(message);
654
+ this.name = "ModelResidencyError";
655
+ this.code = code;
656
+ }
657
+ };
658
+ function requireSafeNonNegativeInteger(value, name) {
659
+ if (!Number.isSafeInteger(value) || value < 0) {
660
+ throw new RangeError(`${name} must be a non-negative safe integer`);
661
+ }
662
+ }
663
+ function requirePositiveSafeInteger(value, name) {
664
+ if (!Number.isSafeInteger(value) || value <= 0) {
665
+ throw new RangeError(`${name} must be a positive safe integer`);
666
+ }
667
+ }
668
+ function requirePriority(priority) {
669
+ if (!Number.isFinite(priority) || priority < 0) {
670
+ throw new RangeError("priority must be a non-negative finite number");
671
+ }
672
+ }
673
+ function requireLod(lod) {
674
+ if (!Number.isInteger(lod) || lod < 0 || lod > 3) {
675
+ throw new RangeError("lod must be an integer from 0 to 3");
676
+ }
677
+ }
678
+ function createModelResidencyKey(assetRef, lod, partitionId) {
679
+ if (!SHA256_PATTERN.test(assetRef.contentHash)) {
680
+ throw new Error("ModelAssetRef.contentHash must be a lowercase SHA-256 value");
681
+ }
682
+ requireLod(lod);
683
+ if (partitionId !== void 0 && !PARTITION_ID_PATTERN.test(partitionId)) {
684
+ throw new Error("partitionId must be a bounded runtime token");
685
+ }
686
+ return `${assetRef.contentHash}:lod${lod}:${partitionId ?? "whole"}`;
687
+ }
688
+ function closedError() {
689
+ return new ModelResidencyError(
690
+ "manager-closed",
691
+ "The model residency manager is closed"
692
+ );
693
+ }
694
+ function abortedError() {
695
+ return new ModelResidencyError(
696
+ "acquire-aborted",
697
+ "The model residency acquisition was aborted"
698
+ );
699
+ }
700
+ function signalIsAborted(signal) {
701
+ return signal?.aborted === true;
702
+ }
703
+ var ModelResidencyManager = class {
704
+ #budget;
705
+ #load;
706
+ #loadTimeoutMs;
707
+ #entries = /* @__PURE__ */ new Map();
708
+ #loadTasks = /* @__PURE__ */ new Set();
709
+ #disposalTasks = /* @__PURE__ */ new Set();
710
+ #metrics = {
711
+ acquireCount: 0,
712
+ cacheHits: 0,
713
+ deduplicatedAcquires: 0,
714
+ loadsStarted: 0,
715
+ loadsCompleted: 0,
716
+ loadsFailed: 0,
717
+ loadsAborted: 0,
718
+ loadsTimedOut: 0,
719
+ evictions: 0,
720
+ disposals: 0,
721
+ disposalFailures: 0
722
+ };
723
+ #nextIdentity = 1;
724
+ #clock = 1;
725
+ #inFlightLoads = 0;
726
+ #closed = false;
727
+ #admissionTail = Promise.resolve();
728
+ #shutdownPromise;
729
+ constructor(options) {
730
+ requirePositiveSafeInteger(
731
+ options.budget.maxCpuBytes,
732
+ "budget.maxCpuBytes"
733
+ );
734
+ requirePositiveSafeInteger(
735
+ options.budget.maxGpuBytes,
736
+ "budget.maxGpuBytes"
737
+ );
738
+ requirePositiveSafeInteger(
739
+ options.budget.maxInFlightLoads,
740
+ "budget.maxInFlightLoads"
741
+ );
742
+ if (options.loadTimeoutMs !== void 0) {
743
+ requirePositiveSafeInteger(options.loadTimeoutMs, "loadTimeoutMs");
744
+ }
745
+ this.#budget = Object.freeze({ ...options.budget });
746
+ this.#load = options.load;
747
+ this.#loadTimeoutMs = options.loadTimeoutMs;
748
+ }
749
+ /** Acquire one shared resource lease, loading it at most once per cache key. */
750
+ acquire(options) {
751
+ try {
752
+ if (this.#closed) throw closedError();
753
+ requirePriority(options.priority);
754
+ requireSafeNonNegativeInteger(
755
+ options.estimatedCpuBytes,
756
+ "estimatedCpuBytes"
757
+ );
758
+ requireSafeNonNegativeInteger(
759
+ options.estimatedGpuBytes,
760
+ "estimatedGpuBytes"
761
+ );
762
+ const key = createModelResidencyKey(
763
+ options.assetRef,
764
+ options.lod,
765
+ options.partitionId
766
+ );
767
+ if (signalIsAborted(options.signal)) throw abortedError();
768
+ this.#metrics.acquireCount += 1;
769
+ const existing = this.#entries.get(key);
770
+ if (existing !== void 0) return this.#acquireExisting(existing, options);
771
+ return this.#scheduleAdmission(key, options);
772
+ } catch (error) {
773
+ return Promise.reject(error);
774
+ }
775
+ }
776
+ #acquireExisting(entry, options) {
777
+ this.#metrics.deduplicatedAcquires += 1;
778
+ if (entry.state === "resident") {
779
+ this.#metrics.cacheHits += 1;
780
+ return Promise.resolve(
781
+ this.#createLease(
782
+ entry,
783
+ options.priority,
784
+ options.pinned ?? false
785
+ )
786
+ );
787
+ }
788
+ return this.#addWaiter(entry, options);
789
+ }
790
+ #scheduleAdmission(key, options) {
791
+ let resolveAcquisition;
792
+ let rejectAcquisition;
793
+ const acquisition = new Promise(
794
+ (resolve, reject) => {
795
+ resolveAcquisition = resolve;
796
+ rejectAcquisition = reject;
797
+ }
798
+ );
799
+ const admission = this.#admissionTail.then(async () => {
800
+ if (this.#closed) throw closedError();
801
+ if (signalIsAborted(options.signal)) throw abortedError();
802
+ const existing = this.#entries.get(key);
803
+ if (existing !== void 0) {
804
+ void this.#acquireExisting(existing, options).then(
805
+ resolveAcquisition,
806
+ rejectAcquisition
807
+ );
808
+ return;
809
+ }
810
+ await this.#ensureCapacityForReservation(
811
+ options.estimatedCpuBytes,
812
+ options.estimatedGpuBytes
813
+ );
814
+ if (this.#closed) throw closedError();
815
+ if (signalIsAborted(options.signal)) throw abortedError();
816
+ const entry = {
817
+ key,
818
+ assetRef: options.assetRef,
819
+ lod: options.lod,
820
+ ...options.partitionId === void 0 ? {} : { partitionId: options.partitionId },
821
+ estimatedCpuBytes: options.estimatedCpuBytes,
822
+ estimatedGpuBytes: options.estimatedGpuBytes,
823
+ queueOrder: this.#nextIdentity++,
824
+ state: "queued",
825
+ priority: options.priority,
826
+ retainedPriority: options.priority,
827
+ lastUsed: this.#clock++,
828
+ abandoned: false,
829
+ loadSlotReleased: true,
830
+ waiters: /* @__PURE__ */ new Map(),
831
+ leases: /* @__PURE__ */ new Map()
832
+ };
833
+ this.#entries.set(key, entry);
834
+ void this.#addWaiter(entry, options).then(
835
+ resolveAcquisition,
836
+ rejectAcquisition
837
+ );
838
+ this.#pumpQueue();
839
+ });
840
+ this.#admissionTail = admission.catch(() => void 0);
841
+ void admission.catch(rejectAcquisition);
842
+ return acquisition;
843
+ }
844
+ /** Dispose every currently unreferenced, unpinned resident cache entry. */
845
+ async evictUnused() {
846
+ const tasks = [];
847
+ for (const entry of this.#evictionCandidates()) {
848
+ tasks.push(this.#evict(entry));
849
+ }
850
+ await Promise.all(tasks);
851
+ }
852
+ /** Wait until queued/loading work and its resulting disposal have settled. */
853
+ async waitForIdle() {
854
+ await this.#admissionTail;
855
+ do {
856
+ this.#pumpQueue();
857
+ const tasks = [...this.#loadTasks];
858
+ if (tasks.length > 0) {
859
+ await Promise.allSettled(tasks);
860
+ }
861
+ } while (this.#loadTasks.size > 0 || this.#queuedCount() > 0);
862
+ await Promise.allSettled([...this.#disposalTasks]);
863
+ }
864
+ /** Abort pending work and dispose every resident or late-completing resource. */
865
+ shutdown() {
866
+ if (this.#shutdownPromise !== void 0) return this.#shutdownPromise;
867
+ this.#closed = true;
868
+ const error = closedError();
869
+ const disposalTasks = [];
870
+ for (const entry of [...this.#entries.values()]) {
871
+ if (entry.state === "resident") {
872
+ this.#entries.delete(entry.key);
873
+ disposalTasks.push(this.#disposeEntry(entry));
874
+ continue;
875
+ }
876
+ this.#rejectWaiters(entry, error);
877
+ if (entry.state === "queued") {
878
+ this.#entries.delete(entry.key);
879
+ } else {
880
+ this.#abandonLoadingEntry(entry, error, false);
881
+ }
882
+ }
883
+ this.#shutdownPromise = (async () => {
884
+ await Promise.allSettled(disposalTasks);
885
+ await this.waitForIdle();
886
+ await Promise.allSettled([...this.#disposalTasks]);
887
+ })();
888
+ return this.#shutdownPromise;
889
+ }
890
+ /** Return immutable byte, queue, cache, and lifetime metrics. */
891
+ snapshot() {
892
+ let cpuBytes = 0;
893
+ let gpuBytes = 0;
894
+ let reservedCpuBytes = 0;
895
+ let reservedGpuBytes = 0;
896
+ let referenceCount = 0;
897
+ let pinnedCount = 0;
898
+ let residentCount = 0;
899
+ let pendingCount = 0;
900
+ const entries = [];
901
+ for (const entry of this.#entries.values()) {
902
+ const pinned = [...entry.leases.values()].some(
903
+ (lease) => lease.active && lease.pinned
904
+ );
905
+ const references = [...entry.leases.values()].filter(
906
+ (lease) => lease.active
907
+ ).length;
908
+ referenceCount += references;
909
+ if (pinned) pinnedCount += 1;
910
+ if (entry.state === "resident") {
911
+ residentCount += 1;
912
+ cpuBytes += entry.loaded.cpuBytes;
913
+ gpuBytes += entry.loaded.gpuBytes;
914
+ } else {
915
+ pendingCount += 1;
916
+ reservedCpuBytes += entry.estimatedCpuBytes;
917
+ reservedGpuBytes += entry.estimatedGpuBytes;
918
+ }
919
+ entries.push(
920
+ Object.freeze({
921
+ key: entry.key,
922
+ state: entry.state,
923
+ priority: entry.priority,
924
+ references,
925
+ pinned,
926
+ cpuBytes: entry.state === "resident" ? entry.loaded.cpuBytes : entry.estimatedCpuBytes,
927
+ gpuBytes: entry.state === "resident" ? entry.loaded.gpuBytes : entry.estimatedGpuBytes
928
+ })
929
+ );
930
+ }
931
+ entries.sort((left, right) => left.key.localeCompare(right.key));
932
+ return Object.freeze({
933
+ ...this.#budget,
934
+ cpuBytes,
935
+ gpuBytes,
936
+ reservedCpuBytes,
937
+ reservedGpuBytes,
938
+ residentCount,
939
+ pendingCount,
940
+ referenceCount,
941
+ pinnedCount,
942
+ inFlightLoads: this.#inFlightLoads,
943
+ queuedLoads: this.#queuedCount(),
944
+ ...this.#metrics,
945
+ residentKeys: Object.freeze(
946
+ entries.filter((entry) => entry.state === "resident").map((entry) => entry.key)
947
+ ),
948
+ entries: Object.freeze(entries)
949
+ });
950
+ }
951
+ #addWaiter(entry, options) {
952
+ const id = this.#nextIdentity++;
953
+ let resolvePromise;
954
+ let rejectPromise;
955
+ const promise = new Promise(
956
+ (resolve, reject) => {
957
+ resolvePromise = resolve;
958
+ rejectPromise = reject;
959
+ }
960
+ );
961
+ const waiter = {
962
+ id,
963
+ priority: options.priority,
964
+ pinned: options.pinned ?? false,
965
+ ...options.signal === void 0 ? {} : { signal: options.signal },
966
+ resolve: resolvePromise,
967
+ reject: rejectPromise
968
+ };
969
+ if (options.signal !== void 0) {
970
+ waiter.onAbort = () => this.#abortWaiter(entry, waiter);
971
+ options.signal.addEventListener("abort", waiter.onAbort, { once: true });
972
+ }
973
+ entry.waiters.set(id, waiter);
974
+ entry.retainedPriority = Math.max(entry.retainedPriority, options.priority);
975
+ this.#recalculatePriority(entry);
976
+ return promise;
977
+ }
978
+ #abortWaiter(entry, waiter) {
979
+ entry.waiters.delete(waiter.id);
980
+ this.#detachWaiterSignal(waiter);
981
+ waiter.reject(abortedError());
982
+ this.#recalculatePriority(entry);
983
+ if (entry.waiters.size > 0) return;
984
+ if (entry.state === "queued") {
985
+ this.#entries.delete(entry.key);
986
+ this.#pumpQueue();
987
+ return;
988
+ }
989
+ this.#abandonLoadingEntry(entry, abortedError(), true);
990
+ }
991
+ #createLease(entry, priority, pinned) {
992
+ const id = this.#nextIdentity++;
993
+ const record = {
994
+ id,
995
+ active: true,
996
+ priority,
997
+ pinned
998
+ };
999
+ entry.leases.set(id, record);
1000
+ entry.retainedPriority = priority;
1001
+ entry.lastUsed = this.#clock++;
1002
+ this.#recalculatePriority(entry);
1003
+ const lease = {
1004
+ key: entry.key,
1005
+ assetRef: entry.assetRef,
1006
+ lod: entry.lod,
1007
+ ...entry.partitionId === void 0 ? {} : { partitionId: entry.partitionId },
1008
+ resource: entry.loaded.resource,
1009
+ release: () => {
1010
+ if (!record.active) return;
1011
+ record.active = false;
1012
+ entry.leases.delete(id);
1013
+ entry.lastUsed = this.#clock++;
1014
+ this.#recalculatePriority(entry);
1015
+ },
1016
+ setPriority: (nextPriority) => {
1017
+ if (!record.active || this.#entries.get(entry.key) !== entry) return;
1018
+ requirePriority(nextPriority);
1019
+ record.priority = nextPriority;
1020
+ entry.retainedPriority = nextPriority;
1021
+ entry.lastUsed = this.#clock++;
1022
+ this.#recalculatePriority(entry);
1023
+ },
1024
+ setPinned: (nextPinned) => {
1025
+ if (!record.active || this.#entries.get(entry.key) !== entry) return;
1026
+ record.pinned = nextPinned;
1027
+ }
1028
+ };
1029
+ return Object.freeze(lease);
1030
+ }
1031
+ #recalculatePriority(entry) {
1032
+ let priority = entry.retainedPriority;
1033
+ for (const waiter of entry.waiters.values()) {
1034
+ priority = Math.max(priority, waiter.priority);
1035
+ }
1036
+ for (const lease of entry.leases.values()) {
1037
+ priority = Math.max(priority, lease.priority);
1038
+ }
1039
+ entry.priority = priority;
1040
+ }
1041
+ #pumpQueue() {
1042
+ if (this.#closed) return;
1043
+ while (this.#inFlightLoads < this.#budget.maxInFlightLoads) {
1044
+ const candidate = [...this.#entries.values()].filter(
1045
+ (entry) => entry.state === "queued" && !entry.abandoned && entry.waiters.size > 0
1046
+ ).sort(
1047
+ (left, right) => right.priority - left.priority || left.queueOrder - right.queueOrder
1048
+ )[0];
1049
+ if (candidate === void 0) return;
1050
+ this.#startLoad(candidate);
1051
+ }
1052
+ }
1053
+ #startLoad(entry) {
1054
+ entry.state = "loading";
1055
+ entry.loadSlotReleased = false;
1056
+ entry.controller = new AbortController();
1057
+ this.#inFlightLoads += 1;
1058
+ this.#metrics.loadsStarted += 1;
1059
+ if (this.#loadTimeoutMs !== void 0) {
1060
+ entry.timeout = setTimeout(() => {
1061
+ this.#metrics.loadsTimedOut += 1;
1062
+ this.#abandonLoadingEntry(
1063
+ entry,
1064
+ new ModelResidencyError(
1065
+ "load-timeout",
1066
+ `Model load exceeded ${this.#loadTimeoutMs} ms`
1067
+ ),
1068
+ true
1069
+ );
1070
+ }, this.#loadTimeoutMs);
1071
+ }
1072
+ let adapterPromise;
1073
+ try {
1074
+ adapterPromise = this.#load({
1075
+ assetRef: entry.assetRef,
1076
+ lod: entry.lod,
1077
+ ...entry.partitionId === void 0 ? {} : { partitionId: entry.partitionId },
1078
+ priority: entry.priority,
1079
+ signal: entry.controller.signal
1080
+ });
1081
+ } catch (error) {
1082
+ adapterPromise = Promise.reject(error);
1083
+ }
1084
+ const task = Promise.resolve(adapterPromise).then((result) => this.#completeLoad(entry, result)).catch((error) => this.#failLoad(entry, error)).finally(() => {
1085
+ if (entry.timeout !== void 0) clearTimeout(entry.timeout);
1086
+ entry.timeout = void 0;
1087
+ entry.controller = void 0;
1088
+ this.#releaseLoadSlot(entry);
1089
+ entry.loadTask = void 0;
1090
+ });
1091
+ entry.loadTask = task;
1092
+ this.#loadTasks.add(task);
1093
+ }
1094
+ async #completeLoad(entry, result) {
1095
+ if (entry.abandoned) {
1096
+ await this.#disposeLoaded(result);
1097
+ return;
1098
+ }
1099
+ try {
1100
+ this.#validateLoadedResource(entry, result);
1101
+ const evictions = this.#ensureCapacityForLoadedResult(entry, result);
1102
+ await Promise.all(evictions);
1103
+ } catch (error) {
1104
+ await this.#disposeLoaded(result);
1105
+ this.#entries.delete(entry.key);
1106
+ this.#rejectWaiters(entry, error);
1107
+ this.#metrics.loadsFailed += 1;
1108
+ return;
1109
+ }
1110
+ entry.state = "resident";
1111
+ entry.loaded = result;
1112
+ entry.lastUsed = this.#clock++;
1113
+ this.#metrics.loadsCompleted += 1;
1114
+ const waiters = [...entry.waiters.values()];
1115
+ entry.waiters.clear();
1116
+ for (const waiter of waiters) {
1117
+ this.#detachWaiterSignal(waiter);
1118
+ waiter.resolve(this.#createLease(entry, waiter.priority, waiter.pinned));
1119
+ }
1120
+ }
1121
+ #failLoad(entry, error) {
1122
+ if (entry.abandoned) return;
1123
+ this.#entries.delete(entry.key);
1124
+ this.#metrics.loadsFailed += 1;
1125
+ this.#rejectWaiters(entry, error);
1126
+ }
1127
+ #abandonLoadingEntry(entry, error, rejectWaiters) {
1128
+ entry.abandoned = true;
1129
+ this.#metrics.loadsAborted += 1;
1130
+ if (entry.timeout !== void 0) clearTimeout(entry.timeout);
1131
+ entry.timeout = void 0;
1132
+ this.#entries.delete(entry.key);
1133
+ if (rejectWaiters) this.#rejectWaiters(entry, error);
1134
+ entry.controller?.abort(error);
1135
+ this.#releaseLoadSlot(entry);
1136
+ }
1137
+ #releaseLoadSlot(entry) {
1138
+ if (entry.loadSlotReleased) return;
1139
+ entry.loadSlotReleased = true;
1140
+ this.#inFlightLoads -= 1;
1141
+ this.#loadTasks.delete(entry.loadTask);
1142
+ this.#pumpQueue();
1143
+ }
1144
+ #rejectWaiters(entry, error) {
1145
+ const waiters = [...entry.waiters.values()];
1146
+ entry.waiters.clear();
1147
+ for (const waiter of waiters) {
1148
+ this.#detachWaiterSignal(waiter);
1149
+ waiter.reject(error);
1150
+ }
1151
+ this.#recalculatePriority(entry);
1152
+ }
1153
+ #detachWaiterSignal(waiter) {
1154
+ if (waiter.signal !== void 0 && waiter.onAbort !== void 0) {
1155
+ waiter.signal.removeEventListener("abort", waiter.onAbort);
1156
+ }
1157
+ waiter.onAbort = void 0;
1158
+ }
1159
+ #validateLoadedResource(entry, result) {
1160
+ if (result === null || typeof result !== "object") {
1161
+ throw new ModelResidencyError(
1162
+ "invalid-load-result",
1163
+ "The model loader returned an invalid resource"
1164
+ );
1165
+ }
1166
+ if (typeof result.dispose !== "function") {
1167
+ throw new ModelResidencyError(
1168
+ "invalid-load-result",
1169
+ "The model loader must return a disposer"
1170
+ );
1171
+ }
1172
+ requireSafeNonNegativeInteger(result.cpuBytes, "loaded.cpuBytes");
1173
+ requireSafeNonNegativeInteger(result.gpuBytes, "loaded.gpuBytes");
1174
+ if (result.contentHash !== entry.assetRef.contentHash) {
1175
+ throw new ModelResidencyError(
1176
+ "content-hash-mismatch",
1177
+ "The loaded model content hash does not match its canonical reference"
1178
+ );
1179
+ }
1180
+ }
1181
+ async #ensureCapacityForReservation(cpuBytes, gpuBytes) {
1182
+ if (cpuBytes > this.#budget.maxCpuBytes || gpuBytes > this.#budget.maxGpuBytes) {
1183
+ throw this.#budgetError();
1184
+ }
1185
+ let usage = this.#totalAccountedBytes();
1186
+ for (const candidate of this.#evictionCandidates()) {
1187
+ if (usage.cpu + cpuBytes <= this.#budget.maxCpuBytes && usage.gpu + gpuBytes <= this.#budget.maxGpuBytes) {
1188
+ return;
1189
+ }
1190
+ await this.#evict(candidate);
1191
+ usage = this.#totalAccountedBytes();
1192
+ }
1193
+ if (usage.cpu + cpuBytes > this.#budget.maxCpuBytes || usage.gpu + gpuBytes > this.#budget.maxGpuBytes) {
1194
+ throw this.#budgetError();
1195
+ }
1196
+ }
1197
+ #ensureCapacityForLoadedResult(loadingEntry, result) {
1198
+ const usage = this.#totalAccountedBytes();
1199
+ usage.cpu += result.cpuBytes - loadingEntry.estimatedCpuBytes;
1200
+ usage.gpu += result.gpuBytes - loadingEntry.estimatedGpuBytes;
1201
+ const disposals = [];
1202
+ for (const candidate of this.#evictionCandidates()) {
1203
+ if (usage.cpu <= this.#budget.maxCpuBytes && usage.gpu <= this.#budget.maxGpuBytes) {
1204
+ return disposals;
1205
+ }
1206
+ usage.cpu -= candidate.loaded.cpuBytes;
1207
+ usage.gpu -= candidate.loaded.gpuBytes;
1208
+ disposals.push(this.#evict(candidate));
1209
+ }
1210
+ if (usage.cpu > this.#budget.maxCpuBytes || usage.gpu > this.#budget.maxGpuBytes) {
1211
+ throw this.#budgetError();
1212
+ }
1213
+ return disposals;
1214
+ }
1215
+ #budgetError() {
1216
+ return new ModelResidencyError(
1217
+ "budget-exceeded",
1218
+ "Pinned, referenced, or pending models leave insufficient residency budget"
1219
+ );
1220
+ }
1221
+ #totalAccountedBytes() {
1222
+ let cpu = 0;
1223
+ let gpu = 0;
1224
+ for (const entry of this.#entries.values()) {
1225
+ if (entry.state === "resident") {
1226
+ cpu += entry.loaded.cpuBytes;
1227
+ gpu += entry.loaded.gpuBytes;
1228
+ } else {
1229
+ cpu += entry.estimatedCpuBytes;
1230
+ gpu += entry.estimatedGpuBytes;
1231
+ }
1232
+ }
1233
+ return { cpu, gpu };
1234
+ }
1235
+ #evictionCandidates() {
1236
+ return [...this.#entries.values()].filter(
1237
+ (entry) => entry.state === "resident" && entry.leases.size === 0
1238
+ ).sort(
1239
+ (left, right) => left.priority - right.priority || left.lastUsed - right.lastUsed
1240
+ );
1241
+ }
1242
+ #evict(entry) {
1243
+ this.#entries.delete(entry.key);
1244
+ this.#metrics.evictions += 1;
1245
+ return this.#disposeEntry(entry);
1246
+ }
1247
+ #disposeEntry(entry) {
1248
+ const result = entry.loaded;
1249
+ entry.loaded = void 0;
1250
+ return this.#disposeLoaded(result);
1251
+ }
1252
+ #disposeLoaded(result) {
1253
+ this.#metrics.disposals += 1;
1254
+ let disposal;
1255
+ try {
1256
+ disposal = Promise.resolve(result.dispose());
1257
+ } catch (error) {
1258
+ disposal = Promise.reject(error);
1259
+ }
1260
+ const tracked = disposal.catch(() => {
1261
+ this.#metrics.disposalFailures += 1;
1262
+ }).finally(() => {
1263
+ this.#disposalTasks.delete(tracked);
1264
+ });
1265
+ this.#disposalTasks.add(tracked);
1266
+ return tracked;
1267
+ }
1268
+ #queuedCount() {
1269
+ let count = 0;
1270
+ for (const entry of this.#entries.values()) {
1271
+ if (entry.state === "queued") count += 1;
1272
+ }
1273
+ return count;
1274
+ }
1275
+ };
1276
+
546
1277
  // src/index.ts
547
1278
  var packageName = "@plasius/gpu-model-runtime";
548
1279
  var packageBootstrap = Object.freeze({
@@ -553,10 +1284,15 @@ var packageBootstrap = Object.freeze({
553
1284
  // Annotate the CommonJS export names for ESM import in node:
554
1285
  0 && (module.exports = {
555
1286
  AdapterRegistry,
1287
+ DEFAULT_MODEL_MAX_BYTES,
556
1288
  MemoryModelCache,
1289
+ ModelResidencyError,
1290
+ ModelResidencyManager,
1291
+ ModelResourceTooLargeError,
557
1292
  ModelRuntime,
558
1293
  PackageResourceResolver,
559
1294
  createModelCacheKey,
1295
+ createModelResidencyKey,
560
1296
  defaultSleep,
561
1297
  fetchResource,
562
1298
  packageBootstrap,