omnius 1.0.409 → 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 +1076 -109
- 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,
|
|
@@ -601886,7 +602339,8 @@ __export(image_ascii_preview_exports, {
|
|
|
601886
602339
|
buildImageAsciiPreview: () => buildImageAsciiPreview,
|
|
601887
602340
|
defaultAsciiPreviewSize: () => defaultAsciiPreviewSize,
|
|
601888
602341
|
extractSavedImagePath: () => extractSavedImagePath,
|
|
601889
|
-
formatImageAsciiContext: () => formatImageAsciiContext
|
|
602342
|
+
formatImageAsciiContext: () => formatImageAsciiContext,
|
|
602343
|
+
isLowInformationAsciiPreview: () => isLowInformationAsciiPreview
|
|
601890
602344
|
});
|
|
601891
602345
|
import { createRequire as createRequire5 } from "node:module";
|
|
601892
602346
|
import { existsSync as existsSync108, readFileSync as readFileSync87, statSync as statSync42 } from "node:fs";
|
|
@@ -602054,6 +602508,15 @@ function normalizeImageToAsciiResult(converted, width, height) {
|
|
|
602054
602508
|
if (!ascii2) return { ascii: null, error: "empty normalized renderer output" };
|
|
602055
602509
|
return { ascii: ascii2, plainAscii: plainAscii || void 0 };
|
|
602056
602510
|
}
|
|
602511
|
+
function isLowInformationAsciiPreview(ascii2) {
|
|
602512
|
+
const plain = stripAsciiAnsi(String(ascii2 ?? "")).replace(/\r/g, "").split("\n").map((line) => line.trimEnd()).filter((line) => line.trim().length > 0).join("\n");
|
|
602513
|
+
if (!plain) return true;
|
|
602514
|
+
const visible = plain.replace(/\s/g, "");
|
|
602515
|
+
if (visible.length < 12) return true;
|
|
602516
|
+
const unique2 = new Set(Array.from(visible));
|
|
602517
|
+
if (unique2.size <= 2 && /[█▓▒░#@]/u.test(visible)) return true;
|
|
602518
|
+
return false;
|
|
602519
|
+
}
|
|
602057
602520
|
function previewFailure(message2, width) {
|
|
602058
602521
|
const clean5 = message2.replace(/\s+/g, " ").trim();
|
|
602059
602522
|
const text2 = `[image-to-ascii preview failed: ${clean5 || "unknown error"}]`;
|
|
@@ -602172,13 +602635,19 @@ async function buildImageAsciiPreview(inputPath, options2 = {}) {
|
|
|
602172
602635
|
if (options2.preferPackage !== false) {
|
|
602173
602636
|
const result = await convertWithImageToAscii(imagePath, width, height, timeoutMs);
|
|
602174
602637
|
if (result.ascii) {
|
|
602638
|
+
let plainAscii = result.plainAscii;
|
|
602639
|
+
if (isLowInformationAsciiPreview(plainAscii)) {
|
|
602640
|
+
plainAscii = await convertWithFfmpeg(imagePath, width, height, timeoutMs) ?? plainAscii;
|
|
602641
|
+
}
|
|
602175
602642
|
return {
|
|
602176
602643
|
path: imagePath,
|
|
602177
602644
|
ascii: result.ascii,
|
|
602178
|
-
plainAscii
|
|
602645
|
+
plainAscii,
|
|
602179
602646
|
renderer: "image-to-ascii",
|
|
602180
602647
|
width,
|
|
602181
|
-
height
|
|
602648
|
+
height,
|
|
602649
|
+
imageWidth: dimensions?.width,
|
|
602650
|
+
imageHeight: dimensions?.height
|
|
602182
602651
|
};
|
|
602183
602652
|
}
|
|
602184
602653
|
return {
|
|
@@ -602186,11 +602655,24 @@ async function buildImageAsciiPreview(inputPath, options2 = {}) {
|
|
|
602186
602655
|
ascii: previewFailure(result.error || "renderer unavailable", width),
|
|
602187
602656
|
renderer: "image-to-ascii",
|
|
602188
602657
|
width,
|
|
602189
|
-
height
|
|
602658
|
+
height,
|
|
602659
|
+
imageWidth: dimensions?.width,
|
|
602660
|
+
imageHeight: dimensions?.height
|
|
602190
602661
|
};
|
|
602191
602662
|
}
|
|
602192
602663
|
const fallback = await convertWithFfmpeg(imagePath, width, height, timeoutMs);
|
|
602193
|
-
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
|
+
}
|
|
602194
602676
|
return null;
|
|
602195
602677
|
}
|
|
602196
602678
|
function formatImageAsciiContext(preview, label) {
|
|
@@ -602309,10 +602791,45 @@ function parseJsonObjectFromText(value2) {
|
|
|
602309
602791
|
const direct = parseJsonObject3(value2);
|
|
602310
602792
|
if (direct) return direct;
|
|
602311
602793
|
const text2 = String(value2 ?? "");
|
|
602312
|
-
const
|
|
602313
|
-
|
|
602314
|
-
|
|
602315
|
-
|
|
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;
|
|
602316
602833
|
}
|
|
602317
602834
|
function normalizeLiveCameraRotation(value2) {
|
|
602318
602835
|
if (value2 === void 0 || value2 === null || value2 === "") return 0;
|
|
@@ -602404,32 +602921,88 @@ function truncateCell(value2, width) {
|
|
|
602404
602921
|
function stripDashboardAnsi(value2) {
|
|
602405
602922
|
return value2.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, "");
|
|
602406
602923
|
}
|
|
602407
|
-
function
|
|
602408
|
-
|
|
602409
|
-
return raw.replace(/\r/g, "").split("\n").map((line) => stripDashboardAnsi(line).replace(/\s+$/g, "")).filter((line) => line.trim().length > 0).slice(0, 10);
|
|
602924
|
+
function dashboardVisibleWidth(value2) {
|
|
602925
|
+
return stripDashboardAnsi(value2).length;
|
|
602410
602926
|
}
|
|
602411
|
-
function
|
|
602412
|
-
|
|
602413
|
-
if (width <=
|
|
602414
|
-
|
|
602415
|
-
|
|
602416
|
-
|
|
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
|
+
}
|
|
602954
|
+
function dashboardPreviewLines(camera) {
|
|
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);
|
|
602417
602957
|
}
|
|
602418
602958
|
function dashboardPreviewAspect(lines) {
|
|
602419
602959
|
const widths = lines.map((line) => stripDashboardAnsi(line).length).filter((n2) => n2 > 0);
|
|
602420
602960
|
const maxWidth = Math.max(1, ...widths);
|
|
602421
602961
|
return Math.max(0.12, Math.min(1.6, lines.length / maxWidth));
|
|
602422
602962
|
}
|
|
602423
|
-
function
|
|
602424
|
-
const
|
|
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
|
+
}
|
|
602971
|
+
function fitDashboardPane(lines, paneWidth, paneHeight, fallback) {
|
|
602972
|
+
const source = lines.length > 0 ? lines : [fallback];
|
|
602425
602973
|
const normalized = [];
|
|
602426
602974
|
for (let i2 = 0; i2 < paneHeight; i2++) {
|
|
602427
602975
|
const sourceIndex = source.length <= paneHeight ? i2 : Math.min(source.length - 1, Math.floor(i2 * source.length / paneHeight));
|
|
602428
|
-
normalized.push(
|
|
602976
|
+
normalized.push(truncateAnsiCell(source[sourceIndex] ?? "", paneWidth));
|
|
602429
602977
|
}
|
|
602430
602978
|
while (normalized.length < paneHeight) normalized.push(" ".repeat(paneWidth));
|
|
602431
602979
|
return normalized;
|
|
602432
602980
|
}
|
|
602981
|
+
function liveDashboardPaneLayout(width, cameraCount) {
|
|
602982
|
+
const innerWidth = Math.max(10, width - 2);
|
|
602983
|
+
const paneCount = Math.max(1, Math.min(cameraCount || 1, width >= 92 ? 2 : 1));
|
|
602984
|
+
const gutter = paneCount - 1;
|
|
602985
|
+
const paneWidth = Math.max(28, Math.floor((innerWidth - gutter) / paneCount));
|
|
602986
|
+
return { innerWidth, paneCount, paneWidth, paneContentWidth: Math.max(10, paneWidth - 2) };
|
|
602987
|
+
}
|
|
602988
|
+
function liveDashboardPreviewWidthForSources(sourceCount, termWidth = process.stdout.columns || 100) {
|
|
602989
|
+
const { paneContentWidth } = liveDashboardPaneLayout(Math.max(60, termWidth), sourceCount);
|
|
602990
|
+
return Math.max(42, Math.min(120, paneContentWidth));
|
|
602991
|
+
}
|
|
602992
|
+
function formatCameraPane(camera, paneWidth, paneHeight, now2) {
|
|
602993
|
+
const contentWidth = Math.max(10, paneWidth - 2);
|
|
602994
|
+
const age = Math.max(0, Math.round((now2 - camera.updatedAt) / 1e3));
|
|
602995
|
+
const title = `${compactSourceId(camera.source)} ${age}s${cameraPaneBadge(camera)}`;
|
|
602996
|
+
const topTitle = ` ${title} `;
|
|
602997
|
+
const left = Math.max(0, Math.floor((contentWidth - topTitle.length) / 2));
|
|
602998
|
+
const right = Math.max(0, contentWidth - topTitle.length - left);
|
|
602999
|
+
const top = `╭${"─".repeat(left)}${topTitle.slice(0, contentWidth)}${"─".repeat(right)}╮`;
|
|
603000
|
+
const bottom = `╰${"─".repeat(contentWidth)}╯`;
|
|
603001
|
+
const previewLines = dashboardPreviewLines(camera);
|
|
603002
|
+
const fallback = camera.framePath ? "preview refreshing" : camera.error ? "capture failed" : "awaiting frame";
|
|
603003
|
+
const body = fitDashboardPane(previewLines, contentWidth, paneHeight, fallback);
|
|
603004
|
+
return [top, ...body.map((line) => `│${truncateAnsiCell(line, contentWidth)}│`), bottom];
|
|
603005
|
+
}
|
|
602433
603006
|
function pushDashboardWrapped(lines, value2, width) {
|
|
602434
603007
|
const contentWidth = Math.max(10, width - 2);
|
|
602435
603008
|
const words = stripDashboardAnsi(value2).replace(/\s+/g, " ").trim().split(" ").filter(Boolean);
|
|
@@ -602457,6 +603030,160 @@ function isAudioFailureText(value2) {
|
|
|
602457
603030
|
if (!text2) return false;
|
|
602458
603031
|
return /^(?:ASR|VAD|Transcription)\s+(?:failed|unavailable)|No module named ['"]?transcribe_cli|torchcodec|torchaudio version/i.test(text2);
|
|
602459
603032
|
}
|
|
603033
|
+
function readPcm16WavActivity(filePath, bins = 36) {
|
|
603034
|
+
try {
|
|
603035
|
+
const buf = readFileSync88(filePath);
|
|
603036
|
+
if (buf.length < 44 || buf.toString("ascii", 0, 4) !== "RIFF" || buf.toString("ascii", 8, 12) !== "WAVE") return void 0;
|
|
603037
|
+
let offset = 12;
|
|
603038
|
+
let channels = 1;
|
|
603039
|
+
let bitsPerSample = 16;
|
|
603040
|
+
let dataStart = -1;
|
|
603041
|
+
let dataSize = 0;
|
|
603042
|
+
while (offset + 8 <= buf.length) {
|
|
603043
|
+
const id2 = buf.toString("ascii", offset, offset + 4);
|
|
603044
|
+
const size = buf.readUInt32LE(offset + 4);
|
|
603045
|
+
const start2 = offset + 8;
|
|
603046
|
+
if (id2 === "fmt " && start2 + 16 <= buf.length) {
|
|
603047
|
+
channels = Math.max(1, buf.readUInt16LE(start2 + 2));
|
|
603048
|
+
bitsPerSample = buf.readUInt16LE(start2 + 14);
|
|
603049
|
+
} else if (id2 === "data") {
|
|
603050
|
+
dataStart = start2;
|
|
603051
|
+
dataSize = Math.min(size, buf.length - start2);
|
|
603052
|
+
break;
|
|
603053
|
+
}
|
|
603054
|
+
offset = start2 + size + size % 2;
|
|
603055
|
+
}
|
|
603056
|
+
if (dataStart < 0 || dataSize <= 0 || bitsPerSample !== 16) return void 0;
|
|
603057
|
+
const frameBytes = channels * 2;
|
|
603058
|
+
const frames = Math.floor(dataSize / frameBytes);
|
|
603059
|
+
if (frames <= 0) return void 0;
|
|
603060
|
+
let sumSquares = 0;
|
|
603061
|
+
let peak = 0;
|
|
603062
|
+
let active = 0;
|
|
603063
|
+
const bucketSquares = new Array(Math.max(1, bins)).fill(0);
|
|
603064
|
+
const bucketCounts = new Array(Math.max(1, bins)).fill(0);
|
|
603065
|
+
for (let i2 = 0; i2 < frames; i2++) {
|
|
603066
|
+
let sampleSum = 0;
|
|
603067
|
+
for (let ch = 0; ch < channels; ch++) {
|
|
603068
|
+
sampleSum += buf.readInt16LE(dataStart + i2 * frameBytes + ch * 2) / 32768;
|
|
603069
|
+
}
|
|
603070
|
+
const sample = sampleSum / channels;
|
|
603071
|
+
const abs = Math.abs(sample);
|
|
603072
|
+
peak = Math.max(peak, abs);
|
|
603073
|
+
sumSquares += sample * sample;
|
|
603074
|
+
if (abs > 0.018) active++;
|
|
603075
|
+
const b = Math.min(bucketSquares.length - 1, Math.floor(i2 / frames * bucketSquares.length));
|
|
603076
|
+
bucketSquares[b] += sample * sample;
|
|
603077
|
+
bucketCounts[b] += 1;
|
|
603078
|
+
}
|
|
603079
|
+
const rms = Math.sqrt(sumSquares / frames);
|
|
603080
|
+
const chars = "▁▂▃▄▅▆▇█";
|
|
603081
|
+
const waveform = bucketSquares.map((sum, idx) => {
|
|
603082
|
+
const count = Math.max(1, bucketCounts[idx] ?? 1);
|
|
603083
|
+
const level = Math.sqrt(sum / count);
|
|
603084
|
+
const normalized = Math.max(0, Math.min(1, level / Math.max(0.02, peak || 0.02)));
|
|
603085
|
+
return chars[Math.min(chars.length - 1, Math.round(normalized * (chars.length - 1)))] ?? "▁";
|
|
603086
|
+
}).join("");
|
|
603087
|
+
return {
|
|
603088
|
+
rms: Number(rms.toFixed(4)),
|
|
603089
|
+
peak: Number(peak.toFixed(4)),
|
|
603090
|
+
rmsDb: Number((20 * Math.log10(Math.max(1e-6, rms))).toFixed(1)),
|
|
603091
|
+
activeRatio: Number((active / frames).toFixed(3)),
|
|
603092
|
+
waveform
|
|
603093
|
+
};
|
|
603094
|
+
} catch {
|
|
603095
|
+
return void 0;
|
|
603096
|
+
}
|
|
603097
|
+
}
|
|
603098
|
+
function extractAsrBackend(output) {
|
|
603099
|
+
const match = String(output ?? "").match(/^Backend:\s*(.+)$/m);
|
|
603100
|
+
if (!match?.[1]) return void 0;
|
|
603101
|
+
const backend = match[1].trim();
|
|
603102
|
+
if (/managed openai-whisper|transcribe-cli|whisper/i.test(backend)) return backend;
|
|
603103
|
+
return backend.slice(0, 80);
|
|
603104
|
+
}
|
|
603105
|
+
function parseLiveAudioIntakeDecision(value2) {
|
|
603106
|
+
const parsed = parseJsonObjectFromText(value2);
|
|
603107
|
+
if (!parsed) return null;
|
|
603108
|
+
const intended = parsed["intended_for_agent"] ?? parsed["intendedForAgent"] ?? parsed["addressed_to_agent"];
|
|
603109
|
+
const confidence2 = Math.max(0, Math.min(1, Number(parsed["confidence"] ?? 0)));
|
|
603110
|
+
if (typeof intended !== "boolean" || !Number.isFinite(confidence2)) return null;
|
|
603111
|
+
const actionRaw = String(parsed["action"] ?? (intended ? "respond" : "ignore")).toLowerCase();
|
|
603112
|
+
const action = actionRaw === "respond" || actionRaw === "monitor" || actionRaw === "ignore" ? actionRaw : intended ? "respond" : "ignore";
|
|
603113
|
+
return {
|
|
603114
|
+
intendedForAgent: intended,
|
|
603115
|
+
confidence: confidence2,
|
|
603116
|
+
action,
|
|
603117
|
+
reason: trimOneLine(parsed["reason"] ?? "", 220) || "intake classifier decision"
|
|
603118
|
+
};
|
|
603119
|
+
}
|
|
603120
|
+
function fallbackLiveAudioIntakeDecision(transcript) {
|
|
603121
|
+
const text2 = transcript.toLowerCase();
|
|
603122
|
+
const addressed = /\b(omnius|hey\s+omnius|assistant|agent|computer)\b/.test(text2) || /\?$/.test(transcript.trim());
|
|
603123
|
+
return {
|
|
603124
|
+
intendedForAgent: addressed,
|
|
603125
|
+
confidence: addressed ? 0.58 : 0.35,
|
|
603126
|
+
action: addressed ? "respond" : "monitor",
|
|
603127
|
+
reason: addressed ? "fallback detected direct address or question shape" : "fallback could not establish that ambient speech was addressed to Omnius"
|
|
603128
|
+
};
|
|
603129
|
+
}
|
|
603130
|
+
async function classifyLiveAudioIntake(transcript, config) {
|
|
603131
|
+
const now2 = Date.now();
|
|
603132
|
+
const clean5 = transcript.trim();
|
|
603133
|
+
if (!clean5) {
|
|
603134
|
+
return {
|
|
603135
|
+
intendedForAgent: false,
|
|
603136
|
+
confidence: 1,
|
|
603137
|
+
action: "ignore",
|
|
603138
|
+
reason: "empty transcript",
|
|
603139
|
+
source: "fallback",
|
|
603140
|
+
updatedAt: now2
|
|
603141
|
+
};
|
|
603142
|
+
}
|
|
603143
|
+
if (config?.backendUrl && config.model) {
|
|
603144
|
+
const url = `${config.backendUrl.replace(/\/+$/, "")}/v1/chat/completions`;
|
|
603145
|
+
const body = {
|
|
603146
|
+
model: config.model,
|
|
603147
|
+
temperature: 0,
|
|
603148
|
+
max_tokens: 180,
|
|
603149
|
+
think: false,
|
|
603150
|
+
messages: [
|
|
603151
|
+
{
|
|
603152
|
+
role: "system",
|
|
603153
|
+
content: [
|
|
603154
|
+
"You are a live-audio intake classifier for Omnius.",
|
|
603155
|
+
"Decide whether a transcribed utterance is intended for the agent or is ambient/background speech.",
|
|
603156
|
+
'Return only JSON: {"intended_for_agent": boolean, "confidence": 0.0-1.0, "action": "respond"|"ignore"|"monitor", "reason": "brief evidence"}.',
|
|
603157
|
+
"Do not answer the utterance. Do not treat all room speech as addressed to the agent."
|
|
603158
|
+
].join("\n")
|
|
603159
|
+
},
|
|
603160
|
+
{ role: "user", content: clean5.slice(0, 1200) }
|
|
603161
|
+
]
|
|
603162
|
+
};
|
|
603163
|
+
try {
|
|
603164
|
+
const controller = new AbortController();
|
|
603165
|
+
const timer = setTimeout(() => controller.abort(), 6e3).unref();
|
|
603166
|
+
const resp = await fetch(url, {
|
|
603167
|
+
method: "POST",
|
|
603168
|
+
headers: {
|
|
603169
|
+
"Content-Type": "application/json",
|
|
603170
|
+
...config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {}
|
|
603171
|
+
},
|
|
603172
|
+
body: JSON.stringify(body),
|
|
603173
|
+
signal: controller.signal
|
|
603174
|
+
});
|
|
603175
|
+
clearTimeout(timer);
|
|
603176
|
+
if (resp.ok) {
|
|
603177
|
+
const json = await resp.json();
|
|
603178
|
+
const content = json.choices?.[0]?.message?.content ?? "";
|
|
603179
|
+
const parsed = parseLiveAudioIntakeDecision(content);
|
|
603180
|
+
if (parsed) return { ...parsed, source: "intake-agent", updatedAt: now2 };
|
|
603181
|
+
}
|
|
603182
|
+
} catch {
|
|
603183
|
+
}
|
|
603184
|
+
}
|
|
603185
|
+
return { ...fallbackLiveAudioIntakeDecision(clean5), source: "fallback", updatedAt: now2 };
|
|
603186
|
+
}
|
|
602460
603187
|
function summarizeInference(value2) {
|
|
602461
603188
|
if (!value2) return "objects: pending";
|
|
602462
603189
|
const lines = value2.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
@@ -602468,9 +603195,152 @@ function summarizeInference(value2) {
|
|
|
602468
603195
|
const sampled = lines.find((line) => /^objects:/i.test(line));
|
|
602469
603196
|
return sampled ? trimOneLine(sampled, 160) : trimOneLine(lines.slice(0, 4).join("; "), 160);
|
|
602470
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
|
+
}
|
|
602471
603341
|
function cameraSnapshotSummary(camera) {
|
|
602472
603342
|
if (camera.error) return `error: ${trimOneLine(camera.error, 160)}`;
|
|
602473
|
-
return camera.summary || summarizeInference(camera.inference);
|
|
603343
|
+
return camera.summary || (camera.classifications?.length ? `classifications: ${cameraClassificationsSummary(camera)}` : "") || summarizeInference(camera.inference);
|
|
602474
603344
|
}
|
|
602475
603345
|
function formatLiveDashboardFromSnapshot(snapshot, opts = {}) {
|
|
602476
603346
|
const width = Math.max(60, opts.width ?? 100);
|
|
@@ -602491,24 +603361,19 @@ function formatLiveDashboardFromSnapshot(snapshot, opts = {}) {
|
|
|
602491
603361
|
}
|
|
602492
603362
|
if (cameras.length > 0) {
|
|
602493
603363
|
lines.push(`├${horizontal}┤`);
|
|
602494
|
-
const
|
|
602495
|
-
const innerWidth = width - 2;
|
|
602496
|
-
const gutter = paneCount - 1;
|
|
602497
|
-
const paneWidth = Math.max(24, Math.floor((innerWidth - gutter) / paneCount));
|
|
603364
|
+
const { innerWidth, paneCount, paneWidth, paneContentWidth } = liveDashboardPaneLayout(width, cameras.length);
|
|
602498
603365
|
for (let start2 = 0; start2 < cameras.length; start2 += paneCount) {
|
|
602499
603366
|
const group = cameras.slice(start2, start2 + paneCount);
|
|
602500
603367
|
const sourceLines = group.map((camera) => dashboardPreviewLines(camera));
|
|
602501
|
-
const
|
|
602502
|
-
const
|
|
602503
|
-
|
|
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)));
|
|
603370
|
+
const paneHeight = Math.max(10, Math.min(32, Math.round(paneContentWidth * aspect)));
|
|
603371
|
+
const panes = group.map((camera) => formatCameraPane(camera, paneWidth, paneHeight, now2));
|
|
603372
|
+
const rowCount = Math.max(...panes.map((pane) => pane.length));
|
|
603373
|
+
for (let row = 0; row < rowCount; row++) {
|
|
602504
603374
|
const body = panes.map((pane) => pane[row] ?? " ".repeat(paneWidth)).join(" ");
|
|
602505
|
-
lines.push(`│${
|
|
603375
|
+
lines.push(`│${truncateAnsiCell(body, innerWidth)}│`);
|
|
602506
603376
|
}
|
|
602507
|
-
const labels = group.map((camera) => {
|
|
602508
|
-
const age = Math.max(0, Math.round((now2 - camera.updatedAt) / 1e3));
|
|
602509
|
-
return centerDashboardCell(`${compactSourceId(camera.source)} age=${age}s`, paneWidth);
|
|
602510
|
-
}).join(" ");
|
|
602511
|
-
lines.push(`│${truncateCell(labels, innerWidth)}│`);
|
|
602512
603377
|
}
|
|
602513
603378
|
}
|
|
602514
603379
|
if (cameras.length > 0) {
|
|
@@ -602518,8 +603383,10 @@ function formatLiveDashboardFromSnapshot(snapshot, opts = {}) {
|
|
|
602518
603383
|
for (const camera of cameras) {
|
|
602519
603384
|
const age = Math.max(0, Math.round((now2 - camera.updatedAt) / 1e3));
|
|
602520
603385
|
const rotation = camera.orientation ? formatCameraRotation(camera.orientation.rotation) : "upright/no correction";
|
|
603386
|
+
const classifications = cameraClassificationsSummary(camera);
|
|
602521
603387
|
const detailLines = [
|
|
602522
603388
|
`${compactSourceId(camera.source)} age=${age}s rotation=${rotation}`,
|
|
603389
|
+
classifications ? `classifications: ${classifications}` : "",
|
|
602523
603390
|
cameraSnapshotSummary(camera),
|
|
602524
603391
|
camera.framePath ? `frame: ${camera.displayPath || camera.framePath}` : "",
|
|
602525
603392
|
camera.orientation?.evidence ? `orientation: ${trimOneLine(camera.orientation.evidence, 120)}` : ""
|
|
@@ -602535,11 +603402,13 @@ function formatLiveDashboardFromSnapshot(snapshot, opts = {}) {
|
|
|
602535
603402
|
const audioError = [snapshot.audio.error, transcriptFailure].filter(Boolean).join(" | ");
|
|
602536
603403
|
const audioBits = [
|
|
602537
603404
|
`audio input=${snapshot.audio.input || snapshot.config.selectedAudioInput || "none"}`,
|
|
603405
|
+
snapshot.audio.activity ? `activity ${snapshot.audio.activity.waveform} rms=${snapshot.audio.activity.rmsDb.toFixed(1)}dB active=${Math.round(snapshot.audio.activity.activeRatio * 100)}%` : "",
|
|
602538
603406
|
audioError ? `error=${trimOneLine(audioError, 120)}` : "",
|
|
602539
|
-
transcript ? `asr=${trimOneLine(transcript, 120)}` : "",
|
|
603407
|
+
transcript ? `asr${snapshot.audio.asrBackend ? `(${snapshot.audio.asrBackend})` : ""}=${trimOneLine(transcript, 120)}` : "",
|
|
603408
|
+
snapshot.audio.intake ? `intake=${snapshot.audio.intake.intendedForAgent ? "for-agent" : "ambient"} conf=${snapshot.audio.intake.confidence.toFixed(2)} ${snapshot.audio.intake.action}` : "",
|
|
602540
603409
|
snapshot.audio.analysis ? `sound=${trimOneLine(snapshot.audio.analysis, 120)}` : ""
|
|
602541
603410
|
].filter(Boolean);
|
|
602542
|
-
for (const item of audioBits.slice(0,
|
|
603411
|
+
for (const item of audioBits.slice(0, 6)) lines.push(`│${truncateCell(` ${item}`, width - 2)}│`);
|
|
602543
603412
|
}
|
|
602544
603413
|
lines.push(`╰${horizontal}╯`);
|
|
602545
603414
|
return lines;
|
|
@@ -602547,7 +603416,7 @@ function formatLiveDashboardFromSnapshot(snapshot, opts = {}) {
|
|
|
602547
603416
|
function parseLiveMediaPayload(value2) {
|
|
602548
603417
|
const parsed = parseJsonObject3(value2);
|
|
602549
603418
|
if (!parsed) return null;
|
|
602550
|
-
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;
|
|
602551
603420
|
return parsed;
|
|
602552
603421
|
}
|
|
602553
603422
|
function observedAtFromOffset(sampledAt, payload, offsetSec) {
|
|
@@ -602644,6 +603513,21 @@ function observationsFromLiveMediaPayload(payload, sampledAt, fallbackSource) {
|
|
|
602644
603513
|
evidence: identity3.evidence
|
|
602645
603514
|
});
|
|
602646
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
|
+
}
|
|
602647
603531
|
const faceCount = people.length;
|
|
602648
603532
|
if (faceCount === 0) {
|
|
602649
603533
|
const personTracks = (payload.tracks ?? []).filter((track) => String(track.label || "").toLowerCase() === "person").slice(0, 8);
|
|
@@ -602769,8 +603653,8 @@ function loadLiveSensorConfig(repoRoot) {
|
|
|
602769
603653
|
...DEFAULT_CONFIG7,
|
|
602770
603654
|
...parsed,
|
|
602771
603655
|
cameraOrientation: sanitizeCameraOrientation(parsed.cameraOrientation),
|
|
602772
|
-
videoIntervalMs: Math.max(
|
|
602773
|
-
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))
|
|
602774
603658
|
};
|
|
602775
603659
|
} catch {
|
|
602776
603660
|
return { ...DEFAULT_CONFIG7 };
|
|
@@ -602814,6 +603698,8 @@ function formatLiveSensorContextFromSnapshot(snapshot, opts = {}) {
|
|
|
602814
603698
|
const age = Math.max(0, Math.round((now2 - camera.updatedAt) / 1e3));
|
|
602815
603699
|
const rotation = camera.orientation ? formatCameraRotation(camera.orientation.rotation) : "upright/no correction";
|
|
602816
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}`);
|
|
602817
603703
|
const summary = cameraSnapshotSummary(camera);
|
|
602818
603704
|
if (summary) lines.push(` summary: ${summary}`);
|
|
602819
603705
|
}
|
|
@@ -602868,6 +603754,8 @@ function formatLiveSensorContextFromSnapshot(snapshot, opts = {}) {
|
|
|
602868
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" : ""}`);
|
|
602869
603755
|
if (snapshot.video.error) lines.push(`error: ${snapshot.video.error}`);
|
|
602870
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}`);
|
|
602871
603759
|
if (snapshot.video.asciiContext) lines.push(snapshot.video.asciiContext);
|
|
602872
603760
|
if (snapshot.video.inference) {
|
|
602873
603761
|
lines.push("Video inference:");
|
|
@@ -602884,6 +603772,8 @@ function formatLiveSensorContextFromSnapshot(snapshot, opts = {}) {
|
|
|
602884
603772
|
lines.push(`timestamp: ${nowIso3(camera.updatedAt)} age=${Math.max(0, Math.round(age / 1e3))}s rotation=${rotation}${age > maxAgeMs ? " STALE" : ""}`);
|
|
602885
603773
|
if (camera.error) lines.push(`error: ${camera.error}`);
|
|
602886
603774
|
if (camera.framePath) lines.push(`frame: ${camera.framePath}`);
|
|
603775
|
+
const classifications = cameraClassificationsSummary(camera);
|
|
603776
|
+
if (classifications) lines.push(`classifications: ${classifications}`);
|
|
602887
603777
|
lines.push(`summary: ${cameraSnapshotSummary(camera)}`);
|
|
602888
603778
|
}
|
|
602889
603779
|
}
|
|
@@ -602909,10 +603799,17 @@ function formatLiveSensorContextFromSnapshot(snapshot, opts = {}) {
|
|
|
602909
603799
|
lines.push(`timestamp: ${nowIso3(snapshot.audio.updatedAt)} age=${Math.max(0, Math.round(age / 1e3))}s${age > maxAgeMs ? " STALE" : ""}`);
|
|
602910
603800
|
if (audioError) lines.push(`error: ${audioError}`);
|
|
602911
603801
|
if (snapshot.audio.recordingPath) lines.push(`recording: ${snapshot.audio.recordingPath}`);
|
|
603802
|
+
if (snapshot.audio.activity) {
|
|
603803
|
+
lines.push(`activity: ${snapshot.audio.activity.waveform} rms_db=${snapshot.audio.activity.rmsDb.toFixed(1)} peak=${snapshot.audio.activity.peak.toFixed(3)} active=${Math.round(snapshot.audio.activity.activeRatio * 100)}%`);
|
|
603804
|
+
}
|
|
602912
603805
|
if (transcript) {
|
|
602913
|
-
lines.push(
|
|
603806
|
+
lines.push(`Live ASR${snapshot.audio.asrBackend ? ` (${snapshot.audio.asrBackend})` : ""}:`);
|
|
602914
603807
|
lines.push(cleanPreview(transcript, 1200));
|
|
602915
603808
|
}
|
|
603809
|
+
if (snapshot.audio.intake) {
|
|
603810
|
+
lines.push(`Live audio intake: intended_for_agent=${snapshot.audio.intake.intendedForAgent ? "true" : "false"} confidence=${snapshot.audio.intake.confidence.toFixed(2)} action=${snapshot.audio.intake.action} source=${snapshot.audio.intake.source}`);
|
|
603811
|
+
lines.push(`intake_reason: ${snapshot.audio.intake.reason}`);
|
|
603812
|
+
}
|
|
602916
603813
|
if (snapshot.audio.analysis) {
|
|
602917
603814
|
lines.push("Live sound analysis:");
|
|
602918
603815
|
lines.push(cleanPreview(snapshot.audio.analysis, 1600));
|
|
@@ -603127,13 +604024,17 @@ function formatLiveStatus(snapshot) {
|
|
|
603127
604024
|
}
|
|
603128
604025
|
return lines.join("\n");
|
|
603129
604026
|
}
|
|
603130
|
-
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;
|
|
603131
604028
|
var init_live_sensors = __esm({
|
|
603132
604029
|
"packages/cli/src/tui/live-sensors.ts"() {
|
|
603133
604030
|
"use strict";
|
|
603134
604031
|
init_dist5();
|
|
603135
604032
|
init_async_process();
|
|
603136
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;
|
|
603137
604038
|
DEFAULT_CONFIG7 = {
|
|
603138
604039
|
videoEnabled: false,
|
|
603139
604040
|
audioEnabled: false,
|
|
@@ -603143,10 +604044,11 @@ var init_live_sensors = __esm({
|
|
|
603143
604044
|
asrEnabled: false,
|
|
603144
604045
|
audioAnalysisEnabled: false,
|
|
603145
604046
|
cameraOrientation: {},
|
|
603146
|
-
videoIntervalMs:
|
|
603147
|
-
audioIntervalMs:
|
|
604047
|
+
videoIntervalMs: 1e3,
|
|
604048
|
+
audioIntervalMs: 6e3
|
|
603148
604049
|
};
|
|
603149
604050
|
managers = /* @__PURE__ */ new Map();
|
|
604051
|
+
DASHBOARD_ANSI_STICKY_PATTERN = /\x1B\[[0-?]*[ -/]*[@-~]/y;
|
|
603150
604052
|
LiveSensorManager = class {
|
|
603151
604053
|
constructor(repoRoot) {
|
|
603152
604054
|
this.repoRoot = repoRoot;
|
|
@@ -603174,6 +604076,7 @@ var init_live_sensors = __esm({
|
|
|
603174
604076
|
lastAgentReviewAt = 0;
|
|
603175
604077
|
autoOrientationInFlight = /* @__PURE__ */ new Set();
|
|
603176
604078
|
lastFeedbackAt = /* @__PURE__ */ new Map();
|
|
604079
|
+
intakeAgentConfig;
|
|
603177
604080
|
setStatusSink(sink) {
|
|
603178
604081
|
this.statusSink = sink ?? null;
|
|
603179
604082
|
this.emitStatus();
|
|
@@ -603184,6 +604087,9 @@ var init_live_sensors = __esm({
|
|
|
603184
604087
|
setAgentActionSink(sink) {
|
|
603185
604088
|
this.agentActionSink = sink ?? null;
|
|
603186
604089
|
}
|
|
604090
|
+
setAudioIntakeAgentConfig(config) {
|
|
604091
|
+
this.intakeAgentConfig = config && config.backendUrl && config.model ? { backendUrl: config.backendUrl, model: config.model, apiKey: config.apiKey } : void 0;
|
|
604092
|
+
}
|
|
603187
604093
|
getConfig() {
|
|
603188
604094
|
return { ...this.config };
|
|
603189
604095
|
}
|
|
@@ -603348,10 +604254,11 @@ var init_live_sensors = __esm({
|
|
|
603348
604254
|
this.config = {
|
|
603349
604255
|
...this.config,
|
|
603350
604256
|
...patch,
|
|
603351
|
-
videoIntervalMs: Math.max(
|
|
603352
|
-
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))
|
|
603353
604259
|
};
|
|
603354
604260
|
this.persist();
|
|
604261
|
+
this.clearLoopTimers();
|
|
603355
604262
|
this.ensureLoops();
|
|
603356
604263
|
this.emitStatus();
|
|
603357
604264
|
return this.getConfig();
|
|
@@ -603377,8 +604284,8 @@ var init_live_sensors = __esm({
|
|
|
603377
604284
|
audioEnabled: true,
|
|
603378
604285
|
asrEnabled: true,
|
|
603379
604286
|
audioAnalysisEnabled: true,
|
|
603380
|
-
videoIntervalMs: options2.lowLatency === false ? this.config.videoIntervalMs : Math.min(this.config.videoIntervalMs,
|
|
603381
|
-
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)
|
|
603382
604289
|
});
|
|
603383
604290
|
for (const source of this.activeVideoSources()) {
|
|
603384
604291
|
const current = this.getCameraOrientation(source);
|
|
@@ -603479,7 +604386,7 @@ var init_live_sensors = __esm({
|
|
|
603479
604386
|
const framePath = extractSavedImagePath(result.output, this.repoRoot);
|
|
603480
604387
|
if (!framePath) return { ok: false, message: "Camera capture succeeded but no saved image path was returned." };
|
|
603481
604388
|
const displayPath = relative11(this.repoRoot, framePath).startsWith("..") ? framePath : relative11(this.repoRoot, framePath);
|
|
603482
|
-
const previewWidth = Math.max(
|
|
604389
|
+
const previewWidth = Math.max(42, Math.min(120, Math.floor(Number(options2.previewWidth ?? (process.stdout.columns || 100) - 14))));
|
|
603483
604390
|
const preview = await buildImageAsciiPreview(framePath, { width: previewWidth });
|
|
603484
604391
|
const asciiContext = preview ? formatImageAsciiContext(preview, displayPath) : void 0;
|
|
603485
604392
|
const observedAt = Date.now();
|
|
@@ -603491,6 +604398,10 @@ var init_live_sensors = __esm({
|
|
|
603491
604398
|
ascii: preview?.ascii,
|
|
603492
604399
|
plainAscii: preview?.plainAscii,
|
|
603493
604400
|
renderer: preview?.renderer,
|
|
604401
|
+
previewWidth: preview?.width,
|
|
604402
|
+
previewHeight: preview?.height,
|
|
604403
|
+
frameWidth: preview?.imageWidth,
|
|
604404
|
+
frameHeight: preview?.imageHeight,
|
|
603494
604405
|
asciiContext,
|
|
603495
604406
|
rotation,
|
|
603496
604407
|
orientation
|
|
@@ -603501,7 +604412,8 @@ var init_live_sensors = __esm({
|
|
|
603501
604412
|
[source]: {
|
|
603502
604413
|
...cameraView,
|
|
603503
604414
|
summary: this.snapshot.cameras?.[source]?.summary,
|
|
603504
|
-
inference: this.snapshot.cameras?.[source]?.inference
|
|
604415
|
+
inference: this.snapshot.cameras?.[source]?.inference,
|
|
604416
|
+
classifications: this.snapshot.cameras?.[source]?.classifications
|
|
603505
604417
|
}
|
|
603506
604418
|
};
|
|
603507
604419
|
this.snapshot.events = mergeRecentById(this.snapshot.events, [{
|
|
@@ -603523,6 +604435,7 @@ var init_live_sensors = __esm({
|
|
|
603523
604435
|
imagePath: framePath,
|
|
603524
604436
|
displayPath,
|
|
603525
604437
|
ascii: preview?.ascii,
|
|
604438
|
+
plainAscii: preview?.plainAscii,
|
|
603526
604439
|
renderer: preview?.renderer
|
|
603527
604440
|
});
|
|
603528
604441
|
}
|
|
@@ -603532,7 +604445,12 @@ var init_live_sensors = __esm({
|
|
|
603532
604445
|
framePath,
|
|
603533
604446
|
displayPath,
|
|
603534
604447
|
ascii: preview?.ascii,
|
|
603535
|
-
|
|
604448
|
+
plainAscii: preview?.plainAscii,
|
|
604449
|
+
renderer: preview?.renderer,
|
|
604450
|
+
frameWidth: preview?.imageWidth,
|
|
604451
|
+
frameHeight: preview?.imageHeight,
|
|
604452
|
+
previewWidth: preview?.width,
|
|
604453
|
+
previewHeight: preview?.height
|
|
603536
604454
|
};
|
|
603537
604455
|
}
|
|
603538
604456
|
async recognizeFrameObjects(framePath, source, observedAt = Date.now(), options2 = {}) {
|
|
@@ -603577,8 +604495,8 @@ var init_live_sensors = __esm({
|
|
|
603577
604495
|
return message2;
|
|
603578
604496
|
}
|
|
603579
604497
|
}
|
|
603580
|
-
async sampleSingleVideoNow(source, forceInfer) {
|
|
603581
|
-
const preview = await this.previewCamera(source, { emit: false });
|
|
604498
|
+
async sampleSingleVideoNow(source, forceInfer, previewWidth) {
|
|
604499
|
+
const preview = await this.previewCamera(source, { emit: false, previewWidth });
|
|
603582
604500
|
let inference = "";
|
|
603583
604501
|
let clipSummary = "";
|
|
603584
604502
|
const orientation = this.getCameraOrientation(source);
|
|
@@ -603589,20 +604507,27 @@ var init_live_sensors = __esm({
|
|
|
603589
604507
|
action: "watch",
|
|
603590
604508
|
source_kind: "camera",
|
|
603591
604509
|
camera: source,
|
|
603592
|
-
|
|
603593
|
-
|
|
603594
|
-
|
|
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,
|
|
603595
604516
|
rotate_degrees: orientation?.rotation ?? 0,
|
|
603596
604517
|
auto_bootstrap: true,
|
|
603597
604518
|
audio_mode: "none",
|
|
603598
604519
|
detect_objects: true,
|
|
603599
604520
|
track_objects: true,
|
|
604521
|
+
segment_objects: true,
|
|
604522
|
+
extract_segments: true,
|
|
604523
|
+
max_segments_per_frame: 8,
|
|
603600
604524
|
crop_faces: true,
|
|
603601
604525
|
recognize_faces: true
|
|
603602
604526
|
});
|
|
603603
604527
|
inference = result.success ? String(result.output || result.llmContent || "") : `Video inference failed: ${result.error || result.output || "unknown error"}`;
|
|
603604
604528
|
const livePayload = result.success ? parseLiveMediaPayload(result.llmContent || result.output) : null;
|
|
603605
604529
|
const observations = observationsFromLiveMediaPayload(livePayload, sampledAt, source);
|
|
604530
|
+
const classifications = classificationsFromLiveMediaPayload(livePayload, sampledAt, source);
|
|
603606
604531
|
this.snapshot.events = mergeRecentById(this.snapshot.events, observations.events, 50);
|
|
603607
604532
|
this.snapshot.people = mergeRecentById(this.snapshot.people, observations.people, 24);
|
|
603608
604533
|
const existing = this.snapshot.cameras?.[source] ?? this.snapshot.video ?? { updatedAt: Date.now(), source };
|
|
@@ -603612,6 +604537,7 @@ var init_live_sensors = __esm({
|
|
|
603612
604537
|
source,
|
|
603613
604538
|
inference,
|
|
603614
604539
|
summary: summarizeInference(inference),
|
|
604540
|
+
classifications: mergeCameraClassifications(existing.classifications, classifications),
|
|
603615
604541
|
rotation: orientation?.rotation ?? 0,
|
|
603616
604542
|
orientation,
|
|
603617
604543
|
...preview.ok ? { error: void 0 } : { error: preview.message }
|
|
@@ -603650,6 +604576,10 @@ var init_live_sensors = __esm({
|
|
|
603650
604576
|
const current = this.snapshot.cameras?.[source];
|
|
603651
604577
|
if (current) {
|
|
603652
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
|
+
);
|
|
603653
604583
|
this.snapshot.cameras = { ...this.snapshot.cameras ?? {}, [source]: current };
|
|
603654
604584
|
if (this.snapshot.video?.source === source) this.snapshot.video = current;
|
|
603655
604585
|
this.persist();
|
|
@@ -603667,8 +604597,9 @@ ${clipSummary}` : ""].filter(Boolean).join("\n");
|
|
|
603667
604597
|
const sources = this.activeVideoSources();
|
|
603668
604598
|
if (sources.length === 0) return "No camera selected. Use /live refresh after connecting hardware.";
|
|
603669
604599
|
const outputs = [];
|
|
604600
|
+
const previewWidth = liveDashboardPreviewWidthForSources(sources.length);
|
|
603670
604601
|
for (const source of sources) {
|
|
603671
|
-
outputs.push(await this.sampleSingleVideoNow(source, forceInfer));
|
|
604602
|
+
outputs.push(await this.sampleSingleVideoNow(source, forceInfer, previewWidth));
|
|
603672
604603
|
}
|
|
603673
604604
|
this.emitDashboard();
|
|
603674
604605
|
return outputs.map((output, index) => `## Camera ${sources[index]}
|
|
@@ -603686,7 +604617,9 @@ ${output}`).join("\n\n");
|
|
|
603686
604617
|
ensureLiveDir(this.repoRoot);
|
|
603687
604618
|
const recordingPath = join124(outputDir2, `audio-${Date.now()}.wav`);
|
|
603688
604619
|
let transcript = "";
|
|
604620
|
+
let asrBackend = "";
|
|
603689
604621
|
let analysis = "";
|
|
604622
|
+
let intake;
|
|
603690
604623
|
const audioErrors = [];
|
|
603691
604624
|
const capture = await recordAudioSampleWithRecovery(input, this.devices.audioInputs, 3, recordingPath);
|
|
603692
604625
|
if (!capture.ok) {
|
|
@@ -603710,11 +604643,13 @@ ${output}`).join("\n\n");
|
|
|
603710
604643
|
if (capture.device !== this.config.selectedAudioInput) {
|
|
603711
604644
|
this.config.selectedAudioInput = capture.device;
|
|
603712
604645
|
}
|
|
604646
|
+
const activity = readPcm16WavActivity(recordingPath);
|
|
603713
604647
|
if (this.config.asrEnabled) {
|
|
603714
604648
|
try {
|
|
603715
604649
|
const asr = await new TranscribeFileTool(this.repoRoot).execute({ path: recordingPath, model: "tiny" });
|
|
603716
604650
|
if (asr.success) {
|
|
603717
604651
|
transcript = cleanPreview(typeof asr.llmContent === "string" ? asr.llmContent : "", 1600);
|
|
604652
|
+
asrBackend = extractAsrBackend(asr.output) ?? "whisper";
|
|
603718
604653
|
} else {
|
|
603719
604654
|
audioErrors.push(`ASR unavailable: ${asr.error || asr.output || "unknown error"}`);
|
|
603720
604655
|
}
|
|
@@ -603740,13 +604675,19 @@ ${output}`).join("\n\n");
|
|
|
603740
604675
|
}
|
|
603741
604676
|
analysis = parts.filter(Boolean).join("\n");
|
|
603742
604677
|
}
|
|
604678
|
+
if (transcript) {
|
|
604679
|
+
intake = await classifyLiveAudioIntake(transcript, this.intakeAgentConfig);
|
|
604680
|
+
}
|
|
603743
604681
|
this.snapshot.audio = {
|
|
603744
604682
|
updatedAt: Date.now(),
|
|
603745
604683
|
input: capture.device,
|
|
603746
604684
|
output: this.config.selectedAudioOutput,
|
|
603747
604685
|
recordingPath,
|
|
603748
604686
|
transcript,
|
|
604687
|
+
asrBackend: asrBackend || void 0,
|
|
603749
604688
|
analysis,
|
|
604689
|
+
activity,
|
|
604690
|
+
intake,
|
|
603750
604691
|
error: audioErrors.length > 0 ? audioErrors.join("\n") : void 0
|
|
603751
604692
|
};
|
|
603752
604693
|
const audioEvents = [];
|
|
@@ -603774,12 +604715,20 @@ ${output}`).join("\n\n");
|
|
|
603774
604715
|
}
|
|
603775
604716
|
this.snapshot.events = mergeRecentById(this.snapshot.events, audioEvents, 50);
|
|
603776
604717
|
this.persist();
|
|
604718
|
+
if (transcript && intake?.intendedForAgent) {
|
|
604719
|
+
this.maybeRequestAgentReview(
|
|
604720
|
+
"Live audio intake classified speech as intended for Omnius",
|
|
604721
|
+
`transcript=${transcript}; confidence=${intake.confidence.toFixed(2)}; reason=${intake.reason}`
|
|
604722
|
+
);
|
|
604723
|
+
}
|
|
603777
604724
|
this.emitDashboard();
|
|
603778
604725
|
return [
|
|
603779
604726
|
`Audio sample captured from ${capture.device}${capture.device !== input ? ` (recovered from ${input})` : ""}: ${recordingPath}`,
|
|
603780
604727
|
transcript ? `
|
|
603781
|
-
ASR:
|
|
604728
|
+
ASR${asrBackend ? ` (${asrBackend})` : ""}:
|
|
603782
604729
|
${transcript}` : "",
|
|
604730
|
+
intake ? `
|
|
604731
|
+
Intake: ${intake.intendedForAgent ? "intended for agent" : "ambient/monitor"} confidence=${intake.confidence.toFixed(2)} (${intake.source})` : "",
|
|
603783
604732
|
analysis ? `
|
|
603784
604733
|
Sound analysis:
|
|
603785
604734
|
${analysis}` : ""
|
|
@@ -603788,30 +604737,63 @@ ${analysis}` : ""
|
|
|
603788
604737
|
this.audioInFlight = false;
|
|
603789
604738
|
}
|
|
603790
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
|
+
}
|
|
603791
604785
|
ensureLoops() {
|
|
603792
604786
|
if (this.config.videoEnabled && !this.videoTimer) {
|
|
603793
|
-
|
|
603794
|
-
});
|
|
603795
|
-
this.videoTimer = setInterval(() => {
|
|
603796
|
-
void this.sampleVideoNow(this.config.inferEnabled || this.config.clipEnabled).catch(() => {
|
|
603797
|
-
});
|
|
603798
|
-
}, this.config.videoIntervalMs);
|
|
603799
|
-
this.videoTimer.unref?.();
|
|
604787
|
+
this.scheduleVideoLoop(0);
|
|
603800
604788
|
} else if (!this.config.videoEnabled && this.videoTimer) {
|
|
603801
|
-
|
|
604789
|
+
clearTimeout(this.videoTimer);
|
|
603802
604790
|
this.videoTimer = null;
|
|
603803
604791
|
}
|
|
603804
604792
|
const wantsAudio = this.config.audioEnabled && (this.config.asrEnabled || this.config.audioAnalysisEnabled);
|
|
603805
604793
|
if (wantsAudio && !this.audioTimer) {
|
|
603806
|
-
|
|
603807
|
-
});
|
|
603808
|
-
this.audioTimer = setInterval(() => {
|
|
603809
|
-
void this.sampleAudioNow().catch(() => {
|
|
603810
|
-
});
|
|
603811
|
-
}, this.config.audioIntervalMs);
|
|
603812
|
-
this.audioTimer.unref?.();
|
|
604794
|
+
this.scheduleAudioLoop(0);
|
|
603813
604795
|
} else if (!wantsAudio && this.audioTimer) {
|
|
603814
|
-
|
|
604796
|
+
clearTimeout(this.audioTimer);
|
|
603815
604797
|
this.audioTimer = null;
|
|
603816
604798
|
}
|
|
603817
604799
|
}
|
|
@@ -624395,15 +625377,6 @@ var init_status_bar = __esm({
|
|
|
624395
625377
|
const cmdPrefix = cmd.startsWith("view:") ? "omnius-view:" + cmd.slice(5) : "omnius-cmd:" + cmd;
|
|
624396
625378
|
return `\x1B]8;;${cmdPrefix}\x07${label}\x1B]8;;\x07`;
|
|
624397
625379
|
};
|
|
624398
|
-
const buttonFg = (cmd) => {
|
|
624399
|
-
let fg2 = TEXT_DIM;
|
|
624400
|
-
if (cmd === "voice" && this._voiceActive) fg2 = 82;
|
|
624401
|
-
if (cmd.startsWith("live") && (this._liveMediaStatus.audio || this._liveMediaStatus.video)) fg2 = 82;
|
|
624402
|
-
if (cmd === "nexus") {
|
|
624403
|
-
fg2 = this._nexusStatus === "connected" ? 82 : this._nexusStatus === "connecting" ? 208 : 196;
|
|
624404
|
-
}
|
|
624405
|
-
return fg2;
|
|
624406
|
-
};
|
|
624407
625380
|
const decorateMenuButton = (cmd, label) => {
|
|
624408
625381
|
return `${HEADER_BUTTON_GLYPH_FG}${HEADER_BUTTON_LEFT}\x1B[0m${HEADER_BUTTON_BG}${HEADER_BUTTON_FG}${label}\x1B[0m${PANEL_BG_SEQ}${HEADER_BUTTON_GLYPH_FG}${HEADER_BUTTON_RIGHT}\x1B[0m${PANEL_BG_SEQ}`;
|
|
624409
625382
|
};
|
|
@@ -624414,14 +625387,7 @@ var init_status_bar = __esm({
|
|
|
624414
625387
|
return `\x1B[38;5;${color}m${HEADER_BUTTON_LEFT}\x1B[0m${weight}${fg2}${bg}${content}\x1B[0m${PANEL_BG_SEQ}\x1B[38;5;${color}m${HEADER_BUTTON_RIGHT}\x1B[0m${PANEL_BG_SEQ}`;
|
|
624415
625388
|
};
|
|
624416
625389
|
const renderBtn = (cmd, label) => {
|
|
624417
|
-
|
|
624418
|
-
const btnW = label.length + 2;
|
|
624419
|
-
const availW2 = getTermWidth() - identity3.width - 1;
|
|
624420
|
-
if (btnW > availW2) return linkify(cmd, `\x1B[38;5;${fg2}m${label}`);
|
|
624421
|
-
return linkify(
|
|
624422
|
-
cmd,
|
|
624423
|
-
`${PANEL_BG_SEQ}${HEADER_BUTTON_GLYPH_FG}${HEADER_BUTTON_LEFT}\x1B[0m${PANEL_BG_SEQ}\x1B[38;5;${fg2}m${label}\x1B[0m${PANEL_BG_SEQ}${HEADER_BUTTON_GLYPH_FG}${HEADER_BUTTON_RIGHT}\x1B[0m${PANEL_BG_SEQ}`
|
|
624424
|
-
);
|
|
625390
|
+
return linkify(cmd, decorateMenuButton(cmd, label));
|
|
624425
625391
|
};
|
|
624426
625392
|
const identity3 = this.buildHeaderIdentityRender();
|
|
624427
625393
|
const modelLabel = this.summarizeHeaderModelName() || "model";
|
|
@@ -624494,10 +625460,9 @@ var init_status_bar = __esm({
|
|
|
624494
625460
|
};
|
|
624495
625461
|
if (this._activeViewId !== "main") {
|
|
624496
625462
|
const mainLabel = ` ↩ main `;
|
|
624497
|
-
const mainColored = `\x1B[38;5;110m${mainLabel}\x1B[0m`;
|
|
624498
625463
|
sysItems.push({
|
|
624499
|
-
render: () =>
|
|
624500
|
-
w: mainLabel.length + 1
|
|
625464
|
+
render: () => linkify("view:main", decorateMenuButton("view:main", mainLabel)) + " ",
|
|
625465
|
+
w: mainLabel.length + 2 + 1
|
|
624501
625466
|
});
|
|
624502
625467
|
}
|
|
624503
625468
|
if (this._agentViews.size > 1) {
|
|
@@ -624535,10 +625500,7 @@ var init_status_bar = __esm({
|
|
|
624535
625500
|
const telegramDot = this._telegramStatus.active ? "●" : "○";
|
|
624536
625501
|
const telegramLabel = this._telegramStatus.activeSubAgents > 0 ? ` ✈ tg ${this._telegramStatus.activeSubAgents} ` : " ✈ tg ";
|
|
624537
625502
|
sysItems.push({
|
|
624538
|
-
render: () => renderBtn(
|
|
624539
|
-
"telegram",
|
|
624540
|
-
`${HEADER_TELEGRAM_FG}${telegramDot}${telegramLabel}\x1B[0m`
|
|
624541
|
-
) + " ",
|
|
625503
|
+
render: () => renderBtn("telegram", `${telegramDot}${telegramLabel}`) + " ",
|
|
624542
625504
|
w: telegramLabel.length + 2
|
|
624543
625505
|
});
|
|
624544
625506
|
const liveMediaActive = this._liveMediaStatus.audio || this._liveMediaStatus.video;
|
|
@@ -624683,7 +625645,7 @@ var init_status_bar = __esm({
|
|
|
624683
625645
|
const trunc3 = (s2) => s2.trim().split(/\s+/).slice(0, 3).join(" ");
|
|
624684
625646
|
if (this._activeViewId !== "main") {
|
|
624685
625647
|
const mainLabel = ` ↩ main `;
|
|
624686
|
-
zones.push({ w: mainLabel.length + 1, id: "main", render: () => "" });
|
|
625648
|
+
zones.push({ w: mainLabel.length + 2 + 1, id: "main", render: () => "" });
|
|
624687
625649
|
}
|
|
624688
625650
|
if (this._agentViews.size > 1) {
|
|
624689
625651
|
for (const view of this._agentViews.values()) {
|
|
@@ -655606,6 +656568,11 @@ ${feedback.message}`);
|
|
|
655606
656568
|
function attachLiveSinks(ctx3, manager, agentEnabled = false) {
|
|
655607
656569
|
manager.setStatusSink(ctx3.setLiveMediaStatus);
|
|
655608
656570
|
manager.setFeedbackSink((feedback) => renderLiveFeedback(ctx3, manager, feedback));
|
|
656571
|
+
manager.setAudioIntakeAgentConfig({
|
|
656572
|
+
backendUrl: ctx3.config.backendUrl,
|
|
656573
|
+
model: ctx3.config.model,
|
|
656574
|
+
apiKey: ctx3.config.apiKey
|
|
656575
|
+
});
|
|
655609
656576
|
if (agentEnabled && ctx3.startBackgroundPrompt) {
|
|
655610
656577
|
manager.setAgentActionSink((prompt) => {
|
|
655611
656578
|
const id2 = ctx3.startBackgroundPrompt?.(prompt);
|