@camstack/addon-pipeline 1.2.296 → 1.2.297
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/audio-analyzer/index.js +2 -2
- package/dist/audio-analyzer/index.mjs +2 -2
- package/dist/{default-detection-model-D1daTtqT.js → default-detection-model-B2n_SFXt.js} +368 -5
- package/dist/{default-detection-model-Co578D8C.mjs → default-detection-model-D6DypCkI.mjs} +339 -6
- package/dist/detection-pipeline/index.js +521 -217
- package/dist/detection-pipeline/index.mjs +520 -216
- package/dist/{dist-CCd0Q3nr.mjs → dist-CxHIpsQs.mjs} +53 -123
- package/dist/{dist-8up-f2TX.js → dist-DUqr1zyq.js} +64 -140
- package/dist/motion-wasm/index.js +1 -1
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/{node-lpQgHes9.js → node-Dx6DLQ1j.js} +1 -1
- package/dist/{node-DgMSXSWP.mjs → node-DyeWu78a.mjs} +1 -1
- package/dist/pipeline-runner/index.js +213 -203
- package/dist/pipeline-runner/index.mjs +213 -203
- package/dist/{process-memory-DFC_O5zE.mjs → process-memory-BVR4592X.mjs} +1 -1
- package/dist/{process-memory-CX_92V_r.js → process-memory-CMJ3NY-s.js} +1 -1
- package/dist/recorder/index.js +2 -2
- package/dist/recorder/index.mjs +2 -2
- package/dist/{segment-demux-js-DzBx6NN2.js → segment-demux-js-CmGIF_uK.js} +1 -1
- package/dist/{segment-demux-js-FZbBuk3F.mjs → segment-demux-js-Dzga-XgE.mjs} +1 -1
- package/dist/stream-broker/_stub.js +2 -2
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-C_i7oFBl.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-P71ze8cu.mjs} +2 -2
- package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DUGQKsKL.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CZkpFU-J.mjs} +1 -1
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-Dti8Ex88.mjs +26 -0
- package/dist/stream-broker/demux-worker-child.js +1 -1
- package/dist/stream-broker/demux-worker-child.mjs +1 -1
- package/dist/stream-broker/{hostInit-BPtppL3W.mjs → hostInit-CRlStbz4.mjs} +2 -2
- package/dist/stream-broker/index.js +3 -3
- package/dist/stream-broker/index.mjs +3 -3
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/package.json +1 -1
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CkbplMHA.mjs +0 -26
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { K as audioKindId, d as COCO_TO_MACRO, ht as hfModelUrl, r as AUDIO_MACRO_LABELS,
|
|
1
|
+
import { K as audioKindId, Kt as stepModelRunsOnFormat, d as COCO_80_LABELS, f as COCO_TO_MACRO, gt as inferModelProvider, ht as hfModelUrl, r as AUDIO_MACRO_LABELS, y as DEVICE_BACKEND_TO_FORMAT } from "./dist-CxHIpsQs.mjs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
//#region src/detection-pipeline/pipeline/landmark-precision-gate.ts
|
|
4
4
|
/**
|
|
@@ -1655,7 +1655,6 @@ var STEP_FACE_DETECTION = new PipelineStepBase({
|
|
|
1655
1655
|
outputClasses: ["face"],
|
|
1656
1656
|
models: [...FACE_DETECTION_MODELS],
|
|
1657
1657
|
defaultModelId: "yunet-2023mar",
|
|
1658
|
-
defaultModelIdByFormat: { tflite: "scrfd-2.5g" },
|
|
1659
1658
|
defaultConfidence: .5,
|
|
1660
1659
|
defaultMinParentScore: .7,
|
|
1661
1660
|
cadence: {
|
|
@@ -2080,17 +2079,24 @@ var LocalFrameRegistry = class {
|
|
|
2080
2079
|
image,
|
|
2081
2080
|
estimatedBytes,
|
|
2082
2081
|
refs: 1,
|
|
2082
|
+
consumerRefs: 1,
|
|
2083
2083
|
releaseNativeOnClose: false
|
|
2084
2084
|
});
|
|
2085
2085
|
this.bytes += estimatedBytes;
|
|
2086
2086
|
this.evictToBounds();
|
|
2087
2087
|
return this.entries.has(ref.id) ? ref : null;
|
|
2088
2088
|
}
|
|
2089
|
-
|
|
2089
|
+
/**
|
|
2090
|
+
* Take a lease on `ref`. `holder` says why (D656): a reader that resolves a
|
|
2091
|
+
* view now is a `consumer` (the default); a lease PARKED for a read that may
|
|
2092
|
+
* come later is `retention`, and does not keep a draining worker alive.
|
|
2093
|
+
*/
|
|
2094
|
+
acquire(ref, holder = "consumer") {
|
|
2090
2095
|
if (ref.registryId === this.registryId && !this.entries.has(ref.id)) this.staleMissCount += 1;
|
|
2091
2096
|
const entry = this.getEntry(ref);
|
|
2092
2097
|
if (!entry) return null;
|
|
2093
2098
|
entry.refs += 1;
|
|
2099
|
+
if (holder === "consumer") this.addConsumer(entry);
|
|
2094
2100
|
this.touch(entry);
|
|
2095
2101
|
let released = false;
|
|
2096
2102
|
return {
|
|
@@ -2098,17 +2104,40 @@ var LocalFrameRegistry = class {
|
|
|
2098
2104
|
release: (reason) => {
|
|
2099
2105
|
if (released) return;
|
|
2100
2106
|
released = true;
|
|
2101
|
-
this.
|
|
2107
|
+
this.releaseRef(ref, reason, holder);
|
|
2102
2108
|
}
|
|
2103
2109
|
};
|
|
2104
2110
|
}
|
|
2111
|
+
/** Release the OWNER reference {@link register} took — a consumer reference. */
|
|
2105
2112
|
release(ref, reason) {
|
|
2113
|
+
this.releaseRef(ref, reason, "consumer");
|
|
2114
|
+
}
|
|
2115
|
+
releaseRef(ref, reason, holder) {
|
|
2106
2116
|
const entry = this.getEntry(ref);
|
|
2107
2117
|
if (!entry) return;
|
|
2108
2118
|
this.releaseCount += 1;
|
|
2109
2119
|
if (releasesNative(reason)) entry.releaseNativeOnClose = true;
|
|
2110
2120
|
entry.refs = Math.max(0, entry.refs - 1);
|
|
2111
|
-
if (entry.refs === 0)
|
|
2121
|
+
if (entry.refs === 0) {
|
|
2122
|
+
this.drop(entry);
|
|
2123
|
+
return;
|
|
2124
|
+
}
|
|
2125
|
+
if (holder === "consumer") this.removeConsumer(entry);
|
|
2126
|
+
}
|
|
2127
|
+
/**
|
|
2128
|
+
* The frame's source learns whether a consumer still holds it, on each
|
|
2129
|
+
* transition only: the last consumer letting go of a frame that stays
|
|
2130
|
+
* PARKED, and a consumer coming back to one. A drop needs no signal — the
|
|
2131
|
+
* image is closed.
|
|
2132
|
+
*/
|
|
2133
|
+
addConsumer(entry) {
|
|
2134
|
+
entry.consumerRefs += 1;
|
|
2135
|
+
if (entry.consumerRefs === 1) entry.image.setPendingConsumer?.(true);
|
|
2136
|
+
}
|
|
2137
|
+
removeConsumer(entry) {
|
|
2138
|
+
if (entry.consumerRefs === 0) return;
|
|
2139
|
+
entry.consumerRefs -= 1;
|
|
2140
|
+
if (entry.consumerRefs === 0) entry.image.setPendingConsumer?.(false);
|
|
2112
2141
|
}
|
|
2113
2142
|
clear(reason = "detach") {
|
|
2114
2143
|
for (const entry of [...this.entries.values()]) {
|
|
@@ -2207,6 +2236,310 @@ function rootObjectGate(steps) {
|
|
|
2207
2236
|
return (steps?.find((s) => s.enabled !== false))?.settings;
|
|
2208
2237
|
}
|
|
2209
2238
|
//#endregion
|
|
2239
|
+
//#region src/pipeline-runner/device-jump.ts
|
|
2240
|
+
/**
|
|
2241
|
+
* Step-tree device jump (same-node, phase 1) — the PURE resolution core.
|
|
2242
|
+
*
|
|
2243
|
+
* Given the camera's elected ("effective") inference device and the node's
|
|
2244
|
+
* enabled-device roster, decide which device ONE enrichment step should run on:
|
|
2245
|
+
*
|
|
2246
|
+
* 1. A manual `jumpDeviceKey` override wins when it names a candidate that can
|
|
2247
|
+
* run the step (bypasses the auto choice; operator intent).
|
|
2248
|
+
* 2. Otherwise, if the effective device's format can run the step's model →
|
|
2249
|
+
* NO jump (the step inherits the effective device — the common case).
|
|
2250
|
+
* 3. Otherwise AUTO-jump: among the node's OTHER enabled devices whose format
|
|
2251
|
+
* can run the step (and that are under their `maxSessions` cap), pick the
|
|
2252
|
+
* weighted-least-loaded one — mirroring the balancer's per-device core.
|
|
2253
|
+
* 4. If nothing on the node can run it → `no-candidate` (the caller emits the
|
|
2254
|
+
* loud once-per-(camera,step) warn that replaces today's silent degrade).
|
|
2255
|
+
*
|
|
2256
|
+
* Framework-free + side-effect-free so the whole decision table is unit-testable
|
|
2257
|
+
* without a live runner/catalog. The `canRun(format)` predicate is injected by
|
|
2258
|
+
* the runner (which owns the model catalog); this module never imports it.
|
|
2259
|
+
*/
|
|
2260
|
+
/** The `ModelFormat` of a `<backend>:<device>` key, or `undefined` for an unknown backend. */
|
|
2261
|
+
function formatOf(deviceKey) {
|
|
2262
|
+
const colon = deviceKey.indexOf(":");
|
|
2263
|
+
return DEVICE_BACKEND_TO_FORMAT[colon >= 0 ? deviceKey.slice(0, colon) : deviceKey];
|
|
2264
|
+
}
|
|
2265
|
+
/** True when the step's model has a build for `deviceKey`'s format. */
|
|
2266
|
+
function canRunOn(deviceKey, canRun) {
|
|
2267
|
+
const format = formatOf(deviceKey);
|
|
2268
|
+
return format !== void 0 && canRun(format, deviceKey);
|
|
2269
|
+
}
|
|
2270
|
+
/** Effective weighted load of a device (session count ÷ weight); lower = preferred. */
|
|
2271
|
+
function weightedLoad(d) {
|
|
2272
|
+
return d.attachedCameras / (d.weight > 0 ? d.weight : 1);
|
|
2273
|
+
}
|
|
2274
|
+
/**
|
|
2275
|
+
* Weighted-least-loaded pick with a deterministic `deviceKey` tie-break —
|
|
2276
|
+
* mirrors the node/device balancer's decision core (least `load/weight`, ties
|
|
2277
|
+
* broken stably). `devices` is assumed non-empty.
|
|
2278
|
+
*/
|
|
2279
|
+
function pickWeightedLeastLoaded(devices) {
|
|
2280
|
+
return devices.reduce((best, d) => {
|
|
2281
|
+
const dScore = weightedLoad(d);
|
|
2282
|
+
const bScore = weightedLoad(best);
|
|
2283
|
+
if (dScore < bScore) return d;
|
|
2284
|
+
if (dScore > bScore) return best;
|
|
2285
|
+
return d.deviceKey < best.deviceKey ? d : best;
|
|
2286
|
+
});
|
|
2287
|
+
}
|
|
2288
|
+
/**
|
|
2289
|
+
* Build the `canRun` predicate for ONE enrichment dispatch. The step and its
|
|
2290
|
+
* enabled descendants ride the SAME `runPipeline` call — one pool, one format
|
|
2291
|
+
* (detail-subtree contract: "its inherited children ride the same call → same
|
|
2292
|
+
* device") — so a format can run the dispatch only when EVERY catalog-known
|
|
2293
|
+
* step in the subtree ships a build for it. Gating only the subtree TOP let a
|
|
2294
|
+
* `face-detection` with a tflite build stay on the Coral while its
|
|
2295
|
+
* `face-embedding` child (no tflite build anywhere) doomed the call.
|
|
2296
|
+
* Unknown/custom addonIds (`getStepDef` → null) have no catalog to gate on
|
|
2297
|
+
* and never block. `getStepDef` is injected — this module stays catalog-free.
|
|
2298
|
+
* Each step is judged by `stepModelRunsOnFormat` (`@camstack/types`), the one
|
|
2299
|
+
* rule every placement reads: a legacy stub build counts only when named (D657).
|
|
2300
|
+
*/
|
|
2301
|
+
function buildSubtreeCanRun(step, getStepDef) {
|
|
2302
|
+
const judged = [];
|
|
2303
|
+
const walk = (s) => {
|
|
2304
|
+
if (s.enabled === false) return;
|
|
2305
|
+
const def = getStepDef(s.addonId);
|
|
2306
|
+
if (def !== null) judged.push({
|
|
2307
|
+
def,
|
|
2308
|
+
modelId: s.modelId
|
|
2309
|
+
});
|
|
2310
|
+
for (const child of s.children ?? []) walk(child);
|
|
2311
|
+
};
|
|
2312
|
+
walk(step);
|
|
2313
|
+
return (format) => judged.every(({ def, modelId }) => stepModelRunsOnFormat(def.models, modelId, format));
|
|
2314
|
+
}
|
|
2315
|
+
/**
|
|
2316
|
+
* Resolve the device one enrichment step runs on. See the module header for the
|
|
2317
|
+
* decision table. Pure — never throws, no I/O.
|
|
2318
|
+
*/
|
|
2319
|
+
function resolveStepDevice(input) {
|
|
2320
|
+
const { effectiveDeviceKey, manualJumpDeviceKey, canRun } = input;
|
|
2321
|
+
if (manualJumpDeviceKey !== void 0 && manualJumpDeviceKey !== effectiveDeviceKey) {
|
|
2322
|
+
const target = input.devices.find((d) => d.deviceKey === manualJumpDeviceKey);
|
|
2323
|
+
if (target && canRunOn(target.deviceKey, canRun)) return {
|
|
2324
|
+
kind: "jumped",
|
|
2325
|
+
deviceKey: target.deviceKey,
|
|
2326
|
+
reason: "manual"
|
|
2327
|
+
};
|
|
2328
|
+
}
|
|
2329
|
+
if (canRunOn(effectiveDeviceKey, canRun)) return {
|
|
2330
|
+
kind: "no-jump",
|
|
2331
|
+
deviceKey: effectiveDeviceKey
|
|
2332
|
+
};
|
|
2333
|
+
const runnable = input.devices.filter((d) => d.deviceKey !== effectiveDeviceKey && (d.maxSessions === null || d.attachedCameras < d.maxSessions) && canRunOn(d.deviceKey, canRun));
|
|
2334
|
+
if (runnable.length === 0) return {
|
|
2335
|
+
kind: "no-candidate",
|
|
2336
|
+
deviceKey: effectiveDeviceKey
|
|
2337
|
+
};
|
|
2338
|
+
return {
|
|
2339
|
+
kind: "jumped",
|
|
2340
|
+
deviceKey: pickWeightedLeastLoaded(runnable).deviceKey,
|
|
2341
|
+
reason: "auto"
|
|
2342
|
+
};
|
|
2343
|
+
}
|
|
2344
|
+
/**
|
|
2345
|
+
* Re-provision a jumped subtree for its TARGET device (D650): every step's
|
|
2346
|
+
* `modelId` / `settings` are REPLACED by the target's entry — the orchestrator
|
|
2347
|
+
* computed it as what the step would carry had the camera been elected onto
|
|
2348
|
+
* that device (its per-(node,device) base with the camera's override for it).
|
|
2349
|
+
* A step absent from the map runs with neither, i.e. the target format's
|
|
2350
|
+
* default model. The elected device's provisioning never follows a step across:
|
|
2351
|
+
* it was chosen for a format this subtree does not run on.
|
|
2352
|
+
*/
|
|
2353
|
+
function provisionSubtreeForDevice(step, provisioning) {
|
|
2354
|
+
const own = provisioning[step.addonId];
|
|
2355
|
+
const { modelId: _electedModelId, settings: _electedSettings, ...rest } = step;
|
|
2356
|
+
return {
|
|
2357
|
+
...rest,
|
|
2358
|
+
...own?.modelId !== void 0 ? { modelId: own.modelId } : {},
|
|
2359
|
+
...own?.settings !== void 0 ? { settings: own.settings } : {},
|
|
2360
|
+
...step.children !== void 0 ? { children: step.children.map((c) => provisionSubtreeForDevice(c, provisioning)) } : {}
|
|
2361
|
+
};
|
|
2362
|
+
}
|
|
2363
|
+
/** The subtree as `deviceKey` would run it: the elected device runs it as sent; another takes its roster entry. */
|
|
2364
|
+
function provisionedFor(step, deviceKey, effectiveDeviceKey, roster) {
|
|
2365
|
+
if (deviceKey === effectiveDeviceKey) return step;
|
|
2366
|
+
const provisioning = roster.find((d) => d.deviceKey === deviceKey)?.steps;
|
|
2367
|
+
return provisioning === void 0 ? step : provisionSubtreeForDevice(step, provisioning);
|
|
2368
|
+
}
|
|
2369
|
+
/**
|
|
2370
|
+
* Place ONE detail subtree (D650): which same-node device runs it
|
|
2371
|
+
* ({@link resolveStepDevice}) and, when it jumps, the provisioning it runs
|
|
2372
|
+
* with there ({@link provisionSubtreeForDevice}). Pure — the caller logs.
|
|
2373
|
+
*
|
|
2374
|
+
* A roster entry without `steps` came from an orchestrator that predates D650:
|
|
2375
|
+
* the step keeps what it was sent with, as it did before.
|
|
2376
|
+
*/
|
|
2377
|
+
function placeDetailStep(input) {
|
|
2378
|
+
const { step, effectiveDeviceKey, roster } = input;
|
|
2379
|
+
if (!effectiveDeviceKey) return {
|
|
2380
|
+
deviceKey: void 0,
|
|
2381
|
+
step,
|
|
2382
|
+
decision: null,
|
|
2383
|
+
provisioned: false
|
|
2384
|
+
};
|
|
2385
|
+
if (!roster || roster.length < 2) return {
|
|
2386
|
+
deviceKey: effectiveDeviceKey,
|
|
2387
|
+
step,
|
|
2388
|
+
decision: null,
|
|
2389
|
+
provisioned: false
|
|
2390
|
+
};
|
|
2391
|
+
const decision = resolveStepDevice({
|
|
2392
|
+
effectiveDeviceKey,
|
|
2393
|
+
devices: roster.map((d) => ({
|
|
2394
|
+
deviceKey: d.deviceKey,
|
|
2395
|
+
weight: d.weight,
|
|
2396
|
+
maxSessions: d.maxSessions,
|
|
2397
|
+
attachedCameras: input.attachedByKey.get(d.deviceKey) ?? 0
|
|
2398
|
+
})),
|
|
2399
|
+
...step.jumpDeviceKey !== void 0 ? { manualJumpDeviceKey: step.jumpDeviceKey } : {},
|
|
2400
|
+
canRun: (format, deviceKey) => buildSubtreeCanRun(provisionedFor(step, deviceKey, effectiveDeviceKey, roster), input.getStepDef)(format)
|
|
2401
|
+
});
|
|
2402
|
+
if (decision.kind !== "jumped") return {
|
|
2403
|
+
deviceKey: decision.deviceKey,
|
|
2404
|
+
step,
|
|
2405
|
+
decision,
|
|
2406
|
+
provisioned: false
|
|
2407
|
+
};
|
|
2408
|
+
const provisioning = roster.find((d) => d.deviceKey === decision.deviceKey)?.steps;
|
|
2409
|
+
return {
|
|
2410
|
+
deviceKey: decision.deviceKey,
|
|
2411
|
+
step: provisioning === void 0 ? step : provisionSubtreeForDevice(step, provisioning),
|
|
2412
|
+
decision,
|
|
2413
|
+
provisioned: provisioning !== void 0
|
|
2414
|
+
};
|
|
2415
|
+
}
|
|
2416
|
+
/**
|
|
2417
|
+
* The steps whose models a camera's elected pool loads (D657).
|
|
2418
|
+
*
|
|
2419
|
+
* The frame-plane dispatch executes only root-plane steps; a detail subtree
|
|
2420
|
+
* rides it solely so its model pre-warms the elected pool. That is right only
|
|
2421
|
+
* when the subtree RUNS there. The decision is {@link placeDetailStep}'s — the
|
|
2422
|
+
* one the detail plane takes for the same subtree — so the pool that loads a
|
|
2423
|
+
* model and the pool that runs it cannot disagree:
|
|
2424
|
+
* - no decision (no elected device, or a roster of one) or `no-jump`: kept;
|
|
2425
|
+
* - `jumped` (auto or manual): left out — the detail dispatch loads it on the
|
|
2426
|
+
* TARGET pool, with the target's provisioning;
|
|
2427
|
+
* - `no-candidate`: left out — nothing on the node can load it, so a
|
|
2428
|
+
* pre-warm could only fail (the detail plane still says so, loudly).
|
|
2429
|
+
* Roots are the elected device's job and always stay. Root-plane children
|
|
2430
|
+
* stay and are walked; a detail subtree is judged whole at its top, because
|
|
2431
|
+
* its children ride its call. Pure — the caller logs.
|
|
2432
|
+
*/
|
|
2433
|
+
function stepsForElectedPool(steps, isDetailStep, place) {
|
|
2434
|
+
const delegated = [];
|
|
2435
|
+
const walk = (nodes, topLevel) => {
|
|
2436
|
+
const kept = [];
|
|
2437
|
+
for (const step of nodes) {
|
|
2438
|
+
if (!topLevel && step.enabled !== false && isDetailStep(step.addonId)) {
|
|
2439
|
+
const decision = place(step).decision;
|
|
2440
|
+
if (decision === null || decision.kind === "no-jump") kept.push(step);
|
|
2441
|
+
else delegated.push(step.addonId);
|
|
2442
|
+
continue;
|
|
2443
|
+
}
|
|
2444
|
+
kept.push(step.children !== void 0 && step.children.length > 0 ? {
|
|
2445
|
+
...step,
|
|
2446
|
+
children: walk(step.children, false)
|
|
2447
|
+
} : step);
|
|
2448
|
+
}
|
|
2449
|
+
return kept;
|
|
2450
|
+
};
|
|
2451
|
+
return {
|
|
2452
|
+
steps: walk(steps, true),
|
|
2453
|
+
delegated
|
|
2454
|
+
};
|
|
2455
|
+
}
|
|
2456
|
+
//#endregion
|
|
2457
|
+
//#region src/detection-pipeline/registry/custom-models.ts
|
|
2458
|
+
/**
|
|
2459
|
+
* Group a flat list of custom-model descriptors (as returned by the
|
|
2460
|
+
* `custom-model-registry` collection cap) into a `stepId → entries` map for
|
|
2461
|
+
* the picker / resolution union. Pure; order within a step preserved.
|
|
2462
|
+
*/
|
|
2463
|
+
function groupCustomModelsByStep(descriptors) {
|
|
2464
|
+
const byStep = /* @__PURE__ */ new Map();
|
|
2465
|
+
for (const d of descriptors) {
|
|
2466
|
+
const arr = byStep.get(d.stepId) ?? [];
|
|
2467
|
+
arr.push(d.entry);
|
|
2468
|
+
byStep.set(d.stepId, arr);
|
|
2469
|
+
}
|
|
2470
|
+
return byStep;
|
|
2471
|
+
}
|
|
2472
|
+
/**
|
|
2473
|
+
* Union a step's static catalog models with operator-registered custom
|
|
2474
|
+
* models. On an `id` collision the static catalog entry wins — a custom
|
|
2475
|
+
* model can never shadow a built-in one.
|
|
2476
|
+
*
|
|
2477
|
+
* Pure + side-effect-free so it can be unit-tested in isolation and called
|
|
2478
|
+
* from the (free) `buildSchemaSlots` builder without any addon context.
|
|
2479
|
+
*/
|
|
2480
|
+
function mergeCustomModels(staticModels, customModels) {
|
|
2481
|
+
const seen = new Set(staticModels.map((m) => m.id));
|
|
2482
|
+
const merged = [...staticModels];
|
|
2483
|
+
for (const m of customModels) {
|
|
2484
|
+
if (seen.has(m.id)) continue;
|
|
2485
|
+
seen.add(m.id);
|
|
2486
|
+
merged.push({
|
|
2487
|
+
...m,
|
|
2488
|
+
provider: inferModelProvider(m)
|
|
2489
|
+
});
|
|
2490
|
+
}
|
|
2491
|
+
return merged;
|
|
2492
|
+
}
|
|
2493
|
+
/**
|
|
2494
|
+
* A step's definition with its custom models merged in (static wins on an id
|
|
2495
|
+
* collision), or `null` for an unknown addonId. Pure.
|
|
2496
|
+
*/
|
|
2497
|
+
function mergedStepDefinition(stepId, customByStep) {
|
|
2498
|
+
let def;
|
|
2499
|
+
try {
|
|
2500
|
+
def = getStepDefinition(stepId);
|
|
2501
|
+
} catch {
|
|
2502
|
+
return null;
|
|
2503
|
+
}
|
|
2504
|
+
const custom = customByStep.get(stepId);
|
|
2505
|
+
if (custom === void 0 || custom.length === 0) return def;
|
|
2506
|
+
return {
|
|
2507
|
+
...def,
|
|
2508
|
+
models: mergeCustomModels(def.models, custom)
|
|
2509
|
+
};
|
|
2510
|
+
}
|
|
2511
|
+
/** TTL-cached read of the custom-model registry, with a synchronous merged view. */
|
|
2512
|
+
var CustomModelCatalog = class {
|
|
2513
|
+
logger;
|
|
2514
|
+
now;
|
|
2515
|
+
byStep = /* @__PURE__ */ new Map();
|
|
2516
|
+
readAtMs = Number.NEGATIVE_INFINITY;
|
|
2517
|
+
constructor(logger, now = Date.now) {
|
|
2518
|
+
this.logger = logger;
|
|
2519
|
+
this.now = now;
|
|
2520
|
+
}
|
|
2521
|
+
/** Re-read the registry when the snapshot is older than the TTL; answer the snapshot. */
|
|
2522
|
+
async refresh(api) {
|
|
2523
|
+
const now = this.now();
|
|
2524
|
+
if (now - this.readAtMs < 5e3) return this.byStep;
|
|
2525
|
+
this.readAtMs = now;
|
|
2526
|
+
if (api === void 0) return this.byStep;
|
|
2527
|
+
try {
|
|
2528
|
+
const providers = await api.addons.listCapabilityProviders.query({ capName: "custom-model-registry" });
|
|
2529
|
+
this.byStep = providers.some((p) => p.isActive) ? groupCustomModelsByStep(await api.customModelRegistry.listModels.query()) : /* @__PURE__ */ new Map();
|
|
2530
|
+
} catch (err) {
|
|
2531
|
+
this.logger.warn("custom-model-registry query failed — keeping the last custom-model snapshot", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
2532
|
+
}
|
|
2533
|
+
return this.byStep;
|
|
2534
|
+
}
|
|
2535
|
+
/** The snapshot, synchronously. */
|
|
2536
|
+
current() {
|
|
2537
|
+
return this.byStep;
|
|
2538
|
+
}
|
|
2539
|
+
/** A step's merged definition from the snapshot, synchronously. */
|
|
2540
|
+
stepDefinition = (stepId) => mergedStepDefinition(stepId, this.byStep);
|
|
2541
|
+
};
|
|
2542
|
+
//#endregion
|
|
2210
2543
|
//#region src/detection-pipeline/default-detection-model.ts
|
|
2211
2544
|
/** The object-detection step id — the only slot this resolver applies to. */
|
|
2212
2545
|
var OBJECT_DETECTION_STEP_ID = "object-detection";
|
|
@@ -2289,4 +2622,4 @@ function resolveDefaultDetectionModel(backend, device, format, getStepDef = getS
|
|
|
2289
2622
|
return getDefaultModelForFormatFromDef(def, format);
|
|
2290
2623
|
}
|
|
2291
2624
|
//#endregion
|
|
2292
|
-
export {
|
|
2625
|
+
export { resolveModelForFormat as _, buildSubtreeCanRun as a, macroGateVerdict as c, resolveFrameViewGeometry as d, ALL_PIPELINE_STEPS as f, getStepDefinition as g, getStep as h, mergeCustomModels as i, rootObjectGate as l, getDefaultModelForFormat as m, resolveDefaultDetectionModel as n, placeDetailStep as o, ALL_STEPS as p, CustomModelCatalog as r, stepsForElectedPool as s, OBJECT_DETECTION_STEP_ID as t, localFrameRegistry as u, RETIRED_MODEL_IDS as v, landmarkPrecisionVerdict as y };
|