web-sdk-pp-detection 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -8
- package/dist/browser-global.js +227 -29
- package/dist/browser-global.js.map +1 -1
- package/dist/index.d.ts +31 -3
- package/dist/index.js +227 -29
- package/dist/index.js.map +1 -1
- package/dist/inference.worker.js +10 -2
- package/dist/inference.worker.js.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -4,13 +4,15 @@
|
|
|
4
4
|
|
|
5
5
|
基于 ONNX Runtime Web 的浏览器端 PP-Detection 目标检测 SDK,支持 PC、移动端与各类 H5 页面。
|
|
6
6
|
|
|
7
|
+
当前 SDK 版本为 **0.3.0**,完整变更见[发布说明](https://github.com/chenmohan123/web-sdk-PP-Detection/blob/main/docs/zh-CN/release-0.3.0.md)。
|
|
8
|
+
|
|
7
9
|
## 安装
|
|
8
10
|
|
|
9
11
|
```bash
|
|
10
|
-
pnpm add web-sdk-pp-detection
|
|
12
|
+
pnpm add web-sdk-pp-detection@0.3.0
|
|
11
13
|
```
|
|
12
14
|
|
|
13
|
-
也可以使用 `npm install web-sdk-pp-detection`。
|
|
15
|
+
也可以使用 `npm install web-sdk-pp-detection@0.3.0`。
|
|
14
16
|
|
|
15
17
|
## 快速开始
|
|
16
18
|
|
|
@@ -35,9 +37,11 @@ await detector.dispose();
|
|
|
35
37
|
|
|
36
38
|
`classThresholds` 按 manifest 标签名称覆盖目标检测置信度过滤阈值,未配置的类别回退到全局 `threshold`。未知类别名称或超出 `0` 到 `1` 的值会被拒绝。
|
|
37
39
|
|
|
38
|
-
|
|
40
|
+
工厂必须显式传入 `model` 或 `manifest`,两者均缺省时抛出 `INVALID_MANIFEST`;同时传入时优先使用 `model`。仓库提供 PicoDet 1.0.1 FP32 stable manifest,npm 包不包含 ONNX 模型本体。自定义模型也应传入经过验证的 runtime manifest 或清单对象。
|
|
41
|
+
|
|
42
|
+
仓库模型 manifest 默认来源为 Hugging Face;在线 Demo 的来源选择默认设为 ModelScope,两者分别属于模型清单和 Demo 配置。
|
|
39
43
|
|
|
40
|
-
模型初始化耗时可以通过 `detector.loadTimings` 查看。`totalMs` 是初始化总耗时,同时提供 `modelDownloadMs`(网络下载)、`modelCacheReadMs`(缓存读取)、`integrityMs`(SHA-256 完整性校验)和 `sessionMs`(ONNX Runtime Session
|
|
44
|
+
模型初始化耗时可以通过 `detector.loadTimings` 查看。`totalMs` 是初始化总耗时,同时提供 `modelDownloadMs`(网络下载)、`modelCacheReadMs`(缓存读取)、`integrityMs`(SHA-256 完整性校验)和 `sessionMs`(ONNX Runtime Session 创建)。从 0.2.0 起,`loadTimings.modelSource` 区分 `network`、`cache`、`memory`,`runtime.runtimeVersion/environment` 记录实际 ORT 版本和环境快照。初始化总耗时包含 manifest 获取,详细语义见[性能文档](https://github.com/chenmohan123/web-sdk-PP-Detection/blob/main/docs/zh-CN/performance.md)。
|
|
41
45
|
|
|
42
46
|
## 运行后端与精度
|
|
43
47
|
|
|
@@ -61,6 +65,8 @@ const detector = await createPPDetection({
|
|
|
61
65
|
|
|
62
66
|
模型加载进度通过 `onProgress` 回调提供。`phase: "model"`、`status: "progress"` 事件中的 `loadedBytes` 和可选的 `totalBytes` 仅表示网络下载字节,不代表完整初始化百分比,也不包含完整性校验或 Session 创建。响应没有 `Content-Length` 时 `totalBytes` 可能缺失,缓存、内存或自定义二进制模型也可能不产生字节进度。可捕获结构化的 `PPDetectionError` 并读取 `code` 与 `details`。SDK 支持模型缓存;可以通过 detector 的缓存方法查询或清理。使用结束后必须调用 `dispose()` 释放 Worker、ONNX Runtime session 与 GPU/CPU 资源。
|
|
63
67
|
|
|
68
|
+
从 0.2.0 起,无活动检测器时可使用 `ModelManager.getCacheEstimate({ id, version })`、`clearCurrentModelCache({ id, version })` 统计或清理同一模型的全部变体/来源。可选的 `ModelCache.scope/list()` 支持自定义缓存协调和模型枚举;缺少 `list()` 的自定义缓存拒绝按模型操作,不扩大清理范围。同一 JavaScript 执行环境内,清理前启动的迟到下载不会重新填充缓存;跨标签页/Worker 的并发协调不在保证范围内。完整契约见 [API](https://github.com/chenmohan123/web-sdk-PP-Detection/blob/main/docs/zh-CN/api.md)。
|
|
69
|
+
|
|
64
70
|
## 微信环境
|
|
65
71
|
|
|
66
72
|
支持微信公众号页面以及微信内嵌浏览器中的 H5/WebView 集成。当前不宣称支持 native Mini Program 原生小程序直接推理;原生小程序需要通过 WebView 承载 H5 页面或使用服务端推理。
|
|
@@ -76,13 +82,15 @@ const detector = await createPPDetection({
|
|
|
76
82
|
|
|
77
83
|
A browser-first PP-Detection object detection SDK powered by ONNX Runtime Web for desktop, mobile, and H5 pages.
|
|
78
84
|
|
|
85
|
+
The current SDK version is **0.3.0**. See the [release notes](https://github.com/chenmohan123/web-sdk-PP-Detection/blob/main/docs/en/release-0.3.0.md) for the complete changes.
|
|
86
|
+
|
|
79
87
|
### Installation
|
|
80
88
|
|
|
81
89
|
```bash
|
|
82
|
-
pnpm add web-sdk-pp-detection
|
|
90
|
+
pnpm add web-sdk-pp-detection@0.3.0
|
|
83
91
|
```
|
|
84
92
|
|
|
85
|
-
`npm install web-sdk-pp-detection` is also supported.
|
|
93
|
+
`npm install web-sdk-pp-detection@0.3.0` is also supported.
|
|
86
94
|
|
|
87
95
|
### Quick start
|
|
88
96
|
|
|
@@ -107,9 +115,11 @@ await detector.dispose();
|
|
|
107
115
|
|
|
108
116
|
`classThresholds` overrides object-detection confidence filtering for matching manifest label names and falls back to the global `threshold` for unspecified classes. Unknown class names and values outside `0` through `1` are rejected.
|
|
109
117
|
|
|
110
|
-
The
|
|
118
|
+
The factory requires an explicit `model` or `manifest`; omitting both throws `INVALID_MANIFEST`, and `model` takes precedence when both are supplied. The repository provides the PicoDet 1.0.1 FP32 stable manifest; the npm package contains no ONNX model binary. Custom models also require a verified runtime or custom manifest.
|
|
111
119
|
|
|
112
|
-
|
|
120
|
+
The repository model manifest defaults to Hugging Face. The live Demo independently configures its source selector to default to ModelScope.
|
|
121
|
+
|
|
122
|
+
Detailed initialization timings are available through `detector.loadTimings`. `totalMs` is the full initialization duration. The additive fields `modelDownloadMs`, `modelCacheReadMs`, `integrityMs`, and `sessionMs` separate network download, cache reads, SHA-256 verification, and Session creation. From 0.2.0, `loadTimings.modelSource` distinguishes `network`, `cache`, and `memory`, while `runtime.runtimeVersion/environment` capture the actual ORT version and environment. Initialization totals include manifest retrieval; see [performance](https://github.com/chenmohan123/web-sdk-PP-Detection/blob/main/docs/en/performance.md) for the full semantics.
|
|
113
123
|
|
|
114
124
|
### Backend and precision
|
|
115
125
|
|
|
@@ -133,10 +143,16 @@ const detector = await createPPDetection({
|
|
|
133
143
|
|
|
134
144
|
Use `onProgress` for model loading progress. On `phase: "model"`, `status: "progress"` events, `loadedBytes` and the optional `totalBytes` describe network-transfer bytes only; they are not an overall initialization percentage and exclude integrity verification and Session creation. `totalBytes` can be absent without a `Content-Length` response header, while cache, memory, or custom binary model sources may emit no byte progress. Structured failures are exposed as `PPDetectionError` with `code` and `details`. Model cache entries can be listed or cleared through the detector. Always call `dispose()` when finished to release the Worker, ONNX Runtime session, and GPU/CPU resources.
|
|
135
145
|
|
|
146
|
+
From 0.2.0, without an active detector, `ModelManager.getCacheEstimate({ id, version })` and `clearCurrentModelCache({ id, version })` inspect or clear all variants/sources of the model. Optional `ModelCache.scope/list()` support custom cache coordination and model enumeration; a custom cache without `list()` rejects identity-based operations without broader deletion. Within the same JavaScript execution environment, downloads started before clearing cannot repopulate caches; coordination across tabs/Workers is outside this guarantee. See the [API](https://github.com/chenmohan123/web-sdk-PP-Detection/blob/main/docs/en/api.md).
|
|
147
|
+
|
|
136
148
|
### WeChat environments
|
|
137
149
|
|
|
138
150
|
WeChat official-account pages and other H5/WebView integrations are supported. Native Mini Program inference is not claimed; a native Mini Program should host the H5 experience in a WebView or use server-side inference.
|
|
139
151
|
|
|
152
|
+
### PP-YOLOE 稳定模型(0.3.0 起)
|
|
153
|
+
|
|
154
|
+
0.3.0 配套提供 PP-YOLOE+ S 640 FP32 `0.1.0 stable` 清单,模型从 ModelScope 或 Hugging Face 的固定 revision 下载,npm 包不包含权重。见[模型接入](https://github.com/chenmohan123/web-sdk-PP-Detection/blob/v0.3.0/examples/ppyoloe-candidate/README.md)。`allowExperimental` 默认关闭,仅运行 labs 候选时显式开启;blocked 仍被拒绝。Demo 默认 PicoDet,两个模型均默认 ModelScope。
|
|
155
|
+
|
|
140
156
|
### Documentation
|
|
141
157
|
|
|
142
158
|
- [Chinese documentation](https://github.com/chenmohan123/web-sdk-PP-Detection#readme)
|
package/dist/browser-global.js
CHANGED
|
@@ -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:
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
15462
|
+
await this.coordinator.clear(this.currentKey);
|
|
15301
15463
|
}
|
|
15302
15464
|
async clearAllCache() {
|
|
15303
|
-
await this.
|
|
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
|
}
|
|
@@ -15324,7 +15485,7 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
|
|
|
15324
15485
|
if (error instanceof PPDetectionError) return error;
|
|
15325
15486
|
const message = error instanceof Error ? error.message : String(error);
|
|
15326
15487
|
const details = { phase, causeMessage: message };
|
|
15327
|
-
if (
|
|
15488
|
+
if (error instanceof Error && error.name === "AbortError")
|
|
15328
15489
|
return new PPDetectionError("ABORTED", "\u63A8\u7406\u5DF2\u53D6\u6D88", details, { cause: error });
|
|
15329
15490
|
if (/memory|out.of.memory|allocation/i.test(message))
|
|
15330
15491
|
return new PPDetectionError("OUT_OF_MEMORY", "\u8FD0\u884C\u65F6\u5185\u5B58\u4E0D\u8DB3", details, { cause: error });
|
|
@@ -15369,6 +15530,7 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
|
|
|
15369
15530
|
return {
|
|
15370
15531
|
plan,
|
|
15371
15532
|
sessionMs,
|
|
15533
|
+
runtimeVersion: ort.env.versions?.web ?? null,
|
|
15372
15534
|
async run(feeds, runOptions = {}) {
|
|
15373
15535
|
if (disposed) throw new PPDetectionError("DISPOSED", "\u4F1A\u8BDD\u5DF2\u91CA\u653E", { phase: "run" });
|
|
15374
15536
|
if (runOptions.signal?.aborted)
|
|
@@ -15505,13 +15667,19 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
|
|
|
15505
15667
|
if (executionMode === "worker" && !capabilities.worker) {
|
|
15506
15668
|
fail("CAPABILITY_UNSUPPORTED", "\u8BF7\u6C42\u7684 worker \u4E0D\u53EF\u7528", { executionMode });
|
|
15507
15669
|
}
|
|
15508
|
-
const
|
|
15509
|
-
(candidate) => candidate.precision === requestedPrecision && candidate.status !== "
|
|
15510
|
-
);
|
|
15670
|
+
const matchingVariants = manifest.variants?.filter(
|
|
15671
|
+
(candidate) => candidate.precision === requestedPrecision && candidate.status !== "blocked"
|
|
15672
|
+
) ?? [];
|
|
15673
|
+
const variant3 = matchingVariants.find((candidate) => candidate.status !== "labs") ?? (options.allowExperimental === true ? matchingVariants.find((candidate) => candidate.status === "labs") : void 0);
|
|
15511
15674
|
if (!variant3) {
|
|
15512
|
-
fail(
|
|
15513
|
-
|
|
15514
|
-
|
|
15675
|
+
fail(
|
|
15676
|
+
"MODEL_INCOMPATIBLE",
|
|
15677
|
+
options.allowExperimental === true ? `manifest \u6CA1\u6709\u53EF\u7528\u7684 ${requestedPrecision} \u53D8\u4F53` : `manifest \u6CA1\u6709\u53EF\u7528\u7684 ${requestedPrecision} \u7A33\u5B9A\u53D8\u4F53`,
|
|
15678
|
+
{
|
|
15679
|
+
requestedPrecision,
|
|
15680
|
+
allowExperimental: options.allowExperimental === true
|
|
15681
|
+
}
|
|
15682
|
+
);
|
|
15515
15683
|
}
|
|
15516
15684
|
const candidates = candidatesForBackend(requestedBackend, capabilities).filter(
|
|
15517
15685
|
(backend) => variant3.backends.includes(backend)
|
|
@@ -15687,7 +15855,7 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
|
|
|
15687
15855
|
};
|
|
15688
15856
|
|
|
15689
15857
|
// src/index.ts
|
|
15690
|
-
var CURRENT_SDK_VERSION = "0.
|
|
15858
|
+
var CURRENT_SDK_VERSION = "0.3.0";
|
|
15691
15859
|
function probePPDetectionCapabilities(options = {}) {
|
|
15692
15860
|
return probeCapabilities(options);
|
|
15693
15861
|
}
|
|
@@ -15782,6 +15950,7 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
|
|
|
15782
15950
|
return new URL("./inference.worker.js", (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('browser-global.js', document.baseURI).href));
|
|
15783
15951
|
}
|
|
15784
15952
|
async function createPPDetection(options = {}) {
|
|
15953
|
+
const loadStartedAt = now4();
|
|
15785
15954
|
if (options.model === void 0 && options.manifest === void 0)
|
|
15786
15955
|
throw new PPDetectionError("INVALID_MANIFEST", "\u521B\u5EFA PPDetection \u5B9E\u4F8B\u9700\u8981 manifest \u6216 model");
|
|
15787
15956
|
const capabilities = probeCapabilities();
|
|
@@ -15801,6 +15970,7 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
|
|
|
15801
15970
|
options.onProgress?.({ phase: "manifest", status: "complete" });
|
|
15802
15971
|
const plan = selectExecutionPlan(
|
|
15803
15972
|
{
|
|
15973
|
+
allowExperimental: options.allowExperimental,
|
|
15804
15974
|
backend: options.backend,
|
|
15805
15975
|
precision: options.precision === "auto" ? void 0 : options.precision,
|
|
15806
15976
|
executionMode: options.executionMode,
|
|
@@ -15814,7 +15984,6 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
|
|
|
15814
15984
|
});
|
|
15815
15985
|
let executor;
|
|
15816
15986
|
try {
|
|
15817
|
-
const loadStartedAt = now4();
|
|
15818
15987
|
options.onProgress?.({ phase: "model", status: "start" });
|
|
15819
15988
|
let modelBytes;
|
|
15820
15989
|
let actualSource;
|
|
@@ -15826,10 +15995,17 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
|
|
|
15826
15995
|
throw new PPDetectionError("MODEL_SOURCE_UNAVAILABLE", "\u6A21\u578B\u53D8\u4F53\u6CA1\u6709\u53EF\u7528\u6765\u6E90", {
|
|
15827
15996
|
variantId: variant3.id
|
|
15828
15997
|
});
|
|
15998
|
+
const integrityStartedAt = now4();
|
|
15829
15999
|
await verifyModelIntegrity(memoryData, source2, options.signal);
|
|
16000
|
+
const integrityMs = now4() - integrityStartedAt;
|
|
15830
16001
|
modelBytes = memoryData;
|
|
15831
16002
|
actualSource = source2;
|
|
15832
|
-
loadTimings = {
|
|
16003
|
+
loadTimings = {
|
|
16004
|
+
sessionMs: 0,
|
|
16005
|
+
totalMs: now4() - loadStartedAt,
|
|
16006
|
+
integrityMs,
|
|
16007
|
+
modelSource: "memory"
|
|
16008
|
+
};
|
|
15833
16009
|
} else {
|
|
15834
16010
|
const loaded = await modelManager.load({
|
|
15835
16011
|
manifest: runtimeManifest,
|
|
@@ -15841,20 +16017,26 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
|
|
|
15841
16017
|
modelBytes = loaded.bytes;
|
|
15842
16018
|
variant3 = loaded.variant;
|
|
15843
16019
|
actualSource = loaded.source;
|
|
15844
|
-
loadTimings = {
|
|
16020
|
+
loadTimings = {
|
|
16021
|
+
...loaded.timings,
|
|
16022
|
+
sessionMs: 0,
|
|
16023
|
+
totalMs: now4() - loadStartedAt,
|
|
16024
|
+
modelSource: loaded.fromCache ? "cache" : "network"
|
|
16025
|
+
};
|
|
15845
16026
|
}
|
|
15846
16027
|
options.onProgress?.({ phase: "model", status: "complete" });
|
|
15847
16028
|
const fallbacks = [];
|
|
15848
16029
|
const createExecutorForPlan = async (candidatePlan) => {
|
|
15849
16030
|
options.onProgress?.({ phase: "session", status: "start" });
|
|
15850
16031
|
let bridge;
|
|
16032
|
+
const sessionStartedAt = now4();
|
|
15851
16033
|
try {
|
|
15852
16034
|
if (candidatePlan.executionMode === "worker") {
|
|
15853
16035
|
if (typeof Worker !== "function")
|
|
15854
16036
|
throw new PPDetectionError("CAPABILITY_UNSUPPORTED", "\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301 Worker");
|
|
15855
16037
|
const worker = new Worker(workerUrl(), { type: "module" });
|
|
15856
16038
|
bridge = new WorkerBridge(worker);
|
|
15857
|
-
await bridge.load(modelBytes.slice(0), candidatePlan, {
|
|
16039
|
+
const metadata = await bridge.load(modelBytes.slice(0), candidatePlan, {
|
|
15858
16040
|
onProgress: (event) => options.onProgress?.({
|
|
15859
16041
|
phase: "session",
|
|
15860
16042
|
status: event.status
|
|
@@ -15864,6 +16046,7 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
|
|
|
15864
16046
|
numThreads: options.ort?.wasm?.numThreads
|
|
15865
16047
|
}
|
|
15866
16048
|
});
|
|
16049
|
+
runtimeVersion = typeof metadata === "object" && metadata !== null && "runtimeVersion" in metadata && typeof metadata.runtimeVersion === "string" ? metadata.runtimeVersion : null;
|
|
15867
16050
|
const activeBridge = bridge;
|
|
15868
16051
|
options.onProgress?.({ phase: "session", status: "complete" });
|
|
15869
16052
|
return {
|
|
@@ -15881,7 +16064,7 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
|
|
|
15881
16064
|
wasmPaths: options.ort?.wasm?.paths,
|
|
15882
16065
|
numThreads: options.ort?.wasm?.numThreads
|
|
15883
16066
|
});
|
|
15884
|
-
|
|
16067
|
+
runtimeVersion = session.runtimeVersion ?? null;
|
|
15885
16068
|
options.onProgress?.({ phase: "session", status: "complete" });
|
|
15886
16069
|
return {
|
|
15887
16070
|
run(input, signal) {
|
|
@@ -15905,10 +16088,13 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
|
|
|
15905
16088
|
{ phase: "create", causeMessage: message },
|
|
15906
16089
|
{ cause: error }
|
|
15907
16090
|
);
|
|
16091
|
+
} finally {
|
|
16092
|
+
sessionMs += now4() - sessionStartedAt;
|
|
15908
16093
|
}
|
|
15909
16094
|
};
|
|
15910
16095
|
let selectedPlan = plan;
|
|
15911
16096
|
let sessionMs = 0;
|
|
16097
|
+
let runtimeVersion = null;
|
|
15912
16098
|
let selectedCandidateIndex = -1;
|
|
15913
16099
|
for (const [candidateIndex, candidate] of plan.candidates.entries()) {
|
|
15914
16100
|
const candidatePlan = {
|
|
@@ -15950,6 +16136,12 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
|
|
|
15950
16136
|
}
|
|
15951
16137
|
if (!executor) throw new PPDetectionError("SESSION_CREATE_FAILED", "\u65E0\u6CD5\u521B\u5EFA\u68C0\u6D4B Session");
|
|
15952
16138
|
const runtime = {
|
|
16139
|
+
runtimeVersion,
|
|
16140
|
+
environment: {
|
|
16141
|
+
userAgent: globalThis.navigator?.userAgent ?? null,
|
|
16142
|
+
platform: globalThis.navigator?.platform ?? null,
|
|
16143
|
+
capturedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
16144
|
+
},
|
|
15953
16145
|
requestedBackend: options.backend ?? "auto",
|
|
15954
16146
|
backend: selectedPlan.actualBackend,
|
|
15955
16147
|
precision: selectedPlan.actualPrecision,
|
|
@@ -15960,8 +16152,9 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
|
|
|
15960
16152
|
let activeExecutor = executor;
|
|
15961
16153
|
const fallbackExecutor = {
|
|
15962
16154
|
async run(input, signal) {
|
|
16155
|
+
const attemptInput = runtime.mode === "worker" && options.allowFallback === true && selectedCandidateIndex < plan.candidates.length - 1 ? { ...input, data: input.data.slice() } : input;
|
|
15963
16156
|
try {
|
|
15964
|
-
return await activeExecutor.run(
|
|
16157
|
+
return await activeExecutor.run(attemptInput, signal);
|
|
15965
16158
|
} catch (error) {
|
|
15966
16159
|
if (options.allowFallback !== true || selectedCandidateIndex < 0 || selectedCandidateIndex >= plan.candidates.length - 1) {
|
|
15967
16160
|
throw error;
|
|
@@ -15986,6 +16179,7 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
|
|
|
15986
16179
|
runtime.backend = nextPlan.actualBackend;
|
|
15987
16180
|
runtime.precision = nextPlan.actualPrecision;
|
|
15988
16181
|
runtime.mode = nextPlan.executionMode;
|
|
16182
|
+
runtime.runtimeVersion = runtimeVersion;
|
|
15989
16183
|
const fallback = {
|
|
15990
16184
|
cause: mapped.cause ?? mapped,
|
|
15991
16185
|
code: mapped.code,
|
|
@@ -16017,6 +16211,7 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
|
|
|
16017
16211
|
disposeResources: () => modelManager.dispose()
|
|
16018
16212
|
});
|
|
16019
16213
|
await detector.load({ signal: options.signal });
|
|
16214
|
+
loadTimings.totalMs = now4() - loadStartedAt;
|
|
16020
16215
|
options.onProgress?.({ phase: "ready", status: "complete" });
|
|
16021
16216
|
return detector;
|
|
16022
16217
|
} catch (error) {
|
|
@@ -16033,8 +16228,11 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
|
|
|
16033
16228
|
}
|
|
16034
16229
|
async function clearModelCache() {
|
|
16035
16230
|
const manager = new ModelManager();
|
|
16036
|
-
|
|
16037
|
-
|
|
16231
|
+
try {
|
|
16232
|
+
await manager.clearAllCache();
|
|
16233
|
+
} finally {
|
|
16234
|
+
await manager.dispose();
|
|
16235
|
+
}
|
|
16038
16236
|
}
|
|
16039
16237
|
|
|
16040
16238
|
// src/browser-global.ts
|