@camstack/addon-pipeline 1.0.0 → 1.0.1
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 +141 -51
- package/dist/audio-analyzer/index.mjs +142 -51
- package/dist/decoder-nodeav/index.js +1 -1
- package/dist/decoder-nodeav/index.mjs +1 -1
- package/dist/detection-pipeline/index.js +56 -321
- package/dist/detection-pipeline/index.mjs +56 -321
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-Bak8zYXf.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-qX99--rF.mjs} +3 -3
- package/dist/stream-broker/{hostInit-CYCw2DW3.mjs → hostInit-Bx41KdYV.mjs} +3 -3
- package/dist/stream-broker/index.js +2 -2
- package/dist/stream-broker/index.mjs +2 -2
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/package.json +2 -7
- package/python/requirements-audio.txt +5 -0
- package/python/yamnet_audio.py +113 -0
- package/dist/constants-B_b0a-6h.mjs +0 -3119
- package/dist/constants-D65v6yp6.js +0 -5963
|
@@ -1955,215 +1955,6 @@ function getDefaultModelForFormat(stepId, format) {
|
|
|
1955
1955
|
})[0].id;
|
|
1956
1956
|
}
|
|
1957
1957
|
//#endregion
|
|
1958
|
-
//#region src/detection-pipeline/engine/node-engine-manager.ts
|
|
1959
|
-
var NodeEngineManager = class {
|
|
1960
|
-
backend;
|
|
1961
|
-
resolveModelPath;
|
|
1962
|
-
createEngine;
|
|
1963
|
-
engines = /* @__PURE__ */ new Map();
|
|
1964
|
-
log;
|
|
1965
|
-
constructor(backend, resolveModelPath, createEngine, logger) {
|
|
1966
|
-
this.backend = backend;
|
|
1967
|
-
this.resolveModelPath = resolveModelPath;
|
|
1968
|
-
this.createEngine = createEngine;
|
|
1969
|
-
this.log = logger;
|
|
1970
|
-
}
|
|
1971
|
-
/**
|
|
1972
|
-
* Apply a new pipeline configuration.
|
|
1973
|
-
* Loads/unloads engines for steps that changed.
|
|
1974
|
-
*/
|
|
1975
|
-
async applyConfig(newSteps) {
|
|
1976
|
-
const enabledSteps = flattenEnabledVideoSteps(newSteps);
|
|
1977
|
-
const desiredMap = /* @__PURE__ */ new Map();
|
|
1978
|
-
for (const step of enabledSteps) desiredMap.set(step.addonId, step);
|
|
1979
|
-
for (const [stepId, loaded] of this.engines) if (!desiredMap.has(stepId)) {
|
|
1980
|
-
this.log.info("Unloading ONNX engine for step", { meta: { step: stepId } });
|
|
1981
|
-
await loaded.engine.dispose();
|
|
1982
|
-
this.engines.delete(stepId);
|
|
1983
|
-
}
|
|
1984
|
-
for (const [stepId, step] of desiredMap) {
|
|
1985
|
-
const existing = this.engines.get(stepId);
|
|
1986
|
-
if (existing && existing.modelId === step.modelId) continue;
|
|
1987
|
-
if (existing) {
|
|
1988
|
-
this.log.info("Replacing ONNX engine for step", { meta: {
|
|
1989
|
-
step: stepId,
|
|
1990
|
-
fromModelId: existing.modelId,
|
|
1991
|
-
toModelId: step.modelId
|
|
1992
|
-
} });
|
|
1993
|
-
await existing.engine.dispose();
|
|
1994
|
-
} else this.log.info("Loading ONNX engine for step", { meta: {
|
|
1995
|
-
step: stepId,
|
|
1996
|
-
modelId: step.modelId
|
|
1997
|
-
} });
|
|
1998
|
-
const modelEntry = getStepDefinition(stepId).models.find((m) => m.id === step.modelId);
|
|
1999
|
-
if (!modelEntry) throw new Error(`Model "${step.modelId}" not found in step "${stepId}" catalog`);
|
|
2000
|
-
const modelPath = this.resolveModelPath(stepId, step.modelId);
|
|
2001
|
-
const meta = {
|
|
2002
|
-
inputSize: modelEntry.inputSize,
|
|
2003
|
-
inputNormalization: modelEntry.inputNormalization ?? "zero-one",
|
|
2004
|
-
inputLayout: modelEntry.inputLayout ?? "nchw",
|
|
2005
|
-
preprocessMode: modelEntry.preprocessMode ?? "letterbox"
|
|
2006
|
-
};
|
|
2007
|
-
const engine = await this.createEngine(modelPath, this.backend, meta, this.log.child(stepId));
|
|
2008
|
-
this.engines.set(stepId, {
|
|
2009
|
-
stepId,
|
|
2010
|
-
modelId: step.modelId,
|
|
2011
|
-
engine
|
|
2012
|
-
});
|
|
2013
|
-
}
|
|
2014
|
-
}
|
|
2015
|
-
/**
|
|
2016
|
-
* Get an IInferenceEngine for a step.
|
|
2017
|
-
* @throws if the step is not loaded.
|
|
2018
|
-
*/
|
|
2019
|
-
getEngine(stepId) {
|
|
2020
|
-
const loaded = this.engines.get(stepId);
|
|
2021
|
-
if (!loaded) throw new Error(`ONNX engine for step "${stepId}" is not loaded`);
|
|
2022
|
-
return loaded.engine;
|
|
2023
|
-
}
|
|
2024
|
-
isLoaded(stepId) {
|
|
2025
|
-
return this.engines.has(stepId);
|
|
2026
|
-
}
|
|
2027
|
-
isLoadedWithModel(stepId, modelId) {
|
|
2028
|
-
const loaded = this.engines.get(stepId);
|
|
2029
|
-
return loaded !== void 0 && loaded.modelId === modelId;
|
|
2030
|
-
}
|
|
2031
|
-
async loadAdditional(steps) {
|
|
2032
|
-
const enabledSteps = flattenEnabledVideoSteps(steps);
|
|
2033
|
-
for (const step of enabledSteps) {
|
|
2034
|
-
const existing = this.engines.get(step.addonId);
|
|
2035
|
-
if (existing && existing.modelId === step.modelId) continue;
|
|
2036
|
-
if (existing) {
|
|
2037
|
-
this.log.info("Replacing ONNX engine for step", { meta: {
|
|
2038
|
-
step: step.addonId,
|
|
2039
|
-
fromModelId: existing.modelId,
|
|
2040
|
-
toModelId: step.modelId
|
|
2041
|
-
} });
|
|
2042
|
-
await existing.engine.dispose();
|
|
2043
|
-
} else this.log.info("Loading additional ONNX engine for step", { meta: {
|
|
2044
|
-
step: step.addonId,
|
|
2045
|
-
modelId: step.modelId
|
|
2046
|
-
} });
|
|
2047
|
-
const modelEntry = getStepDefinition(step.addonId).models.find((m) => m.id === step.modelId);
|
|
2048
|
-
if (!modelEntry) throw new Error(`Model "${step.modelId}" not found in step "${step.addonId}" catalog`);
|
|
2049
|
-
const modelPath = this.resolveModelPath(step.addonId, step.modelId);
|
|
2050
|
-
const meta = {
|
|
2051
|
-
inputSize: modelEntry.inputSize,
|
|
2052
|
-
inputNormalization: modelEntry.inputNormalization ?? "zero-one",
|
|
2053
|
-
inputLayout: modelEntry.inputLayout ?? "nchw",
|
|
2054
|
-
preprocessMode: modelEntry.preprocessMode ?? "letterbox"
|
|
2055
|
-
};
|
|
2056
|
-
const engine = await this.createEngine(modelPath, this.backend, meta, this.log.child(step.addonId));
|
|
2057
|
-
this.engines.set(step.addonId, {
|
|
2058
|
-
stepId: step.addonId,
|
|
2059
|
-
modelId: step.modelId,
|
|
2060
|
-
engine
|
|
2061
|
-
});
|
|
2062
|
-
}
|
|
2063
|
-
}
|
|
2064
|
-
listLoaded() {
|
|
2065
|
-
return [...this.engines.values()].map((l) => ({
|
|
2066
|
-
stepId: l.stepId,
|
|
2067
|
-
modelId: l.modelId
|
|
2068
|
-
}));
|
|
2069
|
-
}
|
|
2070
|
-
async disposeAll() {
|
|
2071
|
-
for (const [, loaded] of this.engines) await loaded.engine.dispose();
|
|
2072
|
-
this.engines.clear();
|
|
2073
|
-
}
|
|
2074
|
-
};
|
|
2075
|
-
//#endregion
|
|
2076
|
-
//#region src/detection-pipeline/engine/model-shape-validator.ts
|
|
2077
|
-
/**
|
|
2078
|
-
* Runtime input-shape validator for ONNX models.
|
|
2079
|
-
*
|
|
2080
|
-
* Reads the actual graph input dimensions from a locally-downloaded
|
|
2081
|
-
* ONNX model and compares them against the catalog declaration. Used
|
|
2082
|
-
* by the engine factory at load time so a converter regression or a
|
|
2083
|
-
* stale catalog entry surfaces as a one-line warning rather than
|
|
2084
|
-
* silent incorrect inference (preprocess uses one size, the model
|
|
2085
|
-
* expects another → garbage detections).
|
|
2086
|
-
*
|
|
2087
|
-
* Only ONNX is validated here. CoreML and OpenVINO converters in our
|
|
2088
|
-
* pipeline derive shape from the same source ONNX, so a passing ONNX
|
|
2089
|
-
* check covers all three formats. If they ever diverge, extend with a
|
|
2090
|
-
* format-specific reader (Manifest.json for .mlpackage, root <input>
|
|
2091
|
-
* tag for OpenVINO XML).
|
|
2092
|
-
*/
|
|
2093
|
-
/**
|
|
2094
|
-
* Probe an ONNX file for its first input's shape. Returns null if the
|
|
2095
|
-
* file is missing or unparseable — callers should treat null as
|
|
2096
|
-
* "validation skipped" (not a mismatch).
|
|
2097
|
-
*
|
|
2098
|
-
* Implementation: uses `onnxruntime-node` to create a session and read
|
|
2099
|
-
* `inputMetadata`. The session is released immediately after probing.
|
|
2100
|
-
*/
|
|
2101
|
-
async function readOnnxInputShape(onnxPath) {
|
|
2102
|
-
if (!fs.existsSync(onnxPath)) return null;
|
|
2103
|
-
try {
|
|
2104
|
-
const session = await (await import("onnxruntime-node")).InferenceSession.create(onnxPath);
|
|
2105
|
-
const firstInputName = session.inputNames[0];
|
|
2106
|
-
if (!firstInputName) return null;
|
|
2107
|
-
const numeric = (session.inputMetadata?.[firstInputName]?.dimensions ?? []).map((d) => typeof d === "number" ? d : null);
|
|
2108
|
-
let height = null;
|
|
2109
|
-
let width = null;
|
|
2110
|
-
if (numeric.length === 4) {
|
|
2111
|
-
const candidates = numeric.map((v, i) => ({
|
|
2112
|
-
v,
|
|
2113
|
-
i
|
|
2114
|
-
})).filter((e) => typeof e.v === "number" && e.v > 4).toSorted((a, b) => b.v - a.v);
|
|
2115
|
-
if (candidates.length >= 2) {
|
|
2116
|
-
const sorted = candidates.slice(0, 2).toSorted((a, b) => a.i - b.i);
|
|
2117
|
-
height = sorted[0]?.v ?? null;
|
|
2118
|
-
width = sorted[1]?.v ?? null;
|
|
2119
|
-
}
|
|
2120
|
-
}
|
|
2121
|
-
if (typeof session.release === "function") await session.release();
|
|
2122
|
-
return {
|
|
2123
|
-
height,
|
|
2124
|
-
width
|
|
2125
|
-
};
|
|
2126
|
-
} catch {
|
|
2127
|
-
return null;
|
|
2128
|
-
}
|
|
2129
|
-
}
|
|
2130
|
-
/**
|
|
2131
|
-
* Check the catalog `expected` size against the actual ONNX input shape.
|
|
2132
|
-
* Returns a ShapeMismatch when the actual shape is known and disagrees;
|
|
2133
|
-
* returns null when the file is missing, unparseable, or matches.
|
|
2134
|
-
*/
|
|
2135
|
-
async function validateOnnxInputShape(opts) {
|
|
2136
|
-
const actual = await readOnnxInputShape(opts.modelPath);
|
|
2137
|
-
if (!actual) return null;
|
|
2138
|
-
const matchesH = actual.height === null || actual.height === opts.expected.height;
|
|
2139
|
-
const matchesW = actual.width === null || actual.width === opts.expected.width;
|
|
2140
|
-
if (matchesH && matchesW) return null;
|
|
2141
|
-
return {
|
|
2142
|
-
modelId: opts.modelId,
|
|
2143
|
-
modelPath: opts.modelPath,
|
|
2144
|
-
expected: opts.expected,
|
|
2145
|
-
actual
|
|
2146
|
-
};
|
|
2147
|
-
}
|
|
2148
|
-
/**
|
|
2149
|
-
* Convenience wrapper: validate, log on mismatch via the supplied
|
|
2150
|
-
* logger. Returns true when validation passed (or was skipped) so
|
|
2151
|
-
* callers can keep loading; mismatches do not throw — they warn.
|
|
2152
|
-
*/
|
|
2153
|
-
async function checkAndLogModelShape(opts, logger) {
|
|
2154
|
-
const mismatch = await validateOnnxInputShape(opts);
|
|
2155
|
-
if (!mismatch) return true;
|
|
2156
|
-
logger.warn("Model input shape mismatch — catalog declaration disagrees with model file", { meta: {
|
|
2157
|
-
modelId: mismatch.modelId,
|
|
2158
|
-
expectedWidth: mismatch.expected.width,
|
|
2159
|
-
expectedHeight: mismatch.expected.height,
|
|
2160
|
-
actualWidth: mismatch.actual.width,
|
|
2161
|
-
actualHeight: mismatch.actual.height,
|
|
2162
|
-
modelPath: mismatch.modelPath
|
|
2163
|
-
} });
|
|
2164
|
-
return false;
|
|
2165
|
-
}
|
|
2166
|
-
//#endregion
|
|
2167
1958
|
//#region src/detection-pipeline/engine/engine-factory.ts
|
|
2168
1959
|
var BACKEND_TO_POOL_RUNTIME = {
|
|
2169
1960
|
coreml: "coreml",
|
|
@@ -2180,32 +1971,29 @@ var RUNTIME_TO_FORMAT = {
|
|
|
2180
1971
|
var EngineFactory = class {
|
|
2181
1972
|
pool = null;
|
|
2182
1973
|
poolManager = null;
|
|
2183
|
-
nodeManager = null;
|
|
2184
1974
|
log;
|
|
2185
1975
|
opts;
|
|
2186
1976
|
constructor(opts) {
|
|
2187
1977
|
this.opts = opts;
|
|
2188
1978
|
this.log = opts.logger;
|
|
2189
1979
|
}
|
|
2190
|
-
/**
|
|
1980
|
+
/** Detection always uses the Python pool. */
|
|
2191
1981
|
get usesPythonPool() {
|
|
2192
|
-
return
|
|
1982
|
+
return true;
|
|
2193
1983
|
}
|
|
2194
1984
|
/**
|
|
2195
1985
|
* Initialize the engine layer and apply initial pipeline config.
|
|
2196
1986
|
*/
|
|
2197
1987
|
async initialize(steps) {
|
|
2198
|
-
|
|
2199
|
-
else await this.initNodeEngines(steps);
|
|
1988
|
+
await this.initPythonPool(steps);
|
|
2200
1989
|
}
|
|
2201
1990
|
/**
|
|
2202
1991
|
* Apply a new pipeline config (hot swap).
|
|
2203
1992
|
* Only loads/unloads models that changed.
|
|
2204
1993
|
*/
|
|
2205
1994
|
async applyConfig(steps) {
|
|
2206
|
-
if (this.poolManager)
|
|
2207
|
-
|
|
2208
|
-
else throw new Error("EngineFactory not initialized");
|
|
1995
|
+
if (!this.poolManager) throw new Error("EngineFactory not initialized");
|
|
1996
|
+
await this.poolManager.applyConfig(steps);
|
|
2209
1997
|
}
|
|
2210
1998
|
/**
|
|
2211
1999
|
* Get an IInferenceEngine for a pipeline step. Without `modelId`,
|
|
@@ -2216,49 +2004,35 @@ var EngineFactory = class {
|
|
|
2216
2004
|
* @throws if step not loaded with the requested (or active) model.
|
|
2217
2005
|
*/
|
|
2218
2006
|
getEngine(stepId, modelId) {
|
|
2219
|
-
if (this.poolManager)
|
|
2220
|
-
|
|
2221
|
-
throw new Error("EngineFactory not initialized");
|
|
2007
|
+
if (!this.poolManager) throw new Error("EngineFactory not initialized");
|
|
2008
|
+
return this.poolManager.getHandle(stepId, modelId);
|
|
2222
2009
|
}
|
|
2223
2010
|
/** Check if a step is loaded (any variant). */
|
|
2224
2011
|
isLoaded(stepId) {
|
|
2225
|
-
|
|
2226
|
-
if (this.nodeManager) return this.nodeManager.isLoaded(stepId);
|
|
2227
|
-
return false;
|
|
2012
|
+
return this.poolManager?.isLoaded(stepId) ?? false;
|
|
2228
2013
|
}
|
|
2229
2014
|
/** Check if a specific (stepId, modelId) variant is resident. */
|
|
2230
2015
|
isLoadedWithModel(stepId, modelId) {
|
|
2231
|
-
|
|
2232
|
-
if (this.nodeManager) return this.nodeManager.isLoadedWithModel(stepId, modelId);
|
|
2233
|
-
return false;
|
|
2016
|
+
return this.poolManager?.isLoadedWithModel(stepId, modelId) ?? false;
|
|
2234
2017
|
}
|
|
2235
2018
|
/**
|
|
2236
|
-
* List every loaded (stepId, modelId) variant — the pool
|
|
2237
|
-
*
|
|
2238
|
-
* the Node path surfaces just the active model per step.
|
|
2019
|
+
* List every loaded (stepId, modelId) variant — the pool surfaces each
|
|
2020
|
+
* warm slot independently (including bench overrides).
|
|
2239
2021
|
*/
|
|
2240
2022
|
listLoaded() {
|
|
2241
|
-
if (this.poolManager) return
|
|
2023
|
+
if (!this.poolManager) return [];
|
|
2024
|
+
return this.poolManager.getLoadedSteps().map((l) => ({
|
|
2242
2025
|
stepId: l.stepId,
|
|
2243
2026
|
modelId: l.modelId,
|
|
2244
2027
|
active: l.active
|
|
2245
2028
|
}));
|
|
2246
|
-
if (this.nodeManager) return this.nodeManager.listLoaded().map((l) => ({
|
|
2247
|
-
...l,
|
|
2248
|
-
active: true
|
|
2249
|
-
}));
|
|
2250
|
-
return [];
|
|
2251
2029
|
}
|
|
2252
2030
|
/** Native pid of the underlying Python pool, if any. */
|
|
2253
2031
|
getPoolPid() {
|
|
2254
2032
|
return this.pool?.getPid() ?? null;
|
|
2255
2033
|
}
|
|
2256
2034
|
/**
|
|
2257
|
-
* Whether this factory exposes the batched fast path
|
|
2258
|
-
* (`batchInferRaw`). Only the Python pool path supports it today —
|
|
2259
|
-
* Node.js ONNX engines run one inference per call with their own
|
|
2260
|
-
* thread pool inside InferenceSession, so batching at this layer
|
|
2261
|
-
* would just queue serially.
|
|
2035
|
+
* Whether this factory exposes the batched fast path (`batchInferRaw`).
|
|
2262
2036
|
*/
|
|
2263
2037
|
supportsBatch() {
|
|
2264
2038
|
return this.poolManager !== null;
|
|
@@ -2272,7 +2046,7 @@ var EngineFactory = class {
|
|
|
2272
2046
|
* in one IPC round-trip, amortising the per-call envelope overhead.
|
|
2273
2047
|
*/
|
|
2274
2048
|
async batchInferRaw(stepId, items, modelId) {
|
|
2275
|
-
if (!this.poolManager) throw new Error("EngineFactory.batchInferRaw: pool
|
|
2049
|
+
if (!this.poolManager) throw new Error("EngineFactory.batchInferRaw: pool not initialized");
|
|
2276
2050
|
const idx = this.poolManager.getPoolIndex(stepId, modelId);
|
|
2277
2051
|
if (idx === null) throw new Error(`EngineFactory.batchInferRaw: step "${stepId}"${modelId ? ` (model "${modelId}")` : ""} is not loaded`);
|
|
2278
2052
|
return this.poolManager.getPool().inferBatch(idx, items);
|
|
@@ -2294,7 +2068,6 @@ var EngineFactory = class {
|
|
|
2294
2068
|
/** Load additional models without unloading existing ones (for benchmark/test). */
|
|
2295
2069
|
async loadAdditional(steps) {
|
|
2296
2070
|
if (this.poolManager) await this.poolManager.loadAdditional(steps);
|
|
2297
|
-
else if (this.nodeManager) await this.nodeManager.loadAdditional(steps);
|
|
2298
2071
|
}
|
|
2299
2072
|
/** Shut down all engines and the pool process. */
|
|
2300
2073
|
async dispose() {
|
|
@@ -2303,14 +2076,10 @@ var EngineFactory = class {
|
|
|
2303
2076
|
this.pool = null;
|
|
2304
2077
|
this.poolManager = null;
|
|
2305
2078
|
}
|
|
2306
|
-
if (this.nodeManager) {
|
|
2307
|
-
await this.nodeManager.disposeAll();
|
|
2308
|
-
this.nodeManager = null;
|
|
2309
|
-
}
|
|
2310
2079
|
}
|
|
2311
2080
|
async initPythonPool(steps) {
|
|
2312
2081
|
const pythonPath = this.opts.pythonPath;
|
|
2313
|
-
if (!pythonPath) throw new Error("EngineFactory: pythonPath is required
|
|
2082
|
+
if (!pythonPath) throw new Error("EngineFactory: pythonPath is required — the addon must call ctx.deps.ensurePython() and pass the result. The embedded portable Python download likely failed; check the addon logs for the download error.");
|
|
2314
2083
|
const poolRuntime = BACKEND_TO_POOL_RUNTIME[this.opts.engine.backend];
|
|
2315
2084
|
if (!poolRuntime) throw new Error(`No pool runtime mapping for backend "${this.opts.engine.backend}"`);
|
|
2316
2085
|
const concurrency = this.opts.concurrency ?? {
|
|
@@ -2361,16 +2130,6 @@ var EngineFactory = class {
|
|
|
2361
2130
|
const filename = urlParts[urlParts.length - 1] ?? `${modelId}.${format}`;
|
|
2362
2131
|
const modelPath = `${this.opts.modelsDir}/${filename}`;
|
|
2363
2132
|
const inputSize = Math.max(modelEntry.inputSize.width, modelEntry.inputSize.height);
|
|
2364
|
-
const onnxEntry = modelEntry.formats.onnx;
|
|
2365
|
-
if (onnxEntry) {
|
|
2366
|
-
const onnxUrlParts = onnxEntry.url.split("/");
|
|
2367
|
-
const onnxFilename = onnxUrlParts[onnxUrlParts.length - 1] ?? `${modelId}.onnx`;
|
|
2368
|
-
checkAndLogModelShape({
|
|
2369
|
-
modelId,
|
|
2370
|
-
modelPath: `${this.opts.modelsDir}/${onnxFilename}`,
|
|
2371
|
-
expected: modelEntry.inputSize
|
|
2372
|
-
}, this.log);
|
|
2373
|
-
}
|
|
2374
2133
|
let labels = def.labels;
|
|
2375
2134
|
if (!labels && modelEntry.extraFiles) {
|
|
2376
2135
|
const labelsFile = modelEntry.extraFiles.find((f) => f.filename.endsWith("-labels.json"));
|
|
@@ -2397,20 +2156,6 @@ var EngineFactory = class {
|
|
|
2397
2156
|
device: this.opts.engine.device ?? (poolRuntime === "coreml" ? "all" : void 0)
|
|
2398
2157
|
};
|
|
2399
2158
|
}
|
|
2400
|
-
async initNodeEngines(steps) {
|
|
2401
|
-
if (!this.opts.createNodeEngine) throw new Error("createNodeEngine function required for nodejs+onnx backend");
|
|
2402
|
-
this.nodeManager = new NodeEngineManager(this.opts.engine.backend, (stepId, modelId) => this.resolveOnnxModelPath(stepId, modelId), this.opts.createNodeEngine, this.log.child("onnx-mgr"));
|
|
2403
|
-
await this.nodeManager.applyConfig(steps);
|
|
2404
|
-
}
|
|
2405
|
-
resolveOnnxModelPath(stepId, modelId) {
|
|
2406
|
-
const modelEntry = getStepDefinition(stepId).models.find((m) => m.id === modelId);
|
|
2407
|
-
if (!modelEntry) throw new Error(`Model "${modelId}" not found in step "${stepId}" catalog`);
|
|
2408
|
-
const onnxFormat = modelEntry.formats["onnx"];
|
|
2409
|
-
if (!onnxFormat) throw new Error(`Model "${modelId}" has no ONNX format`);
|
|
2410
|
-
const urlParts = onnxFormat.url.split("/");
|
|
2411
|
-
const filename = urlParts[urlParts.length - 1] ?? `${modelId}.onnx`;
|
|
2412
|
-
return `${this.opts.modelsDir}/${filename}`;
|
|
2413
|
-
}
|
|
2414
2159
|
};
|
|
2415
2160
|
//#endregion
|
|
2416
2161
|
//#region src/detection-pipeline/postprocess/dispatch.ts
|
|
@@ -4121,9 +3866,10 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4121
3866
|
};
|
|
4122
3867
|
} catch {}
|
|
4123
3868
|
return {
|
|
4124
|
-
runtime: "
|
|
4125
|
-
backend: "
|
|
4126
|
-
format: "onnx"
|
|
3869
|
+
runtime: "python",
|
|
3870
|
+
backend: "onnx",
|
|
3871
|
+
format: "onnx",
|
|
3872
|
+
device: "cpu"
|
|
4127
3873
|
};
|
|
4128
3874
|
}
|
|
4129
3875
|
/** Store the addon context. ctx.api is a lazy getter resolved at call time. */
|
|
@@ -4144,18 +3890,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4144
3890
|
return this.getAvailableEnginesWithDevices().map((e) => e.engine);
|
|
4145
3891
|
}
|
|
4146
3892
|
getAvailableEnginesWithDevices() {
|
|
4147
|
-
const engines = [
|
|
4148
|
-
engine: {
|
|
4149
|
-
runtime: "node",
|
|
4150
|
-
backend: "cpu",
|
|
4151
|
-
format: "onnx"
|
|
4152
|
-
},
|
|
4153
|
-
devices: [{
|
|
4154
|
-
id: "cpu",
|
|
4155
|
-
label: "CPU"
|
|
4156
|
-
}],
|
|
4157
|
-
defaultDevice: "cpu"
|
|
4158
|
-
}];
|
|
3893
|
+
const engines = [];
|
|
4159
3894
|
if (process.platform === "darwin") engines.push({
|
|
4160
3895
|
engine: {
|
|
4161
3896
|
runtime: "python",
|
|
@@ -4208,11 +3943,11 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4208
3943
|
if (audioDef.models.some((m) => m.formats[format])) {
|
|
4209
3944
|
const modelId = getDefaultModelForFormat("audio-classifier", format);
|
|
4210
3945
|
const audioEngine = modelId === "apple-soundanalysis" ? {
|
|
4211
|
-
runtime: "
|
|
3946
|
+
runtime: "python",
|
|
4212
3947
|
backend: "coreml",
|
|
4213
3948
|
format: "coreml"
|
|
4214
3949
|
} : {
|
|
4215
|
-
runtime: "
|
|
3950
|
+
runtime: "python",
|
|
4216
3951
|
backend: "cpu",
|
|
4217
3952
|
format: "onnx"
|
|
4218
3953
|
};
|
|
@@ -5381,18 +5116,19 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5381
5116
|
if (typeof runtime === "string" && typeof backend === "string" && runtime && backend) {
|
|
5382
5117
|
const storedDevice = typeof store["engineDevice"] === "string" ? String(store["engineDevice"]) : "";
|
|
5383
5118
|
const detected = DetectionPipelineProvider.detectBestEngine();
|
|
5384
|
-
if (runtime === "python" && !DetectionPipelineProvider.isPythonBackendAvailable(backend)) {
|
|
5119
|
+
if (runtime === "python" && !DetectionPipelineProvider.isPythonBackendAvailable(backend, this.executorOptions.pythonPath ?? "")) {
|
|
5385
5120
|
this.log.warn("Stored engine backend unavailable on this node — falling back to detected best", { meta: {
|
|
5386
5121
|
stored: `${runtime}/${backend}`,
|
|
5387
5122
|
fallback: `${detected.runtime}/${detected.backend}`
|
|
5388
5123
|
} });
|
|
5389
5124
|
return detected;
|
|
5390
5125
|
}
|
|
5126
|
+
const migratedBackend = runtime === "node" && backend === "cpu" ? "onnx" : backend;
|
|
5391
5127
|
const device = storedDevice || detected.device;
|
|
5392
5128
|
return {
|
|
5393
|
-
runtime:
|
|
5394
|
-
backend,
|
|
5395
|
-
format: backendToFormat(
|
|
5129
|
+
runtime: "python",
|
|
5130
|
+
backend: migratedBackend,
|
|
5131
|
+
format: backendToFormat(migratedBackend),
|
|
5396
5132
|
...device ? { device } : {}
|
|
5397
5133
|
};
|
|
5398
5134
|
}
|
|
@@ -5401,13 +5137,21 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5401
5137
|
}
|
|
5402
5138
|
/**
|
|
5403
5139
|
* Synchronous availability probe for a Python inference backend. Runs a
|
|
5404
|
-
* short
|
|
5140
|
+
* short `<python> -c "import <mod>"` with a 3s timeout. Used at
|
|
5405
5141
|
* `loadEngine` time to reject a persisted backend choice the Python
|
|
5406
5142
|
* interpreter on this host can't actually import — without this the
|
|
5407
5143
|
* detection-pipeline child process exits with code 1 the moment
|
|
5408
5144
|
* `inference_pool.py` hits its `from openvino.runtime import Core`.
|
|
5145
|
+
*
|
|
5146
|
+
* MUST probe the EMBEDDED portable interpreter (`pythonPath`, resolved via
|
|
5147
|
+
* `ctx.deps.ensurePython()`) — that's where `installPythonRequirements`
|
|
5148
|
+
* puts the backend packages and what `SharedInferencePool` actually spawns.
|
|
5149
|
+
* Probing the system `python3` checks the wrong site-packages (and on a
|
|
5150
|
+
* slim container there is no system python3 at all → every backend would
|
|
5151
|
+
* be reported unavailable, silently disabling ML). Falls back to `python3`
|
|
5152
|
+
* only when no embedded interpreter is resolved.
|
|
5409
5153
|
*/
|
|
5410
|
-
static isPythonBackendAvailable(backend) {
|
|
5154
|
+
static isPythonBackendAvailable(backend, pythonPath) {
|
|
5411
5155
|
const mod = {
|
|
5412
5156
|
coreml: "coremltools",
|
|
5413
5157
|
openvino: "openvino.runtime",
|
|
@@ -5417,7 +5161,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5417
5161
|
if (!mod) return false;
|
|
5418
5162
|
try {
|
|
5419
5163
|
const { execFileSync } = __require("node:child_process");
|
|
5420
|
-
execFileSync("python3", ["-c", `import ${mod}`], {
|
|
5164
|
+
execFileSync(pythonPath || "python3", ["-c", `import ${mod}`], {
|
|
5421
5165
|
timeout: 3e3,
|
|
5422
5166
|
stdio: "pipe"
|
|
5423
5167
|
});
|
|
@@ -5808,11 +5552,11 @@ function buildDefaultStepTree(format) {
|
|
|
5808
5552
|
makeStep("instance-segmentation", [], { enabled: false })
|
|
5809
5553
|
].filter((s) => s !== null));
|
|
5810
5554
|
const audioEngine = getDefaultModelForFormat("audio-classifier", format) === "apple-soundanalysis" ? {
|
|
5811
|
-
runtime: "
|
|
5555
|
+
runtime: "python",
|
|
5812
5556
|
backend: "coreml",
|
|
5813
5557
|
format: "coreml"
|
|
5814
5558
|
} : {
|
|
5815
|
-
runtime: "
|
|
5559
|
+
runtime: "python",
|
|
5816
5560
|
backend: "cpu",
|
|
5817
5561
|
format: "onnx"
|
|
5818
5562
|
};
|
|
@@ -5952,30 +5696,21 @@ function resolveAddonPythonDir() {
|
|
|
5952
5696
|
var RUNTIMES = [{
|
|
5953
5697
|
value: "python",
|
|
5954
5698
|
label: "Python (CoreML / OpenVINO / ONNX Runtime)"
|
|
5955
|
-
}, {
|
|
5956
|
-
value: "node",
|
|
5957
|
-
label: "Node.js (onnxruntime-node)"
|
|
5958
5699
|
}];
|
|
5959
|
-
var BACKENDS_BY_RUNTIME = {
|
|
5960
|
-
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
5964
|
-
|
|
5965
|
-
|
|
5966
|
-
|
|
5967
|
-
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
5973
|
-
],
|
|
5974
|
-
node: [{
|
|
5975
|
-
value: "cpu",
|
|
5976
|
-
label: "CPU (onnxruntime-node)"
|
|
5977
|
-
}]
|
|
5978
|
-
};
|
|
5700
|
+
var BACKENDS_BY_RUNTIME = { python: [
|
|
5701
|
+
{
|
|
5702
|
+
value: "coreml",
|
|
5703
|
+
label: "CoreML"
|
|
5704
|
+
},
|
|
5705
|
+
{
|
|
5706
|
+
value: "openvino",
|
|
5707
|
+
label: "OpenVINO"
|
|
5708
|
+
},
|
|
5709
|
+
{
|
|
5710
|
+
value: "onnx",
|
|
5711
|
+
label: "ONNX Runtime"
|
|
5712
|
+
}
|
|
5713
|
+
] };
|
|
5979
5714
|
var DEVICES_BY_BACKEND = {
|
|
5980
5715
|
coreml: [
|
|
5981
5716
|
{
|
|
@@ -6327,7 +6062,7 @@ var DetectionPipelineAddon = class extends BaseAddon {
|
|
|
6327
6062
|
...stored,
|
|
6328
6063
|
...overlay
|
|
6329
6064
|
} : stored;
|
|
6330
|
-
const runtime =
|
|
6065
|
+
const runtime = "python";
|
|
6331
6066
|
const availableBackends = await this.resolveAvailableBackends(ctx, runtime);
|
|
6332
6067
|
const runtimeBackends = availableBackends.length > 0 ? BACKENDS_BY_RUNTIME[runtime].filter((b) => availableBackends.includes(b.value)) : BACKENDS_BY_RUNTIME[runtime];
|
|
6333
6068
|
const storedBackend = typeof merged.engineBackend === "string" ? merged.engineBackend : "";
|
|
@@ -3,7 +3,7 @@ import "./dist-CYZr2fwk.mjs";
|
|
|
3
3
|
var e = {
|
|
4
4
|
"@camstack/sdk": {
|
|
5
5
|
name: "@camstack/sdk",
|
|
6
|
-
version: "1.0.
|
|
6
|
+
version: "1.0.1",
|
|
7
7
|
scope: ["default"],
|
|
8
8
|
loaded: !1,
|
|
9
9
|
from: "addon_stream_broker_widgets",
|
|
@@ -18,7 +18,7 @@ var e = {
|
|
|
18
18
|
},
|
|
19
19
|
"@camstack/types": {
|
|
20
20
|
name: "@camstack/types",
|
|
21
|
-
version: "1.0.
|
|
21
|
+
version: "1.0.1",
|
|
22
22
|
scope: ["default"],
|
|
23
23
|
loaded: !1,
|
|
24
24
|
from: "addon_stream_broker_widgets",
|
|
@@ -33,7 +33,7 @@ var e = {
|
|
|
33
33
|
},
|
|
34
34
|
"@camstack/ui-library": {
|
|
35
35
|
name: "@camstack/ui-library",
|
|
36
|
-
version: "1.0.
|
|
36
|
+
version: "1.0.1",
|
|
37
37
|
scope: ["default"],
|
|
38
38
|
loaded: !1,
|
|
39
39
|
from: "addon_stream_broker_widgets",
|
|
@@ -36,7 +36,7 @@ async function r() {
|
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
38
|
"@camstack/types": {
|
|
39
|
-
version: "1.0.
|
|
39
|
+
version: "1.0.1",
|
|
40
40
|
scope: "default",
|
|
41
41
|
shareConfig: {
|
|
42
42
|
singleton: !0,
|
|
@@ -45,7 +45,7 @@ async function r() {
|
|
|
45
45
|
}
|
|
46
46
|
},
|
|
47
47
|
"@camstack/sdk": {
|
|
48
|
-
version: "1.0.
|
|
48
|
+
version: "1.0.1",
|
|
49
49
|
scope: "default",
|
|
50
50
|
shareConfig: {
|
|
51
51
|
singleton: !0,
|
|
@@ -81,7 +81,7 @@ async function r() {
|
|
|
81
81
|
}
|
|
82
82
|
},
|
|
83
83
|
"@camstack/ui-library": {
|
|
84
|
-
version: "1.0.
|
|
84
|
+
version: "1.0.1",
|
|
85
85
|
scope: "default",
|
|
86
86
|
shareConfig: {
|
|
87
87
|
singleton: !0,
|
|
@@ -14289,7 +14289,7 @@ function getNodeAv$1() {
|
|
|
14289
14289
|
return _navPromise$1 ??= import("node-av");
|
|
14290
14290
|
}
|
|
14291
14291
|
function getConstants$1() {
|
|
14292
|
-
return _constsPromise$1 ??=
|
|
14292
|
+
return _constsPromise$1 ??= import("node-av/constants");
|
|
14293
14293
|
}
|
|
14294
14294
|
/** Convert a presentation timestamp in stream ticks to milliseconds. */
|
|
14295
14295
|
function ticksToMs$1(ptsTicks, timeBase) {
|
|
@@ -14665,7 +14665,7 @@ var AV_NOPTS_VALUE = -9223372036854775808n;
|
|
|
14665
14665
|
var _navPromise = null;
|
|
14666
14666
|
var _constsPromise = null;
|
|
14667
14667
|
var getNodeAv = () => _navPromise ??= import("node-av");
|
|
14668
|
-
var getConstants = () => _constsPromise ??=
|
|
14668
|
+
var getConstants = () => _constsPromise ??= import("node-av/constants");
|
|
14669
14669
|
function ticksToMs(pts, tb) {
|
|
14670
14670
|
return Math.round(Number(pts) * 1e3 * tb.num / tb.den);
|
|
14671
14671
|
}
|
|
@@ -14284,7 +14284,7 @@ function getNodeAv$1() {
|
|
|
14284
14284
|
return _navPromise$1 ??= import("node-av");
|
|
14285
14285
|
}
|
|
14286
14286
|
function getConstants$1() {
|
|
14287
|
-
return _constsPromise$1 ??= import("
|
|
14287
|
+
return _constsPromise$1 ??= import("node-av/constants");
|
|
14288
14288
|
}
|
|
14289
14289
|
/** Convert a presentation timestamp in stream ticks to milliseconds. */
|
|
14290
14290
|
function ticksToMs$1(ptsTicks, timeBase) {
|
|
@@ -14660,7 +14660,7 @@ var AV_NOPTS_VALUE = -9223372036854775808n;
|
|
|
14660
14660
|
var _navPromise = null;
|
|
14661
14661
|
var _constsPromise = null;
|
|
14662
14662
|
var getNodeAv = () => _navPromise ??= import("node-av");
|
|
14663
|
-
var getConstants = () => _constsPromise ??= import("
|
|
14663
|
+
var getConstants = () => _constsPromise ??= import("node-av/constants");
|
|
14664
14664
|
function ticksToMs(pts, tb) {
|
|
14665
14665
|
return Math.round(Number(pts) * 1e3 * tb.num / tb.den);
|
|
14666
14666
|
}
|
|
@@ -30,7 +30,7 @@ async function d(e) {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
async function f() {
|
|
33
|
-
return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-
|
|
33
|
+
return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-qX99--rF.mjs")).catch((e) => {
|
|
34
34
|
throw l = void 0, e;
|
|
35
35
|
}), l;
|
|
36
36
|
}
|