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/dist/index.d.ts
CHANGED
|
@@ -141,6 +141,7 @@ interface PPDetectionProgressEvent {
|
|
|
141
141
|
readonly fallback?: PPDetectionFallback;
|
|
142
142
|
}
|
|
143
143
|
interface CreatePPDetectionOptions {
|
|
144
|
+
readonly allowExperimental?: boolean;
|
|
144
145
|
readonly allowFallback?: boolean;
|
|
145
146
|
readonly backend?: BackendPreference;
|
|
146
147
|
readonly cache?: boolean | "memory" | "indexeddb";
|
|
@@ -228,6 +229,12 @@ interface PPDetectionFallback {
|
|
|
228
229
|
readonly variantId: string;
|
|
229
230
|
}
|
|
230
231
|
interface PPDetectionRuntimeInfo {
|
|
232
|
+
readonly runtimeVersion?: string | null;
|
|
233
|
+
readonly environment?: Readonly<{
|
|
234
|
+
userAgent: string | null;
|
|
235
|
+
platform: string | null;
|
|
236
|
+
capturedAt: string;
|
|
237
|
+
}>;
|
|
231
238
|
readonly requestedBackend: BackendPreference;
|
|
232
239
|
readonly backend: Backend;
|
|
233
240
|
readonly precision: Precision;
|
|
@@ -252,6 +259,7 @@ interface PPDetectionModelSourceInfo {
|
|
|
252
259
|
readonly sha256: string;
|
|
253
260
|
}
|
|
254
261
|
interface PPDetectionLoadTimings {
|
|
262
|
+
readonly modelSource?: "network" | "cache" | "memory";
|
|
255
263
|
readonly modelDownloadMs?: number;
|
|
256
264
|
readonly modelCacheReadMs?: number;
|
|
257
265
|
readonly integrityMs?: number;
|
|
@@ -394,6 +402,7 @@ declare class PPDetectionError extends Error {
|
|
|
394
402
|
}
|
|
395
403
|
|
|
396
404
|
interface SelectExecutionOptions {
|
|
405
|
+
readonly allowExperimental?: boolean;
|
|
397
406
|
readonly backend?: BackendPreference;
|
|
398
407
|
readonly precision?: Precision;
|
|
399
408
|
readonly executionMode?: ExecutionMode;
|
|
@@ -418,6 +427,9 @@ interface OrtInferenceSession {
|
|
|
418
427
|
interface OrtModule {
|
|
419
428
|
readonly env: {
|
|
420
429
|
readonly wasm?: Record<string, unknown>;
|
|
430
|
+
readonly versions?: {
|
|
431
|
+
readonly web?: string;
|
|
432
|
+
};
|
|
421
433
|
};
|
|
422
434
|
readonly InferenceSession: {
|
|
423
435
|
create(model: ArrayBuffer, options: Record<string, unknown>): Promise<OrtInferenceSession>;
|
|
@@ -425,6 +437,7 @@ interface OrtModule {
|
|
|
425
437
|
readonly Tensor?: new (type: string, data: unknown, dims: readonly number[]) => unknown;
|
|
426
438
|
}
|
|
427
439
|
interface OrtSessionHandle {
|
|
440
|
+
readonly runtimeVersion?: string | null;
|
|
428
441
|
readonly plan: ExecutionPlan;
|
|
429
442
|
readonly sessionMs: number;
|
|
430
443
|
run(feeds: Record<string, unknown>, options?: {
|
|
@@ -522,11 +535,16 @@ interface CacheEstimate {
|
|
|
522
535
|
readonly entries: number;
|
|
523
536
|
}
|
|
524
537
|
interface ModelCache {
|
|
538
|
+
readonly scope?: object;
|
|
525
539
|
get(key: string): Promise<ArrayBuffer | undefined>;
|
|
526
540
|
put(key: string, bytes: ArrayBuffer): Promise<void>;
|
|
527
541
|
clearCurrent(key: string): Promise<void>;
|
|
528
542
|
clearAll(): Promise<void>;
|
|
529
543
|
estimate(): Promise<CacheEstimate>;
|
|
544
|
+
list?(): Promise<readonly {
|
|
545
|
+
key: string;
|
|
546
|
+
bytes: number;
|
|
547
|
+
}[]>;
|
|
530
548
|
close?(): Promise<void> | void;
|
|
531
549
|
}
|
|
532
550
|
|
|
@@ -559,6 +577,7 @@ interface LoadedManagedModel {
|
|
|
559
577
|
declare class ModelManager {
|
|
560
578
|
private readonly fetcher?;
|
|
561
579
|
private readonly cache;
|
|
580
|
+
private readonly coordinator;
|
|
562
581
|
private readonly lifecycle;
|
|
563
582
|
private readonly activeLoads;
|
|
564
583
|
private currentKey?;
|
|
@@ -569,8 +588,8 @@ declare class ModelManager {
|
|
|
569
588
|
load(options: LoadManagedModelOptions): Promise<LoadedManagedModel>;
|
|
570
589
|
private loadActive;
|
|
571
590
|
estimate(): Promise<CacheEstimate>;
|
|
572
|
-
getCacheEstimate(): Promise<CacheEstimate>;
|
|
573
|
-
clearCurrentModelCache(): Promise<void>;
|
|
591
|
+
getCacheEstimate(model?: ModelIdentity): Promise<CacheEstimate>;
|
|
592
|
+
clearCurrentModelCache(model?: ModelIdentity): Promise<void>;
|
|
574
593
|
clearAllCache(): Promise<void>;
|
|
575
594
|
dispose(): Promise<void>;
|
|
576
595
|
}
|
|
@@ -583,6 +602,10 @@ declare class MemoryModelCache implements ModelCache {
|
|
|
583
602
|
clearAll(): Promise<void>;
|
|
584
603
|
estimate(): Promise<CacheEstimate>;
|
|
585
604
|
close(): Promise<void>;
|
|
605
|
+
list(): Promise<readonly {
|
|
606
|
+
key: string;
|
|
607
|
+
bytes: number;
|
|
608
|
+
}[]>;
|
|
586
609
|
}
|
|
587
610
|
|
|
588
611
|
interface IndexedDBModelCacheOptions {
|
|
@@ -590,6 +613,7 @@ interface IndexedDBModelCacheOptions {
|
|
|
590
613
|
readonly databaseName?: string;
|
|
591
614
|
}
|
|
592
615
|
declare class IndexedDBModelCache implements ModelCache {
|
|
616
|
+
readonly scope: object;
|
|
593
617
|
private readonly factory;
|
|
594
618
|
private readonly databaseName;
|
|
595
619
|
private database?;
|
|
@@ -600,6 +624,10 @@ declare class IndexedDBModelCache implements ModelCache {
|
|
|
600
624
|
clearAll(): Promise<void>;
|
|
601
625
|
estimate(): Promise<CacheEstimate>;
|
|
602
626
|
close(): Promise<void>;
|
|
627
|
+
list(): Promise<readonly {
|
|
628
|
+
key: string;
|
|
629
|
+
bytes: number;
|
|
630
|
+
}[]>;
|
|
603
631
|
private open;
|
|
604
632
|
private request;
|
|
605
633
|
}
|
|
@@ -607,7 +635,7 @@ declare class IndexedDBModelCache implements ModelCache {
|
|
|
607
635
|
declare global {
|
|
608
636
|
var __PPDETECTION_SCRIPT_URL__: string | undefined;
|
|
609
637
|
}
|
|
610
|
-
declare const CURRENT_SDK_VERSION = "0.
|
|
638
|
+
declare const CURRENT_SDK_VERSION = "0.3.0";
|
|
611
639
|
|
|
612
640
|
declare function probePPDetectionCapabilities(options?: CapabilityProbeOptions): DetectionCapabilities;
|
|
613
641
|
|
package/dist/index.js
CHANGED
|
@@ -622,7 +622,11 @@ var PPDetectionDetectorImplementation = class {
|
|
|
622
622
|
original: { width: decoded.width, height: decoded.height }
|
|
623
623
|
},
|
|
624
624
|
model: this.model,
|
|
625
|
-
runtime:
|
|
625
|
+
runtime: {
|
|
626
|
+
...this.runtime,
|
|
627
|
+
fallbacks: this.runtime.fallbacks.map((fallback) => ({ ...fallback })),
|
|
628
|
+
...this.runtime.environment ? { environment: { ...this.runtime.environment } } : {}
|
|
629
|
+
},
|
|
626
630
|
timings: {
|
|
627
631
|
decodeMs: decoded.decodeMs,
|
|
628
632
|
preprocessMs,
|
|
@@ -1115,8 +1119,84 @@ function adaptModelManifest(manifest) {
|
|
|
1115
1119
|
});
|
|
1116
1120
|
}
|
|
1117
1121
|
|
|
1122
|
+
// src/cache/coordination.ts
|
|
1123
|
+
var CacheCoordinator = class {
|
|
1124
|
+
caches = /* @__PURE__ */ new Map();
|
|
1125
|
+
generation = 0;
|
|
1126
|
+
keys = /* @__PURE__ */ new Map();
|
|
1127
|
+
pending = Promise.resolve();
|
|
1128
|
+
guard(key) {
|
|
1129
|
+
const generation = this.generation;
|
|
1130
|
+
const keyGeneration = this.keys.get(key) ?? 0;
|
|
1131
|
+
this.keys.set(key, keyGeneration);
|
|
1132
|
+
return () => generation === this.generation && keyGeneration === (this.keys.get(key) ?? 0);
|
|
1133
|
+
}
|
|
1134
|
+
run(operation) {
|
|
1135
|
+
const pending = this.pending.then(operation);
|
|
1136
|
+
this.pending = pending.catch(() => void 0);
|
|
1137
|
+
return pending;
|
|
1138
|
+
}
|
|
1139
|
+
clear(key, matches) {
|
|
1140
|
+
if (matches) {
|
|
1141
|
+
for (const [candidate, generation] of this.keys) {
|
|
1142
|
+
if (matches(candidate)) this.keys.set(candidate, generation + 1);
|
|
1143
|
+
}
|
|
1144
|
+
} else if (key === void 0) this.generation += 1;
|
|
1145
|
+
else this.keys.set(key, (this.keys.get(key) ?? 0) + 1);
|
|
1146
|
+
return this.run(async () => {
|
|
1147
|
+
const outcomes = await Promise.allSettled(
|
|
1148
|
+
[...this.caches.keys()].map(async (cache) => {
|
|
1149
|
+
if (matches && cache.list) {
|
|
1150
|
+
for (const entry of await cache.list()) {
|
|
1151
|
+
if (matches(entry.key)) await cache.clearCurrent(entry.key);
|
|
1152
|
+
}
|
|
1153
|
+
} else if (key === void 0) await cache.clearAll();
|
|
1154
|
+
else await cache.clearCurrent(key);
|
|
1155
|
+
})
|
|
1156
|
+
);
|
|
1157
|
+
const failure = outcomes.find((outcome) => outcome.status === "rejected");
|
|
1158
|
+
if (failure?.status === "rejected") throw failure.reason;
|
|
1159
|
+
});
|
|
1160
|
+
}
|
|
1161
|
+
unregister(cache) {
|
|
1162
|
+
const count = this.caches.get(cache) ?? 0;
|
|
1163
|
+
if (count > 1) {
|
|
1164
|
+
this.caches.set(cache, count - 1);
|
|
1165
|
+
return false;
|
|
1166
|
+
}
|
|
1167
|
+
this.caches.delete(cache);
|
|
1168
|
+
return true;
|
|
1169
|
+
}
|
|
1170
|
+
};
|
|
1171
|
+
var coordinators = /* @__PURE__ */ new WeakMap();
|
|
1172
|
+
function coordinateCache(cache) {
|
|
1173
|
+
const scope = cache.scope ?? cache;
|
|
1174
|
+
let coordinator = coordinators.get(scope);
|
|
1175
|
+
if (!coordinator) {
|
|
1176
|
+
coordinator = new CacheCoordinator();
|
|
1177
|
+
coordinators.set(scope, coordinator);
|
|
1178
|
+
}
|
|
1179
|
+
coordinator.caches.set(cache, (coordinator.caches.get(cache) ?? 0) + 1);
|
|
1180
|
+
return coordinator;
|
|
1181
|
+
}
|
|
1182
|
+
var databaseScopes = /* @__PURE__ */ new WeakMap();
|
|
1183
|
+
function databaseScope(factory, name) {
|
|
1184
|
+
let scopes = databaseScopes.get(factory);
|
|
1185
|
+
if (!scopes) {
|
|
1186
|
+
scopes = /* @__PURE__ */ new Map();
|
|
1187
|
+
databaseScopes.set(factory, scopes);
|
|
1188
|
+
}
|
|
1189
|
+
let scope = scopes.get(name);
|
|
1190
|
+
if (!scope) {
|
|
1191
|
+
scope = {};
|
|
1192
|
+
scopes.set(name, scope);
|
|
1193
|
+
}
|
|
1194
|
+
return scope;
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1118
1197
|
// src/cache/indexeddb-cache.ts
|
|
1119
1198
|
var IndexedDBModelCache = class {
|
|
1199
|
+
scope;
|
|
1120
1200
|
factory;
|
|
1121
1201
|
databaseName;
|
|
1122
1202
|
database;
|
|
@@ -1125,6 +1205,7 @@ var IndexedDBModelCache = class {
|
|
|
1125
1205
|
if (!factory) throw new Error("\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301 IndexedDB");
|
|
1126
1206
|
this.factory = factory;
|
|
1127
1207
|
this.databaseName = options.databaseName ?? "web-sdk-pp-detection-models-v1";
|
|
1208
|
+
this.scope = databaseScope(factory, this.databaseName);
|
|
1128
1209
|
}
|
|
1129
1210
|
async get(key) {
|
|
1130
1211
|
const record3 = await this.request(
|
|
@@ -1178,6 +1259,24 @@ var IndexedDBModelCache = class {
|
|
|
1178
1259
|
(await this.database).close();
|
|
1179
1260
|
this.database = void 0;
|
|
1180
1261
|
}
|
|
1262
|
+
async list() {
|
|
1263
|
+
const database = await this.open();
|
|
1264
|
+
return await new Promise((resolve, reject) => {
|
|
1265
|
+
const transaction = database.transaction("models", "readonly");
|
|
1266
|
+
const request = transaction.objectStore("models").openCursor();
|
|
1267
|
+
const entries = [];
|
|
1268
|
+
request.onerror = () => reject(request.error ?? new Error("\u8BFB\u53D6\u7F13\u5B58\u5BB9\u91CF\u5931\u8D25"));
|
|
1269
|
+
request.onsuccess = () => {
|
|
1270
|
+
const cursor = request.result;
|
|
1271
|
+
if (!cursor) return;
|
|
1272
|
+
const record3 = cursor.value;
|
|
1273
|
+
entries.push({ key: record3.key, bytes: record3.size });
|
|
1274
|
+
cursor.continue();
|
|
1275
|
+
};
|
|
1276
|
+
transaction.oncomplete = () => resolve(entries);
|
|
1277
|
+
transaction.onabort = () => reject(transaction.error ?? new Error("\u7F13\u5B58\u5BB9\u91CF\u4E8B\u52A1\u4E2D\u6B62"));
|
|
1278
|
+
});
|
|
1279
|
+
}
|
|
1181
1280
|
open() {
|
|
1182
1281
|
if (this.database) return this.database;
|
|
1183
1282
|
this.database = new Promise((resolve, reject) => {
|
|
@@ -1247,6 +1346,11 @@ var MemoryModelCache = class {
|
|
|
1247
1346
|
this.entries.clear();
|
|
1248
1347
|
return Promise.resolve();
|
|
1249
1348
|
}
|
|
1349
|
+
list() {
|
|
1350
|
+
return Promise.resolve(
|
|
1351
|
+
[...this.entries].map(([key, bytes]) => ({ key, bytes: bytes.byteLength }))
|
|
1352
|
+
);
|
|
1353
|
+
}
|
|
1250
1354
|
};
|
|
1251
1355
|
|
|
1252
1356
|
// src/cache/model-cache.ts
|
|
@@ -1254,9 +1358,11 @@ var TieredModelCache = class {
|
|
|
1254
1358
|
constructor(memory, persistent) {
|
|
1255
1359
|
this.memory = memory;
|
|
1256
1360
|
this.persistent = persistent;
|
|
1361
|
+
this.scope = persistent?.scope ?? persistent ?? memory;
|
|
1257
1362
|
}
|
|
1258
1363
|
memory;
|
|
1259
1364
|
persistent;
|
|
1365
|
+
scope;
|
|
1260
1366
|
async get(key) {
|
|
1261
1367
|
const memoryValue = await this.memory.get(key);
|
|
1262
1368
|
if (memoryValue) return memoryValue;
|
|
@@ -1279,6 +1385,13 @@ var TieredModelCache = class {
|
|
|
1279
1385
|
estimate() {
|
|
1280
1386
|
return this.persistent?.estimate() ?? this.memory.estimate();
|
|
1281
1387
|
}
|
|
1388
|
+
async list() {
|
|
1389
|
+
const entries = /* @__PURE__ */ new Map();
|
|
1390
|
+
for (const cache of [this.memory, this.persistent]) {
|
|
1391
|
+
for (const entry of await cache?.list?.() ?? []) entries.set(entry.key, entry);
|
|
1392
|
+
}
|
|
1393
|
+
return [...entries.values()];
|
|
1394
|
+
}
|
|
1282
1395
|
async close() {
|
|
1283
1396
|
await this.memory.close?.();
|
|
1284
1397
|
await this.persistent?.close?.();
|
|
@@ -1488,6 +1601,14 @@ function resolveModelAsset(selection, manifest) {
|
|
|
1488
1601
|
|
|
1489
1602
|
// src/model/model-manager.ts
|
|
1490
1603
|
var SDK_CACHE_NAMESPACE = "web-sdk-pp-detection:cache-v1";
|
|
1604
|
+
function matchesModel(key, model) {
|
|
1605
|
+
try {
|
|
1606
|
+
const parts = JSON.parse(key);
|
|
1607
|
+
return Array.isArray(parts) && parts[0] === SDK_CACHE_NAMESPACE && (model === void 0 || parts[1] === model.id && parts[2] === model.version);
|
|
1608
|
+
} catch {
|
|
1609
|
+
return false;
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1491
1612
|
function now2() {
|
|
1492
1613
|
return globalThis.performance?.now() ?? Date.now();
|
|
1493
1614
|
}
|
|
@@ -1511,6 +1632,7 @@ function throwIfAborted4(signal) {
|
|
|
1511
1632
|
var ModelManager = class {
|
|
1512
1633
|
fetcher;
|
|
1513
1634
|
cache;
|
|
1635
|
+
coordinator;
|
|
1514
1636
|
lifecycle = new AbortController();
|
|
1515
1637
|
activeLoads = /* @__PURE__ */ new Set();
|
|
1516
1638
|
currentKey;
|
|
@@ -1519,6 +1641,7 @@ var ModelManager = class {
|
|
|
1519
1641
|
constructor(options = {}) {
|
|
1520
1642
|
this.fetcher = options.fetcher;
|
|
1521
1643
|
this.cache = createCache(options.cache);
|
|
1644
|
+
this.coordinator = coordinateCache(this.cache);
|
|
1522
1645
|
}
|
|
1523
1646
|
cacheKey(variant3, source2, model = { id: "unknown", version: "unknown" }) {
|
|
1524
1647
|
return JSON.stringify([
|
|
@@ -1557,16 +1680,24 @@ var ModelManager = class {
|
|
|
1557
1680
|
const variant3 = resolveModelVariant(manifest, options.variantId);
|
|
1558
1681
|
const sourceKind2 = options.sourceKind ?? "auto";
|
|
1559
1682
|
const sources = resolveModelSources(variant3, sourceKind2);
|
|
1683
|
+
const guards = new Map(
|
|
1684
|
+
sources.map((source2) => {
|
|
1685
|
+
const key = this.cacheKey(variant3, source2, manifest.model);
|
|
1686
|
+
return [key, this.coordinator.guard(key)];
|
|
1687
|
+
})
|
|
1688
|
+
);
|
|
1560
1689
|
const failures = [];
|
|
1561
1690
|
let lastError;
|
|
1562
1691
|
for (const source2 of sources) {
|
|
1563
1692
|
throwIfAborted4(options.signal);
|
|
1564
1693
|
const asset = { model: manifest.model, variant: variant3, source: source2 };
|
|
1565
1694
|
const cacheKey = this.cacheKey(variant3, source2, manifest.model);
|
|
1695
|
+
this.currentKey = cacheKey;
|
|
1696
|
+
const canMutate = guards.get(cacheKey);
|
|
1566
1697
|
const cacheStarted = now2();
|
|
1567
1698
|
let cached;
|
|
1568
1699
|
try {
|
|
1569
|
-
cached = await this.cache.get(cacheKey);
|
|
1700
|
+
cached = await this.coordinator.run(() => this.cache.get(cacheKey));
|
|
1570
1701
|
} catch (error) {
|
|
1571
1702
|
if (error instanceof PPDetectionError && error.code === "ABORTED") throw error;
|
|
1572
1703
|
throwIfAborted4(options.signal);
|
|
@@ -1577,7 +1708,6 @@ var ModelManager = class {
|
|
|
1577
1708
|
const integrityStarted = now2();
|
|
1578
1709
|
try {
|
|
1579
1710
|
await verifyModelIntegrity(cached, source2, options.signal);
|
|
1580
|
-
this.currentKey = cacheKey;
|
|
1581
1711
|
return {
|
|
1582
1712
|
bytes: cached,
|
|
1583
1713
|
manifest,
|
|
@@ -1591,7 +1721,9 @@ var ModelManager = class {
|
|
|
1591
1721
|
} catch (error) {
|
|
1592
1722
|
if (error instanceof PPDetectionError && error.code === "ABORTED") throw error;
|
|
1593
1723
|
try {
|
|
1594
|
-
await this.
|
|
1724
|
+
await this.coordinator.run(async () => {
|
|
1725
|
+
if (canMutate()) await this.cache.clearCurrent(cacheKey);
|
|
1726
|
+
});
|
|
1595
1727
|
} catch (clearError) {
|
|
1596
1728
|
if (clearError instanceof PPDetectionError && clearError.code === "ABORTED")
|
|
1597
1729
|
throw clearError;
|
|
@@ -1627,11 +1759,12 @@ var ModelManager = class {
|
|
|
1627
1759
|
}
|
|
1628
1760
|
throwIfAborted4(options.signal);
|
|
1629
1761
|
try {
|
|
1630
|
-
await this.
|
|
1762
|
+
await this.coordinator.run(async () => {
|
|
1763
|
+
if (canMutate()) await this.cache.put(cacheKey, loaded.bytes);
|
|
1764
|
+
});
|
|
1631
1765
|
} catch {
|
|
1632
1766
|
}
|
|
1633
1767
|
throwIfAborted4(options.signal);
|
|
1634
|
-
this.currentKey = cacheKey;
|
|
1635
1768
|
return {
|
|
1636
1769
|
bytes: loaded.bytes,
|
|
1637
1770
|
manifest,
|
|
@@ -1656,16 +1789,44 @@ var ModelManager = class {
|
|
|
1656
1789
|
estimate() {
|
|
1657
1790
|
return this.getCacheEstimate();
|
|
1658
1791
|
}
|
|
1659
|
-
getCacheEstimate() {
|
|
1660
|
-
return this.
|
|
1792
|
+
getCacheEstimate(model) {
|
|
1793
|
+
return this.coordinator.run(async () => {
|
|
1794
|
+
if (!this.cache.list) {
|
|
1795
|
+
if (model)
|
|
1796
|
+
throw new PPDetectionError(
|
|
1797
|
+
"CAPABILITY_UNSUPPORTED",
|
|
1798
|
+
"\u81EA\u5B9A\u4E49\u7F13\u5B58\u9700\u5B9E\u73B0 list \u624D\u80FD\u6309\u6A21\u578B\u4F30\u7B97"
|
|
1799
|
+
);
|
|
1800
|
+
return this.cache.estimate();
|
|
1801
|
+
}
|
|
1802
|
+
const unique = /* @__PURE__ */ new Map();
|
|
1803
|
+
for (const cache of this.coordinator.caches.keys()) {
|
|
1804
|
+
for (const entry of await cache.list?.() ?? []) unique.set(entry.key, entry);
|
|
1805
|
+
}
|
|
1806
|
+
const entries = [...unique.values()].filter((entry) => matchesModel(entry.key, model));
|
|
1807
|
+
return {
|
|
1808
|
+
bytes: entries.reduce((sum, entry) => sum + entry.bytes, 0),
|
|
1809
|
+
entries: entries.length
|
|
1810
|
+
};
|
|
1811
|
+
});
|
|
1661
1812
|
}
|
|
1662
|
-
async clearCurrentModelCache() {
|
|
1813
|
+
async clearCurrentModelCache(model) {
|
|
1814
|
+
if (model) {
|
|
1815
|
+
if (!model.id || !model.version)
|
|
1816
|
+
throw new PPDetectionError("INVALID_MANIFEST", "\u7F13\u5B58\u6A21\u578B\u8EAB\u4EFD\u5FC5\u987B\u5305\u542B id \u548C version");
|
|
1817
|
+
if (!this.cache.list)
|
|
1818
|
+
throw new PPDetectionError(
|
|
1819
|
+
"CAPABILITY_UNSUPPORTED",
|
|
1820
|
+
"\u81EA\u5B9A\u4E49\u7F13\u5B58\u9700\u5B9E\u73B0 list \u624D\u80FD\u6309\u6A21\u578B\u6E05\u7406"
|
|
1821
|
+
);
|
|
1822
|
+
await this.coordinator.clear(void 0, (key) => matchesModel(key, model));
|
|
1823
|
+
return;
|
|
1824
|
+
}
|
|
1663
1825
|
if (!this.currentKey) return;
|
|
1664
|
-
await this.
|
|
1826
|
+
await this.coordinator.clear(this.currentKey);
|
|
1665
1827
|
}
|
|
1666
1828
|
async clearAllCache() {
|
|
1667
|
-
await this.
|
|
1668
|
-
this.currentKey = void 0;
|
|
1829
|
+
await this.coordinator.clear(void 0, (key) => matchesModel(key));
|
|
1669
1830
|
}
|
|
1670
1831
|
async dispose() {
|
|
1671
1832
|
if (this.disposePromise) return await this.disposePromise;
|
|
@@ -1674,7 +1835,7 @@ var ModelManager = class {
|
|
|
1674
1835
|
this.disposePromise = (async () => {
|
|
1675
1836
|
await Promise.all([...this.activeLoads]);
|
|
1676
1837
|
this.currentKey = void 0;
|
|
1677
|
-
await this.cache.close?.();
|
|
1838
|
+
if (this.coordinator.unregister(this.cache)) await this.cache.close?.();
|
|
1678
1839
|
})();
|
|
1679
1840
|
await this.disposePromise;
|
|
1680
1841
|
}
|
|
@@ -1688,7 +1849,7 @@ function mapError(error, phase) {
|
|
|
1688
1849
|
if (error instanceof PPDetectionError) return error;
|
|
1689
1850
|
const message = error instanceof Error ? error.message : String(error);
|
|
1690
1851
|
const details = { phase, causeMessage: message };
|
|
1691
|
-
if (
|
|
1852
|
+
if (error instanceof Error && error.name === "AbortError")
|
|
1692
1853
|
return new PPDetectionError("ABORTED", "\u63A8\u7406\u5DF2\u53D6\u6D88", details, { cause: error });
|
|
1693
1854
|
if (/memory|out.of.memory|allocation/i.test(message))
|
|
1694
1855
|
return new PPDetectionError("OUT_OF_MEMORY", "\u8FD0\u884C\u65F6\u5185\u5B58\u4E0D\u8DB3", details, { cause: error });
|
|
@@ -1733,6 +1894,7 @@ async function createOrtSession(modelBytes, plan, options = {}) {
|
|
|
1733
1894
|
return {
|
|
1734
1895
|
plan,
|
|
1735
1896
|
sessionMs,
|
|
1897
|
+
runtimeVersion: ort.env.versions?.web ?? null,
|
|
1736
1898
|
async run(feeds, runOptions = {}) {
|
|
1737
1899
|
if (disposed) throw new PPDetectionError("DISPOSED", "\u4F1A\u8BDD\u5DF2\u91CA\u653E", { phase: "run" });
|
|
1738
1900
|
if (runOptions.signal?.aborted)
|
|
@@ -1869,13 +2031,19 @@ function selectExecutionPlan(options, capabilities, manifest) {
|
|
|
1869
2031
|
if (executionMode === "worker" && !capabilities.worker) {
|
|
1870
2032
|
fail("CAPABILITY_UNSUPPORTED", "\u8BF7\u6C42\u7684 worker \u4E0D\u53EF\u7528", { executionMode });
|
|
1871
2033
|
}
|
|
1872
|
-
const
|
|
1873
|
-
(candidate) => candidate.precision === requestedPrecision && candidate.status !== "
|
|
1874
|
-
);
|
|
2034
|
+
const matchingVariants = manifest.variants?.filter(
|
|
2035
|
+
(candidate) => candidate.precision === requestedPrecision && candidate.status !== "blocked"
|
|
2036
|
+
) ?? [];
|
|
2037
|
+
const variant3 = matchingVariants.find((candidate) => candidate.status !== "labs") ?? (options.allowExperimental === true ? matchingVariants.find((candidate) => candidate.status === "labs") : void 0);
|
|
1875
2038
|
if (!variant3) {
|
|
1876
|
-
fail(
|
|
1877
|
-
|
|
1878
|
-
|
|
2039
|
+
fail(
|
|
2040
|
+
"MODEL_INCOMPATIBLE",
|
|
2041
|
+
options.allowExperimental === true ? `manifest \u6CA1\u6709\u53EF\u7528\u7684 ${requestedPrecision} \u53D8\u4F53` : `manifest \u6CA1\u6709\u53EF\u7528\u7684 ${requestedPrecision} \u7A33\u5B9A\u53D8\u4F53`,
|
|
2042
|
+
{
|
|
2043
|
+
requestedPrecision,
|
|
2044
|
+
allowExperimental: options.allowExperimental === true
|
|
2045
|
+
}
|
|
2046
|
+
);
|
|
1879
2047
|
}
|
|
1880
2048
|
const candidates = candidatesForBackend(requestedBackend, capabilities).filter(
|
|
1881
2049
|
(backend) => variant3.backends.includes(backend)
|
|
@@ -2051,7 +2219,7 @@ var WorkerBridge = class {
|
|
|
2051
2219
|
};
|
|
2052
2220
|
|
|
2053
2221
|
// src/index.ts
|
|
2054
|
-
var CURRENT_SDK_VERSION = "0.
|
|
2222
|
+
var CURRENT_SDK_VERSION = "0.3.0";
|
|
2055
2223
|
function probePPDetectionCapabilities(options = {}) {
|
|
2056
2224
|
return probeCapabilities(options);
|
|
2057
2225
|
}
|
|
@@ -2146,6 +2314,7 @@ function workerUrl() {
|
|
|
2146
2314
|
return new URL("./inference.worker.js", import.meta.url);
|
|
2147
2315
|
}
|
|
2148
2316
|
async function createPPDetection(options = {}) {
|
|
2317
|
+
const loadStartedAt = now4();
|
|
2149
2318
|
if (options.model === void 0 && options.manifest === void 0)
|
|
2150
2319
|
throw new PPDetectionError("INVALID_MANIFEST", "\u521B\u5EFA PPDetection \u5B9E\u4F8B\u9700\u8981 manifest \u6216 model");
|
|
2151
2320
|
const capabilities = probeCapabilities();
|
|
@@ -2165,6 +2334,7 @@ async function createPPDetection(options = {}) {
|
|
|
2165
2334
|
options.onProgress?.({ phase: "manifest", status: "complete" });
|
|
2166
2335
|
const plan = selectExecutionPlan(
|
|
2167
2336
|
{
|
|
2337
|
+
allowExperimental: options.allowExperimental,
|
|
2168
2338
|
backend: options.backend,
|
|
2169
2339
|
precision: options.precision === "auto" ? void 0 : options.precision,
|
|
2170
2340
|
executionMode: options.executionMode,
|
|
@@ -2178,7 +2348,6 @@ async function createPPDetection(options = {}) {
|
|
|
2178
2348
|
});
|
|
2179
2349
|
let executor;
|
|
2180
2350
|
try {
|
|
2181
|
-
const loadStartedAt = now4();
|
|
2182
2351
|
options.onProgress?.({ phase: "model", status: "start" });
|
|
2183
2352
|
let modelBytes;
|
|
2184
2353
|
let actualSource;
|
|
@@ -2190,10 +2359,17 @@ async function createPPDetection(options = {}) {
|
|
|
2190
2359
|
throw new PPDetectionError("MODEL_SOURCE_UNAVAILABLE", "\u6A21\u578B\u53D8\u4F53\u6CA1\u6709\u53EF\u7528\u6765\u6E90", {
|
|
2191
2360
|
variantId: variant3.id
|
|
2192
2361
|
});
|
|
2362
|
+
const integrityStartedAt = now4();
|
|
2193
2363
|
await verifyModelIntegrity(memoryData, source2, options.signal);
|
|
2364
|
+
const integrityMs = now4() - integrityStartedAt;
|
|
2194
2365
|
modelBytes = memoryData;
|
|
2195
2366
|
actualSource = source2;
|
|
2196
|
-
loadTimings = {
|
|
2367
|
+
loadTimings = {
|
|
2368
|
+
sessionMs: 0,
|
|
2369
|
+
totalMs: now4() - loadStartedAt,
|
|
2370
|
+
integrityMs,
|
|
2371
|
+
modelSource: "memory"
|
|
2372
|
+
};
|
|
2197
2373
|
} else {
|
|
2198
2374
|
const loaded = await modelManager.load({
|
|
2199
2375
|
manifest: runtimeManifest,
|
|
@@ -2205,20 +2381,26 @@ async function createPPDetection(options = {}) {
|
|
|
2205
2381
|
modelBytes = loaded.bytes;
|
|
2206
2382
|
variant3 = loaded.variant;
|
|
2207
2383
|
actualSource = loaded.source;
|
|
2208
|
-
loadTimings = {
|
|
2384
|
+
loadTimings = {
|
|
2385
|
+
...loaded.timings,
|
|
2386
|
+
sessionMs: 0,
|
|
2387
|
+
totalMs: now4() - loadStartedAt,
|
|
2388
|
+
modelSource: loaded.fromCache ? "cache" : "network"
|
|
2389
|
+
};
|
|
2209
2390
|
}
|
|
2210
2391
|
options.onProgress?.({ phase: "model", status: "complete" });
|
|
2211
2392
|
const fallbacks = [];
|
|
2212
2393
|
const createExecutorForPlan = async (candidatePlan) => {
|
|
2213
2394
|
options.onProgress?.({ phase: "session", status: "start" });
|
|
2214
2395
|
let bridge;
|
|
2396
|
+
const sessionStartedAt = now4();
|
|
2215
2397
|
try {
|
|
2216
2398
|
if (candidatePlan.executionMode === "worker") {
|
|
2217
2399
|
if (typeof Worker !== "function")
|
|
2218
2400
|
throw new PPDetectionError("CAPABILITY_UNSUPPORTED", "\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301 Worker");
|
|
2219
2401
|
const worker = new Worker(workerUrl(), { type: "module" });
|
|
2220
2402
|
bridge = new WorkerBridge(worker);
|
|
2221
|
-
await bridge.load(modelBytes.slice(0), candidatePlan, {
|
|
2403
|
+
const metadata = await bridge.load(modelBytes.slice(0), candidatePlan, {
|
|
2222
2404
|
onProgress: (event) => options.onProgress?.({
|
|
2223
2405
|
phase: "session",
|
|
2224
2406
|
status: event.status
|
|
@@ -2228,6 +2410,7 @@ async function createPPDetection(options = {}) {
|
|
|
2228
2410
|
numThreads: options.ort?.wasm?.numThreads
|
|
2229
2411
|
}
|
|
2230
2412
|
});
|
|
2413
|
+
runtimeVersion = typeof metadata === "object" && metadata !== null && "runtimeVersion" in metadata && typeof metadata.runtimeVersion === "string" ? metadata.runtimeVersion : null;
|
|
2231
2414
|
const activeBridge = bridge;
|
|
2232
2415
|
options.onProgress?.({ phase: "session", status: "complete" });
|
|
2233
2416
|
return {
|
|
@@ -2245,7 +2428,7 @@ async function createPPDetection(options = {}) {
|
|
|
2245
2428
|
wasmPaths: options.ort?.wasm?.paths,
|
|
2246
2429
|
numThreads: options.ort?.wasm?.numThreads
|
|
2247
2430
|
});
|
|
2248
|
-
|
|
2431
|
+
runtimeVersion = session.runtimeVersion ?? null;
|
|
2249
2432
|
options.onProgress?.({ phase: "session", status: "complete" });
|
|
2250
2433
|
return {
|
|
2251
2434
|
run(input, signal) {
|
|
@@ -2269,10 +2452,13 @@ async function createPPDetection(options = {}) {
|
|
|
2269
2452
|
{ phase: "create", causeMessage: message },
|
|
2270
2453
|
{ cause: error }
|
|
2271
2454
|
);
|
|
2455
|
+
} finally {
|
|
2456
|
+
sessionMs += now4() - sessionStartedAt;
|
|
2272
2457
|
}
|
|
2273
2458
|
};
|
|
2274
2459
|
let selectedPlan = plan;
|
|
2275
2460
|
let sessionMs = 0;
|
|
2461
|
+
let runtimeVersion = null;
|
|
2276
2462
|
let selectedCandidateIndex = -1;
|
|
2277
2463
|
for (const [candidateIndex, candidate] of plan.candidates.entries()) {
|
|
2278
2464
|
const candidatePlan = {
|
|
@@ -2314,6 +2500,12 @@ async function createPPDetection(options = {}) {
|
|
|
2314
2500
|
}
|
|
2315
2501
|
if (!executor) throw new PPDetectionError("SESSION_CREATE_FAILED", "\u65E0\u6CD5\u521B\u5EFA\u68C0\u6D4B Session");
|
|
2316
2502
|
const runtime = {
|
|
2503
|
+
runtimeVersion,
|
|
2504
|
+
environment: {
|
|
2505
|
+
userAgent: globalThis.navigator?.userAgent ?? null,
|
|
2506
|
+
platform: globalThis.navigator?.platform ?? null,
|
|
2507
|
+
capturedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2508
|
+
},
|
|
2317
2509
|
requestedBackend: options.backend ?? "auto",
|
|
2318
2510
|
backend: selectedPlan.actualBackend,
|
|
2319
2511
|
precision: selectedPlan.actualPrecision,
|
|
@@ -2324,8 +2516,9 @@ async function createPPDetection(options = {}) {
|
|
|
2324
2516
|
let activeExecutor = executor;
|
|
2325
2517
|
const fallbackExecutor = {
|
|
2326
2518
|
async run(input, signal) {
|
|
2519
|
+
const attemptInput = runtime.mode === "worker" && options.allowFallback === true && selectedCandidateIndex < plan.candidates.length - 1 ? { ...input, data: input.data.slice() } : input;
|
|
2327
2520
|
try {
|
|
2328
|
-
return await activeExecutor.run(
|
|
2521
|
+
return await activeExecutor.run(attemptInput, signal);
|
|
2329
2522
|
} catch (error) {
|
|
2330
2523
|
if (options.allowFallback !== true || selectedCandidateIndex < 0 || selectedCandidateIndex >= plan.candidates.length - 1) {
|
|
2331
2524
|
throw error;
|
|
@@ -2350,6 +2543,7 @@ async function createPPDetection(options = {}) {
|
|
|
2350
2543
|
runtime.backend = nextPlan.actualBackend;
|
|
2351
2544
|
runtime.precision = nextPlan.actualPrecision;
|
|
2352
2545
|
runtime.mode = nextPlan.executionMode;
|
|
2546
|
+
runtime.runtimeVersion = runtimeVersion;
|
|
2353
2547
|
const fallback = {
|
|
2354
2548
|
cause: mapped.cause ?? mapped,
|
|
2355
2549
|
code: mapped.code,
|
|
@@ -2381,6 +2575,7 @@ async function createPPDetection(options = {}) {
|
|
|
2381
2575
|
disposeResources: () => modelManager.dispose()
|
|
2382
2576
|
});
|
|
2383
2577
|
await detector.load({ signal: options.signal });
|
|
2578
|
+
loadTimings.totalMs = now4() - loadStartedAt;
|
|
2384
2579
|
options.onProgress?.({ phase: "ready", status: "complete" });
|
|
2385
2580
|
return detector;
|
|
2386
2581
|
} catch (error) {
|
|
@@ -2397,8 +2592,11 @@ async function createPPDetection(options = {}) {
|
|
|
2397
2592
|
}
|
|
2398
2593
|
async function clearModelCache() {
|
|
2399
2594
|
const manager = new ModelManager();
|
|
2400
|
-
|
|
2401
|
-
|
|
2595
|
+
try {
|
|
2596
|
+
await manager.clearAllCache();
|
|
2597
|
+
} finally {
|
|
2598
|
+
await manager.dispose();
|
|
2599
|
+
}
|
|
2402
2600
|
}
|
|
2403
2601
|
|
|
2404
2602
|
export { CURRENT_SDK_VERSION, IndexedDBModelCache, MemoryModelCache, ModelManager, PPDetectionError, adaptModelManifest, clearModelCache, createOrtSession, createPPDetection, loadModelAsset, parseDetectionManifest, parseModelManifest, probeCapabilities, probePPDetectionCapabilities, resolveModelAsset, selectExecutionPlan };
|