@camstack/addon-pipeline 1.2.328 → 1.2.329
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/THIRD_PARTY_MODELS.md +2 -2
- package/dist/audio-analyzer/index.js +2 -2
- package/dist/audio-analyzer/index.mjs +2 -2
- package/dist/{default-detection-model-1SadEKsW.js → default-detection-model-ChH7Y219.js} +15 -5
- package/dist/{default-detection-model-CMeVTcPZ.mjs → default-detection-model-j0nBpRoB.mjs} +15 -5
- package/dist/detection-pipeline/index.js +414 -12
- package/dist/detection-pipeline/index.mjs +414 -12
- package/dist/{dist-Bpub2VcJ.mjs → dist-0HoA3wAi.mjs} +10 -1
- package/dist/{dist-wJO7mXeO.js → dist-BlIWhjnu.js} +10 -1
- package/dist/motion-wasm/index.js +1 -1
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/{node-BCrqaejt.js → node-BPpVMnZy.js} +1 -1
- package/dist/{node-BjCSgD4M.mjs → node-HOMWRPzW.mjs} +1 -1
- package/dist/pipeline-runner/index.js +3 -3
- package/dist/pipeline-runner/index.mjs +3 -3
- package/dist/{process-memory-iUJuTpXs.js → process-memory-BW06vOIQ.js} +1 -1
- package/dist/{process-memory-BaKQ8u2G.mjs → process-memory-DuF8pa2w.mjs} +1 -1
- package/dist/recorder/index.js +2 -2
- package/dist/recorder/index.mjs +2 -2
- package/dist/{segment-demux-js-C5ez9LYG.mjs → segment-demux-js-BU2MXpln.mjs} +1 -1
- package/dist/{segment-demux-js-Opx6Hv9G.js → segment-demux-js-Dl52Zz8w.js} +1 -1
- package/dist/stream-broker/_stub.js +1 -1
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-B94LgsV0.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-vNfS65In.mjs} +3 -3
- package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BPWYf4_O.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DfeHvW4k.mjs} +1 -1
- 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-xOx7AvgW.mjs → hostInit-Ciacp783.mjs} +3 -3
- 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/python/edgetpu_dequant.py +38 -1
- package/python/inference_pool.py +31 -17
- package/python/postprocessors/test_yolox_plate_edgetpu.py +126 -0
- package/python/postprocessors/test_yunet_edgetpu.py +149 -0
- package/python/postprocessors/testdata/yolox_plate_int8_outputs.json +1 -0
- package/python/postprocessors/testdata/yunet_int8_outputs.json +1 -0
- package/python/postprocessors/yolo.py +44 -0
package/THIRD_PARTY_MODELS.md
CHANGED
|
@@ -134,7 +134,7 @@ entry kept resolvable for a saved selection but no longer offered.
|
|
|
134
134
|
|
|
135
135
|
| Catalog ids | Upstream | Code / weights | Training data | CamStack changes | Downloaded from |
|
|
136
136
|
| --- | --- | --- | --- | --- | --- |
|
|
137
|
-
| `yunet-2023mar` (default) | [OpenCV Zoo YuNet](https://github.com/opencv/opencv_zoo/tree/main/models/face_detection_yunet) | BSD-3-Clause (`BSD-3-Clause.txt`) / MIT (`MIT-yunet-reference.txt`) | WIDER FACE: research-oriented terms; images CC BY-NC-ND | input contract baked into the graph (the pool RGB /255 becomes the upstream BGR 0..255: Mul(255) plus a channel Gather); internal tensor names cleared in the OpenVINO IR; ONNX fp32, OpenVINO IR fp32, CoreML fp16 (scripts/build-yunet-model.py) | mirror `faceDetection/yunet` |
|
|
137
|
+
| `yunet-2023mar` (default) | [OpenCV Zoo YuNet](https://github.com/opencv/opencv_zoo/tree/main/models/face_detection_yunet) | BSD-3-Clause (`BSD-3-Clause.txt`) / MIT (`MIT-yunet-reference.txt`) | WIDER FACE: research-oriented terms; images CC BY-NC-ND | input contract baked into the graph (the pool RGB /255 becomes the upstream BGR 0..255: Mul(255) plus a channel Gather); internal tensor names cleared in the OpenVINO IR; ONNX fp32, OpenVINO IR fp32, CoreML fp16 (scripts/build-yunet-model.py); Edge TPU build: the bake folded into the first Conv, full-integer int8 post-training quantisation calibrated on CamStack person crops, compiled with edgetpu_compiler 14 (scripts/build-yunet-edgetpu.py) | mirror `faceDetection/yunet` |
|
|
138
138
|
|
|
139
139
|
### Face recognition
|
|
140
140
|
|
|
@@ -146,7 +146,7 @@ entry kept resolvable for a saved selection but no longer offered.
|
|
|
146
146
|
|
|
147
147
|
| Catalog ids | Upstream | Code / weights | Training data | CamStack changes | Downloaded from |
|
|
148
148
|
| --- | --- | --- | --- | --- | --- |
|
|
149
|
-
| `yolox-tiny-plate` (default) | [open-lpr models (faisalthaheem/open-lpr-models), YOLOX-tiny](https://huggingface.co/faisalthaheem/open-lpr-models) | Apache-2.0 / Apache-2.0 (`Apache-2.0.txt`) | Simanno annotated plate corpus plus community-contributed photographs, pseudo-labelled by a teacher model (not published; the upstream NOTICE says the photographs were uploaded without consent obtained): UNKNOWN | input contract baked into the graph (static 1x3x640x640 RGB /255 becomes the upstream BGR 0..255: Mul(255) plus a channel Gather) and the output reshaped to the YOLOv8 layout (cx, cy, w, h, obj*cls); ONNX fp32, OpenVINO IR fp16, CoreML fp16 (scripts/build-yolox-plate-model.py) | mirror `plateDetection/yolox-tiny-plate` |
|
|
149
|
+
| `yolox-tiny-plate` (default) | [open-lpr models (faisalthaheem/open-lpr-models), YOLOX-tiny](https://huggingface.co/faisalthaheem/open-lpr-models) | Apache-2.0 / Apache-2.0 (`Apache-2.0.txt`) | Simanno annotated plate corpus plus community-contributed photographs, pseudo-labelled by a teacher model (not published; the upstream NOTICE says the photographs were uploaded without consent obtained): UNKNOWN | input contract baked into the graph (static 1x3x640x640 RGB /255 becomes the upstream BGR 0..255: Mul(255) plus a channel Gather) and the output reshaped to the YOLOv8 layout (cx, cy, w, h, obj*cls); ONNX fp32, OpenVINO IR fp16, CoreML fp16 (scripts/build-yolox-plate-model.py); Edge TPU build: the Focus stem rewritten as the equivalent 6x6 stride-2 Conv with the input bake folded into it, the graph cut before the grid decode (raw per-stride head), full-integer int8 post-training quantisation calibrated on CamStack vehicle crops, compiled with edgetpu_compiler 14 (scripts/build-yolox-plate-edgetpu.py) | mirror `plateDetection/yolox-tiny-plate` |
|
|
150
150
|
|
|
151
151
|
### Licence plate reading
|
|
152
152
|
|
|
@@ -3,8 +3,8 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../chunk-emK7D4bc.js");
|
|
6
|
-
const require_dist = require("../dist-
|
|
7
|
-
const require_process_memory = require("../process-memory-
|
|
6
|
+
const require_dist = require("../dist-BlIWhjnu.js");
|
|
7
|
+
const require_process_memory = require("../process-memory-BW06vOIQ.js");
|
|
8
8
|
let node_fs = require("node:fs");
|
|
9
9
|
node_fs = require_chunk.__toESM(node_fs);
|
|
10
10
|
let node_path = require("node:path");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as __require } from "../chunk-DnnnRqeS.mjs";
|
|
2
|
-
import { An as hydrateSchema, F as PoolMemoryWatchdog, J as audioAnalysisCapability, Pn as nodePin, Sn as BaseAddon, Tn as audioChunkBytesPerSample, Y as audioAnalyzerCapability, Zn as EventCategory, bn as errMsg, en as resolvePoolMemoryPolicy, k as HF_BASE_URL, kn as expandAudioChunkToF32le, kt as mapAudioLabelToMacro, m as DEFAULT_AUDIO_ANALYZER_CONFIG, r as AUDIO_BACKEND_CHOICES } from "../dist-
|
|
3
|
-
import { n as pickNodePlatformArch, t as readProcessMemory } from "../process-memory-
|
|
2
|
+
import { An as hydrateSchema, F as PoolMemoryWatchdog, J as audioAnalysisCapability, Pn as nodePin, Sn as BaseAddon, Tn as audioChunkBytesPerSample, Y as audioAnalyzerCapability, Zn as EventCategory, bn as errMsg, en as resolvePoolMemoryPolicy, k as HF_BASE_URL, kn as expandAudioChunkToF32le, kt as mapAudioLabelToMacro, m as DEFAULT_AUDIO_ANALYZER_CONFIG, r as AUDIO_BACKEND_CHOICES } from "../dist-0HoA3wAi.mjs";
|
|
3
|
+
import { n as pickNodePlatformArch, t as readProcessMemory } from "../process-memory-DuF8pa2w.mjs";
|
|
4
4
|
import * as fs from "node:fs";
|
|
5
5
|
import * as path$1 from "node:path";
|
|
6
6
|
import { downloadFile } from "@camstack/system/addon-utils";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_dist = require("./dist-
|
|
1
|
+
const require_dist = require("./dist-BlIWhjnu.js");
|
|
2
2
|
const require_worker_protocol = require("./worker-protocol-C-G8qmye.js");
|
|
3
3
|
let node_crypto = require("node:crypto");
|
|
4
4
|
//#region src/detection-pipeline/registry/effective-confidence.ts
|
|
@@ -90,7 +90,7 @@ var YUNET_MIT = {
|
|
|
90
90
|
},
|
|
91
91
|
url: "https://github.com/opencv/opencv_zoo/blob/main/models/face_detection_yunet/LICENSE",
|
|
92
92
|
attribution: "YuNet weights Copyright (c) 2020 Shiqi Yu, MIT. Training code: ShiqiYu/libfacedetection.train, BSD-3-Clause.",
|
|
93
|
-
modifications: "input contract baked into the graph (the pool RGB /255 becomes the upstream BGR 0..255: Mul(255) plus a channel Gather); internal tensor names cleared in the OpenVINO IR; ONNX fp32, OpenVINO IR fp32, CoreML fp16 (scripts/build-yunet-model.py)",
|
|
93
|
+
modifications: "input contract baked into the graph (the pool RGB /255 becomes the upstream BGR 0..255: Mul(255) plus a channel Gather); internal tensor names cleared in the OpenVINO IR; ONNX fp32, OpenVINO IR fp32, CoreML fp16 (scripts/build-yunet-model.py); Edge TPU build: the bake folded into the first Conv, full-integer int8 post-training quantisation calibrated on CamStack person crops, compiled with edgetpu_compiler 14 (scripts/build-yunet-edgetpu.py)",
|
|
94
94
|
hosting: "camstack-hf"
|
|
95
95
|
};
|
|
96
96
|
var AURAFACE_APACHE = {
|
|
@@ -125,7 +125,7 @@ var OPEN_LPR_YOLOX_APACHE = {
|
|
|
125
125
|
},
|
|
126
126
|
url: "https://huggingface.co/faisalthaheem/open-lpr-models/blob/aed0df35ec2891c5a90437041ebeadb592cc516b/LICENSE",
|
|
127
127
|
attribution: "open-lpr models, Copyright the open-lpr contributors, Apache-2.0 (https://huggingface.co/faisalthaheem/open-lpr-models, tag detector-2026.10.1). YOLOX architecture and COCO pretraining by Megvii (https://github.com/Megvii-BaseDetection/YOLOX), Apache-2.0. The upstream NOTICE is reproduced verbatim (`NOTICE-open-lpr.txt`).",
|
|
128
|
-
modifications: "input contract baked into the graph (static 1x3x640x640 RGB /255 becomes the upstream BGR 0..255: Mul(255) plus a channel Gather) and the output reshaped to the YOLOv8 layout (cx, cy, w, h, obj*cls); ONNX fp32, OpenVINO IR fp16, CoreML fp16 (scripts/build-yolox-plate-model.py)",
|
|
128
|
+
modifications: "input contract baked into the graph (static 1x3x640x640 RGB /255 becomes the upstream BGR 0..255: Mul(255) plus a channel Gather) and the output reshaped to the YOLOv8 layout (cx, cy, w, h, obj*cls); ONNX fp32, OpenVINO IR fp16, CoreML fp16 (scripts/build-yolox-plate-model.py); Edge TPU build: the Focus stem rewritten as the equivalent 6x6 stride-2 Conv with the input bake folded into it, the graph cut before the grid decode (raw per-stride head), full-integer int8 post-training quantisation calibrated on CamStack vehicle crops, compiled with edgetpu_compiler 14 (scripts/build-yolox-plate-edgetpu.py)",
|
|
129
129
|
hosting: "camstack-hf"
|
|
130
130
|
};
|
|
131
131
|
var FAST_PLATE_OCR_MIT = {
|
|
@@ -752,7 +752,12 @@ var FACE_DETECTION_MODELS = [{
|
|
|
752
752
|
files: [...MLPACKAGE_FILES],
|
|
753
753
|
runtimes: ["python"]
|
|
754
754
|
},
|
|
755
|
-
openvino: ovFormat(hf("faceDetection/yunet/openvino/camstack-yunet-2023mar.xml"), .36)
|
|
755
|
+
openvino: ovFormat(hf("faceDetection/yunet/openvino/camstack-yunet-2023mar.xml"), .36),
|
|
756
|
+
tflite: {
|
|
757
|
+
url: hf("faceDetection/yunet/tflite/camstack-yunet-2023mar_edgetpu.tflite"),
|
|
758
|
+
sizeMB: .7,
|
|
759
|
+
runtimes: ["python"]
|
|
760
|
+
}
|
|
756
761
|
}
|
|
757
762
|
}];
|
|
758
763
|
var FACE_EMBEDDING_MODELS = [{
|
|
@@ -812,7 +817,12 @@ var PLATE_DETECTION_MODELS = [{
|
|
|
812
817
|
files: [...MLPACKAGE_FILES],
|
|
813
818
|
runtimes: ["python"]
|
|
814
819
|
},
|
|
815
|
-
openvino: ovFormat(hf("plateDetection/yolox-tiny-plate/openvino/camstack-yolox-tiny-plate.xml"), 10)
|
|
820
|
+
openvino: ovFormat(hf("plateDetection/yolox-tiny-plate/openvino/camstack-yolox-tiny-plate.xml"), 10),
|
|
821
|
+
tflite: {
|
|
822
|
+
url: hf("plateDetection/yolox-tiny-plate/tflite/camstack-yolox-tiny-plate_edgetpu.tflite"),
|
|
823
|
+
sizeMB: 6.9,
|
|
824
|
+
runtimes: ["python"]
|
|
825
|
+
}
|
|
816
826
|
}
|
|
817
827
|
}];
|
|
818
828
|
var PACKAGE_DETECTION_MODELS = [{
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as LABEL_LINK_POLICIES, Ct as inferModelProvider, E as FILLABLE_STEP_VOCABULARIES, St as hfModelUrl, X as audioKindId, b as DEVICE_BACKEND_TO_FORMAT, f as COCO_80_LABELS, i as AUDIO_MACRO_LABELS, j as LABEL_LINK_SETTING_KEY, p as COCO_TO_MACRO, rn as stepModelRunsOnFormat, v as DEFAULT_LABEL_LINK_POLICY } from "./dist-
|
|
1
|
+
import { A as LABEL_LINK_POLICIES, Ct as inferModelProvider, E as FILLABLE_STEP_VOCABULARIES, St as hfModelUrl, X as audioKindId, b as DEVICE_BACKEND_TO_FORMAT, f as COCO_80_LABELS, i as AUDIO_MACRO_LABELS, j as LABEL_LINK_SETTING_KEY, p as COCO_TO_MACRO, rn as stepModelRunsOnFormat, v as DEFAULT_LABEL_LINK_POLICY } from "./dist-0HoA3wAi.mjs";
|
|
2
2
|
import { t as isWorkerReply } from "./worker-protocol-D_NzPcnh.mjs";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
//#region src/detection-pipeline/registry/effective-confidence.ts
|
|
@@ -90,7 +90,7 @@ var YUNET_MIT = {
|
|
|
90
90
|
},
|
|
91
91
|
url: "https://github.com/opencv/opencv_zoo/blob/main/models/face_detection_yunet/LICENSE",
|
|
92
92
|
attribution: "YuNet weights Copyright (c) 2020 Shiqi Yu, MIT. Training code: ShiqiYu/libfacedetection.train, BSD-3-Clause.",
|
|
93
|
-
modifications: "input contract baked into the graph (the pool RGB /255 becomes the upstream BGR 0..255: Mul(255) plus a channel Gather); internal tensor names cleared in the OpenVINO IR; ONNX fp32, OpenVINO IR fp32, CoreML fp16 (scripts/build-yunet-model.py)",
|
|
93
|
+
modifications: "input contract baked into the graph (the pool RGB /255 becomes the upstream BGR 0..255: Mul(255) plus a channel Gather); internal tensor names cleared in the OpenVINO IR; ONNX fp32, OpenVINO IR fp32, CoreML fp16 (scripts/build-yunet-model.py); Edge TPU build: the bake folded into the first Conv, full-integer int8 post-training quantisation calibrated on CamStack person crops, compiled with edgetpu_compiler 14 (scripts/build-yunet-edgetpu.py)",
|
|
94
94
|
hosting: "camstack-hf"
|
|
95
95
|
};
|
|
96
96
|
var AURAFACE_APACHE = {
|
|
@@ -125,7 +125,7 @@ var OPEN_LPR_YOLOX_APACHE = {
|
|
|
125
125
|
},
|
|
126
126
|
url: "https://huggingface.co/faisalthaheem/open-lpr-models/blob/aed0df35ec2891c5a90437041ebeadb592cc516b/LICENSE",
|
|
127
127
|
attribution: "open-lpr models, Copyright the open-lpr contributors, Apache-2.0 (https://huggingface.co/faisalthaheem/open-lpr-models, tag detector-2026.10.1). YOLOX architecture and COCO pretraining by Megvii (https://github.com/Megvii-BaseDetection/YOLOX), Apache-2.0. The upstream NOTICE is reproduced verbatim (`NOTICE-open-lpr.txt`).",
|
|
128
|
-
modifications: "input contract baked into the graph (static 1x3x640x640 RGB /255 becomes the upstream BGR 0..255: Mul(255) plus a channel Gather) and the output reshaped to the YOLOv8 layout (cx, cy, w, h, obj*cls); ONNX fp32, OpenVINO IR fp16, CoreML fp16 (scripts/build-yolox-plate-model.py)",
|
|
128
|
+
modifications: "input contract baked into the graph (static 1x3x640x640 RGB /255 becomes the upstream BGR 0..255: Mul(255) plus a channel Gather) and the output reshaped to the YOLOv8 layout (cx, cy, w, h, obj*cls); ONNX fp32, OpenVINO IR fp16, CoreML fp16 (scripts/build-yolox-plate-model.py); Edge TPU build: the Focus stem rewritten as the equivalent 6x6 stride-2 Conv with the input bake folded into it, the graph cut before the grid decode (raw per-stride head), full-integer int8 post-training quantisation calibrated on CamStack vehicle crops, compiled with edgetpu_compiler 14 (scripts/build-yolox-plate-edgetpu.py)",
|
|
129
129
|
hosting: "camstack-hf"
|
|
130
130
|
};
|
|
131
131
|
var FAST_PLATE_OCR_MIT = {
|
|
@@ -752,7 +752,12 @@ var FACE_DETECTION_MODELS = [{
|
|
|
752
752
|
files: [...MLPACKAGE_FILES],
|
|
753
753
|
runtimes: ["python"]
|
|
754
754
|
},
|
|
755
|
-
openvino: ovFormat(hf("faceDetection/yunet/openvino/camstack-yunet-2023mar.xml"), .36)
|
|
755
|
+
openvino: ovFormat(hf("faceDetection/yunet/openvino/camstack-yunet-2023mar.xml"), .36),
|
|
756
|
+
tflite: {
|
|
757
|
+
url: hf("faceDetection/yunet/tflite/camstack-yunet-2023mar_edgetpu.tflite"),
|
|
758
|
+
sizeMB: .7,
|
|
759
|
+
runtimes: ["python"]
|
|
760
|
+
}
|
|
756
761
|
}
|
|
757
762
|
}];
|
|
758
763
|
var FACE_EMBEDDING_MODELS = [{
|
|
@@ -812,7 +817,12 @@ var PLATE_DETECTION_MODELS = [{
|
|
|
812
817
|
files: [...MLPACKAGE_FILES],
|
|
813
818
|
runtimes: ["python"]
|
|
814
819
|
},
|
|
815
|
-
openvino: ovFormat(hf("plateDetection/yolox-tiny-plate/openvino/camstack-yolox-tiny-plate.xml"), 10)
|
|
820
|
+
openvino: ovFormat(hf("plateDetection/yolox-tiny-plate/openvino/camstack-yolox-tiny-plate.xml"), 10),
|
|
821
|
+
tflite: {
|
|
822
|
+
url: hf("plateDetection/yolox-tiny-plate/tflite/camstack-yolox-tiny-plate_edgetpu.tflite"),
|
|
823
|
+
sizeMB: 6.9,
|
|
824
|
+
runtimes: ["python"]
|
|
825
|
+
}
|
|
816
826
|
}
|
|
817
827
|
}];
|
|
818
828
|
var PACKAGE_DETECTION_MODELS = [{
|
|
@@ -3,11 +3,11 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../chunk-emK7D4bc.js");
|
|
6
|
-
const require_dist = require("../dist-
|
|
7
|
-
const require_node = require("../node-
|
|
8
|
-
const require_default_detection_model = require("../default-detection-model-
|
|
6
|
+
const require_dist = require("../dist-BlIWhjnu.js");
|
|
7
|
+
const require_node = require("../node-BPpVMnZy.js");
|
|
8
|
+
const require_default_detection_model = require("../default-detection-model-ChH7Y219.js");
|
|
9
9
|
const require_lazy_sharp = require("../lazy-sharp-BuwKlQLR.js");
|
|
10
|
-
const require_process_memory = require("../process-memory-
|
|
10
|
+
const require_process_memory = require("../process-memory-BW06vOIQ.js");
|
|
11
11
|
let node_os = require("node:os");
|
|
12
12
|
node_os = require_chunk.__toESM(node_os);
|
|
13
13
|
let node_fs = require("node:fs");
|
|
@@ -2816,6 +2816,8 @@ var PipelineModelManager = class {
|
|
|
2816
2816
|
stepId,
|
|
2817
2817
|
modelId,
|
|
2818
2818
|
poolIndex: index,
|
|
2819
|
+
path: config.path,
|
|
2820
|
+
...settings !== void 0 ? { settings } : {},
|
|
2819
2821
|
lruTick: ++this.lruClock,
|
|
2820
2822
|
decode
|
|
2821
2823
|
};
|
|
@@ -2823,6 +2825,56 @@ var PipelineModelManager = class {
|
|
|
2823
2825
|
return entry;
|
|
2824
2826
|
}
|
|
2825
2827
|
/**
|
|
2828
|
+
* Reload every resident slot whose model FILE is no longer the one
|
|
2829
|
+
* `buildConfig` names — a Coral pool that switched co-compiled set (D724).
|
|
2830
|
+
* The new slot is loaded BEFORE the old one is unloaded and the entry is
|
|
2831
|
+
* replaced in between, so a lookup always finds a loaded index. Returns the
|
|
2832
|
+
* `(step, model)` pairs reloaded. A failed reload keeps the old slot (it
|
|
2833
|
+
* still serves, from the file it was loaded from) and is logged.
|
|
2834
|
+
*/
|
|
2835
|
+
async reloadChangedPaths() {
|
|
2836
|
+
const reloaded = [];
|
|
2837
|
+
for (const [stepId, perStep] of this.loaded) for (const entry of perStep.values()) {
|
|
2838
|
+
const config = this.source.buildConfig(stepId, entry.modelId, entry.settings);
|
|
2839
|
+
if (config.path === entry.path) continue;
|
|
2840
|
+
const index = this.pool.allocateIndex();
|
|
2841
|
+
try {
|
|
2842
|
+
await this.pool.loadModel(index, config);
|
|
2843
|
+
} catch (err) {
|
|
2844
|
+
this.pool.releaseIndex(index);
|
|
2845
|
+
this.log.error("Step variant reload from another model file failed — the old file keeps serving", { meta: {
|
|
2846
|
+
step: stepId,
|
|
2847
|
+
modelId: entry.modelId,
|
|
2848
|
+
deviceKey: this.deviceKey,
|
|
2849
|
+
from: entry.path,
|
|
2850
|
+
to: config.path,
|
|
2851
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2852
|
+
} });
|
|
2853
|
+
continue;
|
|
2854
|
+
}
|
|
2855
|
+
perStep.set(entry.modelId, {
|
|
2856
|
+
...entry,
|
|
2857
|
+
poolIndex: index,
|
|
2858
|
+
path: config.path,
|
|
2859
|
+
decode: poolDecodeSettingsOf(config)
|
|
2860
|
+
});
|
|
2861
|
+
await this.pool.unloadModel(entry.poolIndex);
|
|
2862
|
+
this.log.info("Step variant reloaded from another model file", { meta: {
|
|
2863
|
+
step: stepId,
|
|
2864
|
+
modelId: entry.modelId,
|
|
2865
|
+
deviceKey: this.deviceKey,
|
|
2866
|
+
from: entry.path,
|
|
2867
|
+
to: config.path,
|
|
2868
|
+
poolIndex: index
|
|
2869
|
+
} });
|
|
2870
|
+
reloaded.push({
|
|
2871
|
+
stepId,
|
|
2872
|
+
modelId: entry.modelId
|
|
2873
|
+
});
|
|
2874
|
+
}
|
|
2875
|
+
return reloaded;
|
|
2876
|
+
}
|
|
2877
|
+
/**
|
|
2826
2878
|
* Push a retuned decode setting into ONE already-loaded slot.
|
|
2827
2879
|
*
|
|
2828
2880
|
* Scope is the point: `reconfigure` names a single pool index, so a slider
|
|
@@ -2981,6 +3033,251 @@ function deviceKeyOf(engine) {
|
|
|
2981
3033
|
return engine.device ? `${engine.backend}:${engine.device}` : engine.backend;
|
|
2982
3034
|
}
|
|
2983
3035
|
//#endregion
|
|
3036
|
+
//#region src/detection-pipeline/engine/coral-cocompiled-set.ts
|
|
3037
|
+
var ROOT_DETECTOR_STEP_ID = "object-detection";
|
|
3038
|
+
/** The models a dispatch places on the pool that runs it: every enabled video step. */
|
|
3039
|
+
function placedModelsOf(steps) {
|
|
3040
|
+
return flattenEnabledVideoSteps(steps).map((s) => ({
|
|
3041
|
+
stepId: s.addonId,
|
|
3042
|
+
modelId: s.modelId
|
|
3043
|
+
}));
|
|
3044
|
+
}
|
|
3045
|
+
var sameModel = (a, b) => a.stepId === b.stepId && a.modelId === b.modelId;
|
|
3046
|
+
/**
|
|
3047
|
+
* The set for `placed`, or why there is none.
|
|
3048
|
+
*
|
|
3049
|
+
* 1. A set that reserves the cache for the root detector is eligible only when
|
|
3050
|
+
* the root is placed here, and a rootless set only when it is not: a root
|
|
3051
|
+
* set on a pool without the root wastes the cache the root would have had,
|
|
3052
|
+
* and a rootless set beside a root makes the root swap.
|
|
3053
|
+
* 2. A set must stand for at least two placed models — one model alone is
|
|
3054
|
+
* better off with the whole cache (its separately compiled file).
|
|
3055
|
+
* 3. Most placed models covered wins; then the set with fewest members nobody
|
|
3056
|
+
* placed (they hold cache for nothing); then declaration order.
|
|
3057
|
+
*/
|
|
3058
|
+
function selectCoCompiledSet(placed, sets) {
|
|
3059
|
+
if (placed.length === 0) return {
|
|
3060
|
+
kind: "separate",
|
|
3061
|
+
reason: "nothing-placed"
|
|
3062
|
+
};
|
|
3063
|
+
if (placed.length === 1) return {
|
|
3064
|
+
kind: "separate",
|
|
3065
|
+
reason: "single-model"
|
|
3066
|
+
};
|
|
3067
|
+
const hostsRoot = placed.some((p) => p.stepId === ROOT_DETECTOR_STEP_ID);
|
|
3068
|
+
let best = null;
|
|
3069
|
+
for (const set of sets) {
|
|
3070
|
+
if (set.hostsRoot !== hostsRoot) continue;
|
|
3071
|
+
const covered = placed.filter((p) => set.members.some((m) => sameModel(m, p)));
|
|
3072
|
+
if (covered.length < 2) continue;
|
|
3073
|
+
const idle = set.members.length - covered.length;
|
|
3074
|
+
if (best === null || covered.length > best.covered.length || covered.length === best.covered.length && idle < best.idle) best = {
|
|
3075
|
+
set,
|
|
3076
|
+
covered,
|
|
3077
|
+
idle
|
|
3078
|
+
};
|
|
3079
|
+
}
|
|
3080
|
+
if (best === null) return {
|
|
3081
|
+
kind: "separate",
|
|
3082
|
+
reason: "no-set-covers-two"
|
|
3083
|
+
};
|
|
3084
|
+
const { set, covered } = best;
|
|
3085
|
+
return {
|
|
3086
|
+
kind: "set",
|
|
3087
|
+
set,
|
|
3088
|
+
covered,
|
|
3089
|
+
uncovered: placed.filter((p) => !covered.some((c) => sameModel(c, p)))
|
|
3090
|
+
};
|
|
3091
|
+
}
|
|
3092
|
+
/** The member a selection loads `(stepId, modelId)` from, or `null` (its own `formats.tflite`). */
|
|
3093
|
+
function setMemberFor(selection, stepId, modelId) {
|
|
3094
|
+
if (selection.kind !== "set") return null;
|
|
3095
|
+
return selection.set.members.find((m) => m.stepId === stepId && m.modelId === modelId) ?? null;
|
|
3096
|
+
}
|
|
3097
|
+
/**
|
|
3098
|
+
* `entry` with its `tflite` build pointed at the set's file. Everything else —
|
|
3099
|
+
* labels, postprocessor, input contract, licence — is the entry's own: a
|
|
3100
|
+
* member is the same model, compiled together with others.
|
|
3101
|
+
*/
|
|
3102
|
+
function withSetBuild(entry, member) {
|
|
3103
|
+
return {
|
|
3104
|
+
...entry,
|
|
3105
|
+
formats: {
|
|
3106
|
+
...entry.formats,
|
|
3107
|
+
tflite: {
|
|
3108
|
+
url: member.url,
|
|
3109
|
+
sizeMB: member.sizeMB,
|
|
3110
|
+
runtimes: ["python"]
|
|
3111
|
+
}
|
|
3112
|
+
}
|
|
3113
|
+
};
|
|
3114
|
+
}
|
|
3115
|
+
/** Identity of a selection for change detection: the set AND what it leaves out. */
|
|
3116
|
+
var selectionKey = (s) => s.kind === "set" ? `${s.set.id}|${s.uncovered.map((u) => `${u.stepId}/${u.modelId}`).toSorted().join(",")}` : `separate:${s.reason}`;
|
|
3117
|
+
/**
|
|
3118
|
+
* The placement of ONE Coral pool and the set it loads. Lives exactly as long
|
|
3119
|
+
* as the pool (the factory owns it).
|
|
3120
|
+
*/
|
|
3121
|
+
var CoralSetPlanner = class {
|
|
3122
|
+
sets;
|
|
3123
|
+
placed = [];
|
|
3124
|
+
current = {
|
|
3125
|
+
kind: "separate",
|
|
3126
|
+
reason: "nothing-placed"
|
|
3127
|
+
};
|
|
3128
|
+
constructor(sets) {
|
|
3129
|
+
this.sets = sets;
|
|
3130
|
+
}
|
|
3131
|
+
/** Add the models a dispatch places on this pool; say whether the set changed. */
|
|
3132
|
+
note(models) {
|
|
3133
|
+
let grew = false;
|
|
3134
|
+
for (const m of models) {
|
|
3135
|
+
if (this.placed.some((p) => sameModel(p, m))) continue;
|
|
3136
|
+
this.placed.push({
|
|
3137
|
+
stepId: m.stepId,
|
|
3138
|
+
modelId: m.modelId
|
|
3139
|
+
});
|
|
3140
|
+
grew = true;
|
|
3141
|
+
}
|
|
3142
|
+
if (!grew) return { kind: "unchanged" };
|
|
3143
|
+
const next = selectCoCompiledSet(this.placed, this.sets);
|
|
3144
|
+
if (selectionKey(next) === selectionKey(this.current)) {
|
|
3145
|
+
this.current = next;
|
|
3146
|
+
return { kind: "unchanged" };
|
|
3147
|
+
}
|
|
3148
|
+
const from = this.current;
|
|
3149
|
+
this.current = next;
|
|
3150
|
+
return {
|
|
3151
|
+
kind: "changed",
|
|
3152
|
+
from,
|
|
3153
|
+
to: next,
|
|
3154
|
+
placed: [...this.placed]
|
|
3155
|
+
};
|
|
3156
|
+
}
|
|
3157
|
+
selection() {
|
|
3158
|
+
return this.current;
|
|
3159
|
+
}
|
|
3160
|
+
/** `entry` as this pool must load it: from the set when the set stands for it. */
|
|
3161
|
+
entryFor(stepId, entry) {
|
|
3162
|
+
const member = setMemberFor(this.current, stepId, entry.id);
|
|
3163
|
+
return member === null ? entry : withSetBuild(entry, member);
|
|
3164
|
+
}
|
|
3165
|
+
};
|
|
3166
|
+
//#endregion
|
|
3167
|
+
//#region src/detection-pipeline/registry/edgetpu-cocompiled-sets.ts
|
|
3168
|
+
/**
|
|
3169
|
+
* Co-compiled Edge TPU sets — pool-level artifacts, not models (D724).
|
|
3170
|
+
*
|
|
3171
|
+
* A Coral holds ~6.9 MiB of parameter cache. Two separately compiled models
|
|
3172
|
+
* carry two different cache tokens, so every alternation between them on one
|
|
3173
|
+
* Coral flushes the cache and re-streams the incoming model's parameters over
|
|
3174
|
+
* USB (D710). `edgetpu_compiler` given several models at once allocates the
|
|
3175
|
+
* cache between them, in command-line order, under ONE token: loaded from the
|
|
3176
|
+
* same set, they never evict each other — whatever does not fit is streamed on
|
|
3177
|
+
* every invoke of THAT model instead.
|
|
3178
|
+
*
|
|
3179
|
+
* Each member is the SAME model as its catalog entry's `formats.tflite` (same
|
|
3180
|
+
* `modelId`, same int8 graph, same labels and licence record), compiled
|
|
3181
|
+
* together with the other members (D710: a Coral build is a format of the
|
|
3182
|
+
* survivor; the set is where it is placed, not a new model). A Coral pool picks
|
|
3183
|
+
* at most one set from the models placed on it (`coral-cocompiled-set.ts`) and
|
|
3184
|
+
* falls back to the separately compiled `formats.tflite` for everything else.
|
|
3185
|
+
*
|
|
3186
|
+
* Built by `scripts/build-edgetpu-sets.py` (compile order = `members` order =
|
|
3187
|
+
* cache priority) and hosted under `edgetpuSets/<id>/`. Only sets whose
|
|
3188
|
+
* measured cost is acceptable are listed here: a set that would make the root
|
|
3189
|
+
* detector stream parameters on every frame (root + YuNet at 640: the root
|
|
3190
|
+
* keeps 3.34 of its 6.39 MiB, YuNet's activations take the rest) is
|
|
3191
|
+
* deliberately absent.
|
|
3192
|
+
*/
|
|
3193
|
+
var HF_REPO = "camstack/camstack-models";
|
|
3194
|
+
var setFile = (setId, file) => require_dist.hfModelUrl(HF_REPO, `edgetpuSets/${setId}/${setId}__${file}`);
|
|
3195
|
+
var SSD = "ssd-mobilenet-v2-coco-edgetpu";
|
|
3196
|
+
var YUNET = "yunet-2023mar";
|
|
3197
|
+
var YOLOX = "yolox-tiny-plate";
|
|
3198
|
+
var BIRD = "bird-classifier";
|
|
3199
|
+
var EDGETPU_COCOMPILED_SETS = [
|
|
3200
|
+
{
|
|
3201
|
+
id: "root-plate-bird",
|
|
3202
|
+
hostsRoot: true,
|
|
3203
|
+
members: [
|
|
3204
|
+
{
|
|
3205
|
+
stepId: "object-detection",
|
|
3206
|
+
modelId: SSD,
|
|
3207
|
+
url: setFile("root-plate-bird", "ssd_mobilenet_v2_coco_quant_postprocess_edgetpu.tflite"),
|
|
3208
|
+
sizeMB: 6.9,
|
|
3209
|
+
onChipMiB: 6.39,
|
|
3210
|
+
streamedMiB: 0
|
|
3211
|
+
},
|
|
3212
|
+
{
|
|
3213
|
+
stepId: "plate-detection",
|
|
3214
|
+
modelId: YOLOX,
|
|
3215
|
+
url: setFile("root-plate-bird", "camstack-yolox-tiny-plate_int8_edgetpu.tflite"),
|
|
3216
|
+
sizeMB: 6.9,
|
|
3217
|
+
onChipMiB: .08,
|
|
3218
|
+
streamedMiB: 5.41
|
|
3219
|
+
},
|
|
3220
|
+
{
|
|
3221
|
+
stepId: "bird-classifier",
|
|
3222
|
+
modelId: BIRD,
|
|
3223
|
+
url: setFile("root-plate-bird", "mobilenet_v2_1.0_224_inat_bird_quant_edgetpu.tflite"),
|
|
3224
|
+
sizeMB: 4.9,
|
|
3225
|
+
onChipMiB: 0,
|
|
3226
|
+
streamedMiB: 4.54
|
|
3227
|
+
}
|
|
3228
|
+
]
|
|
3229
|
+
},
|
|
3230
|
+
{
|
|
3231
|
+
id: "face-plate-bird",
|
|
3232
|
+
hostsRoot: false,
|
|
3233
|
+
members: [
|
|
3234
|
+
{
|
|
3235
|
+
stepId: "face-detection",
|
|
3236
|
+
modelId: YUNET,
|
|
3237
|
+
url: setFile("face-plate-bird", "camstack-yunet-2023mar_int8_edgetpu.tflite"),
|
|
3238
|
+
sizeMB: .7,
|
|
3239
|
+
onChipMiB: .11,
|
|
3240
|
+
streamedMiB: 0
|
|
3241
|
+
},
|
|
3242
|
+
{
|
|
3243
|
+
stepId: "plate-detection",
|
|
3244
|
+
modelId: YOLOX,
|
|
3245
|
+
url: setFile("face-plate-bird", "camstack-yolox-tiny-plate_int8_edgetpu.tflite"),
|
|
3246
|
+
sizeMB: 6.9,
|
|
3247
|
+
onChipMiB: 3.23,
|
|
3248
|
+
streamedMiB: 2.27
|
|
3249
|
+
},
|
|
3250
|
+
{
|
|
3251
|
+
stepId: "bird-classifier",
|
|
3252
|
+
modelId: BIRD,
|
|
3253
|
+
url: setFile("face-plate-bird", "mobilenet_v2_1.0_224_inat_bird_quant_edgetpu.tflite"),
|
|
3254
|
+
sizeMB: 4.9,
|
|
3255
|
+
onChipMiB: 0,
|
|
3256
|
+
streamedMiB: 4.54
|
|
3257
|
+
}
|
|
3258
|
+
]
|
|
3259
|
+
},
|
|
3260
|
+
{
|
|
3261
|
+
id: "plate-bird",
|
|
3262
|
+
hostsRoot: false,
|
|
3263
|
+
members: [{
|
|
3264
|
+
stepId: "plate-detection",
|
|
3265
|
+
modelId: YOLOX,
|
|
3266
|
+
url: setFile("plate-bird", "camstack-yolox-tiny-plate_int8_edgetpu.tflite"),
|
|
3267
|
+
sizeMB: 6.9,
|
|
3268
|
+
onChipMiB: 5.55,
|
|
3269
|
+
streamedMiB: 0
|
|
3270
|
+
}, {
|
|
3271
|
+
stepId: "bird-classifier",
|
|
3272
|
+
modelId: BIRD,
|
|
3273
|
+
url: setFile("plate-bird", "mobilenet_v2_1.0_224_inat_bird_quant_edgetpu.tflite"),
|
|
3274
|
+
sizeMB: 4.1,
|
|
3275
|
+
onChipMiB: .92,
|
|
3276
|
+
streamedMiB: 2.8
|
|
3277
|
+
}]
|
|
3278
|
+
}
|
|
3279
|
+
];
|
|
3280
|
+
//#endregion
|
|
2984
3281
|
//#region src/detection-pipeline/engine/engine-factory.ts
|
|
2985
3282
|
var BACKEND_TO_POOL_RUNTIME = {
|
|
2986
3283
|
coreml: "coreml",
|
|
@@ -3074,11 +3371,45 @@ var EngineFactory = class {
|
|
|
3074
3371
|
* trying to be.
|
|
3075
3372
|
*/
|
|
3076
3373
|
resolvedProvisioning;
|
|
3374
|
+
/**
|
|
3375
|
+
* A Coral pool's placement and the co-compiled set it loads (D724); `null`
|
|
3376
|
+
* on every other runtime. Lives exactly as long as this factory — a new
|
|
3377
|
+
* pool starts from nothing placed.
|
|
3378
|
+
*/
|
|
3379
|
+
coralSets;
|
|
3077
3380
|
constructor(opts) {
|
|
3078
3381
|
this.opts = opts;
|
|
3079
3382
|
this.log = opts.logger;
|
|
3080
3383
|
this.deviceKey = deviceKeyOf(opts.engine);
|
|
3081
3384
|
this.resolvedProvisioning = resolvePoolProvisioning(opts.engine, opts.provisioning);
|
|
3385
|
+
this.coralSets = BACKEND_TO_POOL_RUNTIME[opts.engine.backend] === "edgetpu" ? new CoralSetPlanner(opts.coCompiledSets ?? EDGETPU_COCOMPILED_SETS) : null;
|
|
3386
|
+
}
|
|
3387
|
+
/**
|
|
3388
|
+
* Record the models a dispatch places on this pool. On a Coral pool the
|
|
3389
|
+
* answer says whether the co-compiled set it loads from changed; on any
|
|
3390
|
+
* other pool nothing is placed and nothing changes.
|
|
3391
|
+
*/
|
|
3392
|
+
notePlacement(models) {
|
|
3393
|
+
return this.coralSets?.note(models) ?? { kind: "unchanged" };
|
|
3394
|
+
}
|
|
3395
|
+
/** The co-compiled set this pool loads from, or why none (`null`: not a Coral pool). */
|
|
3396
|
+
coralSetSelection() {
|
|
3397
|
+
return this.coralSets?.selection() ?? null;
|
|
3398
|
+
}
|
|
3399
|
+
/**
|
|
3400
|
+
* The catalog entry as THIS pool must download and load it: on a Coral pool
|
|
3401
|
+
* whose set stands for the model, the `tflite` build is the set's file.
|
|
3402
|
+
* Every other entry is returned unchanged.
|
|
3403
|
+
*/
|
|
3404
|
+
entryForPool(stepId, entry) {
|
|
3405
|
+
return this.coralSets?.entryFor(stepId, entry) ?? entry;
|
|
3406
|
+
}
|
|
3407
|
+
/**
|
|
3408
|
+
* Reload every resident model whose file this pool no longer names — after
|
|
3409
|
+
* the Coral set changed. The caller has already downloaded the new files.
|
|
3410
|
+
*/
|
|
3411
|
+
async reloadChangedModelFiles() {
|
|
3412
|
+
return this.poolManager?.reloadChangedPaths() ?? [];
|
|
3082
3413
|
}
|
|
3083
3414
|
/**
|
|
3084
3415
|
* The provisioning this factory's pool runs with — resolved from the pool's
|
|
@@ -3340,6 +3671,7 @@ var EngineFactory = class {
|
|
|
3340
3671
|
stepSettings,
|
|
3341
3672
|
device: this.opts.engine.device,
|
|
3342
3673
|
resolveCustomModel: this.opts.resolveCustomModel,
|
|
3674
|
+
entryForPool: (sid, entry) => this.entryForPool(sid, entry),
|
|
3343
3675
|
onClassMapDrop: ({ stepId: sid, modelId: mid, unmappedLabels }) => {
|
|
3344
3676
|
const key = `${sid}::${mid}`;
|
|
3345
3677
|
if (this.classMapDropLogged.has(key)) return;
|
|
@@ -3365,8 +3697,9 @@ var EngineFactory = class {
|
|
|
3365
3697
|
function buildPoolModelConfigForStep(inputs) {
|
|
3366
3698
|
const { stepId, modelId, poolRuntime, modelsDir } = inputs;
|
|
3367
3699
|
const def = require_default_detection_model.getStepDefinition(stepId);
|
|
3368
|
-
const
|
|
3369
|
-
if (!
|
|
3700
|
+
const resolvedEntry = def.models.find((m) => m.id === modelId) ?? inputs.resolveCustomModel?.(stepId, modelId);
|
|
3701
|
+
if (!resolvedEntry) throw new Error(`Model "${modelId}" not found in step "${stepId}" catalog (static or custom registry)`);
|
|
3702
|
+
const modelEntry = inputs.entryForPool?.(stepId, resolvedEntry) ?? resolvedEntry;
|
|
3370
3703
|
const format = RUNTIME_TO_FORMAT[poolRuntime];
|
|
3371
3704
|
if (!format) throw new Error(`No format mapping for pool runtime "${poolRuntime}"`);
|
|
3372
3705
|
const dropWarning = collectClassMapDropWarning(def, modelEntry);
|
|
@@ -9432,6 +9765,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
9432
9765
|
*/
|
|
9433
9766
|
async loadDispatchModels(steps, factory, format, context, policy) {
|
|
9434
9767
|
const failures = [];
|
|
9768
|
+
if (factory !== null) await this.noteCoralPlacement(steps, factory, format, context);
|
|
9435
9769
|
const walk = async (nodes, root) => {
|
|
9436
9770
|
for (const step of nodes) {
|
|
9437
9771
|
if (!step.enabled || step.slot === "audio-classifier") continue;
|
|
@@ -9456,6 +9790,69 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
9456
9790
|
await walk(steps, true);
|
|
9457
9791
|
return failures;
|
|
9458
9792
|
}
|
|
9793
|
+
/**
|
|
9794
|
+
* Tell a Coral pool which models this dispatch places on it, and act on a
|
|
9795
|
+
* change of the co-compiled set it loads from (D724): download the set's
|
|
9796
|
+
* files for the models already resident and reload them — queued behind the
|
|
9797
|
+
* pool's other loads, so nothing loads from the old file meanwhile. Logged
|
|
9798
|
+
* once per change, tagged with the camera whose dispatch moved it.
|
|
9799
|
+
*/
|
|
9800
|
+
async noteCoralPlacement(steps, factory, format, context) {
|
|
9801
|
+
const change = factory.notePlacement(placedModelsOf(steps));
|
|
9802
|
+
if (change.kind === "unchanged") return;
|
|
9803
|
+
const deviceKey = context.deviceKey ?? deviceKeyOf(this.currentEngine);
|
|
9804
|
+
const tags = context.deviceId !== void 0 ? { tags: { deviceId: context.deviceId } } : {};
|
|
9805
|
+
const placed = change.placed.map((p) => `${p.stepId}/${p.modelId}`);
|
|
9806
|
+
if (change.to.kind === "set") this.log.info("Coral pool loads a co-compiled set — the models placed on it share the parameter cache", {
|
|
9807
|
+
...tags,
|
|
9808
|
+
meta: {
|
|
9809
|
+
deviceKey,
|
|
9810
|
+
set: change.to.set.id,
|
|
9811
|
+
placed,
|
|
9812
|
+
fromSet: change.to.covered.map((p) => `${p.stepId}/${p.modelId}`),
|
|
9813
|
+
separatelyCompiled: change.to.uncovered.map((p) => `${p.stepId}/${p.modelId}`),
|
|
9814
|
+
previous: change.from.kind === "set" ? change.from.set.id : null
|
|
9815
|
+
}
|
|
9816
|
+
});
|
|
9817
|
+
else if (change.to.reason === "no-set-covers-two") this.log.warn("Coral pool has no co-compiled set for the models placed on it — each is compiled alone, so they evict each other from the parameter cache on every alternation", {
|
|
9818
|
+
...tags,
|
|
9819
|
+
meta: {
|
|
9820
|
+
deviceKey,
|
|
9821
|
+
placed,
|
|
9822
|
+
previous: change.from.kind === "set" ? change.from.set.id : null,
|
|
9823
|
+
hint: "keep the extra step off this Coral with its jumpDeviceKey, or place a combination a published set covers"
|
|
9824
|
+
}
|
|
9825
|
+
});
|
|
9826
|
+
const pool = factory;
|
|
9827
|
+
const previous = this.modelLoadTail.get(pool) ?? Promise.resolve();
|
|
9828
|
+
const tail = (async () => {
|
|
9829
|
+
await previous;
|
|
9830
|
+
for (const loaded of factory.listLoaded()) {
|
|
9831
|
+
const resolved = await this.resolveModelEntry(loaded.stepId, loaded.modelId);
|
|
9832
|
+
if (resolved === void 0) continue;
|
|
9833
|
+
const entry = factory.entryForPool(loaded.stepId, resolved);
|
|
9834
|
+
if (!(0, _camstack_system_addon_utils.isModelDownloaded)(this.modelsDir, entry, format)) await this.downloadWithRetry(entry, format, 3);
|
|
9835
|
+
}
|
|
9836
|
+
const reloaded = await factory.reloadChangedModelFiles();
|
|
9837
|
+
if (reloaded.length > 0) this.log.info("Coral pool reloaded its resident models from the new set", {
|
|
9838
|
+
...tags,
|
|
9839
|
+
meta: {
|
|
9840
|
+
deviceKey,
|
|
9841
|
+
reloaded: reloaded.map((r) => `${r.stepId}/${r.modelId}`)
|
|
9842
|
+
}
|
|
9843
|
+
});
|
|
9844
|
+
})().catch((err) => {
|
|
9845
|
+
this.log.error("Coral pool could not move its resident models to the new set — they keep serving from the old files", {
|
|
9846
|
+
...tags,
|
|
9847
|
+
meta: {
|
|
9848
|
+
deviceKey,
|
|
9849
|
+
error: require_dist.errMsg(err)
|
|
9850
|
+
}
|
|
9851
|
+
});
|
|
9852
|
+
});
|
|
9853
|
+
this.modelLoadTail.set(pool, tail);
|
|
9854
|
+
await tail;
|
|
9855
|
+
}
|
|
9459
9856
|
/** Ensure all models needed by steps are downloaded and loaded in the engine pool.
|
|
9460
9857
|
* `factory`/`format` default to the node's engine; a per-device dispatch passes
|
|
9461
9858
|
* that device's factory + format (Phase 2 multi-device). `context` names the
|
|
@@ -9553,7 +9950,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
9553
9950
|
const refusal = this.loadRefusal(pool, deviceKey, models);
|
|
9554
9951
|
if (refusal !== null) throw refusal;
|
|
9555
9952
|
try {
|
|
9556
|
-
await this.downloadNeededModels(needed, format);
|
|
9953
|
+
await this.downloadNeededModels(needed, format, factory);
|
|
9557
9954
|
this.log.info("Loading additional models for benchmark", { meta: {
|
|
9558
9955
|
count: needed.length,
|
|
9559
9956
|
models,
|
|
@@ -9617,16 +10014,21 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
9617
10014
|
});
|
|
9618
10015
|
}
|
|
9619
10016
|
/** Download any model of `needed` missing in `format` (the device's). */
|
|
9620
|
-
async downloadNeededModels(needed, format) {
|
|
10017
|
+
async downloadNeededModels(needed, format, factory = null) {
|
|
9621
10018
|
for (const step of needed) try {
|
|
9622
|
-
await this.ensureStepModelArtifact(step, format);
|
|
10019
|
+
await this.ensureStepModelArtifact(step, format, factory);
|
|
9623
10020
|
} catch (err) {
|
|
9624
10021
|
throw new StepModelArtifactError(step.addonId, step.modelId, err);
|
|
9625
10022
|
}
|
|
9626
10023
|
}
|
|
9627
|
-
/**
|
|
9628
|
-
|
|
9629
|
-
|
|
10024
|
+
/**
|
|
10025
|
+
* Make one step's model artifact present on disk for `format`, or say why it
|
|
10026
|
+
* cannot be. The entry is the one `factory`'s pool will LOAD — on a Coral
|
|
10027
|
+
* pool, a co-compiled set's file when the set stands for the model (D724).
|
|
10028
|
+
*/
|
|
10029
|
+
async ensureStepModelArtifact(step, format, factory = null) {
|
|
10030
|
+
const resolved = await this.resolveModelEntry(step.addonId, step.modelId);
|
|
10031
|
+
const modelEntry = resolved && factory !== null ? factory.entryForPool(step.addonId, resolved) : resolved;
|
|
9630
10032
|
if (modelEntry && !(0, _camstack_system_addon_utils.isModelDownloaded)(this.modelsDir, modelEntry, format)) {
|
|
9631
10033
|
if (modelEntry.formats[format] === void 0) throw new Error(`Model "${modelEntry.id}" has no ${format} format build (available: ${Object.keys(modelEntry.formats).join(", ") || "none"}) — not retrying a permanent format mismatch`);
|
|
9632
10034
|
if (modelEntry.formats[format]?.url.startsWith("camstack-local://") === true) throw new Error(`Custom model "${modelEntry.id}" (${format}) is not present in this node's models directory — distribute it to this node from Model Studio before selecting it here`);
|