web-sdk-pp-detection 0.3.1 → 0.3.2

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.d.ts CHANGED
@@ -138,9 +138,17 @@ interface PPDetectionProgressEvent {
138
138
  readonly status: "start" | "progress" | "complete";
139
139
  readonly loadedBytes?: number;
140
140
  readonly totalBytes?: number;
141
+ readonly attempt?: number;
142
+ readonly maxAttempts?: number;
141
143
  readonly fallback?: PPDetectionFallback;
142
144
  }
145
+ interface ModelDownloadOptions {
146
+ readonly timeoutMs?: number;
147
+ readonly idleTimeoutMs?: number;
148
+ readonly maxRetries?: number;
149
+ }
143
150
  interface CreatePPDetectionOptions {
151
+ readonly download?: ModelDownloadOptions;
144
152
  readonly allowExperimental?: boolean;
145
153
  readonly allowFallback?: boolean;
146
154
  readonly backend?: BackendPreference;
@@ -518,10 +526,13 @@ type ModelFetcher = (input: RequestInfo | URL, init?: RequestInit) => Promise<Re
518
526
  interface ModelDownloadProgress {
519
527
  readonly loadedBytes: number;
520
528
  readonly totalBytes?: number;
529
+ readonly attempt?: number;
530
+ readonly maxAttempts?: number;
521
531
  }
522
532
  interface LoadModelAssetOptions {
523
533
  readonly fetcher?: ModelFetcher;
524
534
  readonly signal?: AbortSignal;
535
+ readonly download?: ModelDownloadOptions;
525
536
  readonly onProgress?: (progress: ModelDownloadProgress) => void;
526
537
  }
527
538
  interface ModelBytes {
@@ -549,6 +560,7 @@ interface ModelCache {
549
560
  }
550
561
 
551
562
  interface ModelManagerOptions {
563
+ readonly download?: ModelDownloadOptions;
552
564
  readonly fetcher?: ModelFetcher;
553
565
  readonly cache?: "memory" | "indexeddb" | false | ModelCache;
554
566
  }
@@ -576,6 +588,7 @@ interface LoadedManagedModel {
576
588
  }
577
589
  declare class ModelManager {
578
590
  private readonly fetcher?;
591
+ private readonly download;
579
592
  private readonly cache;
580
593
  private readonly coordinator;
581
594
  private readonly lifecycle;
@@ -635,11 +648,11 @@ declare class IndexedDBModelCache implements ModelCache {
635
648
  declare global {
636
649
  var __PPDETECTION_SCRIPT_URL__: string | undefined;
637
650
  }
638
- declare const CURRENT_SDK_VERSION = "0.3.1";
651
+ declare const CURRENT_SDK_VERSION = "0.3.2";
639
652
 
640
653
  declare function probePPDetectionCapabilities(options?: CapabilityProbeOptions): DetectionCapabilities;
641
654
 
642
655
  declare function createPPDetection(options?: CreatePPDetectionOptions): Promise<PPDetectionDetectorImplementation>;
643
656
  declare function clearModelCache(): Promise<void>;
644
657
 
645
- export { type Backend, type BackendPreference, CURRENT_SDK_VERSION, type CacheEstimate, type CapabilityProbeOptions, type CreateOrtSessionOptions, type CreatePPDetectionOptions, type DecodedImage, type DetectOptions, type Detection, type DetectionBox, type DetectionCapabilities, type DetectionManifest, type DetectionModelVariant, type DetectionPoint, type DetectionPostprocessing, type DetectionPreprocessing, type DetectionTimings, type ExecutionCandidate, type ExecutionMode, type ExecutionPlan, type ImageRaster, type ImageSource, IndexedDBModelCache, type IndexedDBModelCacheOptions, type LoadManagedModelOptions, type LoadModelAssetOptions, type LoadedManagedModel, MemoryModelCache, type ModelBackend, type ModelBytes, type ModelCache, type ModelDownloadProgress, type ModelFetcher, type ModelInfo, ModelManager, type ModelManagerOptions, type ModelManifest, type ModelManifestMetadata, type ModelManifestSource, type ModelManifestVariant, type ModelPrecision, type ModelSource, type ModelSourceFailure, type ModelSourceKind, type ModelSourceSelection, type ModelVariant, type OrtModule, type OrtSessionHandle, type PPDetectionDetector, PPDetectionError, type PPDetectionErrorCode, type PPDetectionFallback, type PPDetectionLoadTimings, type PPDetectionModel, type PPDetectionModelInfo, type PPDetectionModelSourceInfo, type PPDetectionProgressEvent, type PPDetectionResult, type PPDetectionRuntimeInfo, type Precision, type ResolveModelAssetSelection, type ResolvedModelAsset, type RuntimeDetectionManifest, type RuntimeInfo, type SelectExecutionOptions, type TensorContract, type TimingBreakdown, type WorkerOrtOptions, type WorkerRequest, type WorkerResponse, adaptModelManifest, clearModelCache, createOrtSession, createPPDetection, loadModelAsset, parseDetectionManifest, parseModelManifest, probeCapabilities, probePPDetectionCapabilities, resolveModelAsset, selectExecutionPlan };
658
+ export { type Backend, type BackendPreference, CURRENT_SDK_VERSION, type CacheEstimate, type CapabilityProbeOptions, type CreateOrtSessionOptions, type CreatePPDetectionOptions, type DecodedImage, type DetectOptions, type Detection, type DetectionBox, type DetectionCapabilities, type DetectionManifest, type DetectionModelVariant, type DetectionPoint, type DetectionPostprocessing, type DetectionPreprocessing, type DetectionTimings, type ExecutionCandidate, type ExecutionMode, type ExecutionPlan, type ImageRaster, type ImageSource, IndexedDBModelCache, type IndexedDBModelCacheOptions, type LoadManagedModelOptions, type LoadModelAssetOptions, type LoadedManagedModel, MemoryModelCache, type ModelBackend, type ModelBytes, type ModelCache, type ModelDownloadOptions, type ModelDownloadProgress, type ModelFetcher, type ModelInfo, ModelManager, type ModelManagerOptions, type ModelManifest, type ModelManifestMetadata, type ModelManifestSource, type ModelManifestVariant, type ModelPrecision, type ModelSource, type ModelSourceFailure, type ModelSourceKind, type ModelSourceSelection, type ModelVariant, type OrtModule, type OrtSessionHandle, type PPDetectionDetector, PPDetectionError, type PPDetectionErrorCode, type PPDetectionFallback, type PPDetectionLoadTimings, type PPDetectionModel, type PPDetectionModelInfo, type PPDetectionModelSourceInfo, type PPDetectionProgressEvent, type PPDetectionResult, type PPDetectionRuntimeInfo, type Precision, type ResolveModelAssetSelection, type ResolvedModelAsset, type RuntimeDetectionManifest, type RuntimeInfo, type SelectExecutionOptions, type TensorContract, type TimingBreakdown, type WorkerOrtOptions, type WorkerRequest, type WorkerResponse, adaptModelManifest, clearModelCache, createOrtSession, createPPDetection, loadModelAsset, parseDetectionManifest, parseModelManifest, probeCapabilities, probePPDetectionCapabilities, resolveModelAsset, selectExecutionPlan };
package/dist/index.js CHANGED
@@ -1474,11 +1474,36 @@ async function verifyModelIntegrity(bytes, expected, signal) {
1474
1474
  }
1475
1475
 
1476
1476
  // src/model/download.ts
1477
+ var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504]);
1478
+ var RetryableDownloadError = class extends PPDetectionError {
1479
+ constructor(message, details = {}, cause) {
1480
+ super("MODEL_DOWNLOAD_FAILED", message, details, { cause });
1481
+ }
1482
+ };
1483
+ function resolveDownloadOptions(options = {}) {
1484
+ if (options === null || typeof options !== "object")
1485
+ throw new PPDetectionError("INVALID_INPUT", "\u4E0B\u8F7D\u914D\u7F6E\u5FC5\u987B\u662F\u5BF9\u8C61");
1486
+ const resolved = {
1487
+ timeoutMs: options.timeoutMs === void 0 ? 18e4 : options.timeoutMs,
1488
+ idleTimeoutMs: options.idleTimeoutMs === void 0 ? 3e4 : options.idleTimeoutMs,
1489
+ maxRetries: options.maxRetries === void 0 ? 2 : options.maxRetries
1490
+ };
1491
+ for (const [key, value] of Object.entries(resolved)) {
1492
+ const maximum = key === "maxRetries" ? 5 : 2147483647;
1493
+ if (!Number.isSafeInteger(value) || value < 0 || value > maximum)
1494
+ throw new PPDetectionError("INVALID_INPUT", "\u4E0B\u8F7D\u53C2\u6570\u5FC5\u987B\u4E3A\u5141\u8BB8\u8303\u56F4\u5185\u7684\u5B89\u5168\u6574\u6570", {
1495
+ parameter: key,
1496
+ value,
1497
+ maximum
1498
+ });
1499
+ }
1500
+ return resolved;
1501
+ }
1477
1502
  function now() {
1478
1503
  return globalThis.performance?.now() ?? Date.now();
1479
1504
  }
1480
1505
  function aborted(error, signal) {
1481
- return signal?.aborted === true || error instanceof DOMException && error.name === "AbortError" || error instanceof Error && error.name === "AbortError";
1506
+ return signal?.aborted === true || error instanceof Error && error.name === "AbortError";
1482
1507
  }
1483
1508
  function throwIfAborted3(signal) {
1484
1509
  if (signal?.aborted) throw new PPDetectionError("ABORTED", "\u6A21\u578B\u4E0B\u8F7D\u5DF2\u53D6\u6D88");
@@ -1506,106 +1531,206 @@ function validatePartialResponse(response, expectedBytes) {
1506
1531
  });
1507
1532
  }
1508
1533
  }
1509
- async function readResponse(response, expectedBytes, signal, onProgress) {
1510
- const totalBytes = contentLength(response, expectedBytes);
1511
- if (!response.body) {
1512
- const bytes2 = await response.arrayBuffer();
1513
- throwIfAborted3(signal);
1514
- onProgress?.({ loadedBytes: bytes2.byteLength, totalBytes });
1515
- return bytes2;
1534
+ function cancelQuietly(cancel) {
1535
+ try {
1536
+ void Promise.resolve(cancel()).catch(() => void 0);
1537
+ } catch {
1516
1538
  }
1517
- const reader = response.body.getReader();
1518
- const chunks = [];
1519
- let loadedBytes = 0;
1539
+ }
1540
+ async function downloadAttempt(asset, fetcher, options, policy, attempt) {
1541
+ const controller = new AbortController();
1542
+ let response;
1543
+ let reader;
1544
+ let active = true;
1545
+ let cancelledBody = false;
1546
+ let stopError;
1547
+ let totalTimer;
1548
+ let idleTimer;
1549
+ let rejectStop;
1550
+ const stopped = new Promise((_resolve, reject) => {
1551
+ rejectStop = reject;
1552
+ });
1553
+ void stopped.catch(() => void 0);
1554
+ const cancelBody = (reason) => {
1555
+ if (cancelledBody || !reader && !response?.body) return;
1556
+ cancelledBody = true;
1557
+ if (reader) cancelQuietly(() => reader.cancel(reason));
1558
+ else cancelQuietly(() => response.body.cancel(reason));
1559
+ };
1560
+ const stop = (error) => {
1561
+ if (!active || stopError) return;
1562
+ stopError = error;
1563
+ rejectStop(error);
1564
+ controller.abort();
1565
+ cancelBody(error);
1566
+ };
1567
+ const abort = () => stop(new PPDetectionError("ABORTED", "\u6A21\u578B\u4E0B\u8F7D\u5DF2\u53D6\u6D88"));
1568
+ const renewIdle = () => {
1569
+ clearTimeout(idleTimer);
1570
+ if (policy.idleTimeoutMs > 0)
1571
+ idleTimer = setTimeout(
1572
+ () => stop(
1573
+ new RetryableDownloadError("\u6A21\u578B\u4E0B\u8F7D\u957F\u65F6\u95F4\u6CA1\u6709\u65B0\u589E\u5B57\u8282", {
1574
+ idleTimeoutMs: policy.idleTimeoutMs
1575
+ })
1576
+ ),
1577
+ policy.idleTimeoutMs
1578
+ );
1579
+ };
1580
+ const checkActive = () => {
1581
+ if (stopError) throw stopError;
1582
+ throwIfAborted3(options.signal);
1583
+ };
1584
+ const network = async (operation) => {
1585
+ try {
1586
+ checkActive();
1587
+ const value = await Promise.race([operation(), stopped]);
1588
+ checkActive();
1589
+ return value;
1590
+ } catch (error) {
1591
+ if (stopError) throw stopError;
1592
+ if (aborted(error, options.signal))
1593
+ throw new PPDetectionError("ABORTED", "\u6A21\u578B\u4E0B\u8F7D\u5DF2\u53D6\u6D88", {}, { cause: error });
1594
+ if (error instanceof PPDetectionError) throw error;
1595
+ throw new RetryableDownloadError(
1596
+ "\u6A21\u578B\u4E0B\u8F7D\u8BF7\u6C42\u6216\u54CD\u5E94\u8BFB\u53D6\u5931\u8D25",
1597
+ { sourceKind: asset.source.kind, downloadUrl: asset.source.downloadUrl },
1598
+ error
1599
+ );
1600
+ }
1601
+ };
1602
+ const report = (loadedBytes) => {
1603
+ checkActive();
1604
+ options.onProgress?.({
1605
+ loadedBytes,
1606
+ totalBytes: asset.source.bytes,
1607
+ attempt,
1608
+ maxAttempts: policy.maxRetries + 1
1609
+ });
1610
+ checkActive();
1611
+ };
1612
+ options.signal?.addEventListener("abort", abort, { once: true });
1520
1613
  try {
1614
+ throwIfAborted3(options.signal);
1615
+ if (policy.timeoutMs > 0)
1616
+ totalTimer = setTimeout(
1617
+ () => stop(
1618
+ new RetryableDownloadError("\u6A21\u578B\u4E0B\u8F7D\u8D85\u8FC7\u8BF7\u6C42\u603B\u65F6\u9650", { timeoutMs: policy.timeoutMs })
1619
+ ),
1620
+ policy.timeoutMs
1621
+ );
1622
+ renewIdle();
1623
+ report(0);
1624
+ response = await network(
1625
+ () => Promise.resolve(
1626
+ fetcher(asset.source.downloadUrl, {
1627
+ signal: controller.signal,
1628
+ ...attempt > 1 ? { cache: "reload" } : {}
1629
+ })
1630
+ ).then((value) => {
1631
+ response = value;
1632
+ if (!active || stopError) cancelBody(stopError);
1633
+ return value;
1634
+ })
1635
+ );
1636
+ if (!response.ok) {
1637
+ const details = {
1638
+ sourceKind: asset.source.kind,
1639
+ status: response.status,
1640
+ statusText: response.statusText
1641
+ };
1642
+ if (RETRYABLE_STATUSES.has(response.status))
1643
+ throw new RetryableDownloadError("\u6A21\u578B\u4E0B\u8F7D\u8FD4\u56DE\u53EF\u91CD\u8BD5\u72B6\u6001", details);
1644
+ throw new PPDetectionError("MODEL_DOWNLOAD_FAILED", "\u6A21\u578B\u4E0B\u8F7D\u8FD4\u56DE\u975E\u6210\u529F\u72B6\u6001", details);
1645
+ }
1646
+ validatePartialResponse(response, asset.source.bytes);
1647
+ contentLength(response, asset.source.bytes);
1648
+ if (!response.body) {
1649
+ const bytes2 = await network(() => response.arrayBuffer());
1650
+ report(bytes2.byteLength);
1651
+ return bytes2;
1652
+ }
1653
+ reader = response.body.getReader();
1654
+ const chunks = [];
1655
+ let loadedBytes = 0;
1521
1656
  while (true) {
1522
- throwIfAborted3(signal);
1523
- const chunk = await reader.read();
1657
+ const chunk = await network(() => reader.read());
1524
1658
  if (chunk.done) break;
1659
+ if (chunk.value.byteLength === 0) continue;
1525
1660
  loadedBytes += chunk.value.byteLength;
1526
- if (loadedBytes > expectedBytes) {
1661
+ if (loadedBytes > asset.source.bytes)
1527
1662
  throw new PPDetectionError("MODEL_INTEGRITY_FAILED", "\u6A21\u578B\u54CD\u5E94\u8D85\u8FC7\u6E05\u5355\u58F0\u660E\u5927\u5C0F", {
1528
- expectedBytes,
1663
+ expectedBytes: asset.source.bytes,
1529
1664
  loadedBytes
1530
1665
  });
1531
- }
1532
1666
  chunks.push(chunk.value);
1533
- onProgress?.({ loadedBytes, totalBytes });
1667
+ renewIdle();
1668
+ report(loadedBytes);
1669
+ }
1670
+ const bytes = new Uint8Array(loadedBytes);
1671
+ let offset = 0;
1672
+ for (const chunk of chunks) {
1673
+ bytes.set(chunk, offset);
1674
+ offset += chunk.byteLength;
1534
1675
  }
1676
+ report(loadedBytes);
1677
+ return bytes.buffer;
1535
1678
  } catch (error) {
1679
+ controller.abort();
1680
+ cancelBody(error);
1681
+ throw error;
1682
+ } finally {
1683
+ active = false;
1684
+ clearTimeout(totalTimer);
1685
+ clearTimeout(idleTimer);
1686
+ options.signal?.removeEventListener("abort", abort);
1536
1687
  try {
1537
- await reader.cancel(error);
1688
+ reader?.releaseLock();
1538
1689
  } catch {
1539
1690
  }
1540
- throw error;
1541
- } finally {
1542
- reader.releaseLock();
1543
- }
1544
- const bytes = new Uint8Array(loadedBytes);
1545
- let offset = 0;
1546
- for (const chunk of chunks) {
1547
- bytes.set(chunk, offset);
1548
- offset += chunk.byteLength;
1549
1691
  }
1550
- onProgress?.({ loadedBytes, totalBytes });
1551
- return bytes.buffer;
1692
+ }
1693
+ function waitForRetry(delayMs, signal) {
1694
+ throwIfAborted3(signal);
1695
+ return new Promise((resolve, reject) => {
1696
+ const abort = () => {
1697
+ clearTimeout(timer);
1698
+ signal?.removeEventListener("abort", abort);
1699
+ reject(new PPDetectionError("ABORTED", "\u6A21\u578B\u4E0B\u8F7D\u5DF2\u53D6\u6D88"));
1700
+ };
1701
+ const timer = setTimeout(() => {
1702
+ signal?.removeEventListener("abort", abort);
1703
+ resolve();
1704
+ }, delayMs);
1705
+ signal?.addEventListener("abort", abort, { once: true });
1706
+ if (signal?.aborted) abort();
1707
+ });
1552
1708
  }
1553
1709
  async function loadModelAsset(asset, options = {}) {
1554
1710
  throwIfAborted3(options.signal);
1711
+ const policy = resolveDownloadOptions(options.download);
1555
1712
  const fetcher = options.fetcher ?? globalThis.fetch?.bind(globalThis);
1556
1713
  if (!fetcher)
1557
1714
  throw new PPDetectionError("MODEL_DOWNLOAD_FAILED", "\u5F53\u524D\u73AF\u5883\u6CA1\u6709 fetch API", {
1558
1715
  sourceKind: asset.source.kind
1559
1716
  });
1560
1717
  const downloadStarted = now();
1561
- let response;
1562
- try {
1563
- response = await fetcher(asset.source.downloadUrl, { signal: options.signal });
1564
- } catch (error) {
1565
- if (aborted(error, options.signal))
1566
- throw new PPDetectionError("ABORTED", "\u6A21\u578B\u4E0B\u8F7D\u5DF2\u53D6\u6D88", {}, { cause: error });
1567
- throw new PPDetectionError(
1568
- "MODEL_DOWNLOAD_FAILED",
1569
- "\u6A21\u578B\u4E0B\u8F7D\u8BF7\u6C42\u5931\u8D25",
1570
- {
1571
- sourceKind: asset.source.kind,
1572
- downloadUrl: asset.source.downloadUrl
1573
- },
1574
- { cause: error }
1575
- );
1576
- }
1577
- if (!response.ok) {
1578
- throw new PPDetectionError("MODEL_DOWNLOAD_FAILED", "\u6A21\u578B\u4E0B\u8F7D\u8FD4\u56DE\u975E\u6210\u529F\u72B6\u6001", {
1579
- sourceKind: asset.source.kind,
1580
- status: response.status,
1581
- statusText: response.statusText
1582
- });
1583
- }
1584
- validatePartialResponse(response, asset.source.bytes);
1585
1718
  let bytes;
1586
- try {
1587
- bytes = await readResponse(response, asset.source.bytes, options.signal, options.onProgress);
1588
- } catch (error) {
1589
- if (error instanceof PPDetectionError) throw error;
1590
- if (aborted(error, options.signal))
1591
- throw new PPDetectionError("ABORTED", "\u6A21\u578B\u4E0B\u8F7D\u5DF2\u53D6\u6D88", {}, { cause: error });
1592
- throw new PPDetectionError(
1593
- "MODEL_DOWNLOAD_FAILED",
1594
- "\u8BFB\u53D6\u6A21\u578B\u54CD\u5E94\u5931\u8D25",
1595
- { sourceKind: asset.source.kind },
1596
- { cause: error }
1597
- );
1719
+ for (let attempt = 1; ; attempt++) {
1720
+ throwIfAborted3(options.signal);
1721
+ try {
1722
+ bytes = await downloadAttempt(asset, fetcher, options, policy, attempt);
1723
+ break;
1724
+ } catch (error) {
1725
+ throwIfAborted3(options.signal);
1726
+ if (!(error instanceof RetryableDownloadError) || attempt > policy.maxRetries) throw error;
1727
+ await waitForRetry(Math.min(500 * 2 ** (attempt - 1), 4e3), options.signal);
1728
+ }
1598
1729
  }
1599
1730
  const modelDownloadMs = now() - downloadStarted;
1600
1731
  const integrityStarted = now();
1601
1732
  await verifyModelIntegrity(bytes, asset.source, options.signal);
1602
- return {
1603
- bytes,
1604
- timings: {
1605
- modelDownloadMs,
1606
- integrityMs: now() - integrityStarted
1607
- }
1608
- };
1733
+ return { bytes, timings: { modelDownloadMs, integrityMs: now() - integrityStarted } };
1609
1734
  }
1610
1735
 
1611
1736
  // src/model/source-resolver.ts
@@ -1672,6 +1797,7 @@ function throwIfAborted4(signal) {
1672
1797
  }
1673
1798
  var ModelManager = class {
1674
1799
  fetcher;
1800
+ download;
1675
1801
  cache;
1676
1802
  coordinator;
1677
1803
  lifecycle = new AbortController();
@@ -1680,6 +1806,7 @@ var ModelManager = class {
1680
1806
  disposed = false;
1681
1807
  disposePromise;
1682
1808
  constructor(options = {}) {
1809
+ this.download = resolveDownloadOptions(options.download);
1683
1810
  this.fetcher = options.fetcher;
1684
1811
  this.cache = createCache(options.cache);
1685
1812
  this.coordinator = coordinateCache(this.cache);
@@ -1776,6 +1903,7 @@ var ModelManager = class {
1776
1903
  try {
1777
1904
  loaded = await loadModelAsset(asset, {
1778
1905
  fetcher: this.fetcher,
1906
+ download: this.download,
1779
1907
  signal: options.signal,
1780
1908
  onProgress: options.onProgress
1781
1909
  });
@@ -2260,7 +2388,7 @@ var WorkerBridge = class {
2260
2388
  };
2261
2389
 
2262
2390
  // src/index.ts
2263
- var CURRENT_SDK_VERSION = "0.3.1";
2391
+ var CURRENT_SDK_VERSION = "0.3.2";
2264
2392
  function probePPDetectionCapabilities(options = {}) {
2265
2393
  return probeCapabilities(options);
2266
2394
  }
@@ -2385,6 +2513,7 @@ async function createPPDetection(options = {}) {
2385
2513
  simpleManifest(runtimeManifest)
2386
2514
  );
2387
2515
  const modelManager = new ModelManager({
2516
+ download: options.download,
2388
2517
  cache: options.cache === false ? false : options.cache === "memory" ? "memory" : void 0
2389
2518
  });
2390
2519
  let executor;