omnius 1.0.410 → 1.0.411
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/index.js +824 -56
- package/npm-shrinkwrap.json +14 -14
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -555654,6 +555654,169 @@ function buildYolo26BootstrapPlan(model = DEFAULT_YOLO26_MODEL) {
|
|
|
555654
555654
|
packages: ["ultralytics>=8.4.0", "opencv-python-headless", "yt-dlp", "numpy"]
|
|
555655
555655
|
};
|
|
555656
555656
|
}
|
|
555657
|
+
function buildYolo26DeploymentOptions() {
|
|
555658
|
+
const yolo26Models = YOLO26_SCALES.flatMap((scale) => Object.values(YOLO26_TASKS).map((task) => `yolo26${scale}${task.suffix}.pt`));
|
|
555659
|
+
const yoloeModels = YOLO26_SCALES.flatMap((scale) => [`yoloe-26${scale}-seg.pt`, `yoloe-26${scale}-seg-pf.pt`]);
|
|
555660
|
+
return {
|
|
555661
|
+
defaultModel: DEFAULT_YOLO26_MODEL,
|
|
555662
|
+
scales: [...YOLO26_SCALES],
|
|
555663
|
+
tasks: YOLO26_TASKS,
|
|
555664
|
+
families: {
|
|
555665
|
+
yolo26: {
|
|
555666
|
+
description: "YOLO26 closed-vocabulary models for detection, segmentation, semantic segmentation, pose, OBB, and classification.",
|
|
555667
|
+
models: yolo26Models
|
|
555668
|
+
},
|
|
555669
|
+
"yoloe-26": {
|
|
555670
|
+
description: "YOLOE-26 open-vocabulary segmentation models for text, visual, or prompt-free detection/segmentation workflows.",
|
|
555671
|
+
models: yoloeModels
|
|
555672
|
+
}
|
|
555673
|
+
},
|
|
555674
|
+
yoloePromptModes: [
|
|
555675
|
+
{ mode: "text", description: "Set open-vocabulary classes with model.set_classes([...]) before prediction or export." },
|
|
555676
|
+
{ mode: "prompt_free", description: "Use YOLOE prompt-free weights such as yoloe-26s-seg-pf.pt for built-in large-vocabulary discovery." },
|
|
555677
|
+
{ mode: "visual", description: "Use YOLOE visual prompts where supported by the installed Ultralytics version; exported prompts are static." }
|
|
555678
|
+
],
|
|
555679
|
+
exportFormats: YOLO26_EXPORT_FORMATS,
|
|
555680
|
+
headModes: [
|
|
555681
|
+
{ value: true, name: "end2end", description: "YOLO26 default one-to-one head; NMS-free output optimized for deployment latency." },
|
|
555682
|
+
{ value: false, name: "one-to-many", description: "Traditional output requiring NMS post-processing; useful for compatibility or accuracy-sensitive pipelines." }
|
|
555683
|
+
],
|
|
555684
|
+
exportArguments: [
|
|
555685
|
+
"format",
|
|
555686
|
+
"imgsz",
|
|
555687
|
+
"quantize",
|
|
555688
|
+
"dynamic",
|
|
555689
|
+
"simplify",
|
|
555690
|
+
"opset",
|
|
555691
|
+
"workspace",
|
|
555692
|
+
"nms",
|
|
555693
|
+
"batch",
|
|
555694
|
+
"device",
|
|
555695
|
+
"data",
|
|
555696
|
+
"fraction",
|
|
555697
|
+
"end2end",
|
|
555698
|
+
"optimize",
|
|
555699
|
+
"keras",
|
|
555700
|
+
"name"
|
|
555701
|
+
]
|
|
555702
|
+
};
|
|
555703
|
+
}
|
|
555704
|
+
function normalizeYolo26Scale(value2) {
|
|
555705
|
+
const scale = String(value2 ?? "n").trim().toLowerCase();
|
|
555706
|
+
return YOLO26_SCALES.includes(scale) ? scale : "n";
|
|
555707
|
+
}
|
|
555708
|
+
function normalizeYolo26Task(value2) {
|
|
555709
|
+
const raw = String(value2 ?? "detect").trim().toLowerCase();
|
|
555710
|
+
const aliases = {
|
|
555711
|
+
detection: "detect",
|
|
555712
|
+
det: "detect",
|
|
555713
|
+
instance: "segment",
|
|
555714
|
+
segmentation: "segment",
|
|
555715
|
+
seg: "segment",
|
|
555716
|
+
sem: "semantic",
|
|
555717
|
+
semantic_segmentation: "semantic",
|
|
555718
|
+
keypoint: "pose",
|
|
555719
|
+
keypoints: "pose",
|
|
555720
|
+
classify: "classify",
|
|
555721
|
+
classification: "classify",
|
|
555722
|
+
cls: "classify",
|
|
555723
|
+
oriented: "obb",
|
|
555724
|
+
oriented_detection: "obb"
|
|
555725
|
+
};
|
|
555726
|
+
const normalized = aliases[raw] ?? raw;
|
|
555727
|
+
return Object.prototype.hasOwnProperty.call(YOLO26_TASKS, normalized) ? normalized : "detect";
|
|
555728
|
+
}
|
|
555729
|
+
function normalizeYolo26Family(value2) {
|
|
555730
|
+
const raw = String(value2 ?? "yolo26").trim().toLowerCase();
|
|
555731
|
+
if (["yoloe", "yoloe26", "yoloe-26", "open-vocabulary", "open_vocab"].includes(raw))
|
|
555732
|
+
return "yoloe-26";
|
|
555733
|
+
return "yolo26";
|
|
555734
|
+
}
|
|
555735
|
+
function resolveYolo26Model(args) {
|
|
555736
|
+
const explicit = args["yolo_model"] ?? args["model"];
|
|
555737
|
+
if (typeof explicit === "string" && explicit.trim())
|
|
555738
|
+
return explicit.trim();
|
|
555739
|
+
const family = normalizeYolo26Family(args["yolo_family"] ?? args["family"] ?? args["variant"]);
|
|
555740
|
+
const scale = normalizeYolo26Scale(args["yolo_scale"] ?? args["scale"]);
|
|
555741
|
+
const promptMode = String(args["yoloe_prompt_mode"] ?? args["prompt_mode"] ?? "").trim().toLowerCase();
|
|
555742
|
+
const promptFree = promptMode === "prompt_free" || promptMode === "prompt-free" || promptMode === "pf" || args["prompt_free"] === true;
|
|
555743
|
+
if (family === "yoloe-26")
|
|
555744
|
+
return `yoloe-26${scale}-seg${promptFree ? "-pf" : ""}.pt`;
|
|
555745
|
+
const task = normalizeYolo26Task(args["yolo_task"] ?? args["task"]);
|
|
555746
|
+
return `yolo26${scale}${YOLO26_TASKS[task].suffix}.pt`;
|
|
555747
|
+
}
|
|
555748
|
+
function normalizeYolo26ExportFormat(value2) {
|
|
555749
|
+
const raw = String(value2 ?? "onnx").trim().toLowerCase().replace(/-/g, "_");
|
|
555750
|
+
const aliases = {
|
|
555751
|
+
trt: "engine",
|
|
555752
|
+
tensorrt: "engine",
|
|
555753
|
+
tf_saved_model: "saved_model",
|
|
555754
|
+
savedmodel: "saved_model",
|
|
555755
|
+
tensorflow_saved_model: "saved_model",
|
|
555756
|
+
graphdef: "pb",
|
|
555757
|
+
edge_tpu: "edgetpu",
|
|
555758
|
+
tflite: "litert",
|
|
555759
|
+
tensorflow_lite: "litert",
|
|
555760
|
+
lite_rt: "litert",
|
|
555761
|
+
torch: "pytorch",
|
|
555762
|
+
pt: "pytorch"
|
|
555763
|
+
};
|
|
555764
|
+
const normalized = aliases[raw] ?? raw;
|
|
555765
|
+
if (YOLO26_EXPORT_FORMATS.some((entry) => entry.format === normalized))
|
|
555766
|
+
return normalized;
|
|
555767
|
+
throw new Error(`Unsupported YOLO26 export format: ${String(value2)}. Use one of: ${YOLO26_EXPORT_FORMATS.map((entry) => entry.format).join(", ")}`);
|
|
555768
|
+
}
|
|
555769
|
+
function optionalBoolean(args, key) {
|
|
555770
|
+
if (!(key in args) || args[key] === void 0 || args[key] === null || args[key] === "")
|
|
555771
|
+
return void 0;
|
|
555772
|
+
if (typeof args[key] === "boolean")
|
|
555773
|
+
return args[key];
|
|
555774
|
+
const raw = String(args[key]).trim().toLowerCase();
|
|
555775
|
+
if (["1", "true", "yes", "on"].includes(raw))
|
|
555776
|
+
return true;
|
|
555777
|
+
if (["0", "false", "no", "off"].includes(raw))
|
|
555778
|
+
return false;
|
|
555779
|
+
return void 0;
|
|
555780
|
+
}
|
|
555781
|
+
function optionalNumber(args, key) {
|
|
555782
|
+
if (!(key in args) || args[key] === void 0 || args[key] === null || args[key] === "")
|
|
555783
|
+
return void 0;
|
|
555784
|
+
const n2 = Number(args[key]);
|
|
555785
|
+
return Number.isFinite(n2) ? n2 : void 0;
|
|
555786
|
+
}
|
|
555787
|
+
function exportOptionsFromArgs(args, format3) {
|
|
555788
|
+
const options2 = { format: format3 };
|
|
555789
|
+
for (const key of ["imgsz", "opset", "workspace", "batch", "fraction"]) {
|
|
555790
|
+
const n2 = optionalNumber(args, key);
|
|
555791
|
+
if (n2 !== void 0)
|
|
555792
|
+
options2[key] = n2;
|
|
555793
|
+
}
|
|
555794
|
+
for (const key of ["dynamic", "simplify", "nms", "optimize", "keras", "end2end"]) {
|
|
555795
|
+
const b = optionalBoolean(args, key);
|
|
555796
|
+
if (b !== void 0)
|
|
555797
|
+
options2[key] = b;
|
|
555798
|
+
}
|
|
555799
|
+
const quantize = args["quantize"] ?? args["precision"];
|
|
555800
|
+
if (quantize !== void 0 && quantize !== null && String(quantize).trim()) {
|
|
555801
|
+
const asNumber2 = Number(quantize);
|
|
555802
|
+
options2["quantize"] = Number.isFinite(asNumber2) ? asNumber2 : String(quantize).trim();
|
|
555803
|
+
}
|
|
555804
|
+
for (const key of ["device", "data", "name"]) {
|
|
555805
|
+
const value2 = args[key];
|
|
555806
|
+
if (typeof value2 === "string" && value2.trim())
|
|
555807
|
+
options2[key] = value2.trim();
|
|
555808
|
+
}
|
|
555809
|
+
return options2;
|
|
555810
|
+
}
|
|
555811
|
+
function stringListFromArg(value2) {
|
|
555812
|
+
if (Array.isArray(value2)) {
|
|
555813
|
+
return value2.map((item) => String(item).trim()).filter(Boolean);
|
|
555814
|
+
}
|
|
555815
|
+
if (typeof value2 === "string") {
|
|
555816
|
+
return value2.split(/[,;\n]/).map((item) => item.trim()).filter(Boolean);
|
|
555817
|
+
}
|
|
555818
|
+
return [];
|
|
555819
|
+
}
|
|
555657
555820
|
function sourceKind(args) {
|
|
555658
555821
|
const explicit = String(args["source_kind"] ?? args["source_type"] ?? "").toLowerCase();
|
|
555659
555822
|
if (["camera", "youtube", "file", "direct"].includes(explicit))
|
|
@@ -555797,6 +555960,59 @@ async function ensureRuntime(autoBootstrap) {
|
|
|
555797
555960
|
return { ok: false, python: plan.python, error: err instanceof Error ? err.message : String(err) };
|
|
555798
555961
|
}
|
|
555799
555962
|
}
|
|
555963
|
+
async function exportYolo26Model(python, model, options2, yoloeClasses = []) {
|
|
555964
|
+
const cfgPath = join92(tmpdir19(), `omnius-yolo26-export-${Date.now()}.json`);
|
|
555965
|
+
const scriptPath2 = join92(tmpdir19(), `omnius-yolo26-export-${Date.now()}.py`);
|
|
555966
|
+
writeFileSync39(cfgPath, JSON.stringify({ model, options: options2, yoloeClasses }, null, 2), "utf8");
|
|
555967
|
+
writeFileSync39(scriptPath2, String.raw`
|
|
555968
|
+
import json, sys
|
|
555969
|
+
from ultralytics import YOLO
|
|
555970
|
+
try:
|
|
555971
|
+
from ultralytics import YOLOE
|
|
555972
|
+
except Exception:
|
|
555973
|
+
YOLOE = None
|
|
555974
|
+
|
|
555975
|
+
cfg_path = sys.argv[1]
|
|
555976
|
+
with open(cfg_path, "r", encoding="utf-8") as fh:
|
|
555977
|
+
cfg = json.load(fh)
|
|
555978
|
+
|
|
555979
|
+
model_name = cfg["model"]
|
|
555980
|
+
options = cfg["options"]
|
|
555981
|
+
yoloe_classes = [str(x).strip() for x in cfg.get("yoloeClasses", []) if str(x).strip()]
|
|
555982
|
+
model = YOLOE(model_name) if str(model_name).startswith("yoloe-") and YOLOE is not None else YOLO(model_name)
|
|
555983
|
+
if yoloe_classes and hasattr(model, "set_classes"):
|
|
555984
|
+
model.set_classes(yoloe_classes)
|
|
555985
|
+
artifact = model.export(**options)
|
|
555986
|
+
print(json.dumps({
|
|
555987
|
+
"success": True,
|
|
555988
|
+
"model": model_name,
|
|
555989
|
+
"format": options.get("format"),
|
|
555990
|
+
"artifact": str(artifact),
|
|
555991
|
+
"options": options,
|
|
555992
|
+
"yoloe_classes": yoloe_classes,
|
|
555993
|
+
}))
|
|
555994
|
+
`, "utf8");
|
|
555995
|
+
try {
|
|
555996
|
+
const raw = await execFileText(python, [scriptPath2, cfgPath], {
|
|
555997
|
+
timeout: 30 * 6e4,
|
|
555998
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
555999
|
+
env: { ...process.env, PYTHONUNBUFFERED: "1" }
|
|
556000
|
+
});
|
|
556001
|
+
const jsonLine2 = raw.trim().split("\n").reverse().find((line) => line.trim().startsWith("{") && line.trim().endsWith("}"));
|
|
556002
|
+
if (!jsonLine2)
|
|
556003
|
+
throw new Error(`YOLO26 export returned no JSON result; stdout tail: ${raw.slice(-1e3)}`);
|
|
556004
|
+
return JSON.parse(jsonLine2);
|
|
556005
|
+
} finally {
|
|
556006
|
+
try {
|
|
556007
|
+
rmSync10(cfgPath, { force: true });
|
|
556008
|
+
} catch {
|
|
556009
|
+
}
|
|
556010
|
+
try {
|
|
556011
|
+
rmSync10(scriptPath2, { force: true });
|
|
556012
|
+
} catch {
|
|
556013
|
+
}
|
|
556014
|
+
}
|
|
556015
|
+
}
|
|
555800
556016
|
function makePythonScript() {
|
|
555801
556017
|
return String.raw`
|
|
555802
556018
|
import json, os, sys, time
|
|
@@ -555818,6 +556034,8 @@ def main():
|
|
|
555818
556034
|
os.makedirs(out_dir, exist_ok=True)
|
|
555819
556035
|
crop_dir = os.path.join(out_dir, "crops")
|
|
555820
556036
|
os.makedirs(crop_dir, exist_ok=True)
|
|
556037
|
+
segment_dir = os.path.join(out_dir, "segments")
|
|
556038
|
+
os.makedirs(segment_dir, exist_ok=True)
|
|
555821
556039
|
source_kind = cfg["source_kind"]
|
|
555822
556040
|
source = cfg["source"]
|
|
555823
556041
|
media_path = None
|
|
@@ -555827,6 +556045,10 @@ def main():
|
|
|
555827
556045
|
import cv2
|
|
555828
556046
|
import numpy as np
|
|
555829
556047
|
from ultralytics import YOLO
|
|
556048
|
+
try:
|
|
556049
|
+
from ultralytics import YOLOE
|
|
556050
|
+
except Exception:
|
|
556051
|
+
YOLOE = None
|
|
555830
556052
|
except Exception as exc:
|
|
555831
556053
|
print(json.dumps({"success": False, "error": "python imports failed: " + str(exc)}))
|
|
555832
556054
|
return
|
|
@@ -555869,16 +556091,26 @@ def main():
|
|
|
555869
556091
|
return
|
|
555870
556092
|
|
|
555871
556093
|
yolo_model = cfg.get("yolo_model") or "yolo26n.pt"
|
|
555872
|
-
|
|
556094
|
+
yoloe_classes = [str(x).strip() for x in (cfg.get("yoloe_classes") or []) if str(x).strip()]
|
|
556095
|
+
yoloe_prompt_mode = cfg.get("yoloe_prompt_mode") or ("text" if yoloe_classes else "prompt_free" if str(yolo_model).endswith("-pf.pt") else "closed_set")
|
|
556096
|
+
use_yoloe = str(yolo_model).startswith("yoloe-") and YOLOE is not None
|
|
556097
|
+
model = (YOLOE(yolo_model) if use_yoloe else YOLO(yolo_model)) if cfg.get("detect_objects", True) else None
|
|
556098
|
+
if model is not None and yoloe_classes and hasattr(model, "set_classes"):
|
|
556099
|
+
try:
|
|
556100
|
+
model.set_classes(yoloe_classes)
|
|
556101
|
+
except TypeError:
|
|
556102
|
+
model.set_classes(list(yoloe_classes))
|
|
555873
556103
|
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml")
|
|
555874
556104
|
objects = []
|
|
555875
556105
|
faces = []
|
|
556106
|
+
segments = []
|
|
555876
556107
|
track_stats = {}
|
|
555877
556108
|
max_frames = int(cfg.get("max_frames") or 8)
|
|
555878
556109
|
interval = float(cfg.get("sample_interval_sec") or 1.0)
|
|
555879
556110
|
duration = float(cfg.get("duration_sec") or 8.0)
|
|
555880
556111
|
conf = float(cfg.get("confidence") or 0.25)
|
|
555881
556112
|
imgsz = int(cfg.get("imgsz") or 640)
|
|
556113
|
+
end2end = cfg.get("end2end", None)
|
|
555882
556114
|
start_wall = time.time()
|
|
555883
556115
|
frame_index = 0
|
|
555884
556116
|
|
|
@@ -555902,10 +556134,17 @@ def main():
|
|
|
555902
556134
|
|
|
555903
556135
|
if model is not None:
|
|
555904
556136
|
try:
|
|
556137
|
+
predict_kwargs = {"verbose": False, "conf": conf, "imgsz": imgsz}
|
|
556138
|
+
if end2end is not None:
|
|
556139
|
+
predict_kwargs["end2end"] = bool(end2end)
|
|
556140
|
+
if cfg.get("agnostic_nms", None) is not None:
|
|
556141
|
+
predict_kwargs["agnostic_nms"] = bool(cfg.get("agnostic_nms"))
|
|
556142
|
+
elif use_yoloe:
|
|
556143
|
+
predict_kwargs["agnostic_nms"] = True
|
|
555905
556144
|
if cfg.get("track_objects", True):
|
|
555906
|
-
results = model.track(frame, persist=True,
|
|
556145
|
+
results = model.track(frame, persist=True, **predict_kwargs)
|
|
555907
556146
|
else:
|
|
555908
|
-
results = model.predict(frame,
|
|
556147
|
+
results = model.predict(frame, **predict_kwargs)
|
|
555909
556148
|
r = results[0] if results else None
|
|
555910
556149
|
if r is not None and getattr(r, "boxes", None) is not None:
|
|
555911
556150
|
boxes = r.boxes
|
|
@@ -555935,6 +556174,44 @@ def main():
|
|
|
555935
556174
|
st["last_seen_sec"] = max(st["last_seen_sec"], ts)
|
|
555936
556175
|
st["observations"] += 1
|
|
555937
556176
|
st["confidence_sum"] += c
|
|
556177
|
+
want_segments = bool(cfg.get("segment_objects") or cfg.get("extract_segments") or use_yoloe)
|
|
556178
|
+
if want_segments and getattr(r, "masks", None) is not None and getattr(r.masks, "data", None) is not None:
|
|
556179
|
+
mask_data = r.masks.data.cpu().numpy()
|
|
556180
|
+
max_segments = int(cfg.get("max_segments_per_frame") or 8)
|
|
556181
|
+
for i, raw_mask in enumerate(mask_data[:max_segments]):
|
|
556182
|
+
label = str(names.get(int(cls[i]), int(cls[i]))) if i < len(cls) else "object"
|
|
556183
|
+
c = float(confs[i]) if i < len(confs) else 0.0
|
|
556184
|
+
box = xyxy[i] if i < len(xyxy) else [0, 0, frame.shape[1], frame.shape[0]]
|
|
556185
|
+
mask = (raw_mask > 0.5).astype("uint8") * 255
|
|
556186
|
+
if mask.shape[:2] != frame.shape[:2]:
|
|
556187
|
+
mask = cv2.resize(mask, (frame.shape[1], frame.shape[0]), interpolation=cv2.INTER_NEAREST)
|
|
556188
|
+
x1, y1, x2, y2 = [int(max(0, round(float(v)))) for v in box]
|
|
556189
|
+
x1 = min(x1, frame.shape[1] - 1)
|
|
556190
|
+
x2 = min(max(x2, x1 + 1), frame.shape[1])
|
|
556191
|
+
y1 = min(y1, frame.shape[0] - 1)
|
|
556192
|
+
y2 = min(max(y2, y1 + 1), frame.shape[0])
|
|
556193
|
+
mask_path = os.path.join(segment_dir, f"seg_f{frame_index:04d}_{i:02d}_mask.png")
|
|
556194
|
+
cv2.imwrite(mask_path, mask)
|
|
556195
|
+
crop_path = None
|
|
556196
|
+
if cfg.get("extract_segments", False):
|
|
556197
|
+
crop_bgr = frame[y1:y2, x1:x2]
|
|
556198
|
+
crop_alpha = mask[y1:y2, x1:x2]
|
|
556199
|
+
if crop_bgr.size > 0 and crop_alpha.size > 0:
|
|
556200
|
+
crop_rgba = cv2.cvtColor(crop_bgr, cv2.COLOR_BGR2BGRA)
|
|
556201
|
+
crop_rgba[:, :, 3] = crop_alpha
|
|
556202
|
+
crop_path = os.path.join(segment_dir, f"seg_f{frame_index:04d}_{i:02d}_{label.replace('/', '_')}.png")
|
|
556203
|
+
cv2.imwrite(crop_path, crop_rgba)
|
|
556204
|
+
area_ratio = float((mask > 0).sum()) / max(1.0, float(frame.shape[0] * frame.shape[1]))
|
|
556205
|
+
segments.append({
|
|
556206
|
+
"frame_index": frame_index,
|
|
556207
|
+
"timestamp_sec": round(float(ts), 3),
|
|
556208
|
+
"label": label,
|
|
556209
|
+
"confidence": c,
|
|
556210
|
+
"bbox_xyxy": [int(x1), int(y1), int(x2), int(y2)],
|
|
556211
|
+
"mask_path": mask_path,
|
|
556212
|
+
"crop_path": crop_path,
|
|
556213
|
+
"area_ratio": round(area_ratio, 6),
|
|
556214
|
+
})
|
|
555938
556215
|
except Exception as exc:
|
|
555939
556216
|
objects.append({"frame_index": frame_index, "timestamp_sec": round(float(ts), 3), "label": "yolo_error", "confidence": 0.0, "bbox_xyxy": [0,0,0,0], "error": str(exc)})
|
|
555940
556217
|
|
|
@@ -555984,6 +556261,9 @@ def main():
|
|
|
555984
556261
|
"objects": objects,
|
|
555985
556262
|
"tracks": tracks,
|
|
555986
556263
|
"faces": faces,
|
|
556264
|
+
"segments": segments,
|
|
556265
|
+
"yoloe_classes": yoloe_classes,
|
|
556266
|
+
"yoloe_prompt_mode": yoloe_prompt_mode,
|
|
555987
556267
|
}))
|
|
555988
556268
|
|
|
555989
556269
|
if __name__ == "__main__":
|
|
@@ -555995,8 +556275,13 @@ function mockResult(args, workingDir) {
|
|
|
555995
556275
|
const rotation = normalizeCameraRotationDegrees(args["rotate_degrees"] ?? args["rotation_degrees"] ?? args["rotate"]);
|
|
555996
556276
|
const outDir = join92(workingDir, ".omnius", "live-media", `mock-${Date.now().toString(36)}`);
|
|
555997
556277
|
mkdirSync47(join92(outDir, "crops"), { recursive: true });
|
|
556278
|
+
mkdirSync47(join92(outDir, "segments"), { recursive: true });
|
|
555998
556279
|
const facePath = join92(outDir, "crops", "face_f0000_00.jpg");
|
|
556280
|
+
const maskPath = join92(outDir, "segments", "seg_f0001_00_mask.png");
|
|
556281
|
+
const segmentPath = join92(outDir, "segments", "seg_f0001_00_red_cup.png");
|
|
555999
556282
|
writeFileSync39(facePath, "mock face crop", "utf8");
|
|
556283
|
+
writeFileSync39(maskPath, "mock segment mask", "utf8");
|
|
556284
|
+
writeFileSync39(segmentPath, "mock extracted segment", "utf8");
|
|
556000
556285
|
return {
|
|
556001
556286
|
success: true,
|
|
556002
556287
|
source_kind: kind,
|
|
@@ -556016,10 +556301,22 @@ function mockResult(args, workingDir) {
|
|
|
556016
556301
|
{ track_id: "1", label: "person", first_seen_sec: 0, last_seen_sec: 0, observations: 1, mean_confidence: 0.91 },
|
|
556017
556302
|
{ track_id: "2", label: "red cup", first_seen_sec: 1, last_seen_sec: 2, observations: 2, mean_confidence: 0.84 }
|
|
556018
556303
|
],
|
|
556019
|
-
faces: [{ frame_index: 0, timestamp_sec: 0, crop_path: facePath, bbox_xyxy: [20, 30, 80, 90], confidence: 0.5 }]
|
|
556304
|
+
faces: [{ frame_index: 0, timestamp_sec: 0, crop_path: facePath, bbox_xyxy: [20, 30, 80, 90], confidence: 0.5 }],
|
|
556305
|
+
segments: [{
|
|
556306
|
+
frame_index: 1,
|
|
556307
|
+
timestamp_sec: 1,
|
|
556308
|
+
label: "red cup",
|
|
556309
|
+
confidence: 0.84,
|
|
556310
|
+
bbox_xyxy: [130, 140, 180, 210],
|
|
556311
|
+
mask_path: maskPath,
|
|
556312
|
+
crop_path: segmentPath,
|
|
556313
|
+
area_ratio: 0.012
|
|
556314
|
+
}],
|
|
556315
|
+
yoloe_classes: stringListFromArg(args["yoloe_classes"] ?? args["classes"]),
|
|
556316
|
+
yoloe_prompt_mode: String(args["yoloe_prompt_mode"] ?? "closed_set")
|
|
556020
556317
|
};
|
|
556021
556318
|
}
|
|
556022
|
-
var LIVE_MEDIA_ROOT, YOLO26_VENV, DEFAULT_YOLO26_MODEL, LiveMediaLoopTool;
|
|
556319
|
+
var LIVE_MEDIA_ROOT, YOLO26_VENV, DEFAULT_YOLO26_MODEL, YOLO26_SCALES, YOLO26_TASKS, YOLO26_EXPORT_FORMATS, LiveMediaLoopTool;
|
|
556023
556320
|
var init_live_media_loop = __esm({
|
|
556024
556321
|
"packages/execution/dist/tools/live-media-loop.js"() {
|
|
556025
556322
|
"use strict";
|
|
@@ -556033,14 +556330,44 @@ var init_live_media_loop = __esm({
|
|
|
556033
556330
|
LIVE_MEDIA_ROOT = join92(homedir28(), ".omnius", "runtimes", "live-media");
|
|
556034
556331
|
YOLO26_VENV = join92(LIVE_MEDIA_ROOT, ".venv-yolo26");
|
|
556035
556332
|
DEFAULT_YOLO26_MODEL = "yolo26n.pt";
|
|
556333
|
+
YOLO26_SCALES = ["n", "s", "m", "l", "x"];
|
|
556334
|
+
YOLO26_TASKS = {
|
|
556335
|
+
detect: { suffix: "", description: "Object detection" },
|
|
556336
|
+
segment: { suffix: "-seg", description: "Instance segmentation" },
|
|
556337
|
+
semantic: { suffix: "-sem", description: "Semantic segmentation" },
|
|
556338
|
+
pose: { suffix: "-pose", description: "Pose/keypoints" },
|
|
556339
|
+
obb: { suffix: "-obb", description: "Oriented object detection" },
|
|
556340
|
+
classify: { suffix: "-cls", description: "Image classification" }
|
|
556341
|
+
};
|
|
556342
|
+
YOLO26_EXPORT_FORMATS = [
|
|
556343
|
+
{ format: "pytorch", artifact: "yolo26n.pt", description: "Native PyTorch checkpoint", arguments: [], precisions: ["fp32"] },
|
|
556344
|
+
{ format: "torchscript", artifact: "yolo26n.torchscript", description: "TorchScript runtime artifact", arguments: ["imgsz", "quantize", "dynamic", "optimize", "nms", "batch", "device"], precisions: ["fp32", "fp16"] },
|
|
556345
|
+
{ format: "onnx", artifact: "yolo26n.onnx", description: "ONNX Runtime and cross-platform inference", arguments: ["imgsz", "quantize", "dynamic", "simplify", "opset", "nms", "batch", "data", "fraction", "device"], precisions: ["fp32", "fp16", "int8"] },
|
|
556346
|
+
{ format: "openvino", artifact: "yolo26n_openvino_model/", description: "Intel OpenVINO CPU/iGPU/NPU deployment", arguments: ["imgsz", "quantize", "dynamic", "nms", "batch", "data", "fraction", "device"], precisions: ["fp32", "fp16", "int8"] },
|
|
556347
|
+
{ format: "engine", artifact: "yolo26n.engine", description: "NVIDIA TensorRT engine", arguments: ["imgsz", "quantize", "dynamic", "simplify", "workspace", "nms", "batch", "data", "fraction", "device"], precisions: ["fp32", "fp16", "int8"] },
|
|
556348
|
+
{ format: "coreml", artifact: "yolo26n.mlpackage", description: "Apple CoreML deployment", arguments: ["imgsz", "dynamic", "quantize", "nms", "batch", "device"], precisions: ["fp32", "fp16", "int8", "w8a16"] },
|
|
556349
|
+
{ format: "saved_model", artifact: "yolo26n_saved_model/", description: "TensorFlow SavedModel", arguments: ["imgsz", "keras", "quantize", "nms", "batch", "data", "fraction", "device"], precisions: ["fp32", "int8"] },
|
|
556350
|
+
{ format: "pb", artifact: "yolo26n.pb", description: "TensorFlow GraphDef", arguments: ["imgsz", "batch", "device"], precisions: ["fp32"] },
|
|
556351
|
+
{ format: "edgetpu", artifact: "yolo26n_edgetpu.tflite", description: "Google Coral Edge TPU", arguments: ["imgsz", "quantize", "data", "fraction", "device"], precisions: ["int8"] },
|
|
556352
|
+
{ format: "paddle", artifact: "yolo26n_paddle_model/", description: "PaddlePaddle", arguments: ["imgsz", "batch", "device"], precisions: ["fp32"] },
|
|
556353
|
+
{ format: "mnn", artifact: "yolo26n.mnn", description: "MNN mobile/embedded runtime", arguments: ["imgsz", "batch", "quantize", "device"], precisions: ["fp32", "fp16", "int8"] },
|
|
556354
|
+
{ format: "ncnn", artifact: "yolo26n_ncnn_model/", description: "Tencent NCNN mobile/embedded runtime", arguments: ["imgsz", "quantize", "batch", "device"], precisions: ["fp32", "fp16"] },
|
|
556355
|
+
{ format: "imx", artifact: "yolo26n_imx_model/", description: "Sony IMX500", arguments: ["imgsz", "quantize", "data", "fraction", "nms", "device"], precisions: ["int8", "w8a16"] },
|
|
556356
|
+
{ format: "rknn", artifact: "yolo26n_rknn_model/", description: "Rockchip RKNN", arguments: ["imgsz", "batch", "name", "quantize", "data", "fraction", "device"], precisions: ["fp16", "int8"] },
|
|
556357
|
+
{ format: "executorch", artifact: "yolo26n_executorch_model/", description: "PyTorch ExecuTorch", arguments: ["imgsz", "batch", "device"], precisions: ["fp32"] },
|
|
556358
|
+
{ format: "axelera", artifact: "yolo26n_axelera_model/", description: "Axelera AI deployment", arguments: ["imgsz", "batch", "quantize", "data", "fraction", "device"], precisions: ["int8"] },
|
|
556359
|
+
{ format: "deepx", artifact: "yolo26n_deepx_model/", description: "DEEPX accelerator deployment", arguments: ["imgsz", "quantize", "data", "optimize", "device"], precisions: ["int8"] },
|
|
556360
|
+
{ format: "qnn", artifact: "yolo26n_qnn.onnx", description: "Qualcomm QNN HTP", arguments: ["imgsz", "batch", "name", "quantize", "data", "fraction", "device"], precisions: ["w8a16"] },
|
|
556361
|
+
{ format: "litert", artifact: "yolo26n.tflite", description: "Google LiteRT / TensorFlow Lite successor", arguments: ["imgsz", "quantize", "batch", "data", "fraction", "device"], precisions: ["fp32", "int8", "w8a16", "w8a32"] }
|
|
556362
|
+
];
|
|
556036
556363
|
LiveMediaLoopTool = class {
|
|
556037
556364
|
workingDir;
|
|
556038
556365
|
name = "live_media_loop";
|
|
556039
|
-
description = "Watch a live or file media source in a bounded loop: YouTube/direct video/local file/camera, YOLO26 object detection/tracking, face cropping, optional face familiarity recall, ASR/audio comprehension, and visual trigger evaluation. Use this for live video input, object tracking, face crops, visual reminders/triggers, and 'watch this video/camera and react when X appears'.";
|
|
556366
|
+
description = "Watch a live or file media source in a bounded loop: YouTube/direct video/local file/camera, YOLO26/YOLOE object detection/tracking, open-vocabulary segmentation masks, extracted transparent object crops, face cropping, optional face familiarity recall, ASR/audio comprehension, and visual trigger evaluation. Use this for live video input, object tracking, object segmentation/extraction, face crops, visual reminders/triggers, and 'watch this video/camera and react when X appears'.";
|
|
556040
556367
|
parameters = {
|
|
556041
556368
|
type: "object",
|
|
556042
556369
|
properties: {
|
|
556043
|
-
action: { type: "string", enum: ["watch", "analyze", "ensure", "status"] },
|
|
556370
|
+
action: { type: "string", enum: ["watch", "analyze", "ensure", "status", "deployment_options", "options", "export"] },
|
|
556044
556371
|
source_kind: { type: "string", enum: ["youtube", "camera", "file", "direct"] },
|
|
556045
556372
|
url: { type: "string", description: "YouTube or direct video URL" },
|
|
556046
556373
|
path: { type: "string", description: "Local media path" },
|
|
@@ -556053,6 +556380,36 @@ var init_live_media_loop = __esm({
|
|
|
556053
556380
|
sample_interval_sec: { type: "number", description: "Seconds between sampled frames, default 1" },
|
|
556054
556381
|
max_frames: { type: "number", description: "Max sampled frames, default 8" },
|
|
556055
556382
|
yolo_model: { type: "string", description: "Ultralytics model, default yolo26n.pt" },
|
|
556383
|
+
yolo_family: { type: "string", enum: ["yolo26", "yoloe-26"], description: "Model family when yolo_model is omitted." },
|
|
556384
|
+
yolo_task: { type: "string", enum: ["detect", "segment", "semantic", "pose", "obb", "classify"], description: "YOLO26 task suffix when yolo_model is omitted." },
|
|
556385
|
+
yolo_scale: { type: "string", enum: ["n", "s", "m", "l", "x"], description: "YOLO26 model scale when yolo_model is omitted." },
|
|
556386
|
+
yoloe_prompt_mode: { type: "string", enum: ["closed_set", "text", "prompt_free", "visual"], description: "YOLOE prompting mode. text uses yoloe_classes; prompt_free uses *-seg-pf weights when no explicit yolo_model is set." },
|
|
556387
|
+
prompt_mode: { type: "string", description: "Alias for yoloe_prompt_mode." },
|
|
556388
|
+
prompt_free: { type: "boolean", description: "Alias for yoloe_prompt_mode=prompt_free." },
|
|
556389
|
+
yoloe_classes: { type: ["array", "string"], description: "YOLOE text prompt classes, e.g. ['person','bus'] or comma-separated string." },
|
|
556390
|
+
classes: { type: ["array", "string"], description: "Alias for yoloe_classes." },
|
|
556391
|
+
segment_objects: { type: "boolean", description: "Capture instance segmentation masks when model returns masks. Enabled automatically for YOLOE." },
|
|
556392
|
+
extract_segments: { type: "boolean", description: "Save transparent RGBA crops for segmented objects." },
|
|
556393
|
+
max_segments_per_frame: { type: "number", description: "Limit saved mask/crop artifacts per sampled frame, default 8." },
|
|
556394
|
+
agnostic_nms: { type: "boolean", description: "Override YOLOE agnostic NMS behavior." },
|
|
556395
|
+
export_format: { type: "string", description: "YOLO26 export format for action=export, e.g. onnx, engine, openvino, coreml, saved_model, rknn, ncnn, litert." },
|
|
556396
|
+
format: { type: "string", description: "Alias for export_format when action=export." },
|
|
556397
|
+
quantize: { type: ["string", "number"], description: "Export precision: 8/int8/w8a8, 16/fp16/w16a16, 32/fp32, w8a16, or w8a32 where supported." },
|
|
556398
|
+
precision: { type: ["string", "number"], description: "Alias for quantize." },
|
|
556399
|
+
end2end: { type: "boolean", description: "YOLO26 one-to-one NMS-free head when true; set false for traditional one-to-many/NMS pipeline." },
|
|
556400
|
+
imgsz: { type: "number", description: "YOLO input/export image size, default 640." },
|
|
556401
|
+
dynamic: { type: "boolean", description: "Enable dynamic input size for supported export formats." },
|
|
556402
|
+
simplify: { type: "boolean", description: "Simplify ONNX/TensorRT export graphs when supported." },
|
|
556403
|
+
opset: { type: "number", description: "ONNX opset version." },
|
|
556404
|
+
workspace: { type: "number", description: "TensorRT workspace size in GiB." },
|
|
556405
|
+
nms: { type: "boolean", description: "Embed NMS in supported exports. Not available for YOLO26 end2end exports." },
|
|
556406
|
+
batch: { type: "number", description: "Export batch size." },
|
|
556407
|
+
device: { type: "string", description: "Export/inference device, e.g. cpu, 0, mps, npu:0, dla:0." },
|
|
556408
|
+
data: { type: "string", description: "Calibration dataset yaml for INT8/W8A16 exports." },
|
|
556409
|
+
fraction: { type: "number", description: "Calibration data fraction for quantized export." },
|
|
556410
|
+
optimize: { type: "boolean", description: "Export optimization for supported targets." },
|
|
556411
|
+
keras: { type: "boolean", description: "Keras SavedModel export option." },
|
|
556412
|
+
name: { type: "string", description: "Target/chip/name option for formats such as RKNN or QNN." },
|
|
556056
556413
|
auto_bootstrap: { type: "boolean", description: "Create managed YOLO26 venv if missing, default true" },
|
|
556057
556414
|
detect_objects: { type: "boolean" },
|
|
556058
556415
|
track_objects: { type: "boolean" },
|
|
@@ -556071,9 +556428,27 @@ var init_live_media_loop = __esm({
|
|
|
556071
556428
|
async execute(args) {
|
|
556072
556429
|
const start2 = performance.now();
|
|
556073
556430
|
const action = String(args["action"] ?? "watch").toLowerCase();
|
|
556074
|
-
const yoloModel =
|
|
556431
|
+
const yoloModel = resolveYolo26Model(args);
|
|
556075
556432
|
const rotation = normalizeCameraRotationDegrees(args["rotate_degrees"] ?? args["rotation_degrees"] ?? args["rotate"]);
|
|
556076
556433
|
const plan = buildYolo26BootstrapPlan(yoloModel);
|
|
556434
|
+
if (action === "deployment_options" || action === "options") {
|
|
556435
|
+
const options2 = buildYolo26DeploymentOptions();
|
|
556436
|
+
const text2 = [
|
|
556437
|
+
"YOLO26 / YOLOE deployment options",
|
|
556438
|
+
`default model: ${options2.defaultModel}`,
|
|
556439
|
+
`scales: ${options2.scales.join(", ")}`,
|
|
556440
|
+
`tasks: ${Object.entries(options2.tasks).map(([task, meta]) => `${task}${meta.suffix}`).join(", ")}`,
|
|
556441
|
+
`formats: ${options2.exportFormats.map((entry) => entry.format).join(", ")}`,
|
|
556442
|
+
`YOLOE prompt modes: ${options2.yoloePromptModes.map((entry) => entry.mode).join(", ")}`,
|
|
556443
|
+
"head modes: end2end=true for default NMS-free one-to-one head; end2end=false for traditional one-to-many/NMS pipeline"
|
|
556444
|
+
].join("\n");
|
|
556445
|
+
return {
|
|
556446
|
+
success: true,
|
|
556447
|
+
output: text2,
|
|
556448
|
+
llmContent: JSON.stringify(options2, null, 2),
|
|
556449
|
+
durationMs: performance.now() - start2
|
|
556450
|
+
};
|
|
556451
|
+
}
|
|
556077
556452
|
if (action === "status" || action === "ensure") {
|
|
556078
556453
|
const runtime = action === "ensure" ? await ensureRuntime(args["auto_bootstrap"] !== false) : await ensureRuntime(false);
|
|
556079
556454
|
return {
|
|
@@ -556084,13 +556459,69 @@ var init_live_media_loop = __esm({
|
|
|
556084
556459
|
`python: ${plan.python}`,
|
|
556085
556460
|
`model: ${plan.model}`,
|
|
556086
556461
|
`packages: ${plan.packages.join(", ")}`,
|
|
556462
|
+
`deployment formats: ${YOLO26_EXPORT_FORMATS.map((entry) => entry.format).join(", ")}`,
|
|
556087
556463
|
runtime.error ? `error: ${runtime.error}` : ""
|
|
556088
556464
|
].filter(Boolean).join("\n"),
|
|
556089
556465
|
error: runtime.ok ? void 0 : runtime.error,
|
|
556090
|
-
llmContent: JSON.stringify({ ...plan, ready: runtime.ok }, null, 2),
|
|
556466
|
+
llmContent: JSON.stringify({ ...plan, ready: runtime.ok, deploymentOptions: buildYolo26DeploymentOptions() }, null, 2),
|
|
556091
556467
|
durationMs: performance.now() - start2
|
|
556092
556468
|
};
|
|
556093
556469
|
}
|
|
556470
|
+
if (action === "export") {
|
|
556471
|
+
let format3;
|
|
556472
|
+
try {
|
|
556473
|
+
format3 = normalizeYolo26ExportFormat(args["export_format"] ?? args["format"] ?? args["deploy_format"]);
|
|
556474
|
+
} catch (err) {
|
|
556475
|
+
return { success: false, output: "", error: err instanceof Error ? err.message : String(err), durationMs: performance.now() - start2 };
|
|
556476
|
+
}
|
|
556477
|
+
if (format3 === "pytorch") {
|
|
556478
|
+
const payload = { success: true, model: yoloModel, format: format3, artifact: yoloModel, options: { format: format3 } };
|
|
556479
|
+
return {
|
|
556480
|
+
success: true,
|
|
556481
|
+
output: `YOLO26 native PyTorch model selected: ${yoloModel}`,
|
|
556482
|
+
llmContent: JSON.stringify(payload, null, 2),
|
|
556483
|
+
durationMs: performance.now() - start2
|
|
556484
|
+
};
|
|
556485
|
+
}
|
|
556486
|
+
const exportOptions = exportOptionsFromArgs(args, format3);
|
|
556487
|
+
const yoloeClasses = stringListFromArg(args["yoloe_classes"] ?? args["classes"]);
|
|
556488
|
+
if (Boolean(args["mock"]) || process.env["OMNIUS_LIVE_MEDIA_MOCK"] === "1") {
|
|
556489
|
+
const payload = {
|
|
556490
|
+
success: true,
|
|
556491
|
+
model: yoloModel,
|
|
556492
|
+
format: format3,
|
|
556493
|
+
artifact: `${basename19(yoloModel, ".pt")}.${format3 === "engine" ? "engine" : format3}`,
|
|
556494
|
+
options: exportOptions,
|
|
556495
|
+
yoloe_classes: yoloeClasses,
|
|
556496
|
+
mock: true
|
|
556497
|
+
};
|
|
556498
|
+
return {
|
|
556499
|
+
success: true,
|
|
556500
|
+
output: `YOLO26 export plan (mock): ${yoloModel} -> ${format3}`,
|
|
556501
|
+
llmContent: JSON.stringify(payload, null, 2),
|
|
556502
|
+
durationMs: performance.now() - start2
|
|
556503
|
+
};
|
|
556504
|
+
}
|
|
556505
|
+
const runtime = await ensureRuntime(args["auto_bootstrap"] !== false);
|
|
556506
|
+
if (!runtime.ok)
|
|
556507
|
+
return { success: false, output: "", error: runtime.error, durationMs: performance.now() - start2 };
|
|
556508
|
+
try {
|
|
556509
|
+
const exported = await exportYolo26Model(runtime.python, yoloModel, exportOptions, yoloeClasses);
|
|
556510
|
+
return {
|
|
556511
|
+
success: true,
|
|
556512
|
+
output: [
|
|
556513
|
+
`YOLO26 export complete: ${yoloModel}`,
|
|
556514
|
+
`format: ${format3}`,
|
|
556515
|
+
`artifact: ${String(exported["artifact"] ?? "")}`,
|
|
556516
|
+
`options: ${JSON.stringify(exportOptions)}`
|
|
556517
|
+
].join("\n"),
|
|
556518
|
+
llmContent: JSON.stringify(exported, null, 2),
|
|
556519
|
+
durationMs: performance.now() - start2
|
|
556520
|
+
};
|
|
556521
|
+
} catch (err) {
|
|
556522
|
+
return { success: false, output: "", error: err instanceof Error ? err.message : String(err), durationMs: performance.now() - start2 };
|
|
556523
|
+
}
|
|
556524
|
+
}
|
|
556094
556525
|
const mock = Boolean(args["mock"]) || process.env["OMNIUS_LIVE_MEDIA_MOCK"] === "1";
|
|
556095
556526
|
let result;
|
|
556096
556527
|
if (mock) {
|
|
@@ -556118,6 +556549,13 @@ var init_live_media_loop = __esm({
|
|
|
556118
556549
|
sample_interval_sec: Number(args["sample_interval_sec"] ?? 1),
|
|
556119
556550
|
max_frames: Number(args["max_frames"] ?? 8),
|
|
556120
556551
|
rotate_degrees: rotation,
|
|
556552
|
+
end2end: optionalBoolean(args, "end2end"),
|
|
556553
|
+
yoloe_classes: stringListFromArg(args["yoloe_classes"] ?? args["classes"]),
|
|
556554
|
+
yoloe_prompt_mode: String(args["yoloe_prompt_mode"] ?? args["prompt_mode"] ?? ""),
|
|
556555
|
+
segment_objects: args["segment_objects"] !== false,
|
|
556556
|
+
extract_segments: args["extract_segments"] !== false,
|
|
556557
|
+
max_segments_per_frame: Number(args["max_segments_per_frame"] ?? 8),
|
|
556558
|
+
agnostic_nms: optionalBoolean(args, "agnostic_nms"),
|
|
556121
556559
|
detect_objects: args["detect_objects"] !== false,
|
|
556122
556560
|
track_objects: args["track_objects"] !== false,
|
|
556123
556561
|
crop_faces: args["crop_faces"] !== false,
|
|
@@ -556215,10 +556653,13 @@ var init_live_media_loop = __esm({
|
|
|
556215
556653
|
if (result.media_path)
|
|
556216
556654
|
lines.push(`media: ${result.media_path}`);
|
|
556217
556655
|
lines.push(`YOLO model: ${result.yolo_model} (${result.yolo_live ? "live" : "mock/harness"})`);
|
|
556656
|
+
if (result.yoloe_prompt_mode && result.yoloe_prompt_mode !== "closed_set") {
|
|
556657
|
+
lines.push(`YOLOE prompt mode: ${result.yoloe_prompt_mode}${result.yoloe_classes?.length ? ` classes=${result.yoloe_classes.join(", ")}` : ""}`);
|
|
556658
|
+
}
|
|
556218
556659
|
if (result.rotate_degrees)
|
|
556219
556660
|
lines.push(`frame rotation correction: ${result.rotate_degrees} degrees clockwise`);
|
|
556220
556661
|
lines.push(`sampled: ${result.frames_sampled} frame(s) over ${result.duration_observed_sec.toFixed(1)}s`);
|
|
556221
|
-
lines.push(`objects: ${result.objects.length} detection(s), tracks: ${result.tracks.length}, face crops: ${result.faces.length}`);
|
|
556662
|
+
lines.push(`objects: ${result.objects.length} detection(s), tracks: ${result.tracks.length}, face crops: ${result.faces.length}, segments: ${result.segments?.length ?? 0}`);
|
|
556222
556663
|
const objectSummary = summarizeObjects(result.objects);
|
|
556223
556664
|
if (objectSummary.length) {
|
|
556224
556665
|
lines.push("");
|
|
@@ -556240,6 +556681,13 @@ var init_live_media_loop = __esm({
|
|
|
556240
556681
|
lines.push(`- ${basename19(face.crop_path)} at ${face.timestamp_sec.toFixed(1)}s ${face.crop_path}`);
|
|
556241
556682
|
}
|
|
556242
556683
|
}
|
|
556684
|
+
if (result.segments?.length) {
|
|
556685
|
+
lines.push("");
|
|
556686
|
+
lines.push("## Segments");
|
|
556687
|
+
for (const segment of result.segments.slice(0, 12)) {
|
|
556688
|
+
lines.push(`- ${segment.label} conf=${segment.confidence.toFixed(2)} mask=${segment.mask_path}${segment.crop_path ? ` crop=${segment.crop_path}` : ""} area=${(segment.area_ratio ?? 0).toFixed(4)}`);
|
|
556689
|
+
}
|
|
556690
|
+
}
|
|
556243
556691
|
if (audioEvidence.length) {
|
|
556244
556692
|
lines.push("");
|
|
556245
556693
|
lines.push("## Audio Evidence");
|
|
@@ -556263,11 +556711,14 @@ var init_live_media_loop = __esm({
|
|
|
556263
556711
|
media_path: result.media_path,
|
|
556264
556712
|
yolo_model: result.yolo_model,
|
|
556265
556713
|
yolo_live: result.yolo_live,
|
|
556714
|
+
yoloe_classes: result.yoloe_classes ?? [],
|
|
556715
|
+
yoloe_prompt_mode: result.yoloe_prompt_mode,
|
|
556266
556716
|
rotate_degrees: result.rotate_degrees ?? 0,
|
|
556267
556717
|
frames_sampled: result.frames_sampled,
|
|
556268
556718
|
objects: result.objects.slice(0, 80),
|
|
556269
556719
|
tracks: result.tracks.slice(0, 40),
|
|
556270
556720
|
faces: result.faces.slice(0, 20),
|
|
556721
|
+
segments: (result.segments ?? []).slice(0, 40),
|
|
556271
556722
|
audio_evidence: audioEvidence,
|
|
556272
556723
|
trigger_hits: hits,
|
|
556273
556724
|
artifacts_dir: result.output_dir
|
|
@@ -557548,6 +557999,7 @@ __export(dist_exports2, {
|
|
|
557548
557999
|
buildToolManifestFromModule: () => buildToolManifestFromModule,
|
|
557549
558000
|
buildWorkboardSynthesisContext: () => buildWorkboardSynthesisContext,
|
|
557550
558001
|
buildYolo26BootstrapPlan: () => buildYolo26BootstrapPlan,
|
|
558002
|
+
buildYolo26DeploymentOptions: () => buildYolo26DeploymentOptions,
|
|
557551
558003
|
builtInMediaModelCatalog: () => builtInMediaModelCatalog,
|
|
557552
558004
|
canInvokeTool: () => canInvokeTool,
|
|
557553
558005
|
checkConstraints: () => checkConstraints,
|
|
@@ -557781,6 +558233,7 @@ __export(dist_exports2, {
|
|
|
557781
558233
|
resolveMediaCudaVisibleDevicesForEnv: () => resolveMediaCudaVisibleDevicesForEnv,
|
|
557782
558234
|
resolveMediaModel: () => resolveMediaModel,
|
|
557783
558235
|
resolveSecret: () => resolveSecret,
|
|
558236
|
+
resolveYolo26Model: () => resolveYolo26Model,
|
|
557784
558237
|
revokeSecret: () => revokeSecret,
|
|
557785
558238
|
runBenchmark: () => runBenchmark,
|
|
557786
558239
|
runBuild: () => runBuild,
|
|
@@ -602192,7 +602645,9 @@ async function buildImageAsciiPreview(inputPath, options2 = {}) {
|
|
|
602192
602645
|
plainAscii,
|
|
602193
602646
|
renderer: "image-to-ascii",
|
|
602194
602647
|
width,
|
|
602195
|
-
height
|
|
602648
|
+
height,
|
|
602649
|
+
imageWidth: dimensions?.width,
|
|
602650
|
+
imageHeight: dimensions?.height
|
|
602196
602651
|
};
|
|
602197
602652
|
}
|
|
602198
602653
|
return {
|
|
@@ -602200,11 +602655,24 @@ async function buildImageAsciiPreview(inputPath, options2 = {}) {
|
|
|
602200
602655
|
ascii: previewFailure(result.error || "renderer unavailable", width),
|
|
602201
602656
|
renderer: "image-to-ascii",
|
|
602202
602657
|
width,
|
|
602203
|
-
height
|
|
602658
|
+
height,
|
|
602659
|
+
imageWidth: dimensions?.width,
|
|
602660
|
+
imageHeight: dimensions?.height
|
|
602204
602661
|
};
|
|
602205
602662
|
}
|
|
602206
602663
|
const fallback = await convertWithFfmpeg(imagePath, width, height, timeoutMs);
|
|
602207
|
-
if (fallback)
|
|
602664
|
+
if (fallback) {
|
|
602665
|
+
return {
|
|
602666
|
+
path: imagePath,
|
|
602667
|
+
ascii: fallback,
|
|
602668
|
+
plainAscii: fallback,
|
|
602669
|
+
renderer: "ffmpeg",
|
|
602670
|
+
width,
|
|
602671
|
+
height,
|
|
602672
|
+
imageWidth: dimensions?.width,
|
|
602673
|
+
imageHeight: dimensions?.height
|
|
602674
|
+
};
|
|
602675
|
+
}
|
|
602208
602676
|
return null;
|
|
602209
602677
|
}
|
|
602210
602678
|
function formatImageAsciiContext(preview, label) {
|
|
@@ -602323,10 +602791,45 @@ function parseJsonObjectFromText(value2) {
|
|
|
602323
602791
|
const direct = parseJsonObject3(value2);
|
|
602324
602792
|
if (direct) return direct;
|
|
602325
602793
|
const text2 = String(value2 ?? "");
|
|
602326
|
-
const
|
|
602327
|
-
|
|
602328
|
-
|
|
602329
|
-
|
|
602794
|
+
const candidates = [];
|
|
602795
|
+
let depth = 0;
|
|
602796
|
+
let start2 = -1;
|
|
602797
|
+
let inString = false;
|
|
602798
|
+
let escaped = false;
|
|
602799
|
+
for (let i2 = 0; i2 < text2.length; i2++) {
|
|
602800
|
+
const ch = text2[i2];
|
|
602801
|
+
if (inString) {
|
|
602802
|
+
if (escaped) {
|
|
602803
|
+
escaped = false;
|
|
602804
|
+
} else if (ch === "\\") {
|
|
602805
|
+
escaped = true;
|
|
602806
|
+
} else if (ch === '"') {
|
|
602807
|
+
inString = false;
|
|
602808
|
+
}
|
|
602809
|
+
continue;
|
|
602810
|
+
}
|
|
602811
|
+
if (ch === '"') {
|
|
602812
|
+
inString = true;
|
|
602813
|
+
continue;
|
|
602814
|
+
}
|
|
602815
|
+
if (ch === "{") {
|
|
602816
|
+
if (depth === 0) start2 = i2;
|
|
602817
|
+
depth++;
|
|
602818
|
+
continue;
|
|
602819
|
+
}
|
|
602820
|
+
if (ch === "}" && depth > 0) {
|
|
602821
|
+
depth--;
|
|
602822
|
+
if (depth === 0 && start2 >= 0) {
|
|
602823
|
+
candidates.push(text2.slice(start2, i2 + 1));
|
|
602824
|
+
start2 = -1;
|
|
602825
|
+
}
|
|
602826
|
+
}
|
|
602827
|
+
}
|
|
602828
|
+
for (const candidate of candidates.reverse()) {
|
|
602829
|
+
const parsed = parseJsonObject3(candidate);
|
|
602830
|
+
if (parsed) return parsed;
|
|
602831
|
+
}
|
|
602832
|
+
return null;
|
|
602330
602833
|
}
|
|
602331
602834
|
function normalizeLiveCameraRotation(value2) {
|
|
602332
602835
|
if (value2 === void 0 || value2 === null || value2 === "") return 0;
|
|
@@ -602418,21 +602921,59 @@ function truncateCell(value2, width) {
|
|
|
602418
602921
|
function stripDashboardAnsi(value2) {
|
|
602419
602922
|
return value2.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, "");
|
|
602420
602923
|
}
|
|
602924
|
+
function dashboardVisibleWidth(value2) {
|
|
602925
|
+
return stripDashboardAnsi(value2).length;
|
|
602926
|
+
}
|
|
602927
|
+
function truncateAnsiCell(value2, width) {
|
|
602928
|
+
value2 = String(value2 ?? "").replace(/\r?\n/g, " ");
|
|
602929
|
+
if (width <= 0) return "";
|
|
602930
|
+
let out = "";
|
|
602931
|
+
let visible = 0;
|
|
602932
|
+
let i2 = 0;
|
|
602933
|
+
let sawAnsi = false;
|
|
602934
|
+
while (i2 < value2.length && visible < width) {
|
|
602935
|
+
DASHBOARD_ANSI_STICKY_PATTERN.lastIndex = i2;
|
|
602936
|
+
const ansi5 = DASHBOARD_ANSI_STICKY_PATTERN.exec(value2);
|
|
602937
|
+
if (ansi5) {
|
|
602938
|
+
sawAnsi = true;
|
|
602939
|
+
out += ansi5[0];
|
|
602940
|
+
i2 = DASHBOARD_ANSI_STICKY_PATTERN.lastIndex;
|
|
602941
|
+
continue;
|
|
602942
|
+
}
|
|
602943
|
+
const codePoint = value2.codePointAt(i2);
|
|
602944
|
+
if (codePoint == null) break;
|
|
602945
|
+
const ch = String.fromCodePoint(codePoint);
|
|
602946
|
+
out += ch;
|
|
602947
|
+
i2 += ch.length;
|
|
602948
|
+
visible++;
|
|
602949
|
+
}
|
|
602950
|
+
const currentWidth = dashboardVisibleWidth(out);
|
|
602951
|
+
if (currentWidth < width) out += " ".repeat(width - currentWidth);
|
|
602952
|
+
return sawAnsi ? `${out}\x1B[0m` : out;
|
|
602953
|
+
}
|
|
602421
602954
|
function dashboardPreviewLines(camera) {
|
|
602422
|
-
const raw = camera.
|
|
602423
|
-
return raw.replace(/\r/g, "").split("\n").map((line) =>
|
|
602955
|
+
const raw = camera.ascii || camera.plainAscii || "";
|
|
602956
|
+
return raw.replace(/\r/g, "").split("\n").map((line) => line.replace(/\s+$/g, "")).filter((line) => stripDashboardAnsi(line).trim().length > 0);
|
|
602424
602957
|
}
|
|
602425
602958
|
function dashboardPreviewAspect(lines) {
|
|
602426
602959
|
const widths = lines.map((line) => stripDashboardAnsi(line).length).filter((n2) => n2 > 0);
|
|
602427
602960
|
const maxWidth = Math.max(1, ...widths);
|
|
602428
602961
|
return Math.max(0.12, Math.min(1.6, lines.length / maxWidth));
|
|
602429
602962
|
}
|
|
602963
|
+
function cameraDashboardAspect(camera, lines) {
|
|
602964
|
+
const frameWidth = Number(camera.frameWidth ?? 0);
|
|
602965
|
+
const frameHeight = Number(camera.frameHeight ?? 0);
|
|
602966
|
+
if (frameWidth > 0 && frameHeight > 0) {
|
|
602967
|
+
return Math.max(0.12, Math.min(1.4, frameHeight / frameWidth * 0.52));
|
|
602968
|
+
}
|
|
602969
|
+
return dashboardPreviewAspect(lines);
|
|
602970
|
+
}
|
|
602430
602971
|
function fitDashboardPane(lines, paneWidth, paneHeight, fallback) {
|
|
602431
602972
|
const source = lines.length > 0 ? lines : [fallback];
|
|
602432
602973
|
const normalized = [];
|
|
602433
602974
|
for (let i2 = 0; i2 < paneHeight; i2++) {
|
|
602434
602975
|
const sourceIndex = source.length <= paneHeight ? i2 : Math.min(source.length - 1, Math.floor(i2 * source.length / paneHeight));
|
|
602435
|
-
normalized.push(
|
|
602976
|
+
normalized.push(truncateAnsiCell(source[sourceIndex] ?? "", paneWidth));
|
|
602436
602977
|
}
|
|
602437
602978
|
while (normalized.length < paneHeight) normalized.push(" ".repeat(paneWidth));
|
|
602438
602979
|
return normalized;
|
|
@@ -602451,7 +602992,7 @@ function liveDashboardPreviewWidthForSources(sourceCount, termWidth = process.st
|
|
|
602451
602992
|
function formatCameraPane(camera, paneWidth, paneHeight, now2) {
|
|
602452
602993
|
const contentWidth = Math.max(10, paneWidth - 2);
|
|
602453
602994
|
const age = Math.max(0, Math.round((now2 - camera.updatedAt) / 1e3));
|
|
602454
|
-
const title = `${compactSourceId(camera.source)} ${age}s`;
|
|
602995
|
+
const title = `${compactSourceId(camera.source)} ${age}s${cameraPaneBadge(camera)}`;
|
|
602455
602996
|
const topTitle = ` ${title} `;
|
|
602456
602997
|
const left = Math.max(0, Math.floor((contentWidth - topTitle.length) / 2));
|
|
602457
602998
|
const right = Math.max(0, contentWidth - topTitle.length - left);
|
|
@@ -602460,7 +603001,7 @@ function formatCameraPane(camera, paneWidth, paneHeight, now2) {
|
|
|
602460
603001
|
const previewLines = dashboardPreviewLines(camera);
|
|
602461
603002
|
const fallback = camera.framePath ? "preview refreshing" : camera.error ? "capture failed" : "awaiting frame";
|
|
602462
603003
|
const body = fitDashboardPane(previewLines, contentWidth, paneHeight, fallback);
|
|
602463
|
-
return [top, ...body.map((line) => `│${
|
|
603004
|
+
return [top, ...body.map((line) => `│${truncateAnsiCell(line, contentWidth)}│`), bottom];
|
|
602464
603005
|
}
|
|
602465
603006
|
function pushDashboardWrapped(lines, value2, width) {
|
|
602466
603007
|
const contentWidth = Math.max(10, width - 2);
|
|
@@ -602654,9 +603195,152 @@ function summarizeInference(value2) {
|
|
|
602654
603195
|
const sampled = lines.find((line) => /^objects:/i.test(line));
|
|
602655
603196
|
return sampled ? trimOneLine(sampled, 160) : trimOneLine(lines.slice(0, 4).join("; "), 160);
|
|
602656
603197
|
}
|
|
603198
|
+
function boundedConfidence(value2) {
|
|
603199
|
+
const n2 = Number(value2);
|
|
603200
|
+
return Number.isFinite(n2) ? Math.max(0, Math.min(1, n2)) : void 0;
|
|
603201
|
+
}
|
|
603202
|
+
function mergeClassificationInto(map2, item) {
|
|
603203
|
+
const label = trimOneLine(item.label || item.kind, 80) || item.kind;
|
|
603204
|
+
const key = `${item.kind}:${label}:${item.trackId ?? ""}:${item.evidencePath ?? ""}`;
|
|
603205
|
+
const existing = map2.get(key);
|
|
603206
|
+
const count = Math.max(1, Number(item.count ?? 1));
|
|
603207
|
+
if (!existing) {
|
|
603208
|
+
map2.set(key, { ...item, label, count });
|
|
603209
|
+
return;
|
|
603210
|
+
}
|
|
603211
|
+
const total = existing.count + count;
|
|
603212
|
+
const confidence2 = existing.confidence !== void 0 || item.confidence !== void 0 ? ((existing.confidence ?? 0) * existing.count + (item.confidence ?? 0) * count) / total : void 0;
|
|
603213
|
+
map2.set(key, {
|
|
603214
|
+
...existing,
|
|
603215
|
+
count: total,
|
|
603216
|
+
confidence: confidence2,
|
|
603217
|
+
updatedAt: Math.max(existing.updatedAt, item.updatedAt)
|
|
603218
|
+
});
|
|
603219
|
+
}
|
|
603220
|
+
function classificationsFromLiveMediaPayload(payload, sampledAt, fallbackSource) {
|
|
603221
|
+
if (!payload) return [];
|
|
603222
|
+
const map2 = /* @__PURE__ */ new Map();
|
|
603223
|
+
void fallbackSource;
|
|
603224
|
+
for (const object of payload.objects ?? []) {
|
|
603225
|
+
const label = trimOneLine(object.label || "object", 80) || "object";
|
|
603226
|
+
mergeClassificationInto(map2, {
|
|
603227
|
+
kind: "object",
|
|
603228
|
+
label,
|
|
603229
|
+
count: 1,
|
|
603230
|
+
confidence: boundedConfidence(object.confidence),
|
|
603231
|
+
trackId: object.track_id === void 0 || object.track_id === null ? void 0 : String(object.track_id),
|
|
603232
|
+
updatedAt: observedAtFromOffset(sampledAt, payload, Number(object.timestamp_sec ?? 0))
|
|
603233
|
+
});
|
|
603234
|
+
}
|
|
603235
|
+
for (const track of payload.tracks ?? []) {
|
|
603236
|
+
const label = trimOneLine(track.label || "object", 80) || "object";
|
|
603237
|
+
mergeClassificationInto(map2, {
|
|
603238
|
+
kind: "track",
|
|
603239
|
+
label,
|
|
603240
|
+
count: 1,
|
|
603241
|
+
confidence: boundedConfidence(track.mean_confidence),
|
|
603242
|
+
trackId: track.track_id === void 0 || track.track_id === null ? void 0 : String(track.track_id),
|
|
603243
|
+
updatedAt: observedAtFromOffset(sampledAt, payload, Number(track.last_seen_sec ?? track.first_seen_sec ?? 0))
|
|
603244
|
+
});
|
|
603245
|
+
}
|
|
603246
|
+
for (const face of payload.faces ?? []) {
|
|
603247
|
+
const identity3 = parseFaceIdentity(face);
|
|
603248
|
+
const label = identity3.status === "known" && identity3.name ? identity3.name : "unknown face";
|
|
603249
|
+
mergeClassificationInto(map2, {
|
|
603250
|
+
kind: identity3.status === "known" ? "identity" : "face",
|
|
603251
|
+
label,
|
|
603252
|
+
count: 1,
|
|
603253
|
+
confidence: boundedConfidence(identity3.confidence ?? face.confidence),
|
|
603254
|
+
evidencePath: typeof face.crop_path === "string" ? face.crop_path : void 0,
|
|
603255
|
+
updatedAt: observedAtFromOffset(sampledAt, payload, Number(face.timestamp_sec ?? 0))
|
|
603256
|
+
});
|
|
603257
|
+
}
|
|
603258
|
+
for (const segment of payload.segments ?? []) {
|
|
603259
|
+
const label = trimOneLine(segment.label || "segment", 80) || "segment";
|
|
603260
|
+
mergeClassificationInto(map2, {
|
|
603261
|
+
kind: "segment",
|
|
603262
|
+
label,
|
|
603263
|
+
count: 1,
|
|
603264
|
+
confidence: boundedConfidence(segment.confidence),
|
|
603265
|
+
evidencePath: typeof segment.crop_path === "string" && segment.crop_path ? segment.crop_path : typeof segment.mask_path === "string" ? segment.mask_path : void 0,
|
|
603266
|
+
updatedAt: observedAtFromOffset(sampledAt, payload, Number(segment.timestamp_sec ?? 0))
|
|
603267
|
+
});
|
|
603268
|
+
}
|
|
603269
|
+
for (const hit of payload.trigger_hits ?? []) {
|
|
603270
|
+
const label = trimOneLine(hit.triggerName || hit.matched || "visual trigger", 80) || "visual trigger";
|
|
603271
|
+
mergeClassificationInto(map2, {
|
|
603272
|
+
kind: "trigger",
|
|
603273
|
+
label,
|
|
603274
|
+
count: 1,
|
|
603275
|
+
confidence: boundedConfidence(hit.confidence),
|
|
603276
|
+
updatedAt: sampledAt
|
|
603277
|
+
});
|
|
603278
|
+
}
|
|
603279
|
+
return [...map2.values()].sort((a2, b) => {
|
|
603280
|
+
const aPriority = a2.kind === "face" || a2.kind === "identity" ? 0 : a2.kind === "segment" ? 1 : a2.kind === "object" ? 2 : 3;
|
|
603281
|
+
const bPriority = b.kind === "face" || b.kind === "identity" ? 0 : b.kind === "segment" ? 1 : b.kind === "object" ? 2 : 3;
|
|
603282
|
+
if (aPriority !== bPriority) return aPriority - bPriority;
|
|
603283
|
+
return (b.confidence ?? 0) - (a2.confidence ?? 0);
|
|
603284
|
+
}).slice(0, 24).map((item) => ({ ...item }));
|
|
603285
|
+
}
|
|
603286
|
+
function classificationsFromClipSummary(summary, observedAt, evidencePath) {
|
|
603287
|
+
const parsed = parseJsonObjectFromText(summary);
|
|
603288
|
+
const matches = Array.isArray(parsed?.["matches"]) ? parsed["matches"] : [];
|
|
603289
|
+
return matches.slice(0, 8).map((match) => {
|
|
603290
|
+
const label = trimOneLine(
|
|
603291
|
+
match["label"] ?? match["name"] ?? match["title"] ?? match["id"] ?? "visual-memory match",
|
|
603292
|
+
80
|
|
603293
|
+
) || "visual-memory match";
|
|
603294
|
+
return {
|
|
603295
|
+
kind: "clip",
|
|
603296
|
+
label,
|
|
603297
|
+
count: 1,
|
|
603298
|
+
confidence: boundedConfidence(match["score"] ?? match["similarity"] ?? match["confidence"]),
|
|
603299
|
+
evidencePath,
|
|
603300
|
+
updatedAt: observedAt
|
|
603301
|
+
};
|
|
603302
|
+
});
|
|
603303
|
+
}
|
|
603304
|
+
function mergeCameraClassifications(existing, incoming, limit = 24) {
|
|
603305
|
+
const map2 = /* @__PURE__ */ new Map();
|
|
603306
|
+
for (const item of [...existing ?? [], ...incoming]) {
|
|
603307
|
+
mergeClassificationInto(map2, item);
|
|
603308
|
+
}
|
|
603309
|
+
const merged = [...map2.values()].sort((a2, b) => b.updatedAt - a2.updatedAt || (b.confidence ?? 0) - (a2.confidence ?? 0)).slice(0, limit);
|
|
603310
|
+
return merged.length > 0 ? merged : existing;
|
|
603311
|
+
}
|
|
603312
|
+
function cameraClassificationsSummary(camera, maxChars = 220) {
|
|
603313
|
+
const items = (camera.classifications ?? []).sort((a2, b) => {
|
|
603314
|
+
const aPriority = a2.kind === "face" || a2.kind === "identity" ? 0 : a2.kind === "segment" ? 1 : a2.kind === "object" ? 2 : a2.kind === "track" ? 3 : 4;
|
|
603315
|
+
const bPriority = b.kind === "face" || b.kind === "identity" ? 0 : b.kind === "segment" ? 1 : b.kind === "object" ? 2 : b.kind === "track" ? 3 : 4;
|
|
603316
|
+
if (aPriority !== bPriority) return aPriority - bPriority;
|
|
603317
|
+
return (b.confidence ?? 0) - (a2.confidence ?? 0);
|
|
603318
|
+
}).slice(0, 8).map((item) => {
|
|
603319
|
+
const count = item.count > 1 ? ` x${item.count}` : "";
|
|
603320
|
+
const confidence2 = item.confidence !== void 0 ? ` ${item.confidence.toFixed(2)}` : "";
|
|
603321
|
+
return `${item.kind}:${item.label}${count}${confidence2}`;
|
|
603322
|
+
});
|
|
603323
|
+
return trimOneLine(items.join("; "), maxChars);
|
|
603324
|
+
}
|
|
603325
|
+
function cameraPaneBadge(camera) {
|
|
603326
|
+
const classifications = camera.classifications ?? [];
|
|
603327
|
+
const faceCount = classifications.filter((item) => item.kind === "face" || item.kind === "identity").reduce((sum, item) => sum + Math.max(1, item.count), 0);
|
|
603328
|
+
const objectCount = classifications.filter((item) => item.kind === "object").reduce((sum, item) => sum + Math.max(1, item.count), 0);
|
|
603329
|
+
const segmentCount = classifications.filter((item) => item.kind === "segment").reduce((sum, item) => sum + Math.max(1, item.count), 0);
|
|
603330
|
+
const trackCount = classifications.filter((item) => item.kind === "track").reduce((sum, item) => sum + Math.max(1, item.count), 0);
|
|
603331
|
+
const triggerCount = classifications.filter((item) => item.kind === "trigger").reduce((sum, item) => sum + Math.max(1, item.count), 0);
|
|
603332
|
+
const parts = [
|
|
603333
|
+
faceCount > 0 ? `F${faceCount}` : "",
|
|
603334
|
+
segmentCount > 0 ? `S${segmentCount}` : "",
|
|
603335
|
+
objectCount > 0 ? `O${objectCount}` : "",
|
|
603336
|
+
trackCount > 0 ? `T${trackCount}` : "",
|
|
603337
|
+
triggerCount > 0 ? `!${triggerCount}` : ""
|
|
603338
|
+
].filter(Boolean);
|
|
603339
|
+
return parts.length > 0 ? ` ${parts.join(" ")}` : "";
|
|
603340
|
+
}
|
|
602657
603341
|
function cameraSnapshotSummary(camera) {
|
|
602658
603342
|
if (camera.error) return `error: ${trimOneLine(camera.error, 160)}`;
|
|
602659
|
-
return camera.summary || summarizeInference(camera.inference);
|
|
603343
|
+
return camera.summary || (camera.classifications?.length ? `classifications: ${cameraClassificationsSummary(camera)}` : "") || summarizeInference(camera.inference);
|
|
602660
603344
|
}
|
|
602661
603345
|
function formatLiveDashboardFromSnapshot(snapshot, opts = {}) {
|
|
602662
603346
|
const width = Math.max(60, opts.width ?? 100);
|
|
@@ -602681,13 +603365,14 @@ function formatLiveDashboardFromSnapshot(snapshot, opts = {}) {
|
|
|
602681
603365
|
for (let start2 = 0; start2 < cameras.length; start2 += paneCount) {
|
|
602682
603366
|
const group = cameras.slice(start2, start2 + paneCount);
|
|
602683
603367
|
const sourceLines = group.map((camera) => dashboardPreviewLines(camera));
|
|
602684
|
-
const
|
|
603368
|
+
const aspects = group.map((camera, idx) => cameraDashboardAspect(camera, sourceLines[idx] ?? []));
|
|
603369
|
+
const aspect = Math.max(0.18, Math.min(0.92, Math.max(...aspects)));
|
|
602685
603370
|
const paneHeight = Math.max(10, Math.min(32, Math.round(paneContentWidth * aspect)));
|
|
602686
603371
|
const panes = group.map((camera) => formatCameraPane(camera, paneWidth, paneHeight, now2));
|
|
602687
603372
|
const rowCount = Math.max(...panes.map((pane) => pane.length));
|
|
602688
603373
|
for (let row = 0; row < rowCount; row++) {
|
|
602689
603374
|
const body = panes.map((pane) => pane[row] ?? " ".repeat(paneWidth)).join(" ");
|
|
602690
|
-
lines.push(`│${
|
|
603375
|
+
lines.push(`│${truncateAnsiCell(body, innerWidth)}│`);
|
|
602691
603376
|
}
|
|
602692
603377
|
}
|
|
602693
603378
|
}
|
|
@@ -602698,8 +603383,10 @@ function formatLiveDashboardFromSnapshot(snapshot, opts = {}) {
|
|
|
602698
603383
|
for (const camera of cameras) {
|
|
602699
603384
|
const age = Math.max(0, Math.round((now2 - camera.updatedAt) / 1e3));
|
|
602700
603385
|
const rotation = camera.orientation ? formatCameraRotation(camera.orientation.rotation) : "upright/no correction";
|
|
603386
|
+
const classifications = cameraClassificationsSummary(camera);
|
|
602701
603387
|
const detailLines = [
|
|
602702
603388
|
`${compactSourceId(camera.source)} age=${age}s rotation=${rotation}`,
|
|
603389
|
+
classifications ? `classifications: ${classifications}` : "",
|
|
602703
603390
|
cameraSnapshotSummary(camera),
|
|
602704
603391
|
camera.framePath ? `frame: ${camera.displayPath || camera.framePath}` : "",
|
|
602705
603392
|
camera.orientation?.evidence ? `orientation: ${trimOneLine(camera.orientation.evidence, 120)}` : ""
|
|
@@ -602729,7 +603416,7 @@ function formatLiveDashboardFromSnapshot(snapshot, opts = {}) {
|
|
|
602729
603416
|
function parseLiveMediaPayload(value2) {
|
|
602730
603417
|
const parsed = parseJsonObject3(value2);
|
|
602731
603418
|
if (!parsed) return null;
|
|
602732
|
-
if (!Array.isArray(parsed["objects"]) && !Array.isArray(parsed["tracks"]) && !Array.isArray(parsed["faces"])) return null;
|
|
603419
|
+
if (!Array.isArray(parsed["objects"]) && !Array.isArray(parsed["tracks"]) && !Array.isArray(parsed["faces"]) && !Array.isArray(parsed["segments"])) return null;
|
|
602733
603420
|
return parsed;
|
|
602734
603421
|
}
|
|
602735
603422
|
function observedAtFromOffset(sampledAt, payload, offsetSec) {
|
|
@@ -602826,6 +603513,21 @@ function observationsFromLiveMediaPayload(payload, sampledAt, fallbackSource) {
|
|
|
602826
603513
|
evidence: identity3.evidence
|
|
602827
603514
|
});
|
|
602828
603515
|
}
|
|
603516
|
+
for (const segment of (payload.segments ?? []).slice(0, 24)) {
|
|
603517
|
+
const label = String(segment.label || "segment");
|
|
603518
|
+
const observedAt = observedAtFromOffset(sampledAt, payload, Number(segment.timestamp_sec ?? 0));
|
|
603519
|
+
const evidencePath = typeof segment.crop_path === "string" && segment.crop_path ? segment.crop_path : typeof segment.mask_path === "string" ? segment.mask_path : void 0;
|
|
603520
|
+
events.push({
|
|
603521
|
+
id: stableEventId("camera_segment", source, observedAt, `${label}:${segment.frame_index ?? ""}:${evidencePath ?? ""}`),
|
|
603522
|
+
kind: "camera_segment",
|
|
603523
|
+
observedAt,
|
|
603524
|
+
source,
|
|
603525
|
+
title: `Segmented ${label}`,
|
|
603526
|
+
summary: `frame=${segment.frame_index ?? "?"} t=${Number(segment.timestamp_sec ?? 0).toFixed(1)}s conf=${Number(segment.confidence ?? 0).toFixed(2)} area=${Number(segment.area_ratio ?? 0).toFixed(4)} mask=${segment.mask_path ?? "none"} crop=${segment.crop_path ?? "none"} bbox=${JSON.stringify(segment.bbox_xyxy ?? [])}`,
|
|
603527
|
+
confidence: typeof segment.confidence === "number" ? segment.confidence : void 0,
|
|
603528
|
+
evidencePath
|
|
603529
|
+
});
|
|
603530
|
+
}
|
|
602829
603531
|
const faceCount = people.length;
|
|
602830
603532
|
if (faceCount === 0) {
|
|
602831
603533
|
const personTracks = (payload.tracks ?? []).filter((track) => String(track.label || "").toLowerCase() === "person").slice(0, 8);
|
|
@@ -602951,8 +603653,8 @@ function loadLiveSensorConfig(repoRoot) {
|
|
|
602951
603653
|
...DEFAULT_CONFIG7,
|
|
602952
603654
|
...parsed,
|
|
602953
603655
|
cameraOrientation: sanitizeCameraOrientation(parsed.cameraOrientation),
|
|
602954
|
-
videoIntervalMs: Math.max(
|
|
602955
|
-
audioIntervalMs: Math.max(
|
|
603656
|
+
videoIntervalMs: Math.max(MIN_VIDEO_INTERVAL_MS, Number(parsed.videoIntervalMs ?? DEFAULT_CONFIG7.videoIntervalMs)),
|
|
603657
|
+
audioIntervalMs: Math.max(MIN_AUDIO_INTERVAL_MS, Number(parsed.audioIntervalMs ?? DEFAULT_CONFIG7.audioIntervalMs))
|
|
602956
603658
|
};
|
|
602957
603659
|
} catch {
|
|
602958
603660
|
return { ...DEFAULT_CONFIG7 };
|
|
@@ -602996,6 +603698,8 @@ function formatLiveSensorContextFromSnapshot(snapshot, opts = {}) {
|
|
|
602996
603698
|
const age = Math.max(0, Math.round((now2 - camera.updatedAt) / 1e3));
|
|
602997
603699
|
const rotation = camera.orientation ? formatCameraRotation(camera.orientation.rotation) : "upright/no correction";
|
|
602998
603700
|
lines.push(`- ${camera.source} age=${age}s rotation=${rotation}${camera.framePath ? ` frame=${camera.framePath}` : ""}${camera.error ? ` error=${trimOneLine(camera.error, 240)}` : ""}`);
|
|
603701
|
+
const classifications = cameraClassificationsSummary(camera);
|
|
603702
|
+
if (classifications) lines.push(` classifications: ${classifications}`);
|
|
602999
603703
|
const summary = cameraSnapshotSummary(camera);
|
|
603000
603704
|
if (summary) lines.push(` summary: ${summary}`);
|
|
603001
603705
|
}
|
|
@@ -603050,6 +603754,8 @@ function formatLiveSensorContextFromSnapshot(snapshot, opts = {}) {
|
|
|
603050
603754
|
lines.push(`timestamp: ${nowIso3(snapshot.video.updatedAt)} age=${Math.max(0, Math.round(age / 1e3))}s source=${snapshot.video.source}${snapshot.video.orientation ? ` rotation=${formatCameraRotation(snapshot.video.orientation.rotation)}` : ""}${age > maxAgeMs ? " STALE" : ""}`);
|
|
603051
603755
|
if (snapshot.video.error) lines.push(`error: ${snapshot.video.error}`);
|
|
603052
603756
|
if (snapshot.video.framePath) lines.push(`frame: ${snapshot.video.framePath}`);
|
|
603757
|
+
const classifications = cameraClassificationsSummary(snapshot.video);
|
|
603758
|
+
if (classifications) lines.push(`Live classifications: ${classifications}`);
|
|
603053
603759
|
if (snapshot.video.asciiContext) lines.push(snapshot.video.asciiContext);
|
|
603054
603760
|
if (snapshot.video.inference) {
|
|
603055
603761
|
lines.push("Video inference:");
|
|
@@ -603066,6 +603772,8 @@ function formatLiveSensorContextFromSnapshot(snapshot, opts = {}) {
|
|
|
603066
603772
|
lines.push(`timestamp: ${nowIso3(camera.updatedAt)} age=${Math.max(0, Math.round(age / 1e3))}s rotation=${rotation}${age > maxAgeMs ? " STALE" : ""}`);
|
|
603067
603773
|
if (camera.error) lines.push(`error: ${camera.error}`);
|
|
603068
603774
|
if (camera.framePath) lines.push(`frame: ${camera.framePath}`);
|
|
603775
|
+
const classifications = cameraClassificationsSummary(camera);
|
|
603776
|
+
if (classifications) lines.push(`classifications: ${classifications}`);
|
|
603069
603777
|
lines.push(`summary: ${cameraSnapshotSummary(camera)}`);
|
|
603070
603778
|
}
|
|
603071
603779
|
}
|
|
@@ -603316,13 +604024,17 @@ function formatLiveStatus(snapshot) {
|
|
|
603316
604024
|
}
|
|
603317
604025
|
return lines.join("\n");
|
|
603318
604026
|
}
|
|
603319
|
-
var DEFAULT_CONFIG7, managers, LiveSensorManager;
|
|
604027
|
+
var MIN_VIDEO_INTERVAL_MS, LOW_LATENCY_VIDEO_INTERVAL_MS, MIN_AUDIO_INTERVAL_MS, LOW_LATENCY_AUDIO_INTERVAL_MS, DEFAULT_CONFIG7, managers, DASHBOARD_ANSI_STICKY_PATTERN, LiveSensorManager;
|
|
603320
604028
|
var init_live_sensors = __esm({
|
|
603321
604029
|
"packages/cli/src/tui/live-sensors.ts"() {
|
|
603322
604030
|
"use strict";
|
|
603323
604031
|
init_dist5();
|
|
603324
604032
|
init_async_process();
|
|
603325
604033
|
init_image_ascii_preview();
|
|
604034
|
+
MIN_VIDEO_INTERVAL_MS = 250;
|
|
604035
|
+
LOW_LATENCY_VIDEO_INTERVAL_MS = 250;
|
|
604036
|
+
MIN_AUDIO_INTERVAL_MS = 1500;
|
|
604037
|
+
LOW_LATENCY_AUDIO_INTERVAL_MS = 2e3;
|
|
603326
604038
|
DEFAULT_CONFIG7 = {
|
|
603327
604039
|
videoEnabled: false,
|
|
603328
604040
|
audioEnabled: false,
|
|
@@ -603332,10 +604044,11 @@ var init_live_sensors = __esm({
|
|
|
603332
604044
|
asrEnabled: false,
|
|
603333
604045
|
audioAnalysisEnabled: false,
|
|
603334
604046
|
cameraOrientation: {},
|
|
603335
|
-
videoIntervalMs:
|
|
603336
|
-
audioIntervalMs:
|
|
604047
|
+
videoIntervalMs: 1e3,
|
|
604048
|
+
audioIntervalMs: 6e3
|
|
603337
604049
|
};
|
|
603338
604050
|
managers = /* @__PURE__ */ new Map();
|
|
604051
|
+
DASHBOARD_ANSI_STICKY_PATTERN = /\x1B\[[0-?]*[ -/]*[@-~]/y;
|
|
603339
604052
|
LiveSensorManager = class {
|
|
603340
604053
|
constructor(repoRoot) {
|
|
603341
604054
|
this.repoRoot = repoRoot;
|
|
@@ -603541,10 +604254,11 @@ var init_live_sensors = __esm({
|
|
|
603541
604254
|
this.config = {
|
|
603542
604255
|
...this.config,
|
|
603543
604256
|
...patch,
|
|
603544
|
-
videoIntervalMs: Math.max(
|
|
603545
|
-
audioIntervalMs: Math.max(
|
|
604257
|
+
videoIntervalMs: Math.max(MIN_VIDEO_INTERVAL_MS, Number(patch.videoIntervalMs ?? this.config.videoIntervalMs)),
|
|
604258
|
+
audioIntervalMs: Math.max(MIN_AUDIO_INTERVAL_MS, Number(patch.audioIntervalMs ?? this.config.audioIntervalMs))
|
|
603546
604259
|
};
|
|
603547
604260
|
this.persist();
|
|
604261
|
+
this.clearLoopTimers();
|
|
603548
604262
|
this.ensureLoops();
|
|
603549
604263
|
this.emitStatus();
|
|
603550
604264
|
return this.getConfig();
|
|
@@ -603570,8 +604284,8 @@ var init_live_sensors = __esm({
|
|
|
603570
604284
|
audioEnabled: true,
|
|
603571
604285
|
asrEnabled: true,
|
|
603572
604286
|
audioAnalysisEnabled: true,
|
|
603573
|
-
videoIntervalMs: options2.lowLatency === false ? this.config.videoIntervalMs : Math.min(this.config.videoIntervalMs,
|
|
603574
|
-
audioIntervalMs: options2.lowLatency === false ? this.config.audioIntervalMs : Math.min(this.config.audioIntervalMs,
|
|
604287
|
+
videoIntervalMs: options2.lowLatency === false ? this.config.videoIntervalMs : Math.min(this.config.videoIntervalMs, LOW_LATENCY_VIDEO_INTERVAL_MS),
|
|
604288
|
+
audioIntervalMs: options2.lowLatency === false ? this.config.audioIntervalMs : Math.min(this.config.audioIntervalMs, LOW_LATENCY_AUDIO_INTERVAL_MS)
|
|
603575
604289
|
});
|
|
603576
604290
|
for (const source of this.activeVideoSources()) {
|
|
603577
604291
|
const current = this.getCameraOrientation(source);
|
|
@@ -603684,6 +604398,10 @@ var init_live_sensors = __esm({
|
|
|
603684
604398
|
ascii: preview?.ascii,
|
|
603685
604399
|
plainAscii: preview?.plainAscii,
|
|
603686
604400
|
renderer: preview?.renderer,
|
|
604401
|
+
previewWidth: preview?.width,
|
|
604402
|
+
previewHeight: preview?.height,
|
|
604403
|
+
frameWidth: preview?.imageWidth,
|
|
604404
|
+
frameHeight: preview?.imageHeight,
|
|
603687
604405
|
asciiContext,
|
|
603688
604406
|
rotation,
|
|
603689
604407
|
orientation
|
|
@@ -603694,7 +604412,8 @@ var init_live_sensors = __esm({
|
|
|
603694
604412
|
[source]: {
|
|
603695
604413
|
...cameraView,
|
|
603696
604414
|
summary: this.snapshot.cameras?.[source]?.summary,
|
|
603697
|
-
inference: this.snapshot.cameras?.[source]?.inference
|
|
604415
|
+
inference: this.snapshot.cameras?.[source]?.inference,
|
|
604416
|
+
classifications: this.snapshot.cameras?.[source]?.classifications
|
|
603698
604417
|
}
|
|
603699
604418
|
};
|
|
603700
604419
|
this.snapshot.events = mergeRecentById(this.snapshot.events, [{
|
|
@@ -603727,7 +604446,11 @@ var init_live_sensors = __esm({
|
|
|
603727
604446
|
displayPath,
|
|
603728
604447
|
ascii: preview?.ascii,
|
|
603729
604448
|
plainAscii: preview?.plainAscii,
|
|
603730
|
-
renderer: preview?.renderer
|
|
604449
|
+
renderer: preview?.renderer,
|
|
604450
|
+
frameWidth: preview?.imageWidth,
|
|
604451
|
+
frameHeight: preview?.imageHeight,
|
|
604452
|
+
previewWidth: preview?.width,
|
|
604453
|
+
previewHeight: preview?.height
|
|
603731
604454
|
};
|
|
603732
604455
|
}
|
|
603733
604456
|
async recognizeFrameObjects(framePath, source, observedAt = Date.now(), options2 = {}) {
|
|
@@ -603784,20 +604507,27 @@ var init_live_sensors = __esm({
|
|
|
603784
604507
|
action: "watch",
|
|
603785
604508
|
source_kind: "camera",
|
|
603786
604509
|
camera: source,
|
|
603787
|
-
|
|
603788
|
-
|
|
603789
|
-
|
|
604510
|
+
yolo_family: "yoloe-26",
|
|
604511
|
+
yolo_scale: "n",
|
|
604512
|
+
yoloe_prompt_mode: "prompt_free",
|
|
604513
|
+
duration_sec: 0.75,
|
|
604514
|
+
sample_interval_sec: 0.25,
|
|
604515
|
+
max_frames: 1,
|
|
603790
604516
|
rotate_degrees: orientation?.rotation ?? 0,
|
|
603791
604517
|
auto_bootstrap: true,
|
|
603792
604518
|
audio_mode: "none",
|
|
603793
604519
|
detect_objects: true,
|
|
603794
604520
|
track_objects: true,
|
|
604521
|
+
segment_objects: true,
|
|
604522
|
+
extract_segments: true,
|
|
604523
|
+
max_segments_per_frame: 8,
|
|
603795
604524
|
crop_faces: true,
|
|
603796
604525
|
recognize_faces: true
|
|
603797
604526
|
});
|
|
603798
604527
|
inference = result.success ? String(result.output || result.llmContent || "") : `Video inference failed: ${result.error || result.output || "unknown error"}`;
|
|
603799
604528
|
const livePayload = result.success ? parseLiveMediaPayload(result.llmContent || result.output) : null;
|
|
603800
604529
|
const observations = observationsFromLiveMediaPayload(livePayload, sampledAt, source);
|
|
604530
|
+
const classifications = classificationsFromLiveMediaPayload(livePayload, sampledAt, source);
|
|
603801
604531
|
this.snapshot.events = mergeRecentById(this.snapshot.events, observations.events, 50);
|
|
603802
604532
|
this.snapshot.people = mergeRecentById(this.snapshot.people, observations.people, 24);
|
|
603803
604533
|
const existing = this.snapshot.cameras?.[source] ?? this.snapshot.video ?? { updatedAt: Date.now(), source };
|
|
@@ -603807,6 +604537,7 @@ var init_live_sensors = __esm({
|
|
|
603807
604537
|
source,
|
|
603808
604538
|
inference,
|
|
603809
604539
|
summary: summarizeInference(inference),
|
|
604540
|
+
classifications: mergeCameraClassifications(existing.classifications, classifications),
|
|
603810
604541
|
rotation: orientation?.rotation ?? 0,
|
|
603811
604542
|
orientation,
|
|
603812
604543
|
...preview.ok ? { error: void 0 } : { error: preview.message }
|
|
@@ -603845,6 +604576,10 @@ var init_live_sensors = __esm({
|
|
|
603845
604576
|
const current = this.snapshot.cameras?.[source];
|
|
603846
604577
|
if (current) {
|
|
603847
604578
|
current.summary = [current.summary, clipSummary ? `clip: ${trimOneLine(clipSummary, 160)}` : ""].filter(Boolean).join(" | ");
|
|
604579
|
+
current.classifications = mergeCameraClassifications(
|
|
604580
|
+
current.classifications,
|
|
604581
|
+
classificationsFromClipSummary(clipSummary, sampledAt, preview.framePath)
|
|
604582
|
+
);
|
|
603848
604583
|
this.snapshot.cameras = { ...this.snapshot.cameras ?? {}, [source]: current };
|
|
603849
604584
|
if (this.snapshot.video?.source === source) this.snapshot.video = current;
|
|
603850
604585
|
this.persist();
|
|
@@ -604002,30 +604737,63 @@ ${analysis}` : ""
|
|
|
604002
604737
|
this.audioInFlight = false;
|
|
604003
604738
|
}
|
|
604004
604739
|
}
|
|
604740
|
+
clearLoopTimers() {
|
|
604741
|
+
if (this.videoTimer) {
|
|
604742
|
+
clearTimeout(this.videoTimer);
|
|
604743
|
+
this.videoTimer = null;
|
|
604744
|
+
}
|
|
604745
|
+
if (this.audioTimer) {
|
|
604746
|
+
clearTimeout(this.audioTimer);
|
|
604747
|
+
this.audioTimer = null;
|
|
604748
|
+
}
|
|
604749
|
+
}
|
|
604750
|
+
scheduleVideoLoop(delayMs) {
|
|
604751
|
+
if (!this.config.videoEnabled || this.videoTimer) return;
|
|
604752
|
+
this.videoTimer = setTimeout(async () => {
|
|
604753
|
+
this.videoTimer = null;
|
|
604754
|
+
if (!this.config.videoEnabled) return;
|
|
604755
|
+
try {
|
|
604756
|
+
await this.sampleVideoNow(this.config.inferEnabled || this.config.clipEnabled);
|
|
604757
|
+
} catch {
|
|
604758
|
+
} finally {
|
|
604759
|
+
if (this.config.videoEnabled) {
|
|
604760
|
+
this.scheduleVideoLoop(this.config.videoIntervalMs);
|
|
604761
|
+
}
|
|
604762
|
+
}
|
|
604763
|
+
}, Math.max(0, delayMs));
|
|
604764
|
+
this.videoTimer.unref?.();
|
|
604765
|
+
}
|
|
604766
|
+
scheduleAudioLoop(delayMs) {
|
|
604767
|
+
const wantsAudio = this.config.audioEnabled && (this.config.asrEnabled || this.config.audioAnalysisEnabled);
|
|
604768
|
+
if (!wantsAudio || this.audioTimer) return;
|
|
604769
|
+
this.audioTimer = setTimeout(async () => {
|
|
604770
|
+
this.audioTimer = null;
|
|
604771
|
+
const stillWantsAudio = this.config.audioEnabled && (this.config.asrEnabled || this.config.audioAnalysisEnabled);
|
|
604772
|
+
if (!stillWantsAudio) return;
|
|
604773
|
+
try {
|
|
604774
|
+
await this.sampleAudioNow();
|
|
604775
|
+
} catch {
|
|
604776
|
+
} finally {
|
|
604777
|
+
const wantsNext = this.config.audioEnabled && (this.config.asrEnabled || this.config.audioAnalysisEnabled);
|
|
604778
|
+
if (wantsNext) {
|
|
604779
|
+
this.scheduleAudioLoop(this.config.audioIntervalMs);
|
|
604780
|
+
}
|
|
604781
|
+
}
|
|
604782
|
+
}, Math.max(0, delayMs));
|
|
604783
|
+
this.audioTimer.unref?.();
|
|
604784
|
+
}
|
|
604005
604785
|
ensureLoops() {
|
|
604006
604786
|
if (this.config.videoEnabled && !this.videoTimer) {
|
|
604007
|
-
|
|
604008
|
-
});
|
|
604009
|
-
this.videoTimer = setInterval(() => {
|
|
604010
|
-
void this.sampleVideoNow(this.config.inferEnabled || this.config.clipEnabled).catch(() => {
|
|
604011
|
-
});
|
|
604012
|
-
}, this.config.videoIntervalMs);
|
|
604013
|
-
this.videoTimer.unref?.();
|
|
604787
|
+
this.scheduleVideoLoop(0);
|
|
604014
604788
|
} else if (!this.config.videoEnabled && this.videoTimer) {
|
|
604015
|
-
|
|
604789
|
+
clearTimeout(this.videoTimer);
|
|
604016
604790
|
this.videoTimer = null;
|
|
604017
604791
|
}
|
|
604018
604792
|
const wantsAudio = this.config.audioEnabled && (this.config.asrEnabled || this.config.audioAnalysisEnabled);
|
|
604019
604793
|
if (wantsAudio && !this.audioTimer) {
|
|
604020
|
-
|
|
604021
|
-
});
|
|
604022
|
-
this.audioTimer = setInterval(() => {
|
|
604023
|
-
void this.sampleAudioNow().catch(() => {
|
|
604024
|
-
});
|
|
604025
|
-
}, this.config.audioIntervalMs);
|
|
604026
|
-
this.audioTimer.unref?.();
|
|
604794
|
+
this.scheduleAudioLoop(0);
|
|
604027
604795
|
} else if (!wantsAudio && this.audioTimer) {
|
|
604028
|
-
|
|
604796
|
+
clearTimeout(this.audioTimer);
|
|
604029
604797
|
this.audioTimer = null;
|
|
604030
604798
|
}
|
|
604031
604799
|
}
|