web-sdk-pp-detection 0.1.0 → 0.2.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.
@@ -14258,7 +14258,11 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
14258
14258
  original: { width: decoded.width, height: decoded.height }
14259
14259
  },
14260
14260
  model: this.model,
14261
- runtime: this.runtime,
14261
+ runtime: {
14262
+ ...this.runtime,
14263
+ fallbacks: this.runtime.fallbacks.map((fallback) => ({ ...fallback })),
14264
+ ...this.runtime.environment ? { environment: { ...this.runtime.environment } } : {}
14265
+ },
14262
14266
  timings: {
14263
14267
  decodeMs: decoded.decodeMs,
14264
14268
  preprocessMs,
@@ -14751,8 +14755,84 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
14751
14755
  });
14752
14756
  }
14753
14757
 
14758
+ // src/cache/coordination.ts
14759
+ var CacheCoordinator = class {
14760
+ caches = /* @__PURE__ */ new Map();
14761
+ generation = 0;
14762
+ keys = /* @__PURE__ */ new Map();
14763
+ pending = Promise.resolve();
14764
+ guard(key) {
14765
+ const generation = this.generation;
14766
+ const keyGeneration = this.keys.get(key) ?? 0;
14767
+ this.keys.set(key, keyGeneration);
14768
+ return () => generation === this.generation && keyGeneration === (this.keys.get(key) ?? 0);
14769
+ }
14770
+ run(operation) {
14771
+ const pending = this.pending.then(operation);
14772
+ this.pending = pending.catch(() => void 0);
14773
+ return pending;
14774
+ }
14775
+ clear(key, matches) {
14776
+ if (matches) {
14777
+ for (const [candidate, generation] of this.keys) {
14778
+ if (matches(candidate)) this.keys.set(candidate, generation + 1);
14779
+ }
14780
+ } else if (key === void 0) this.generation += 1;
14781
+ else this.keys.set(key, (this.keys.get(key) ?? 0) + 1);
14782
+ return this.run(async () => {
14783
+ const outcomes = await Promise.allSettled(
14784
+ [...this.caches.keys()].map(async (cache) => {
14785
+ if (matches && cache.list) {
14786
+ for (const entry of await cache.list()) {
14787
+ if (matches(entry.key)) await cache.clearCurrent(entry.key);
14788
+ }
14789
+ } else if (key === void 0) await cache.clearAll();
14790
+ else await cache.clearCurrent(key);
14791
+ })
14792
+ );
14793
+ const failure = outcomes.find((outcome) => outcome.status === "rejected");
14794
+ if (failure?.status === "rejected") throw failure.reason;
14795
+ });
14796
+ }
14797
+ unregister(cache) {
14798
+ const count = this.caches.get(cache) ?? 0;
14799
+ if (count > 1) {
14800
+ this.caches.set(cache, count - 1);
14801
+ return false;
14802
+ }
14803
+ this.caches.delete(cache);
14804
+ return true;
14805
+ }
14806
+ };
14807
+ var coordinators = /* @__PURE__ */ new WeakMap();
14808
+ function coordinateCache(cache) {
14809
+ const scope = cache.scope ?? cache;
14810
+ let coordinator = coordinators.get(scope);
14811
+ if (!coordinator) {
14812
+ coordinator = new CacheCoordinator();
14813
+ coordinators.set(scope, coordinator);
14814
+ }
14815
+ coordinator.caches.set(cache, (coordinator.caches.get(cache) ?? 0) + 1);
14816
+ return coordinator;
14817
+ }
14818
+ var databaseScopes = /* @__PURE__ */ new WeakMap();
14819
+ function databaseScope(factory, name) {
14820
+ let scopes = databaseScopes.get(factory);
14821
+ if (!scopes) {
14822
+ scopes = /* @__PURE__ */ new Map();
14823
+ databaseScopes.set(factory, scopes);
14824
+ }
14825
+ let scope = scopes.get(name);
14826
+ if (!scope) {
14827
+ scope = {};
14828
+ scopes.set(name, scope);
14829
+ }
14830
+ return scope;
14831
+ }
14832
+
14754
14833
  // src/cache/indexeddb-cache.ts
14755
14834
  var IndexedDBModelCache = class {
14835
+ scope;
14756
14836
  factory;
14757
14837
  databaseName;
14758
14838
  database;
@@ -14761,6 +14841,7 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
14761
14841
  if (!factory) throw new Error("\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301 IndexedDB");
14762
14842
  this.factory = factory;
14763
14843
  this.databaseName = options.databaseName ?? "web-sdk-pp-detection-models-v1";
14844
+ this.scope = databaseScope(factory, this.databaseName);
14764
14845
  }
14765
14846
  async get(key) {
14766
14847
  const record3 = await this.request(
@@ -14814,6 +14895,24 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
14814
14895
  (await this.database).close();
14815
14896
  this.database = void 0;
14816
14897
  }
14898
+ async list() {
14899
+ const database = await this.open();
14900
+ return await new Promise((resolve, reject) => {
14901
+ const transaction = database.transaction("models", "readonly");
14902
+ const request = transaction.objectStore("models").openCursor();
14903
+ const entries = [];
14904
+ request.onerror = () => reject(request.error ?? new Error("\u8BFB\u53D6\u7F13\u5B58\u5BB9\u91CF\u5931\u8D25"));
14905
+ request.onsuccess = () => {
14906
+ const cursor = request.result;
14907
+ if (!cursor) return;
14908
+ const record3 = cursor.value;
14909
+ entries.push({ key: record3.key, bytes: record3.size });
14910
+ cursor.continue();
14911
+ };
14912
+ transaction.oncomplete = () => resolve(entries);
14913
+ transaction.onabort = () => reject(transaction.error ?? new Error("\u7F13\u5B58\u5BB9\u91CF\u4E8B\u52A1\u4E2D\u6B62"));
14914
+ });
14915
+ }
14817
14916
  open() {
14818
14917
  if (this.database) return this.database;
14819
14918
  this.database = new Promise((resolve, reject) => {
@@ -14883,6 +14982,11 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
14883
14982
  this.entries.clear();
14884
14983
  return Promise.resolve();
14885
14984
  }
14985
+ list() {
14986
+ return Promise.resolve(
14987
+ [...this.entries].map(([key, bytes]) => ({ key, bytes: bytes.byteLength }))
14988
+ );
14989
+ }
14886
14990
  };
14887
14991
 
14888
14992
  // src/cache/model-cache.ts
@@ -14890,9 +14994,11 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
14890
14994
  constructor(memory, persistent) {
14891
14995
  this.memory = memory;
14892
14996
  this.persistent = persistent;
14997
+ this.scope = persistent?.scope ?? persistent ?? memory;
14893
14998
  }
14894
14999
  memory;
14895
15000
  persistent;
15001
+ scope;
14896
15002
  async get(key) {
14897
15003
  const memoryValue = await this.memory.get(key);
14898
15004
  if (memoryValue) return memoryValue;
@@ -14915,6 +15021,13 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
14915
15021
  estimate() {
14916
15022
  return this.persistent?.estimate() ?? this.memory.estimate();
14917
15023
  }
15024
+ async list() {
15025
+ const entries = /* @__PURE__ */ new Map();
15026
+ for (const cache of [this.memory, this.persistent]) {
15027
+ for (const entry of await cache?.list?.() ?? []) entries.set(entry.key, entry);
15028
+ }
15029
+ return [...entries.values()];
15030
+ }
14918
15031
  async close() {
14919
15032
  await this.memory.close?.();
14920
15033
  await this.persistent?.close?.();
@@ -15124,6 +15237,14 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
15124
15237
 
15125
15238
  // src/model/model-manager.ts
15126
15239
  var SDK_CACHE_NAMESPACE = "web-sdk-pp-detection:cache-v1";
15240
+ function matchesModel(key, model) {
15241
+ try {
15242
+ const parts = JSON.parse(key);
15243
+ return Array.isArray(parts) && parts[0] === SDK_CACHE_NAMESPACE && (model === void 0 || parts[1] === model.id && parts[2] === model.version);
15244
+ } catch {
15245
+ return false;
15246
+ }
15247
+ }
15127
15248
  function now2() {
15128
15249
  return globalThis.performance?.now() ?? Date.now();
15129
15250
  }
@@ -15147,6 +15268,7 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
15147
15268
  var ModelManager = class {
15148
15269
  fetcher;
15149
15270
  cache;
15271
+ coordinator;
15150
15272
  lifecycle = new AbortController();
15151
15273
  activeLoads = /* @__PURE__ */ new Set();
15152
15274
  currentKey;
@@ -15155,6 +15277,7 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
15155
15277
  constructor(options = {}) {
15156
15278
  this.fetcher = options.fetcher;
15157
15279
  this.cache = createCache(options.cache);
15280
+ this.coordinator = coordinateCache(this.cache);
15158
15281
  }
15159
15282
  cacheKey(variant3, source2, model = { id: "unknown", version: "unknown" }) {
15160
15283
  return JSON.stringify([
@@ -15193,16 +15316,24 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
15193
15316
  const variant3 = resolveModelVariant(manifest, options.variantId);
15194
15317
  const sourceKind2 = options.sourceKind ?? "auto";
15195
15318
  const sources = resolveModelSources(variant3, sourceKind2);
15319
+ const guards = new Map(
15320
+ sources.map((source2) => {
15321
+ const key = this.cacheKey(variant3, source2, manifest.model);
15322
+ return [key, this.coordinator.guard(key)];
15323
+ })
15324
+ );
15196
15325
  const failures = [];
15197
15326
  let lastError;
15198
15327
  for (const source2 of sources) {
15199
15328
  throwIfAborted4(options.signal);
15200
15329
  const asset = { model: manifest.model, variant: variant3, source: source2 };
15201
15330
  const cacheKey = this.cacheKey(variant3, source2, manifest.model);
15331
+ this.currentKey = cacheKey;
15332
+ const canMutate = guards.get(cacheKey);
15202
15333
  const cacheStarted = now2();
15203
15334
  let cached;
15204
15335
  try {
15205
- cached = await this.cache.get(cacheKey);
15336
+ cached = await this.coordinator.run(() => this.cache.get(cacheKey));
15206
15337
  } catch (error) {
15207
15338
  if (error instanceof PPDetectionError && error.code === "ABORTED") throw error;
15208
15339
  throwIfAborted4(options.signal);
@@ -15213,7 +15344,6 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
15213
15344
  const integrityStarted = now2();
15214
15345
  try {
15215
15346
  await verifyModelIntegrity(cached, source2, options.signal);
15216
- this.currentKey = cacheKey;
15217
15347
  return {
15218
15348
  bytes: cached,
15219
15349
  manifest,
@@ -15227,7 +15357,9 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
15227
15357
  } catch (error) {
15228
15358
  if (error instanceof PPDetectionError && error.code === "ABORTED") throw error;
15229
15359
  try {
15230
- await this.cache.clearCurrent(cacheKey);
15360
+ await this.coordinator.run(async () => {
15361
+ if (canMutate()) await this.cache.clearCurrent(cacheKey);
15362
+ });
15231
15363
  } catch (clearError) {
15232
15364
  if (clearError instanceof PPDetectionError && clearError.code === "ABORTED")
15233
15365
  throw clearError;
@@ -15263,11 +15395,12 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
15263
15395
  }
15264
15396
  throwIfAborted4(options.signal);
15265
15397
  try {
15266
- await this.cache.put(cacheKey, loaded.bytes);
15398
+ await this.coordinator.run(async () => {
15399
+ if (canMutate()) await this.cache.put(cacheKey, loaded.bytes);
15400
+ });
15267
15401
  } catch {
15268
15402
  }
15269
15403
  throwIfAborted4(options.signal);
15270
- this.currentKey = cacheKey;
15271
15404
  return {
15272
15405
  bytes: loaded.bytes,
15273
15406
  manifest,
@@ -15292,16 +15425,44 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
15292
15425
  estimate() {
15293
15426
  return this.getCacheEstimate();
15294
15427
  }
15295
- getCacheEstimate() {
15296
- return this.cache.estimate();
15428
+ getCacheEstimate(model) {
15429
+ return this.coordinator.run(async () => {
15430
+ if (!this.cache.list) {
15431
+ if (model)
15432
+ throw new PPDetectionError(
15433
+ "CAPABILITY_UNSUPPORTED",
15434
+ "\u81EA\u5B9A\u4E49\u7F13\u5B58\u9700\u5B9E\u73B0 list \u624D\u80FD\u6309\u6A21\u578B\u4F30\u7B97"
15435
+ );
15436
+ return this.cache.estimate();
15437
+ }
15438
+ const unique = /* @__PURE__ */ new Map();
15439
+ for (const cache of this.coordinator.caches.keys()) {
15440
+ for (const entry of await cache.list?.() ?? []) unique.set(entry.key, entry);
15441
+ }
15442
+ const entries = [...unique.values()].filter((entry) => matchesModel(entry.key, model));
15443
+ return {
15444
+ bytes: entries.reduce((sum, entry) => sum + entry.bytes, 0),
15445
+ entries: entries.length
15446
+ };
15447
+ });
15297
15448
  }
15298
- async clearCurrentModelCache() {
15449
+ async clearCurrentModelCache(model) {
15450
+ if (model) {
15451
+ if (!model.id || !model.version)
15452
+ throw new PPDetectionError("INVALID_MANIFEST", "\u7F13\u5B58\u6A21\u578B\u8EAB\u4EFD\u5FC5\u987B\u5305\u542B id \u548C version");
15453
+ if (!this.cache.list)
15454
+ throw new PPDetectionError(
15455
+ "CAPABILITY_UNSUPPORTED",
15456
+ "\u81EA\u5B9A\u4E49\u7F13\u5B58\u9700\u5B9E\u73B0 list \u624D\u80FD\u6309\u6A21\u578B\u6E05\u7406"
15457
+ );
15458
+ await this.coordinator.clear(void 0, (key) => matchesModel(key, model));
15459
+ return;
15460
+ }
15299
15461
  if (!this.currentKey) return;
15300
- await this.cache.clearCurrent(this.currentKey);
15462
+ await this.coordinator.clear(this.currentKey);
15301
15463
  }
15302
15464
  async clearAllCache() {
15303
- await this.cache.clearAll();
15304
- this.currentKey = void 0;
15465
+ await this.coordinator.clear(void 0, (key) => matchesModel(key));
15305
15466
  }
15306
15467
  async dispose() {
15307
15468
  if (this.disposePromise) return await this.disposePromise;
@@ -15310,7 +15471,7 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
15310
15471
  this.disposePromise = (async () => {
15311
15472
  await Promise.all([...this.activeLoads]);
15312
15473
  this.currentKey = void 0;
15313
- await this.cache.close?.();
15474
+ if (this.coordinator.unregister(this.cache)) await this.cache.close?.();
15314
15475
  })();
15315
15476
  await this.disposePromise;
15316
15477
  }
@@ -15323,14 +15484,15 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
15323
15484
  function mapError(error, phase) {
15324
15485
  if (error instanceof PPDetectionError) return error;
15325
15486
  const message = error instanceof Error ? error.message : String(error);
15326
- if (/abort|cancel/i.test(message))
15327
- return new PPDetectionError("ABORTED", "\u63A8\u7406\u5DF2\u53D6\u6D88", { phase }, { cause: error });
15487
+ const details = { phase, causeMessage: message };
15488
+ if (error instanceof Error && error.name === "AbortError")
15489
+ return new PPDetectionError("ABORTED", "\u63A8\u7406\u5DF2\u53D6\u6D88", details, { cause: error });
15328
15490
  if (/memory|out.of.memory|allocation/i.test(message))
15329
- return new PPDetectionError("OUT_OF_MEMORY", "\u8FD0\u884C\u65F6\u5185\u5B58\u4E0D\u8DB3", { phase }, { cause: error });
15491
+ return new PPDetectionError("OUT_OF_MEMORY", "\u8FD0\u884C\u65F6\u5185\u5B58\u4E0D\u8DB3", details, { cause: error });
15330
15492
  return new PPDetectionError(
15331
15493
  phase === "create" ? "SESSION_CREATE_FAILED" : "INFERENCE_FAILED",
15332
15494
  phase === "create" ? "\u521B\u5EFA ONNX Runtime \u4F1A\u8BDD\u5931\u8D25" : "ONNX Runtime \u63A8\u7406\u5931\u8D25",
15333
- { phase },
15495
+ details,
15334
15496
  { cause: error }
15335
15497
  );
15336
15498
  }
@@ -15368,6 +15530,7 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
15368
15530
  return {
15369
15531
  plan,
15370
15532
  sessionMs,
15533
+ runtimeVersion: ort.env.versions?.web ?? null,
15371
15534
  async run(feeds, runOptions = {}) {
15372
15535
  if (disposed) throw new PPDetectionError("DISPOSED", "\u4F1A\u8BDD\u5DF2\u91CA\u653E", { phase: "run" });
15373
15536
  if (runOptions.signal?.aborted)
@@ -15488,14 +15651,13 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
15488
15651
  function fail(code, message, details) {
15489
15652
  throw new PPDetectionError(code, message, details);
15490
15653
  }
15491
- function candidatesForBackend(requested, capabilities, allowFallback) {
15654
+ function candidatesForBackend(requested, capabilities) {
15492
15655
  if (requested === "wasm") return ["wasm"];
15493
15656
  if (requested === "webgpu")
15494
15657
  return capabilities.webgpu ? ["webgpu"] : fail("CAPABILITY_UNSUPPORTED", "\u8BF7\u6C42\u7684 webgpu \u4E0D\u53EF\u7528", { requestedBackend: requested });
15495
15658
  const available = [];
15496
15659
  if (capabilities.webgpu) available.push("webgpu");
15497
15660
  available.push("wasm");
15498
- if (!allowFallback) return available.slice(0, 1);
15499
15661
  return available;
15500
15662
  }
15501
15663
  function selectExecutionPlan(options, capabilities, manifest) {
@@ -15513,19 +15675,18 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
15513
15675
  requestedPrecision
15514
15676
  });
15515
15677
  }
15516
- const candidates = candidatesForBackend(
15517
- requestedBackend,
15518
- capabilities,
15519
- options.allowFallback === true
15520
- ).filter((backend) => variant3.backends.includes(backend));
15521
- if (candidates.length === 0) {
15678
+ const candidates = candidatesForBackend(requestedBackend, capabilities).filter(
15679
+ (backend) => variant3.backends.includes(backend)
15680
+ );
15681
+ const selectedCandidates = options.allowFallback === true ? candidates : candidates.slice(0, 1);
15682
+ if (selectedCandidates.length === 0) {
15522
15683
  fail("CAPABILITY_UNSUPPORTED", "\u6CA1\u6709\u4E0E\u6A21\u578B\u53D8\u4F53\u5339\u914D\u7684\u53EF\u7528\u540E\u7AEF", {
15523
15684
  requestedBackend,
15524
15685
  requestedPrecision,
15525
15686
  availableBackends: variant3.backends
15526
15687
  });
15527
15688
  }
15528
- const actualBackend = candidates[0];
15689
+ const actualBackend = selectedCandidates[0];
15529
15690
  return {
15530
15691
  variantId: variant3.id,
15531
15692
  requestedBackend,
@@ -15533,7 +15694,7 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
15533
15694
  requestedPrecision,
15534
15695
  actualPrecision: variant3.precision,
15535
15696
  executionMode,
15536
- candidates: candidates.map((backend) => ({
15697
+ candidates: selectedCandidates.map((backend) => ({
15537
15698
  variantId: variant3.id,
15538
15699
  backend,
15539
15700
  precision: variant3.precision,
@@ -15688,7 +15849,7 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
15688
15849
  };
15689
15850
 
15690
15851
  // src/index.ts
15691
- var CURRENT_SDK_VERSION = "0.1.0";
15852
+ var CURRENT_SDK_VERSION = "0.2.0";
15692
15853
  function probePPDetectionCapabilities(options = {}) {
15693
15854
  return probeCapabilities(options);
15694
15855
  }
@@ -15783,6 +15944,7 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
15783
15944
  return new URL("./inference.worker.js", (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('browser-global.js', document.baseURI).href));
15784
15945
  }
15785
15946
  async function createPPDetection(options = {}) {
15947
+ const loadStartedAt = now4();
15786
15948
  if (options.model === void 0 && options.manifest === void 0)
15787
15949
  throw new PPDetectionError("INVALID_MANIFEST", "\u521B\u5EFA PPDetection \u5B9E\u4F8B\u9700\u8981 manifest \u6216 model");
15788
15950
  const capabilities = probeCapabilities();
@@ -15814,9 +15976,7 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
15814
15976
  cache: options.cache === false ? false : options.cache === "memory" ? "memory" : void 0
15815
15977
  });
15816
15978
  let executor;
15817
- let activeBridge;
15818
15979
  try {
15819
- const loadStartedAt = now4();
15820
15980
  options.onProgress?.({ phase: "model", status: "start" });
15821
15981
  let modelBytes;
15822
15982
  let actualSource;
@@ -15828,10 +15988,17 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
15828
15988
  throw new PPDetectionError("MODEL_SOURCE_UNAVAILABLE", "\u6A21\u578B\u53D8\u4F53\u6CA1\u6709\u53EF\u7528\u6765\u6E90", {
15829
15989
  variantId: variant3.id
15830
15990
  });
15991
+ const integrityStartedAt = now4();
15831
15992
  await verifyModelIntegrity(memoryData, source2, options.signal);
15993
+ const integrityMs = now4() - integrityStartedAt;
15832
15994
  modelBytes = memoryData;
15833
15995
  actualSource = source2;
15834
- loadTimings = { sessionMs: 0, totalMs: now4() - loadStartedAt, integrityMs: 0 };
15996
+ loadTimings = {
15997
+ sessionMs: 0,
15998
+ totalMs: now4() - loadStartedAt,
15999
+ integrityMs,
16000
+ modelSource: "memory"
16001
+ };
15835
16002
  } else {
15836
16003
  const loaded = await modelManager.load({
15837
16004
  manifest: runtimeManifest,
@@ -15843,33 +16010,26 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
15843
16010
  modelBytes = loaded.bytes;
15844
16011
  variant3 = loaded.variant;
15845
16012
  actualSource = loaded.source;
15846
- loadTimings = { ...loaded.timings, sessionMs: 0, totalMs: now4() - loadStartedAt };
16013
+ loadTimings = {
16014
+ ...loaded.timings,
16015
+ sessionMs: 0,
16016
+ totalMs: now4() - loadStartedAt,
16017
+ modelSource: loaded.fromCache ? "cache" : "network"
16018
+ };
15847
16019
  }
15848
16020
  options.onProgress?.({ phase: "model", status: "complete" });
15849
16021
  const fallbacks = [];
15850
- let selectedPlan = plan;
15851
- let sessionMs = 0;
15852
- for (const candidate of plan.candidates) {
15853
- const candidatePlan = {
15854
- ...plan,
15855
- variantId: candidate.variantId,
15856
- actualBackend: candidate.backend,
15857
- actualPrecision: candidate.precision,
15858
- executionMode: candidate.executionMode,
15859
- candidates: [candidate]
15860
- };
16022
+ const createExecutorForPlan = async (candidatePlan) => {
15861
16023
  options.onProgress?.({ phase: "session", status: "start" });
16024
+ let bridge;
16025
+ const sessionStartedAt = now4();
15862
16026
  try {
15863
- if (candidate.executionMode === "worker") {
16027
+ if (candidatePlan.executionMode === "worker") {
15864
16028
  if (typeof Worker !== "function")
15865
16029
  throw new PPDetectionError("CAPABILITY_UNSUPPORTED", "\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301 Worker");
15866
- const worker = new Worker(workerUrl(), {
15867
- type: "module"
15868
- });
15869
- const bridge = new WorkerBridge(worker);
15870
- activeBridge = bridge;
15871
- const workerModelBytes = modelBytes.slice(0);
15872
- await bridge.load(workerModelBytes, candidatePlan, {
16030
+ const worker = new Worker(workerUrl(), { type: "module" });
16031
+ bridge = new WorkerBridge(worker);
16032
+ const metadata = await bridge.load(modelBytes.slice(0), candidatePlan, {
15873
16033
  onProgress: (event) => options.onProgress?.({
15874
16034
  phase: "session",
15875
16035
  status: event.status
@@ -15879,35 +16039,69 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
15879
16039
  numThreads: options.ort?.wasm?.numThreads
15880
16040
  }
15881
16041
  });
15882
- executor = {
15883
- run(input, signal) {
15884
- return bridge.run(
15885
- { [input.inputName]: { data: input.data, dims: input.dims } },
15886
- { signal }
15887
- );
15888
- },
15889
- dispose: () => bridge.dispose()
15890
- };
15891
- activeBridge = void 0;
15892
- } else {
15893
- const session = await createOrtSession(modelBytes, candidatePlan, {
15894
- ort: options.ort?.module,
15895
- wasmPaths: options.ort?.wasm?.paths,
15896
- numThreads: options.ort?.wasm?.numThreads
15897
- });
15898
- sessionMs = session.sessionMs;
15899
- executor = {
16042
+ runtimeVersion = typeof metadata === "object" && metadata !== null && "runtimeVersion" in metadata && typeof metadata.runtimeVersion === "string" ? metadata.runtimeVersion : null;
16043
+ const activeBridge = bridge;
16044
+ options.onProgress?.({ phase: "session", status: "complete" });
16045
+ return {
15900
16046
  run(input, signal) {
15901
- return session.run(
16047
+ return activeBridge.run(
15902
16048
  { [input.inputName]: { data: input.data, dims: input.dims } },
15903
16049
  { signal }
15904
16050
  );
15905
16051
  },
15906
- dispose: () => session.dispose()
16052
+ dispose: () => activeBridge.dispose()
15907
16053
  };
15908
16054
  }
15909
- selectedPlan = candidatePlan;
16055
+ const session = await createOrtSession(modelBytes, candidatePlan, {
16056
+ ort: options.ort?.module,
16057
+ wasmPaths: options.ort?.wasm?.paths,
16058
+ numThreads: options.ort?.wasm?.numThreads
16059
+ });
16060
+ runtimeVersion = session.runtimeVersion ?? null;
15910
16061
  options.onProgress?.({ phase: "session", status: "complete" });
16062
+ return {
16063
+ run(input, signal) {
16064
+ return session.run(
16065
+ { [input.inputName]: { data: input.data, dims: input.dims } },
16066
+ { signal }
16067
+ );
16068
+ },
16069
+ dispose: () => session.dispose()
16070
+ };
16071
+ } catch (error) {
16072
+ try {
16073
+ await bridge?.dispose();
16074
+ } catch {
16075
+ }
16076
+ if (error instanceof PPDetectionError) throw error;
16077
+ const message = error instanceof Error ? error.message : String(error);
16078
+ throw new PPDetectionError(
16079
+ "SESSION_CREATE_FAILED",
16080
+ "\u521B\u5EFA ONNX Runtime \u4F1A\u8BDD\u5931\u8D25",
16081
+ { phase: "create", causeMessage: message },
16082
+ { cause: error }
16083
+ );
16084
+ } finally {
16085
+ sessionMs += now4() - sessionStartedAt;
16086
+ }
16087
+ };
16088
+ let selectedPlan = plan;
16089
+ let sessionMs = 0;
16090
+ let runtimeVersion = null;
16091
+ let selectedCandidateIndex = -1;
16092
+ for (const [candidateIndex, candidate] of plan.candidates.entries()) {
16093
+ const candidatePlan = {
16094
+ ...plan,
16095
+ variantId: candidate.variantId,
16096
+ actualBackend: candidate.backend,
16097
+ actualPrecision: candidate.precision,
16098
+ executionMode: candidate.executionMode,
16099
+ candidates: [candidate]
16100
+ };
16101
+ try {
16102
+ executor = await createExecutorForPlan(candidatePlan);
16103
+ selectedPlan = candidatePlan;
16104
+ selectedCandidateIndex = candidateIndex;
15911
16105
  break;
15912
16106
  } catch (error) {
15913
16107
  const mapped = error instanceof PPDetectionError ? error : new PPDetectionError("SESSION_CREATE_FAILED", String(error));
@@ -15917,11 +16111,6 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
15917
16111
  } catch {
15918
16112
  }
15919
16113
  executor = void 0;
15920
- try {
15921
- await activeBridge?.dispose();
15922
- } catch {
15923
- }
15924
- activeBridge = void 0;
15925
16114
  if (!hasNext) {
15926
16115
  throw mapped;
15927
16116
  }
@@ -15939,22 +16128,75 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
15939
16128
  }
15940
16129
  }
15941
16130
  if (!executor) throw new PPDetectionError("SESSION_CREATE_FAILED", "\u65E0\u6CD5\u521B\u5EFA\u68C0\u6D4B Session");
15942
- const loadedExecutor = executor;
16131
+ const runtime = {
16132
+ runtimeVersion,
16133
+ environment: {
16134
+ userAgent: globalThis.navigator?.userAgent ?? null,
16135
+ platform: globalThis.navigator?.platform ?? null,
16136
+ capturedAt: (/* @__PURE__ */ new Date()).toISOString()
16137
+ },
16138
+ requestedBackend: options.backend ?? "auto",
16139
+ backend: selectedPlan.actualBackend,
16140
+ precision: selectedPlan.actualPrecision,
16141
+ mode: selectedPlan.executionMode,
16142
+ fallbacks,
16143
+ capabilities
16144
+ };
16145
+ let activeExecutor = executor;
16146
+ const fallbackExecutor = {
16147
+ async run(input, signal) {
16148
+ const attemptInput = runtime.mode === "worker" && options.allowFallback === true && selectedCandidateIndex < plan.candidates.length - 1 ? { ...input, data: input.data.slice() } : input;
16149
+ try {
16150
+ return await activeExecutor.run(attemptInput, signal);
16151
+ } catch (error) {
16152
+ if (options.allowFallback !== true || selectedCandidateIndex < 0 || selectedCandidateIndex >= plan.candidates.length - 1) {
16153
+ throw error;
16154
+ }
16155
+ const failedCandidate = plan.candidates[selectedCandidateIndex];
16156
+ const mapped = error instanceof PPDetectionError ? error : new PPDetectionError("INFERENCE_FAILED", String(error), {}, { cause: error });
16157
+ if (mapped.code === "ABORTED") throw mapped;
16158
+ const nextCandidate = plan.candidates[selectedCandidateIndex + 1];
16159
+ const nextPlan = {
16160
+ ...plan,
16161
+ variantId: nextCandidate.variantId,
16162
+ actualBackend: nextCandidate.backend,
16163
+ actualPrecision: nextCandidate.precision,
16164
+ executionMode: nextCandidate.executionMode,
16165
+ candidates: [nextCandidate]
16166
+ };
16167
+ await activeExecutor.dispose();
16168
+ const nextExecutor = await createExecutorForPlan(nextPlan);
16169
+ activeExecutor = nextExecutor;
16170
+ selectedCandidateIndex += 1;
16171
+ selectedPlan = nextPlan;
16172
+ runtime.backend = nextPlan.actualBackend;
16173
+ runtime.precision = nextPlan.actualPrecision;
16174
+ runtime.mode = nextPlan.executionMode;
16175
+ runtime.runtimeVersion = runtimeVersion;
16176
+ const fallback = {
16177
+ cause: mapped.cause ?? mapped,
16178
+ code: mapped.code,
16179
+ message: mapped.message,
16180
+ precision: failedCandidate.precision,
16181
+ provider: failedCandidate.backend,
16182
+ stage: "inference",
16183
+ variantId: failedCandidate.variantId
16184
+ };
16185
+ fallbacks.push(fallback);
16186
+ options.onProgress?.({ phase: "fallback", status: "complete", fallback });
16187
+ return activeExecutor.run(input, signal);
16188
+ }
16189
+ },
16190
+ dispose: () => activeExecutor.dispose()
16191
+ };
15943
16192
  loadTimings = { ...loadTimings, sessionMs, totalMs: now4() - loadStartedAt };
15944
16193
  const detector = new PPDetectionDetectorImplementation({
15945
16194
  capabilities,
15946
16195
  manifest: runtimeManifest,
15947
16196
  model: modelInfo(runtimeManifest, variant3.id, actualSource),
15948
- runtime: {
15949
- requestedBackend: options.backend ?? "auto",
15950
- backend: selectedPlan.actualBackend,
15951
- precision: selectedPlan.actualPrecision,
15952
- mode: selectedPlan.executionMode,
15953
- fallbacks,
15954
- capabilities
15955
- },
16197
+ runtime,
15956
16198
  loadTimings,
15957
- loadExecutor: () => Promise.resolve(loadedExecutor),
16199
+ loadExecutor: () => Promise.resolve(fallbackExecutor),
15958
16200
  onProgress: options.onProgress,
15959
16201
  clearCurrentModelCache: () => modelManager.clearCurrentModelCache(),
15960
16202
  clearAllCache: () => modelManager.clearAllCache(),
@@ -15962,6 +16204,7 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
15962
16204
  disposeResources: () => modelManager.dispose()
15963
16205
  });
15964
16206
  await detector.load({ signal: options.signal });
16207
+ loadTimings.totalMs = now4() - loadStartedAt;
15965
16208
  options.onProgress?.({ phase: "ready", status: "complete" });
15966
16209
  return detector;
15967
16210
  } catch (error) {
@@ -15969,10 +16212,6 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
15969
16212
  await executor?.dispose();
15970
16213
  } catch {
15971
16214
  }
15972
- try {
15973
- await activeBridge?.dispose();
15974
- } catch {
15975
- }
15976
16215
  try {
15977
16216
  await modelManager.dispose();
15978
16217
  } catch {
@@ -15982,8 +16221,11 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
15982
16221
  }
15983
16222
  async function clearModelCache() {
15984
16223
  const manager = new ModelManager();
15985
- await manager.clearAllCache();
15986
- await manager.dispose();
16224
+ try {
16225
+ await manager.clearAllCache();
16226
+ } finally {
16227
+ await manager.dispose();
16228
+ }
15987
16229
  }
15988
16230
 
15989
16231
  // src/browser-global.ts