@camstack/types 1.2.404 → 1.2.406
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/addon.js +21 -2
- package/dist/addon.mjs +21 -2
- package/dist/capabilities/index.d.ts +6 -3
- package/dist/capabilities/notification-rules.cap.d.ts +3 -78
- package/dist/capabilities/osd-manager.cap.d.ts +0 -18
- package/dist/capabilities/pipeline-analytics.cap.d.ts +24 -0
- package/dist/capabilities/pipeline-orchestrator.cap.d.ts +36 -0
- package/dist/capabilities/platform-probe.cap.d.ts +34 -0
- package/dist/capabilities/running-state.cap.d.ts +76 -0
- package/dist/capabilities/schemas/package-llm-verdict.d.ts +14 -0
- package/dist/capabilities/schemas/zone-rule.d.ts +4 -0
- package/dist/capabilities/zone-rules.cap.d.ts +10 -0
- package/dist/client.js +10 -62
- package/dist/client.mjs +10 -62
- package/dist/{composition-eoYYVfOp.js → composition-RmnnN39T.js} +86 -2
- package/dist/{composition-CB6PMT9p.mjs → composition-xz-ud5EQ.mjs} +86 -2
- package/dist/device/cap-vocabulary/entries/appliances.d.ts +1 -1
- package/dist/enums/event-category.d.ts +4 -0
- package/dist/enums.js +1 -1
- package/dist/enums.mjs +1 -1
- package/dist/{event-category-fDPduoeC.mjs → event-category-Ceif8n-v.mjs} +4 -0
- package/dist/{event-category-CXrwjC3e.js → event-category-DG6Vir8Z.js} +4 -0
- package/dist/expression/builtins.d.ts +7 -18
- package/dist/expression/expression-source.d.ts +14 -2
- package/dist/generated/addon-api.d.ts +14 -0
- package/dist/generated/addon-cap-facts.d.ts +1 -1
- package/dist/generated/cap-status-types.d.ts +3 -1
- package/dist/generated/capability-router-map.d.ts +5 -2
- package/dist/generated/device-local-state.d.ts +3 -0
- package/dist/generated/device-proxy.d.ts +3 -0
- package/dist/generated/device-scoped-caps.d.ts +1 -1
- package/dist/generated/history-catalog.d.ts +1 -1
- package/dist/generated/method-access-map.d.ts +1 -1
- package/dist/generated/method-device-selectors.d.ts +1 -1
- package/dist/generated/runtime-state-durability.d.ts +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +2534 -1415
- package/dist/index.mjs +2471 -1415
- package/dist/interfaces/config-ui.d.ts +69 -2
- package/dist/interfaces/platform.d.ts +7 -0
- package/dist/node-color.d.ts +1 -1
- package/dist/node.js +1 -1
- package/dist/node.mjs +1 -1
- package/dist/pipeline/auto-tune/accelerator-compute.d.ts +116 -0
- package/dist/pipeline/auto-tune/detector-performance.d.ts +5 -0
- package/dist/pipeline/auto-tune/hardware-class.d.ts +5 -4
- package/dist/pipeline/auto-tune/index.d.ts +1 -0
- package/dist/pipeline/auto-tune/plan-schemas.d.ts +100 -0
- package/dist/pipeline/auto-tune/recommend.d.ts +31 -9
- package/dist/pipeline/second-pass/index.d.ts +2 -0
- package/dist/pipeline/second-pass/motion-crop-plan.d.ts +183 -0
- package/dist/pipeline/second-pass/small-subject-recovery.d.ts +216 -0
- package/dist/running-state/assignment.d.ts +34 -0
- package/dist/running-state/defaults.d.ts +3 -0
- package/dist/running-state/from-operation.d.ts +14 -0
- package/dist/{sleep-CHIEEJkY.mjs → sleep-CYfIZzGv.mjs} +7 -3
- package/dist/{sleep-BKS4OPMo.js → sleep-UP964hNP.js} +7 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_sleep = require("./sleep-
|
|
3
|
-
const require_event_category = require("./event-category-
|
|
4
|
-
const require_composition = require("./composition-
|
|
2
|
+
const require_sleep = require("./sleep-UP964hNP.js");
|
|
3
|
+
const require_event_category = require("./event-category-DG6Vir8Z.js");
|
|
4
|
+
const require_composition = require("./composition-RmnnN39T.js");
|
|
5
5
|
const require_node_color = require("./node-color.js");
|
|
6
6
|
const require_err_msg = require("./err-msg-BhVbr0NX.js");
|
|
7
7
|
const require_enums = require("./enums.js");
|
|
@@ -12871,7 +12871,15 @@ var BUILTIN_RESULT_KINDS = Object.freeze({
|
|
|
12871
12871
|
kind: "fixed",
|
|
12872
12872
|
result: ["number", "null"]
|
|
12873
12873
|
},
|
|
12874
|
-
count: NUMBER
|
|
12874
|
+
count: NUMBER,
|
|
12875
|
+
dwell: {
|
|
12876
|
+
kind: "fixed",
|
|
12877
|
+
result: ["boolean", "null"]
|
|
12878
|
+
},
|
|
12879
|
+
latchedAt: {
|
|
12880
|
+
kind: "fixed",
|
|
12881
|
+
result: ["number", "null"]
|
|
12882
|
+
}
|
|
12875
12883
|
});
|
|
12876
12884
|
var KindInferenceError = class extends Error {};
|
|
12877
12885
|
function kindOfValue(value) {
|
|
@@ -12883,7 +12891,7 @@ function kindOfValue(value) {
|
|
|
12883
12891
|
function only(kind) {
|
|
12884
12892
|
return new Set([kind]);
|
|
12885
12893
|
}
|
|
12886
|
-
function union(...sets) {
|
|
12894
|
+
function union$1(...sets) {
|
|
12887
12895
|
return new Set(sets.flatMap((s) => [...s]));
|
|
12888
12896
|
}
|
|
12889
12897
|
function withoutNull(set) {
|
|
@@ -12911,8 +12919,8 @@ function callKinds(callee, args) {
|
|
|
12911
12919
|
const rule = BUILTIN_RESULT_KINDS[callee];
|
|
12912
12920
|
if (rule === void 0) throw new KindInferenceError(`no result-kind rule for builtin '${callee}'`);
|
|
12913
12921
|
if (rule.kind === "fixed") return new Set(rule.result);
|
|
12914
|
-
const nonNull = union(...args.map(withoutNull));
|
|
12915
|
-
return args.every((a) => a.has("null")) ? union(nonNull, only("null")) : nonNull;
|
|
12922
|
+
const nonNull = union$1(...args.map(withoutNull));
|
|
12923
|
+
return args.every((a) => a.has("null")) ? union$1(nonNull, only("null")) : nonNull;
|
|
12916
12924
|
}
|
|
12917
12925
|
function kindsOf(node, bindings) {
|
|
12918
12926
|
switch (node.kind) {
|
|
@@ -12925,8 +12933,8 @@ function kindsOf(node, bindings) {
|
|
|
12925
12933
|
}
|
|
12926
12934
|
case "unary": return only(node.op === "!" ? "boolean" : "number");
|
|
12927
12935
|
case "binary": return binaryKinds(node.op, kindsOf(node.left, bindings), kindsOf(node.right, bindings));
|
|
12928
|
-
case "logical": return union(kindsOf(node.left, bindings), kindsOf(node.right, bindings));
|
|
12929
|
-
case "conditional": return union(kindsOf(node.consequent, bindings), kindsOf(node.alternate, bindings));
|
|
12936
|
+
case "logical": return union$1(kindsOf(node.left, bindings), kindsOf(node.right, bindings));
|
|
12937
|
+
case "conditional": return union$1(kindsOf(node.consequent, bindings), kindsOf(node.alternate, bindings));
|
|
12930
12938
|
case "call": return callKinds(node.callee, node.args.map((a) => kindsOf(a, bindings)));
|
|
12931
12939
|
}
|
|
12932
12940
|
}
|
|
@@ -43726,9 +43734,6 @@ var NcSystemEventKindSchema = zod.z.enum([
|
|
|
43726
43734
|
"device-battery-normal",
|
|
43727
43735
|
"device-consumable-low",
|
|
43728
43736
|
"device-consumable-normal",
|
|
43729
|
-
"device-tank-low",
|
|
43730
|
-
"device-tank-full",
|
|
43731
|
-
"device-tank-normal",
|
|
43732
43737
|
"stream-online",
|
|
43733
43738
|
"stream-offline",
|
|
43734
43739
|
"node-online",
|
|
@@ -43775,18 +43780,19 @@ var NcSystemEventKindSchema = zod.z.enum([
|
|
|
43775
43780
|
"camera-enabled"
|
|
43776
43781
|
]);
|
|
43777
43782
|
/** The legacy tail of {@link NcSystemEventKindSchema} — see its docblock. */
|
|
43778
|
-
var NC_LEGACY_SYSTEM_EVENT_KINDS =
|
|
43783
|
+
var NC_LEGACY_SYSTEM_EVENT_KINDS = [
|
|
43779
43784
|
"camera-online",
|
|
43780
43785
|
"camera-offline",
|
|
43781
43786
|
"camera-disabled",
|
|
43782
43787
|
"camera-enabled"
|
|
43783
|
-
]
|
|
43788
|
+
];
|
|
43789
|
+
var NC_LEGACY_SYSTEM_EVENT_KIND_SET = new Set(NC_LEGACY_SYSTEM_EVENT_KINDS);
|
|
43784
43790
|
/**
|
|
43785
43791
|
* The kinds a rule may be AUTHORED with — every schema member except the
|
|
43786
43792
|
* legacy tail. Editors render THIS list; the schema still parses the tail so a
|
|
43787
43793
|
* durable row (and an unmigrated rule) survives being read.
|
|
43788
43794
|
*/
|
|
43789
|
-
var NC_AUTHORABLE_SYSTEM_EVENT_KINDS = NcSystemEventKindSchema.options.filter((kind) => !
|
|
43795
|
+
var NC_AUTHORABLE_SYSTEM_EVENT_KINDS = NcSystemEventKindSchema.options.filter((kind) => !NC_LEGACY_SYSTEM_EVENT_KIND_SET.has(kind));
|
|
43790
43796
|
/**
|
|
43791
43797
|
* The panel's transitions, as ONE list.
|
|
43792
43798
|
*
|
|
@@ -43831,12 +43837,6 @@ var NC_BATTERY_CONSUMABLE_SYSTEM_EVENT_KINDS = [
|
|
|
43831
43837
|
* and both editors read it.
|
|
43832
43838
|
*/
|
|
43833
43839
|
var NC_CONSUMABLE_SYSTEM_EVENT_KINDS = ["device-consumable-low", "device-consumable-normal"];
|
|
43834
|
-
/** A vacuum's tank alerts (D642) — raised on the provider's own verdict. */
|
|
43835
|
-
var NC_TANK_SYSTEM_EVENT_KINDS = [
|
|
43836
|
-
"device-tank-low",
|
|
43837
|
-
"device-tank-full",
|
|
43838
|
-
"device-tank-normal"
|
|
43839
|
-
];
|
|
43840
43840
|
/**
|
|
43841
43841
|
* The consumable threshold a rule that does not state one judges at (D642).
|
|
43842
43842
|
* The operator's number ("default 15 %, configurable on the rule"), and the
|
|
@@ -45072,18 +45072,18 @@ var NcRuleSchema = NcRuleInputSchema.extend({
|
|
|
45072
45072
|
* `addon-updates` the addons that are behind (D708 split them). `backups-exports` and `alarm` are not sections — a completed
|
|
45073
45073
|
* job is not an open state, and the alarm stays instant.
|
|
45074
45074
|
*/
|
|
45075
|
-
var
|
|
45075
|
+
var NC_STATE_SUMMARY_SECTION_IDS = [
|
|
45076
45076
|
"device-state",
|
|
45077
45077
|
"streams",
|
|
45078
45078
|
"battery",
|
|
45079
45079
|
"consumables",
|
|
45080
|
-
"tanks",
|
|
45081
45080
|
"detection-blind",
|
|
45082
45081
|
"nodes",
|
|
45083
45082
|
"addons",
|
|
45084
45083
|
"addon-updates",
|
|
45085
45084
|
"updates"
|
|
45086
|
-
]
|
|
45085
|
+
];
|
|
45086
|
+
var NcStateSummarySectionIdSchema = zod.z.enum(NC_STATE_SUMMARY_SECTION_IDS);
|
|
45087
45087
|
/** One section of a previewed state summary. `unavailable` = its state could not be read. */
|
|
45088
45088
|
var NcStateSummaryPreviewSectionSchema = zod.z.object({
|
|
45089
45089
|
id: NcStateSummarySectionIdSchema,
|
|
@@ -45460,18 +45460,6 @@ var NC_CONDITION_CATALOG = [
|
|
|
45460
45460
|
value: "device-consumable-normal",
|
|
45461
45461
|
label: "Consumable replaced"
|
|
45462
45462
|
},
|
|
45463
|
-
{
|
|
45464
|
-
value: "device-tank-low",
|
|
45465
|
-
label: "Tank low (water, detergent)"
|
|
45466
|
-
},
|
|
45467
|
-
{
|
|
45468
|
-
value: "device-tank-full",
|
|
45469
|
-
label: "Tank full (dirty water, dust)"
|
|
45470
|
-
},
|
|
45471
|
-
{
|
|
45472
|
-
value: "device-tank-normal",
|
|
45473
|
-
label: "Tank back to normal"
|
|
45474
|
-
},
|
|
45475
45463
|
{
|
|
45476
45464
|
value: "export-completed",
|
|
45477
45465
|
label: "Export completed"
|
|
@@ -47289,6 +47277,20 @@ var CAMERA_SENSORS_NOT_LOADED = Object.freeze({
|
|
|
47289
47277
|
reason: "camera sensor mappings not loaded"
|
|
47290
47278
|
});
|
|
47291
47279
|
//#endregion
|
|
47280
|
+
//#region src/capabilities/schemas/package-llm-verdict.ts
|
|
47281
|
+
/**
|
|
47282
|
+
* What the vision model must answer before a package event is emitted
|
|
47283
|
+
* when a packages rule has "Confirm with the LLM" on.
|
|
47284
|
+
*
|
|
47285
|
+
* `package` is the verdict. `confidence` is 0..1. `reason` is the model's
|
|
47286
|
+
* one-line explanation, stored on the event so a drop is not silent.
|
|
47287
|
+
*/
|
|
47288
|
+
var PackageLlmVerdictSchema = zod.z.object({
|
|
47289
|
+
package: zod.z.boolean(),
|
|
47290
|
+
confidence: zod.z.number().min(0).max(1),
|
|
47291
|
+
reason: zod.z.string().min(1)
|
|
47292
|
+
});
|
|
47293
|
+
//#endregion
|
|
47292
47294
|
//#region src/pipeline/cluster-model-scope.ts
|
|
47293
47295
|
/**
|
|
47294
47296
|
* A model choice has a SCOPE, and a scope change has a workflow.
|
|
@@ -48407,6 +48409,21 @@ var ObjectEventSchema = zod.z.object({
|
|
|
48407
48409
|
* pick-up written before D684 — UNKNOWN, never 0 and never `timestamp`.
|
|
48408
48410
|
*/
|
|
48409
48411
|
observedAt: zod.z.number().optional(),
|
|
48412
|
+
/**
|
|
48413
|
+
* The delivery was emitted without a vision-model yes (D931).
|
|
48414
|
+
*
|
|
48415
|
+
* Set only when the packages rule asked for an LLM confirm and the model
|
|
48416
|
+
* was unreachable, timed out, or answered something that did not parse.
|
|
48417
|
+
* A notification reads it so it can say "possible" instead of a definite
|
|
48418
|
+
* delivery. Absent on every other row, and on a confirm that said yes.
|
|
48419
|
+
*/
|
|
48420
|
+
packagePossible: zod.z.boolean().optional(),
|
|
48421
|
+
/**
|
|
48422
|
+
* The vision model's verdict, when a packages rule asked for one and the
|
|
48423
|
+
* model answered the schema (D931). A "no" never becomes a row — the
|
|
48424
|
+
* detector drops it and logs the reason. Present on a yes.
|
|
48425
|
+
*/
|
|
48426
|
+
packageLlmVerdict: PackageLlmVerdictSchema.optional(),
|
|
48410
48427
|
/** The parent track's key-event importance [0,1], propagated to every object
|
|
48411
48428
|
* event of the track (so an event row can be sorted by importance without a
|
|
48412
48429
|
* track join). Absent on legacy rows / before the track was scored. */
|
|
@@ -51632,11 +51649,18 @@ var KeptReasonSchema = zod.z.enum(["custom-pin"]);
|
|
|
51632
51649
|
* Where a choice's latency came from (D791): this node's own benchmark
|
|
51633
51650
|
* result, or the hardware-class table. Measured on the node beats the table.
|
|
51634
51651
|
*/
|
|
51635
|
-
var LatencySourceSchema = zod.z.discriminatedUnion("kind", [
|
|
51636
|
-
kind: zod.z.literal("
|
|
51637
|
-
|
|
51638
|
-
|
|
51639
|
-
|
|
51652
|
+
var LatencySourceSchema = zod.z.discriminatedUnion("kind", [
|
|
51653
|
+
zod.z.object({ kind: zod.z.literal("table") }),
|
|
51654
|
+
zod.z.object({
|
|
51655
|
+
kind: zod.z.literal("node-benchmark"),
|
|
51656
|
+
resultId: zod.z.string(),
|
|
51657
|
+
measuredAtMs: zod.z.number()
|
|
51658
|
+
}),
|
|
51659
|
+
zod.z.object({
|
|
51660
|
+
kind: zod.z.literal("estimated"),
|
|
51661
|
+
estimatedFrom: HardwareClassIdSchema
|
|
51662
|
+
})
|
|
51663
|
+
]);
|
|
51640
51664
|
var DeviceModelChoiceSchema = zod.z.object({
|
|
51641
51665
|
modelId: zod.z.string(),
|
|
51642
51666
|
p50Ms: zod.z.number(),
|
|
@@ -51644,6 +51668,11 @@ var DeviceModelChoiceSchema = zod.z.object({
|
|
|
51644
51668
|
measured: zod.z.boolean(),
|
|
51645
51669
|
/** Absent on a plan from before D791 — read as the table. */
|
|
51646
51670
|
source: LatencySourceSchema.optional(),
|
|
51671
|
+
/**
|
|
51672
|
+
* The measured class this latency was scaled from (D930). Absent when the
|
|
51673
|
+
* cell is the table or a node benchmark.
|
|
51674
|
+
*/
|
|
51675
|
+
estimatedFrom: HardwareClassIdSchema.optional(),
|
|
51647
51676
|
/** Parallel sessions at the camera rate with the headroom kept free: ⌊fps × (1 − headroom) / camera fps⌋. */
|
|
51648
51677
|
capacitySessions: zod.z.number(),
|
|
51649
51678
|
personRecall: zod.z.number()
|
|
@@ -67662,427 +67691,1201 @@ includeCrops: zod.z.boolean().optional() }).optional(), zod.z.array(VehicleSchem
|
|
|
67662
67691
|
}
|
|
67663
67692
|
};
|
|
67664
67693
|
//#endregion
|
|
67665
|
-
//#region src/
|
|
67666
|
-
var
|
|
67667
|
-
|
|
67668
|
-
|
|
67669
|
-
|
|
67670
|
-
|
|
67671
|
-
|
|
67672
|
-
|
|
67673
|
-
|
|
67674
|
-
|
|
67675
|
-
|
|
67676
|
-
|
|
67677
|
-
|
|
67678
|
-
|
|
67679
|
-
|
|
67680
|
-
|
|
67681
|
-
|
|
67682
|
-
|
|
67683
|
-
|
|
67684
|
-
|
|
67685
|
-
|
|
67686
|
-
|
|
67687
|
-
|
|
67688
|
-
}
|
|
67689
|
-
|
|
67690
|
-
|
|
67691
|
-
|
|
67692
|
-
|
|
67693
|
-
|
|
67694
|
-
|
|
67695
|
-
|
|
67696
|
-
|
|
67697
|
-
|
|
67698
|
-
|
|
67699
|
-
|
|
67700
|
-
|
|
67701
|
-
|
|
67702
|
-
|
|
67703
|
-
|
|
67704
|
-
|
|
67705
|
-
|
|
67706
|
-
|
|
67707
|
-
|
|
67708
|
-
|
|
67709
|
-
|
|
67710
|
-
|
|
67711
|
-
|
|
67712
|
-
|
|
67713
|
-
|
|
67714
|
-
|
|
67715
|
-
|
|
67716
|
-
|
|
67717
|
-
|
|
67718
|
-
|
|
67719
|
-
|
|
67720
|
-
|
|
67721
|
-
|
|
67722
|
-
|
|
67723
|
-
|
|
67724
|
-
|
|
67725
|
-
|
|
67726
|
-
|
|
67727
|
-
|
|
67728
|
-
|
|
67729
|
-
"linux",
|
|
67730
|
-
"win32"
|
|
67731
|
-
]);
|
|
67732
|
-
var HardwareArchSchema = zod.z.enum(["arm64", "x64"]);
|
|
67733
|
-
var GpuInfoSchema = zod.z.object({
|
|
67734
|
-
type: zod.z.enum([
|
|
67735
|
-
"nvidia",
|
|
67736
|
-
"amd",
|
|
67737
|
-
"intel",
|
|
67738
|
-
"apple"
|
|
67739
|
-
]),
|
|
67740
|
-
name: zod.z.string(),
|
|
67741
|
-
memoryMB: zod.z.number().optional()
|
|
67742
|
-
});
|
|
67743
|
-
var NpuInfoSchema = zod.z.object({
|
|
67744
|
-
type: zod.z.enum([
|
|
67745
|
-
"apple-ane",
|
|
67746
|
-
"intel-npu",
|
|
67747
|
-
"amd-xdna"
|
|
67748
|
-
]),
|
|
67749
|
-
name: zod.z.string().optional(),
|
|
67750
|
-
generation: zod.z.enum(["xdna1", "xdna2"]).optional()
|
|
67751
|
-
});
|
|
67752
|
-
var CoralBusSchema = zod.z.enum(["usb", "pci"]);
|
|
67753
|
-
var CoralInfoSchema = zod.z.object({
|
|
67754
|
-
type: zod.z.literal("coral-edgetpu"),
|
|
67755
|
-
/** Legacy single-bus report (older nodes) — read via `coralUsableUnits`. */
|
|
67756
|
-
bus: zod.z.string().optional(),
|
|
67757
|
-
/** Legacy USB unit count (older nodes) — read via `coralUsableUnits`. */
|
|
67758
|
-
count: zod.z.number().int().nonnegative().optional(),
|
|
67759
|
-
/** Usable units per bus — each one is its own `edgetpu:<bus>[:N]` device. */
|
|
67760
|
-
units: zod.z.array(zod.z.object({
|
|
67761
|
-
bus: CoralBusSchema,
|
|
67762
|
-
count: zod.z.number().int().nonnegative()
|
|
67763
|
-
})).readonly().optional(),
|
|
67764
|
-
/** Present-but-unusable units, each with its typed reason (never "absent"). */
|
|
67765
|
-
unavailable: zod.z.array(zod.z.object({
|
|
67766
|
-
bus: CoralBusSchema,
|
|
67767
|
-
count: zod.z.number().int().nonnegative(),
|
|
67768
|
-
reason: zod.z.enum(["driver-missing", "device-not-accessible"])
|
|
67769
|
-
})).readonly().optional()
|
|
67770
|
-
});
|
|
67771
|
-
var HardwareInfoSchema = zod.z.object({
|
|
67772
|
-
platform: HardwarePlatformSchema,
|
|
67773
|
-
arch: HardwareArchSchema,
|
|
67774
|
-
cpuModel: zod.z.string(),
|
|
67775
|
-
cpuCores: zod.z.number(),
|
|
67776
|
-
totalRAM_MB: zod.z.number(),
|
|
67777
|
-
availableRAM_MB: zod.z.number(),
|
|
67778
|
-
gpu: GpuInfoSchema.nullable(),
|
|
67779
|
-
npu: NpuInfoSchema.nullable(),
|
|
67780
|
-
coral: CoralInfoSchema.nullable().optional()
|
|
67781
|
-
});
|
|
67782
|
-
var PlatformScoreSchema = zod.z.object({
|
|
67783
|
-
runtime: zod.z.literal("python"),
|
|
67784
|
-
backend: zod.z.string(),
|
|
67785
|
-
format: zod.z.enum([
|
|
67786
|
-
"onnx",
|
|
67787
|
-
"coreml",
|
|
67788
|
-
"openvino",
|
|
67789
|
-
"tflite"
|
|
67790
|
-
]),
|
|
67791
|
-
score: zod.z.number(),
|
|
67792
|
-
reason: zod.z.string(),
|
|
67793
|
-
available: zod.z.boolean()
|
|
67794
|
-
});
|
|
67795
|
-
var InferenceDeviceDescriptorSchema = zod.z.object({
|
|
67796
|
-
key: zod.z.string(),
|
|
67797
|
-
backend: zod.z.string(),
|
|
67798
|
-
device: zod.z.string(),
|
|
67799
|
-
format: ModelFormatSchema,
|
|
67800
|
-
runtime: zod.z.literal("python"),
|
|
67801
|
-
score: zod.z.number(),
|
|
67802
|
-
available: zod.z.boolean()
|
|
67803
|
-
});
|
|
67804
|
-
var PlatformCapabilitiesSchema = zod.z.object({
|
|
67805
|
-
hardware: HardwareInfoSchema,
|
|
67806
|
-
scores: zod.z.array(PlatformScoreSchema).readonly(),
|
|
67807
|
-
bestScore: PlatformScoreSchema,
|
|
67808
|
-
pythonPath: zod.z.string().nullable(),
|
|
67809
|
-
devices: zod.z.array(InferenceDeviceDescriptorSchema).readonly()
|
|
67810
|
-
});
|
|
67811
|
-
var ModelRequirementSchema = zod.z.object({
|
|
67812
|
-
modelId: zod.z.string(),
|
|
67813
|
-
name: zod.z.string(),
|
|
67814
|
-
minRAM_MB: zod.z.number(),
|
|
67815
|
-
accuracyScore: zod.z.number(),
|
|
67816
|
-
formats: zod.z.array(zod.z.string()).readonly()
|
|
67817
|
-
});
|
|
67818
|
-
var ResolvedInferenceConfigSchema = zod.z.object({
|
|
67819
|
-
modelId: zod.z.string(),
|
|
67820
|
-
runtime: zod.z.literal("python"),
|
|
67821
|
-
backend: zod.z.string(),
|
|
67822
|
-
format: ModelFormatSchema,
|
|
67823
|
-
reason: zod.z.string()
|
|
67824
|
-
});
|
|
67825
|
-
/**
|
|
67826
|
-
* The answer of an accelerator rescan (D919): the node's device list after
|
|
67827
|
-
* the re-probe, what changed, and the evidence that decided the Coral answer
|
|
67828
|
-
* (`usbBus` — could the USB bus be read at all; `decidedBy` — USB ids, the PCI
|
|
67829
|
-
* bus, the installed-delegate fallback, or nothing).
|
|
67830
|
-
*/
|
|
67831
|
-
var AcceleratorRescanSchema = zod.z.object({
|
|
67832
|
-
devices: zod.z.array(InferenceDeviceDescriptorSchema).readonly(),
|
|
67833
|
-
appeared: zod.z.array(zod.z.string()).readonly(),
|
|
67834
|
-
disappeared: zod.z.array(zod.z.string()).readonly(),
|
|
67835
|
-
usbBus: zod.z.enum(["observed", "unobservable"]),
|
|
67836
|
-
decidedBy: zod.z.enum([
|
|
67837
|
-
"usb-ids",
|
|
67838
|
-
"pci",
|
|
67839
|
-
"delegate-fallback",
|
|
67840
|
-
"none"
|
|
67841
|
-
])
|
|
67842
|
-
});
|
|
67843
|
-
var platformProbeCapability = {
|
|
67844
|
-
name: "platform-probe",
|
|
67845
|
-
scope: "system",
|
|
67846
|
-
mode: "singleton",
|
|
67847
|
-
methods: {
|
|
67848
|
-
getCapabilities: require_sleep.method(zod.z.void(), PlatformCapabilitiesSchema),
|
|
67849
|
-
getHardware: require_sleep.method(zod.z.void(), HardwareInfoSchema),
|
|
67850
|
-
resolveInferenceConfig: require_sleep.method(zod.z.object({ requirements: zod.z.array(ModelRequirementSchema).readonly() }), ResolvedInferenceConfigSchema),
|
|
67851
|
-
resolveHwAccel: require_sleep.method(zod.z.object({ prefer: HwAccelBackendInputSchema }), HwAccelResolutionSchema),
|
|
67852
|
-
/**
|
|
67853
|
-
* Re-probe this node's accelerators NOW (Coral USB ids + PCIe apex units;
|
|
67854
|
-
* `/sys` and `/dev` reads, no model load) and rebuild the device list that
|
|
67855
|
-
* `getCapabilities` serves. The same re-probe runs every 60 s on its own;
|
|
67856
|
-
* this is the operator's "rescan inference devices". Target a node with the
|
|
67857
|
-
* `{ nodeId }` routing input, like `getCapabilities`.
|
|
67858
|
-
*/
|
|
67859
|
-
rescanAccelerators: require_sleep.method(zod.z.void(), AcceleratorRescanSchema, {
|
|
67860
|
-
kind: "mutation",
|
|
67861
|
-
auth: "admin"
|
|
67862
|
-
})
|
|
67863
|
-
}
|
|
67864
|
-
};
|
|
67865
|
-
//#endregion
|
|
67866
|
-
//#region src/capabilities/power-meter.cap.ts
|
|
67867
|
-
/**
|
|
67868
|
-
* Multi-metric electrical meter. One slice can carry any combination
|
|
67869
|
-
* of instantaneous power (W), cumulative energy (kWh), voltage (V),
|
|
67870
|
-
* and current (A) — all fields optional so a single-metric source
|
|
67871
|
-
* (HA `sensor` with `device_class: power`) populates only what it
|
|
67872
|
-
* has and aggregators (energy dashboards, billing exports) compose
|
|
67873
|
-
* across providers without needing one cap per metric.
|
|
67874
|
-
*
|
|
67875
|
-
* Trade-off acknowledged: a HomeKit export that wants to surface
|
|
67876
|
-
* "power" and "energy" as separate services has to crack open the
|
|
67877
|
-
* slice and emit two characteristics from one cap. Worth it to avoid
|
|
67878
|
-
* exploding the cap catalog with `power-w` / `energy-kwh` /
|
|
67879
|
-
* `voltage-v` / `current-a` quartets that always travel together
|
|
67880
|
-
* in real-world devices.
|
|
67881
|
-
*/
|
|
67882
|
-
var PowerMeterStatusSchema = zod.z.object({
|
|
67883
|
-
/** Instantaneous power draw in watts. */
|
|
67884
|
-
watts: zod.z.number().optional(),
|
|
67885
|
-
/** Cumulative energy in kilowatt-hours since the meter was reset. */
|
|
67886
|
-
kwhTotal: zod.z.number().optional(),
|
|
67887
|
-
/** Voltage in volts. */
|
|
67888
|
-
volts: zod.z.number().optional(),
|
|
67889
|
-
/** Current in amperes. */
|
|
67890
|
-
amps: zod.z.number().optional(),
|
|
67891
|
-
/** Apparent power in volt-amperes (W × power-factor reciprocal). */
|
|
67892
|
-
va: zod.z.number().optional(),
|
|
67893
|
-
/** Power factor (0..1) if reported. */
|
|
67894
|
-
powerFactor: zod.z.number().min(0).max(1).optional(),
|
|
67895
|
-
/** Ms epoch when the slice was last updated. */
|
|
67896
|
-
lastFetchedAt: zod.z.number(),
|
|
67897
|
-
/** Live display unit of the single metric this slice carries (e.g. HA
|
|
67898
|
-
* `attributes.unit_of_measurement` → 'V' / 'A' / 'W' / 'kW' / 'kWh').
|
|
67899
|
-
* Each upstream `sensor.*` entity surfaces ONE device_class, so one
|
|
67900
|
-
* unit per slice is unambiguous. The UI prefers this over the role's
|
|
67901
|
-
* canonical unit so a 'kW' / 'Wh' feed renders verbatim. */
|
|
67902
|
-
unit: zod.z.string().optional(),
|
|
67903
|
-
/** Suggested decimal places for numeric display.
|
|
67904
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
67905
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
67906
|
-
* auto-formatting when absent. */
|
|
67907
|
-
precision: zod.z.number().int().min(0).max(10).optional()
|
|
67908
|
-
});
|
|
67909
|
-
var powerMeterCapability = {
|
|
67910
|
-
name: "power-meter",
|
|
67911
|
-
scope: "device",
|
|
67912
|
-
deviceNative: true,
|
|
67913
|
-
mode: "singleton",
|
|
67914
|
-
deviceTypes: [require_err_msg.DeviceType.Sensor],
|
|
67915
|
-
methods: {},
|
|
67916
|
-
status: {
|
|
67917
|
-
schema: PowerMeterStatusSchema,
|
|
67918
|
-
kind: "push"
|
|
67919
|
-
},
|
|
67920
|
-
runtimeState: PowerMeterStatusSchema,
|
|
67921
|
-
/**
|
|
67922
|
-
* Runtime-state durability: **restored** — as `numeric-sensor`; `kwhTotal` is an accumulator whose restored value is the baseline.
|
|
67923
|
-
*
|
|
67924
|
-
* See `RuntimeStateDurability`. Enforced by
|
|
67925
|
-
* `scripts/check-runtime-state-durability.ts`.
|
|
67926
|
-
*/
|
|
67927
|
-
durability: "restored",
|
|
67928
|
-
/**
|
|
67929
|
-
* History (D850): recorded series. Gauges are floored at 30 s; `kwhTotal` is the one monotonic meter (counter).
|
|
67930
|
-
*/
|
|
67931
|
-
history: { fields: {
|
|
67932
|
-
watts: {
|
|
67933
|
-
kind: "gauge",
|
|
67934
|
-
unit: "W",
|
|
67935
|
-
deadband: 1,
|
|
67936
|
-
minIntervalMs: 3e4
|
|
67937
|
-
},
|
|
67938
|
-
kwhTotal: {
|
|
67939
|
-
kind: "counter",
|
|
67940
|
-
unit: "kWh",
|
|
67941
|
-
deadband: .01,
|
|
67942
|
-
minIntervalMs: 6e4
|
|
67694
|
+
//#region src/pipeline/auto-tune/detector-performance.ts
|
|
67695
|
+
var DETECTOR_PERF = {
|
|
67696
|
+
"intel-core-ultra": {
|
|
67697
|
+
"intel-npu": {
|
|
67698
|
+
"yolov9t-320": {
|
|
67699
|
+
p50Ms: 2.9,
|
|
67700
|
+
fps: 383,
|
|
67701
|
+
measured: true
|
|
67702
|
+
},
|
|
67703
|
+
"yolov9t-320-int8": {
|
|
67704
|
+
p50Ms: 3.8,
|
|
67705
|
+
fps: 333,
|
|
67706
|
+
measured: true
|
|
67707
|
+
},
|
|
67708
|
+
"yolov9t-640": {
|
|
67709
|
+
p50Ms: 7.4,
|
|
67710
|
+
fps: 139,
|
|
67711
|
+
measured: true
|
|
67712
|
+
},
|
|
67713
|
+
"yolov9t-640-int8": {
|
|
67714
|
+
p50Ms: 7.5,
|
|
67715
|
+
fps: 144,
|
|
67716
|
+
measured: true
|
|
67717
|
+
},
|
|
67718
|
+
"yolov9s-320": {
|
|
67719
|
+
p50Ms: 4.4,
|
|
67720
|
+
fps: 241,
|
|
67721
|
+
measured: true
|
|
67722
|
+
},
|
|
67723
|
+
"yolov9s-320-int8": {
|
|
67724
|
+
p50Ms: 4.1,
|
|
67725
|
+
fps: 264,
|
|
67726
|
+
measured: true
|
|
67727
|
+
},
|
|
67728
|
+
"yolov9s-640": {
|
|
67729
|
+
p50Ms: 12.7,
|
|
67730
|
+
fps: 82,
|
|
67731
|
+
measured: true
|
|
67732
|
+
},
|
|
67733
|
+
"yolov9s-640-int8": {
|
|
67734
|
+
p50Ms: 10.7,
|
|
67735
|
+
fps: 99,
|
|
67736
|
+
measured: true
|
|
67737
|
+
},
|
|
67738
|
+
"yolov9m-320": {
|
|
67739
|
+
p50Ms: 8.4,
|
|
67740
|
+
fps: 123,
|
|
67741
|
+
measured: true
|
|
67742
|
+
},
|
|
67743
|
+
"yolov9m-320-int8": {
|
|
67744
|
+
p50Ms: 6.5,
|
|
67745
|
+
fps: 165,
|
|
67746
|
+
measured: true
|
|
67747
|
+
},
|
|
67748
|
+
"yolov9m-640": {
|
|
67749
|
+
p50Ms: 29.5,
|
|
67750
|
+
fps: 32,
|
|
67751
|
+
measured: true
|
|
67752
|
+
},
|
|
67753
|
+
"yolov9m-640-int8": {
|
|
67754
|
+
p50Ms: 19.5,
|
|
67755
|
+
fps: 52,
|
|
67756
|
+
measured: true
|
|
67757
|
+
}
|
|
67943
67758
|
},
|
|
67944
|
-
|
|
67945
|
-
|
|
67946
|
-
|
|
67947
|
-
|
|
67948
|
-
|
|
67759
|
+
"intel-igpu": {
|
|
67760
|
+
"yolov9t-320": {
|
|
67761
|
+
p50Ms: 2.6,
|
|
67762
|
+
fps: 405,
|
|
67763
|
+
measured: true
|
|
67764
|
+
},
|
|
67765
|
+
"yolov9t-320-int8": {
|
|
67766
|
+
p50Ms: 2.8,
|
|
67767
|
+
fps: 324,
|
|
67768
|
+
measured: true
|
|
67769
|
+
},
|
|
67770
|
+
"yolov9t-640": {
|
|
67771
|
+
p50Ms: 6.5,
|
|
67772
|
+
fps: 157,
|
|
67773
|
+
measured: true
|
|
67774
|
+
},
|
|
67775
|
+
"yolov9t-640-int8": {
|
|
67776
|
+
p50Ms: 5.6,
|
|
67777
|
+
fps: 185,
|
|
67778
|
+
measured: true
|
|
67779
|
+
},
|
|
67780
|
+
"yolov9s-320": {
|
|
67781
|
+
p50Ms: 4.6,
|
|
67782
|
+
fps: 200,
|
|
67783
|
+
measured: true
|
|
67784
|
+
},
|
|
67785
|
+
"yolov9s-320-int8": {
|
|
67786
|
+
p50Ms: 4.1,
|
|
67787
|
+
fps: 239,
|
|
67788
|
+
measured: true
|
|
67789
|
+
},
|
|
67790
|
+
"yolov9s-640": {
|
|
67791
|
+
p50Ms: 15.2,
|
|
67792
|
+
fps: 69,
|
|
67793
|
+
measured: true
|
|
67794
|
+
},
|
|
67795
|
+
"yolov9s-640-int8": {
|
|
67796
|
+
p50Ms: 10.5,
|
|
67797
|
+
fps: 99,
|
|
67798
|
+
measured: true
|
|
67799
|
+
},
|
|
67800
|
+
"yolov9m-320": {
|
|
67801
|
+
p50Ms: 11.4,
|
|
67802
|
+
fps: 88,
|
|
67803
|
+
measured: true
|
|
67804
|
+
},
|
|
67805
|
+
"yolov9m-320-int8": {
|
|
67806
|
+
p50Ms: 7,
|
|
67807
|
+
fps: 127,
|
|
67808
|
+
measured: true
|
|
67809
|
+
},
|
|
67810
|
+
"yolov9m-640": {
|
|
67811
|
+
p50Ms: 40,
|
|
67812
|
+
fps: 25,
|
|
67813
|
+
measured: true
|
|
67814
|
+
},
|
|
67815
|
+
"yolov9m-640-int8": {
|
|
67816
|
+
p50Ms: 23.5,
|
|
67817
|
+
fps: 44,
|
|
67818
|
+
measured: true
|
|
67819
|
+
}
|
|
67949
67820
|
},
|
|
67950
|
-
|
|
67951
|
-
|
|
67952
|
-
|
|
67953
|
-
|
|
67954
|
-
|
|
67821
|
+
"x86-cpu": {
|
|
67822
|
+
"yolov9t-320": {
|
|
67823
|
+
p50Ms: 6.9,
|
|
67824
|
+
fps: 145,
|
|
67825
|
+
measured: true
|
|
67826
|
+
},
|
|
67827
|
+
"yolov9t-320-int8": {
|
|
67828
|
+
p50Ms: 2.7,
|
|
67829
|
+
fps: 405,
|
|
67830
|
+
measured: true
|
|
67831
|
+
},
|
|
67832
|
+
"yolov9t-640": {
|
|
67833
|
+
p50Ms: 23,
|
|
67834
|
+
fps: 44,
|
|
67835
|
+
measured: true
|
|
67836
|
+
},
|
|
67837
|
+
"yolov9t-640-int8": {
|
|
67838
|
+
p50Ms: 8.8,
|
|
67839
|
+
fps: 124,
|
|
67840
|
+
measured: true
|
|
67841
|
+
},
|
|
67842
|
+
"yolov9s-320": {
|
|
67843
|
+
p50Ms: 15.8,
|
|
67844
|
+
fps: 63,
|
|
67845
|
+
measured: true
|
|
67846
|
+
},
|
|
67847
|
+
"yolov9s-320-int8": {
|
|
67848
|
+
p50Ms: 6.5,
|
|
67849
|
+
fps: 180,
|
|
67850
|
+
measured: true
|
|
67851
|
+
},
|
|
67852
|
+
"yolov9s-640": {
|
|
67853
|
+
p50Ms: 58.2,
|
|
67854
|
+
fps: 17,
|
|
67855
|
+
measured: true
|
|
67856
|
+
},
|
|
67857
|
+
"yolov9s-640-int8": {
|
|
67858
|
+
p50Ms: 20.1,
|
|
67859
|
+
fps: 52,
|
|
67860
|
+
measured: true
|
|
67861
|
+
},
|
|
67862
|
+
"yolov9m-320": {
|
|
67863
|
+
p50Ms: 40,
|
|
67864
|
+
fps: 25,
|
|
67865
|
+
measured: true
|
|
67866
|
+
},
|
|
67867
|
+
"yolov9m-320-int8": {
|
|
67868
|
+
p50Ms: 14.2,
|
|
67869
|
+
fps: 74,
|
|
67870
|
+
measured: true
|
|
67871
|
+
},
|
|
67872
|
+
"yolov9m-640": {
|
|
67873
|
+
p50Ms: 155.8,
|
|
67874
|
+
fps: 6,
|
|
67875
|
+
measured: true
|
|
67876
|
+
},
|
|
67877
|
+
"yolov9m-640-int8": {
|
|
67878
|
+
p50Ms: 52.4,
|
|
67879
|
+
fps: 19,
|
|
67880
|
+
measured: true
|
|
67881
|
+
}
|
|
67955
67882
|
},
|
|
67956
|
-
|
|
67957
|
-
|
|
67958
|
-
|
|
67959
|
-
|
|
67960
|
-
|
|
67883
|
+
coral: { "yolov9s-320-edgetpu": {
|
|
67884
|
+
p50Ms: 32,
|
|
67885
|
+
fps: 31,
|
|
67886
|
+
measured: false
|
|
67887
|
+
} }
|
|
67888
|
+
},
|
|
67889
|
+
"intel-n-series": {
|
|
67890
|
+
"intel-igpu": {
|
|
67891
|
+
"yolov9t-320": {
|
|
67892
|
+
p50Ms: 15,
|
|
67893
|
+
fps: 48,
|
|
67894
|
+
measured: true
|
|
67895
|
+
},
|
|
67896
|
+
"yolov9t-320-int8": {
|
|
67897
|
+
p50Ms: 14.1,
|
|
67898
|
+
fps: 67,
|
|
67899
|
+
measured: true
|
|
67900
|
+
},
|
|
67901
|
+
"yolov9t-640": {
|
|
67902
|
+
p50Ms: 38.5,
|
|
67903
|
+
fps: 26,
|
|
67904
|
+
measured: true
|
|
67905
|
+
},
|
|
67906
|
+
"yolov9t-640-int8": {
|
|
67907
|
+
p50Ms: 50,
|
|
67908
|
+
fps: 20,
|
|
67909
|
+
measured: true
|
|
67910
|
+
},
|
|
67911
|
+
"yolov9s-320": {
|
|
67912
|
+
p50Ms: 41.4,
|
|
67913
|
+
fps: 24,
|
|
67914
|
+
measured: true
|
|
67915
|
+
},
|
|
67916
|
+
"yolov9s-320-int8": {
|
|
67917
|
+
p50Ms: 23.3,
|
|
67918
|
+
fps: 34,
|
|
67919
|
+
measured: true
|
|
67920
|
+
},
|
|
67921
|
+
"yolov9s-640": {
|
|
67922
|
+
p50Ms: 127.4,
|
|
67923
|
+
fps: 8,
|
|
67924
|
+
measured: true
|
|
67925
|
+
},
|
|
67926
|
+
"yolov9s-640-int8": {
|
|
67927
|
+
p50Ms: 81,
|
|
67928
|
+
fps: 12,
|
|
67929
|
+
measured: true
|
|
67930
|
+
},
|
|
67931
|
+
"yolov9m-320": {
|
|
67932
|
+
p50Ms: 63.5,
|
|
67933
|
+
fps: 16,
|
|
67934
|
+
measured: true
|
|
67935
|
+
},
|
|
67936
|
+
"yolov9m-320-int8": {
|
|
67937
|
+
p50Ms: 66,
|
|
67938
|
+
fps: 15,
|
|
67939
|
+
measured: true
|
|
67940
|
+
},
|
|
67941
|
+
"yolov9m-640": {
|
|
67942
|
+
p50Ms: 215.5,
|
|
67943
|
+
fps: 5,
|
|
67944
|
+
measured: true
|
|
67945
|
+
},
|
|
67946
|
+
"yolov9m-640-int8": {
|
|
67947
|
+
p50Ms: 163,
|
|
67948
|
+
fps: 6,
|
|
67949
|
+
measured: true
|
|
67950
|
+
}
|
|
67961
67951
|
},
|
|
67962
|
-
|
|
67963
|
-
|
|
67964
|
-
|
|
67965
|
-
|
|
67966
|
-
|
|
67967
|
-
|
|
67968
|
-
|
|
67969
|
-
|
|
67970
|
-
|
|
67971
|
-
|
|
67952
|
+
"x86-cpu": {
|
|
67953
|
+
"yolov9t-320": {
|
|
67954
|
+
p50Ms: 86.2,
|
|
67955
|
+
fps: 12,
|
|
67956
|
+
measured: true
|
|
67957
|
+
},
|
|
67958
|
+
"yolov9t-320-int8": {
|
|
67959
|
+
p50Ms: 52.7,
|
|
67960
|
+
fps: 20,
|
|
67961
|
+
measured: true
|
|
67962
|
+
},
|
|
67963
|
+
"yolov9t-640": {
|
|
67964
|
+
p50Ms: 322.1,
|
|
67965
|
+
fps: 3,
|
|
67966
|
+
measured: true
|
|
67967
|
+
},
|
|
67968
|
+
"yolov9t-640-int8": {
|
|
67969
|
+
p50Ms: 205.6,
|
|
67970
|
+
fps: 5,
|
|
67971
|
+
measured: true
|
|
67972
|
+
},
|
|
67973
|
+
"yolov9s-320": {
|
|
67974
|
+
p50Ms: 164.4,
|
|
67975
|
+
fps: 6,
|
|
67976
|
+
measured: true
|
|
67977
|
+
},
|
|
67978
|
+
"yolov9s-320-int8": {
|
|
67979
|
+
p50Ms: 86.9,
|
|
67980
|
+
fps: 12,
|
|
67981
|
+
measured: true
|
|
67982
|
+
},
|
|
67983
|
+
"yolov9s-640": {
|
|
67984
|
+
p50Ms: 341.3,
|
|
67985
|
+
fps: 3,
|
|
67986
|
+
measured: true
|
|
67987
|
+
},
|
|
67988
|
+
"yolov9s-640-int8": {
|
|
67989
|
+
p50Ms: 185.3,
|
|
67990
|
+
fps: 5,
|
|
67991
|
+
measured: true
|
|
67992
|
+
},
|
|
67993
|
+
"yolov9m-320": {
|
|
67994
|
+
p50Ms: 166.8,
|
|
67995
|
+
fps: 6,
|
|
67996
|
+
measured: true
|
|
67997
|
+
},
|
|
67998
|
+
"yolov9m-320-int8": {
|
|
67999
|
+
p50Ms: 226.3,
|
|
68000
|
+
fps: 4,
|
|
68001
|
+
measured: true
|
|
68002
|
+
},
|
|
68003
|
+
"yolov9m-640": {
|
|
68004
|
+
p50Ms: 663.5,
|
|
68005
|
+
fps: 2,
|
|
68006
|
+
measured: true
|
|
68007
|
+
},
|
|
68008
|
+
"yolov9m-640-int8": {
|
|
68009
|
+
p50Ms: 324.4,
|
|
68010
|
+
fps: 3,
|
|
68011
|
+
measured: true
|
|
68012
|
+
}
|
|
68013
|
+
},
|
|
68014
|
+
coral: { "yolov9s-320-edgetpu": {
|
|
68015
|
+
p50Ms: 32,
|
|
68016
|
+
fps: 31,
|
|
68017
|
+
measured: false
|
|
68018
|
+
} }
|
|
68019
|
+
},
|
|
68020
|
+
"amd-phoenix": {
|
|
68021
|
+
"x86-cpu": {
|
|
68022
|
+
"yolov9t-320": {
|
|
68023
|
+
p50Ms: 24,
|
|
68024
|
+
fps: 42,
|
|
68025
|
+
measured: true
|
|
68026
|
+
},
|
|
68027
|
+
"yolov9t-320-int8": {
|
|
68028
|
+
p50Ms: 12,
|
|
68029
|
+
fps: 83,
|
|
68030
|
+
measured: true
|
|
68031
|
+
},
|
|
68032
|
+
"yolov9t-640": {
|
|
68033
|
+
p50Ms: 50,
|
|
68034
|
+
fps: 20,
|
|
68035
|
+
measured: true
|
|
68036
|
+
},
|
|
68037
|
+
"yolov9t-640-int8": {
|
|
68038
|
+
p50Ms: 20,
|
|
68039
|
+
fps: 50,
|
|
68040
|
+
measured: true
|
|
68041
|
+
},
|
|
68042
|
+
"yolov9s-320": {
|
|
68043
|
+
p50Ms: 30,
|
|
68044
|
+
fps: 33,
|
|
68045
|
+
measured: true
|
|
68046
|
+
},
|
|
68047
|
+
"yolov9s-320-int8": {
|
|
68048
|
+
p50Ms: 15,
|
|
68049
|
+
fps: 67,
|
|
68050
|
+
measured: true
|
|
68051
|
+
},
|
|
68052
|
+
"yolov9s-640": {
|
|
68053
|
+
p50Ms: 92,
|
|
68054
|
+
fps: 11,
|
|
68055
|
+
measured: true
|
|
68056
|
+
},
|
|
68057
|
+
"yolov9s-640-int8": {
|
|
68058
|
+
p50Ms: 27,
|
|
68059
|
+
fps: 37,
|
|
68060
|
+
measured: true
|
|
68061
|
+
},
|
|
68062
|
+
"yolov9m-320": {
|
|
68063
|
+
p50Ms: 67,
|
|
68064
|
+
fps: 15,
|
|
68065
|
+
measured: true
|
|
68066
|
+
},
|
|
68067
|
+
"yolov9m-320-int8": {
|
|
68068
|
+
p50Ms: 21,
|
|
68069
|
+
fps: 48,
|
|
68070
|
+
measured: true
|
|
68071
|
+
},
|
|
68072
|
+
"yolov9m-640": {
|
|
68073
|
+
p50Ms: 229,
|
|
68074
|
+
fps: 4,
|
|
68075
|
+
measured: true
|
|
68076
|
+
},
|
|
68077
|
+
"yolov9m-640-int8": {
|
|
68078
|
+
p50Ms: 48,
|
|
68079
|
+
fps: 21,
|
|
68080
|
+
measured: true
|
|
68081
|
+
}
|
|
68082
|
+
},
|
|
68083
|
+
"amd-igpu": {
|
|
68084
|
+
"yolov9t-320": {
|
|
68085
|
+
p50Ms: 40,
|
|
68086
|
+
fps: 25,
|
|
68087
|
+
measured: true
|
|
68088
|
+
},
|
|
68089
|
+
"yolov9t-640": {
|
|
68090
|
+
p50Ms: 89,
|
|
68091
|
+
fps: 11,
|
|
68092
|
+
measured: true
|
|
68093
|
+
},
|
|
68094
|
+
"yolov9s-320": {
|
|
68095
|
+
p50Ms: 65,
|
|
68096
|
+
fps: 15,
|
|
68097
|
+
measured: true
|
|
68098
|
+
},
|
|
68099
|
+
"yolov9s-640": {
|
|
68100
|
+
p50Ms: 125,
|
|
68101
|
+
fps: 8,
|
|
68102
|
+
measured: true
|
|
68103
|
+
},
|
|
68104
|
+
"yolov9m-320": {
|
|
68105
|
+
p50Ms: 85,
|
|
68106
|
+
fps: 12,
|
|
68107
|
+
measured: true
|
|
68108
|
+
},
|
|
68109
|
+
"yolov9m-640": {
|
|
68110
|
+
p50Ms: 228,
|
|
68111
|
+
fps: 4,
|
|
68112
|
+
measured: true
|
|
68113
|
+
}
|
|
68114
|
+
},
|
|
68115
|
+
coral: { "yolov9s-320-edgetpu": {
|
|
68116
|
+
p50Ms: 32,
|
|
68117
|
+
fps: 31,
|
|
68118
|
+
measured: true
|
|
68119
|
+
} }
|
|
68120
|
+
},
|
|
68121
|
+
"apple-m-pro": {
|
|
68122
|
+
"apple-ane": {
|
|
68123
|
+
"yolov9t-320": {
|
|
68124
|
+
p50Ms: 1.1,
|
|
68125
|
+
fps: 952,
|
|
68126
|
+
measured: true
|
|
68127
|
+
},
|
|
68128
|
+
"yolov9t-640": {
|
|
68129
|
+
p50Ms: 3,
|
|
68130
|
+
fps: 333,
|
|
68131
|
+
measured: true
|
|
68132
|
+
},
|
|
68133
|
+
"yolov9s-320": {
|
|
68134
|
+
p50Ms: 1.5,
|
|
68135
|
+
fps: 667,
|
|
68136
|
+
measured: true
|
|
68137
|
+
},
|
|
68138
|
+
"yolov9s-640": {
|
|
68139
|
+
p50Ms: 5.1,
|
|
68140
|
+
fps: 196,
|
|
68141
|
+
measured: true
|
|
68142
|
+
},
|
|
68143
|
+
"yolov9m-320": {
|
|
68144
|
+
p50Ms: 3.2,
|
|
68145
|
+
fps: 312,
|
|
68146
|
+
measured: true
|
|
68147
|
+
},
|
|
68148
|
+
"yolov9m-640": {
|
|
68149
|
+
p50Ms: 10.2,
|
|
68150
|
+
fps: 98,
|
|
68151
|
+
measured: true
|
|
68152
|
+
}
|
|
68153
|
+
},
|
|
68154
|
+
coral: { "yolov9s-320-edgetpu": {
|
|
68155
|
+
p50Ms: 32,
|
|
68156
|
+
fps: 31,
|
|
68157
|
+
measured: false
|
|
68158
|
+
} }
|
|
68159
|
+
},
|
|
68160
|
+
"apple-m-base": {
|
|
68161
|
+
"apple-ane": {
|
|
68162
|
+
"yolov9t-320": {
|
|
68163
|
+
p50Ms: 1.32,
|
|
68164
|
+
fps: 758,
|
|
68165
|
+
measured: false
|
|
68166
|
+
},
|
|
68167
|
+
"yolov9t-640": {
|
|
68168
|
+
p50Ms: 3.45,
|
|
68169
|
+
fps: 290,
|
|
68170
|
+
measured: true
|
|
68171
|
+
},
|
|
68172
|
+
"yolov9s-320": {
|
|
68173
|
+
p50Ms: 1.8,
|
|
68174
|
+
fps: 556,
|
|
68175
|
+
measured: false
|
|
68176
|
+
},
|
|
68177
|
+
"yolov9s-640": {
|
|
68178
|
+
p50Ms: 5.46,
|
|
68179
|
+
fps: 183,
|
|
68180
|
+
measured: true
|
|
68181
|
+
},
|
|
68182
|
+
"yolov9m-320": {
|
|
68183
|
+
p50Ms: 3.79,
|
|
68184
|
+
fps: 264,
|
|
68185
|
+
measured: true
|
|
68186
|
+
},
|
|
68187
|
+
"yolov9m-640": {
|
|
68188
|
+
p50Ms: 10.34,
|
|
68189
|
+
fps: 97,
|
|
68190
|
+
measured: true
|
|
68191
|
+
}
|
|
68192
|
+
},
|
|
68193
|
+
coral: { "yolov9s-320-edgetpu": {
|
|
68194
|
+
p50Ms: 32,
|
|
68195
|
+
fps: 31,
|
|
68196
|
+
measured: false
|
|
68197
|
+
} }
|
|
68198
|
+
}
|
|
67972
68199
|
};
|
|
67973
|
-
|
|
67974
|
-
|
|
67975
|
-
|
|
67976
|
-
|
|
67977
|
-
* on `DeviceType.Presence`. Read-only — no setters: presence is
|
|
67978
|
-
* derived from upstream signals (mobile-app location, router DHCP
|
|
67979
|
-
* lease, Bluetooth beacon, manual zone change) and pushed into the
|
|
67980
|
-
* slice.
|
|
67981
|
-
*
|
|
67982
|
-
* `state` follows HA's canonical values — `home` / `not_home` /
|
|
67983
|
-
* `<zone-name>` for any other named zone. The zone name is freeform
|
|
67984
|
-
* because users define arbitrary zones (`work`, `gym`, `school`).
|
|
67985
|
-
*
|
|
67986
|
-
* GPS coordinates are nullable — tracker types without location
|
|
67987
|
-
* (Bluetooth scanner, MQTT presence) omit them. The presence of
|
|
67988
|
-
* GPS data is signaled by `DeviceFeature.PresenceGps` for fast
|
|
67989
|
-
* UI map-pin rendering.
|
|
67990
|
-
*/
|
|
67991
|
-
var GpsLocationSchema = zod.z.object({
|
|
67992
|
-
/** Latitude in decimal degrees, -90..90. */
|
|
67993
|
-
latitude: zod.z.number().min(-90).max(90),
|
|
67994
|
-
/** Longitude in decimal degrees, -180..180. */
|
|
67995
|
-
longitude: zod.z.number().min(-180).max(180),
|
|
67996
|
-
/** Reported accuracy in meters (lower = better). */
|
|
67997
|
-
accuracyMeters: zod.z.number().nonnegative()
|
|
67998
|
-
});
|
|
67999
|
-
var PresenceStatusSchema = zod.z.object({
|
|
68000
|
-
/** `home` / `not_home` / any user-defined zone name. */
|
|
68001
|
-
state: zod.z.string(),
|
|
68002
|
-
/** Optional textual location label (zone name, city, address). Null
|
|
68003
|
-
* when only the binary state is known. */
|
|
68004
|
-
location: zod.z.string().nullable(),
|
|
68005
|
-
/** GPS coordinates when available. Null otherwise. */
|
|
68006
|
-
gps: GpsLocationSchema.nullable(),
|
|
68007
|
-
/** Optional battery level of the tracking device (0..100). Null
|
|
68008
|
-
* when not reported. */
|
|
68009
|
-
batteryPercent: zod.z.number().min(0).max(100).nullable(),
|
|
68010
|
-
/** Ms epoch when the slice was last updated. */
|
|
68011
|
-
lastChangedAt: zod.z.number()
|
|
68012
|
-
});
|
|
68013
|
-
var presenceCapability = {
|
|
68014
|
-
name: "presence",
|
|
68015
|
-
scope: "device",
|
|
68016
|
-
deviceNative: true,
|
|
68017
|
-
mode: "singleton",
|
|
68018
|
-
deviceTypes: [require_err_msg.DeviceType.Presence],
|
|
68019
|
-
methods: {},
|
|
68020
|
-
status: {
|
|
68021
|
-
schema: PresenceStatusSchema,
|
|
68022
|
-
kind: "push"
|
|
68200
|
+
var DETECTOR_ACCURACY = {
|
|
68201
|
+
"yolov9m-320": {
|
|
68202
|
+
personPrecision: .979,
|
|
68203
|
+
personRecall: .649
|
|
68023
68204
|
},
|
|
68024
|
-
|
|
68025
|
-
|
|
68026
|
-
|
|
68027
|
-
|
|
68028
|
-
|
|
68029
|
-
|
|
68030
|
-
|
|
68031
|
-
|
|
68032
|
-
|
|
68033
|
-
|
|
68034
|
-
|
|
68035
|
-
|
|
68036
|
-
|
|
68037
|
-
|
|
68038
|
-
|
|
68039
|
-
|
|
68040
|
-
|
|
68041
|
-
|
|
68042
|
-
|
|
68043
|
-
|
|
68044
|
-
|
|
68045
|
-
|
|
68046
|
-
|
|
68047
|
-
|
|
68048
|
-
|
|
68205
|
+
"yolov9m-320-int8": {
|
|
68206
|
+
personPrecision: .974,
|
|
68207
|
+
personRecall: .684
|
|
68208
|
+
},
|
|
68209
|
+
"yolov9m-640": {
|
|
68210
|
+
personPrecision: .976,
|
|
68211
|
+
personRecall: .854
|
|
68212
|
+
},
|
|
68213
|
+
"yolov9m-640-int8": {
|
|
68214
|
+
personPrecision: .976,
|
|
68215
|
+
personRecall: .847
|
|
68216
|
+
},
|
|
68217
|
+
"yolov9s-320": {
|
|
68218
|
+
personPrecision: .958,
|
|
68219
|
+
personRecall: .479
|
|
68220
|
+
},
|
|
68221
|
+
"yolov9s-320-int8": {
|
|
68222
|
+
personPrecision: .969,
|
|
68223
|
+
personRecall: .438
|
|
68224
|
+
},
|
|
68225
|
+
"yolov9s-640": {
|
|
68226
|
+
personPrecision: .986,
|
|
68227
|
+
personRecall: .753
|
|
68228
|
+
},
|
|
68229
|
+
"yolov9s-640-int8": {
|
|
68230
|
+
personPrecision: .963,
|
|
68231
|
+
personRecall: .823
|
|
68232
|
+
},
|
|
68233
|
+
"yolov9t-320": {
|
|
68234
|
+
personPrecision: .946,
|
|
68235
|
+
personRecall: .368
|
|
68236
|
+
},
|
|
68237
|
+
"yolov9t-320-int8": {
|
|
68238
|
+
personPrecision: .943,
|
|
68239
|
+
personRecall: .347
|
|
68240
|
+
},
|
|
68241
|
+
"yolov9t-640": {
|
|
68242
|
+
personPrecision: .979,
|
|
68243
|
+
personRecall: .646
|
|
68244
|
+
},
|
|
68245
|
+
"yolov9t-640-int8": {
|
|
68246
|
+
personPrecision: .979,
|
|
68247
|
+
personRecall: .646
|
|
68248
|
+
}
|
|
68049
68249
|
};
|
|
68050
68250
|
//#endregion
|
|
68051
|
-
//#region src/
|
|
68251
|
+
//#region src/pipeline/auto-tune/accelerator-compute.ts
|
|
68252
|
+
/**
|
|
68253
|
+
* Capacity of an accelerator we have not measured (D930).
|
|
68254
|
+
*
|
|
68255
|
+
* The latency table stays the measured columns. A device that reports compute
|
|
68256
|
+
* (OpenVINO GOPS, or execution units × clock, or compute units, or TOPS) is
|
|
68257
|
+
* estimated from the nearest measured column of the same accelerator kind,
|
|
68258
|
+
* scaled by the compute ratio and clamped. A node benchmark still replaces
|
|
68259
|
+
* the estimate. A device that reports neither compute nor a known class stays
|
|
68260
|
+
* `unknown`.
|
|
68261
|
+
*
|
|
68262
|
+
* A failed probe field is `null`, never `0` (D393): zero would be a compute
|
|
68263
|
+
* ratio of zero and would size the device as if it could not run.
|
|
68264
|
+
*/
|
|
68265
|
+
/**
|
|
68266
|
+
* An unmeasured device is never estimated outside this band of its reference.
|
|
68267
|
+
*
|
|
68268
|
+
* Peak GOPS and detector latency do not move together: a 640 model is memory
|
|
68269
|
+
* bound, a 320 model less so, and the N100→N305 public EU×clock ratio is
|
|
68270
|
+
* about 2.2×. 2× is the widest that spread justifies without a measurement
|
|
68271
|
+
* on the device; 0.5× is the symmetric floor so a tiny iGPU is not told it
|
|
68272
|
+
* is many times slower than the weakest column we measured. A node benchmark
|
|
68273
|
+
* replaces the estimate and is not clamped.
|
|
68274
|
+
*/
|
|
68275
|
+
var ESTIMATE_COMPUTE_RATIO_MIN = .5;
|
|
68276
|
+
var ESTIMATE_COMPUTE_RATIO_MAX = 2;
|
|
68277
|
+
/**
|
|
68278
|
+
* Within this of 1.0, a device whose class IS the reference is that measured
|
|
68279
|
+
* machine (a probe clock a few percent off the spec), not a different chip.
|
|
68280
|
+
* An unknown class is still estimated, so the plan says so.
|
|
68281
|
+
*/
|
|
68282
|
+
var REFERENCE_MATCH_FRACTION = .05;
|
|
68283
|
+
/** N100 iGPU: 24 execution units, 750 MHz graphics max (Intel specification). */
|
|
68284
|
+
var N100_IGPU_EU = 24;
|
|
68285
|
+
var N100_IGPU_CLOCK_MHZ = 750;
|
|
68286
|
+
/**
|
|
68287
|
+
* Core Ultra 7 265K iGPU: 4 Xe-cores. OpenVINO's execution-unit count for
|
|
68288
|
+
* Xe-LP is 16 per Xe-core, and the graphics max dynamic frequency is 2000 MHz.
|
|
68289
|
+
* Used only to choose the nearest column for a chip that is not itself a
|
|
68290
|
+
* Core Ultra — that class is the measurement and is never scaled.
|
|
68291
|
+
*/
|
|
68292
|
+
var CORE_ULTRA_265K_IGPU_EU = 64;
|
|
68293
|
+
var CORE_ULTRA_265K_IGPU_CLOCK_MHZ = 2e3;
|
|
68294
|
+
/** Phoenix / Hawk Point Radeon 780M: 12 compute units. */
|
|
68295
|
+
var PHOENIX_780M_CU = 12;
|
|
68296
|
+
var positiveOrNull = zod.z.number().nullable().transform((value) => value !== null && Number.isFinite(value) && value > 0 ? value : null);
|
|
68297
|
+
var textOrNull = zod.z.string().nullable().transform((value) => value !== null && value.trim() !== "" ? value : null);
|
|
68298
|
+
var PrecisionGopsSchema = zod.z.object({
|
|
68299
|
+
int8: positiveOrNull,
|
|
68300
|
+
fp16: positiveOrNull
|
|
68301
|
+
});
|
|
68302
|
+
/** What one accelerator device reported. Every failed query is null. */
|
|
68303
|
+
var AcceleratorComputeSchema = zod.z.object({
|
|
68304
|
+
deviceKey: zod.z.string(),
|
|
68305
|
+
fullDeviceName: textOrNull,
|
|
68306
|
+
executionUnits: positiveOrNull,
|
|
68307
|
+
clockMHz: positiveOrNull,
|
|
68308
|
+
gops: PrecisionGopsSchema.nullable(),
|
|
68309
|
+
architecture: textOrNull,
|
|
68310
|
+
npuName: textOrNull,
|
|
68311
|
+
tops: positiveOrNull,
|
|
68312
|
+
vendor: textOrNull,
|
|
68313
|
+
webgpuArchitecture: textOrNull,
|
|
68314
|
+
webgpuDevice: textOrNull,
|
|
68315
|
+
computeUnits: positiveOrNull
|
|
68316
|
+
});
|
|
68317
|
+
var AcceleratorFactsPayloadSchema = zod.z.object({ accelerators: zod.z.array(AcceleratorComputeSchema).nullable() });
|
|
68318
|
+
/**
|
|
68319
|
+
* Compute of the machines the latency columns were measured on.
|
|
68320
|
+
* GOPS is preferred when a column carries it; none of these do yet — the
|
|
68321
|
+
* boxes were measured before the probe reported `DEVICE_GOPS` — so the scale
|
|
68322
|
+
* uses execution units × clock (Intel) or compute units (AMD) until a
|
|
68323
|
+
* re-measure locks a GOPS figure on the column.
|
|
68324
|
+
*/
|
|
68325
|
+
var REFERENCE_COMPUTE = [
|
|
68326
|
+
{
|
|
68327
|
+
hardwareClass: "intel-n-series",
|
|
68328
|
+
accelerator: "intel-igpu",
|
|
68329
|
+
score: {
|
|
68330
|
+
metric: "eu-mhz",
|
|
68331
|
+
value: 24 * 750
|
|
68332
|
+
}
|
|
68333
|
+
},
|
|
68334
|
+
{
|
|
68335
|
+
hardwareClass: "intel-core-ultra",
|
|
68336
|
+
accelerator: "intel-igpu",
|
|
68337
|
+
score: {
|
|
68338
|
+
metric: "eu-mhz",
|
|
68339
|
+
value: 64 * CORE_ULTRA_265K_IGPU_CLOCK_MHZ
|
|
68340
|
+
}
|
|
68341
|
+
},
|
|
68342
|
+
{
|
|
68343
|
+
hardwareClass: "amd-phoenix",
|
|
68344
|
+
accelerator: "amd-igpu",
|
|
68345
|
+
score: {
|
|
68346
|
+
metric: "cu",
|
|
68347
|
+
value: 12
|
|
68348
|
+
}
|
|
68349
|
+
}
|
|
68350
|
+
];
|
|
68351
|
+
/** Parse a probe payload. `null` means the probe failed — not "no accelerators". */
|
|
68352
|
+
function parseAcceleratorFactsJson(raw) {
|
|
68353
|
+
const parsed = AcceleratorFactsPayloadSchema.safeParse(raw);
|
|
68354
|
+
if (!parsed.success || parsed.data.accelerators === null) return null;
|
|
68355
|
+
return parsed.data.accelerators;
|
|
68356
|
+
}
|
|
68357
|
+
function positive$1(value) {
|
|
68358
|
+
return value !== null && value > 0 ? value : null;
|
|
68359
|
+
}
|
|
68360
|
+
/** Scores in the order the estimate prefers. A non-positive field is absent. */
|
|
68361
|
+
function computeScores(report) {
|
|
68362
|
+
const scores = [];
|
|
68363
|
+
const int8 = positive$1(report.gops?.int8 ?? null);
|
|
68364
|
+
if (int8 !== null) scores.push({
|
|
68365
|
+
metric: "gops-int8",
|
|
68366
|
+
value: int8
|
|
68367
|
+
});
|
|
68368
|
+
const fp16 = positive$1(report.gops?.fp16 ?? null);
|
|
68369
|
+
if (fp16 !== null) scores.push({
|
|
68370
|
+
metric: "gops-fp16",
|
|
68371
|
+
value: fp16
|
|
68372
|
+
});
|
|
68373
|
+
const units = positive$1(report.executionUnits);
|
|
68374
|
+
const clock = positive$1(report.clockMHz);
|
|
68375
|
+
if (units !== null && clock !== null) scores.push({
|
|
68376
|
+
metric: "eu-mhz",
|
|
68377
|
+
value: units * clock
|
|
68378
|
+
});
|
|
68379
|
+
const computeUnits = positive$1(report.computeUnits);
|
|
68380
|
+
if (computeUnits !== null) scores.push({
|
|
68381
|
+
metric: "cu",
|
|
68382
|
+
value: computeUnits
|
|
68383
|
+
});
|
|
68384
|
+
const tops = positive$1(report.tops);
|
|
68385
|
+
if (tops !== null) scores.push({
|
|
68386
|
+
metric: "tops",
|
|
68387
|
+
value: tops
|
|
68388
|
+
});
|
|
68389
|
+
return scores;
|
|
68390
|
+
}
|
|
68391
|
+
function nearestReference(kind, score, references) {
|
|
68392
|
+
const same = references.filter((ref) => ref.accelerator === kind && ref.score.metric === score.metric && ref.score.value > 0);
|
|
68393
|
+
const first = same[0];
|
|
68394
|
+
if (first === void 0) return null;
|
|
68395
|
+
return same.reduce((best, ref) => {
|
|
68396
|
+
return Math.abs(Math.log(score.value / ref.score.value)) < Math.abs(Math.log(score.value / best.score.value)) ? ref : best;
|
|
68397
|
+
}, first);
|
|
68398
|
+
}
|
|
68399
|
+
function scoreAgainstReferences(report, kind, references) {
|
|
68400
|
+
for (const score of computeScores(report)) if (nearestReference(kind, score, references) !== null) return score;
|
|
68401
|
+
return null;
|
|
68402
|
+
}
|
|
68403
|
+
/** True when this report can scale a measured column of `kind`. */
|
|
68404
|
+
function reportIsScalable(report, kind, references = REFERENCE_COMPUTE) {
|
|
68405
|
+
return scoreAgainstReferences(report, kind, references) !== null;
|
|
68406
|
+
}
|
|
68407
|
+
function classColumn(perf, hardwareClass, accelerator) {
|
|
68408
|
+
if (hardwareClass === "unknown") return void 0;
|
|
68409
|
+
return perf[hardwareClass]?.[accelerator];
|
|
68410
|
+
}
|
|
68411
|
+
function round1(value) {
|
|
68412
|
+
return Math.round(value * 10) / 10;
|
|
68413
|
+
}
|
|
68414
|
+
function clampRatio(ratio) {
|
|
68415
|
+
return Math.min(2, Math.max(ESTIMATE_COMPUTE_RATIO_MIN, ratio));
|
|
68416
|
+
}
|
|
68417
|
+
function scaleColumn(column, ratio, estimatedFrom) {
|
|
68418
|
+
const out = {};
|
|
68419
|
+
for (const [modelId, cell] of Object.entries(column)) out[modelId] = {
|
|
68420
|
+
p50Ms: round1(cell.p50Ms / ratio),
|
|
68421
|
+
fps: Math.max(1, Math.round(cell.fps * ratio)),
|
|
68422
|
+
measured: false,
|
|
68423
|
+
estimatedFrom
|
|
68424
|
+
};
|
|
68425
|
+
return out;
|
|
68426
|
+
}
|
|
68052
68427
|
/**
|
|
68053
|
-
*
|
|
68054
|
-
*
|
|
68055
|
-
*
|
|
68056
|
-
*
|
|
68057
|
-
* descriptor constant in the UI (ROLE_DESCRIPTOR), not stored in
|
|
68058
|
-
* `sourceInfo`.
|
|
68428
|
+
* The latency column for one device. A measured class other than the N-series
|
|
68429
|
+
* spread is returned unchanged — one class there is one measured machine.
|
|
68430
|
+
* `intel-n-series` is the N100 column; a different reported compute scales it.
|
|
68431
|
+
* An unknown class scales from the nearest reference of the same kind.
|
|
68059
68432
|
*/
|
|
68060
|
-
|
|
68061
|
-
|
|
68062
|
-
|
|
68063
|
-
|
|
68064
|
-
|
|
68065
|
-
|
|
68066
|
-
|
|
68067
|
-
|
|
68068
|
-
|
|
68069
|
-
|
|
68070
|
-
|
|
68071
|
-
|
|
68072
|
-
|
|
68073
|
-
|
|
68074
|
-
|
|
68075
|
-
|
|
68076
|
-
|
|
68077
|
-
|
|
68078
|
-
|
|
68079
|
-
|
|
68080
|
-
|
|
68081
|
-
|
|
68082
|
-
|
|
68083
|
-
|
|
68084
|
-
|
|
68085
|
-
|
|
68433
|
+
function resolveLatencyColumn(input) {
|
|
68434
|
+
const perf = input.perf ?? DETECTOR_PERF;
|
|
68435
|
+
const references = input.references ?? REFERENCE_COMPUTE;
|
|
68436
|
+
const measured = classColumn(perf, input.hardwareClass, input.accelerator);
|
|
68437
|
+
if (measured !== void 0 && input.hardwareClass !== "intel-n-series") return {
|
|
68438
|
+
column: measured,
|
|
68439
|
+
estimatedFrom: null
|
|
68440
|
+
};
|
|
68441
|
+
const score = input.report === null ? null : scoreAgainstReferences(input.report, input.accelerator, references);
|
|
68442
|
+
if (score === null) return measured === void 0 ? null : {
|
|
68443
|
+
column: measured,
|
|
68444
|
+
estimatedFrom: null
|
|
68445
|
+
};
|
|
68446
|
+
const nearest = nearestReference(input.accelerator, score, references);
|
|
68447
|
+
const base = nearest === null ? void 0 : classColumn(perf, nearest.hardwareClass, input.accelerator);
|
|
68448
|
+
if (nearest === null || base === void 0) return measured === void 0 ? null : {
|
|
68449
|
+
column: measured,
|
|
68450
|
+
estimatedFrom: null
|
|
68451
|
+
};
|
|
68452
|
+
const rawRatio = score.value / nearest.score.value;
|
|
68453
|
+
if (input.hardwareClass === nearest.hardwareClass && Math.abs(rawRatio - 1) <= .05) return {
|
|
68454
|
+
column: base,
|
|
68455
|
+
estimatedFrom: null
|
|
68456
|
+
};
|
|
68457
|
+
return {
|
|
68458
|
+
column: scaleColumn(base, clampRatio(rawRatio), nearest.hardwareClass),
|
|
68459
|
+
estimatedFrom: nearest.hardwareClass
|
|
68460
|
+
};
|
|
68461
|
+
}
|
|
68462
|
+
//#endregion
|
|
68463
|
+
//#region src/capabilities/platform-probe.cap.ts
|
|
68464
|
+
var ModelFormatSchema = zod.z.enum(require_sleep.MODEL_FORMATS);
|
|
68465
|
+
var HwAccelBackendInputSchema = zod.z.enum([
|
|
68466
|
+
"videotoolbox",
|
|
68467
|
+
"cuda",
|
|
68468
|
+
"nvdec",
|
|
68469
|
+
"vaapi",
|
|
68470
|
+
"qsv",
|
|
68471
|
+
"d3d11va",
|
|
68472
|
+
"dxva2",
|
|
68473
|
+
"amf",
|
|
68474
|
+
"vdpau",
|
|
68475
|
+
"drm",
|
|
68476
|
+
"coreml",
|
|
68477
|
+
"tensorrt",
|
|
68478
|
+
"openvino",
|
|
68479
|
+
"directml",
|
|
68480
|
+
"webgpu",
|
|
68481
|
+
"none"
|
|
68482
|
+
]).nullable().optional();
|
|
68483
|
+
var HwAccelResolutionSchema = zod.z.object({
|
|
68484
|
+
preferred: zod.z.array(zod.z.string()).readonly(),
|
|
68485
|
+
rationale: zod.z.string()
|
|
68486
|
+
});
|
|
68487
|
+
var HardwareEncoderIdSchema = zod.z.enum([
|
|
68488
|
+
"h264_videotoolbox",
|
|
68489
|
+
"hevc_videotoolbox",
|
|
68490
|
+
"h264_vaapi",
|
|
68491
|
+
"hevc_vaapi",
|
|
68492
|
+
"h264_nvenc",
|
|
68493
|
+
"hevc_nvenc",
|
|
68494
|
+
"h264_qsv",
|
|
68495
|
+
"hevc_qsv",
|
|
68496
|
+
"h264_amf",
|
|
68497
|
+
"hevc_amf",
|
|
68498
|
+
"libx264",
|
|
68499
|
+
"libx265"
|
|
68500
|
+
]);
|
|
68501
|
+
var HardwareEncoderProbeSchema = zod.z.object({
|
|
68502
|
+
encoder: HardwareEncoderIdSchema,
|
|
68503
|
+
codec: zod.z.enum(["H264", "H265"]),
|
|
68504
|
+
family: zod.z.enum([
|
|
68505
|
+
"videotoolbox",
|
|
68506
|
+
"vaapi",
|
|
68507
|
+
"nvenc",
|
|
68508
|
+
"qsv",
|
|
68509
|
+
"amf",
|
|
68510
|
+
"software"
|
|
68511
|
+
]),
|
|
68512
|
+
available: zod.z.boolean(),
|
|
68513
|
+
reason: zod.z.string().optional()
|
|
68514
|
+
});
|
|
68515
|
+
zod.z.object({
|
|
68516
|
+
encoders: zod.z.array(HardwareEncoderProbeSchema).readonly(),
|
|
68517
|
+
defaultH264: HardwareEncoderIdSchema,
|
|
68518
|
+
defaultH265: HardwareEncoderIdSchema,
|
|
68519
|
+
probedAt: zod.z.number()
|
|
68520
|
+
});
|
|
68521
|
+
zod.z.object({
|
|
68522
|
+
methods: zod.z.array(zod.z.string()).readonly(),
|
|
68523
|
+
probedAt: zod.z.number()
|
|
68524
|
+
});
|
|
68525
|
+
var HardwarePlatformSchema = zod.z.enum([
|
|
68526
|
+
"darwin",
|
|
68527
|
+
"linux",
|
|
68528
|
+
"win32"
|
|
68529
|
+
]);
|
|
68530
|
+
var HardwareArchSchema = zod.z.enum(["arm64", "x64"]);
|
|
68531
|
+
var GpuInfoSchema = zod.z.object({
|
|
68532
|
+
type: zod.z.enum([
|
|
68533
|
+
"nvidia",
|
|
68534
|
+
"amd",
|
|
68535
|
+
"intel",
|
|
68536
|
+
"apple"
|
|
68537
|
+
]),
|
|
68538
|
+
name: zod.z.string(),
|
|
68539
|
+
memoryMB: zod.z.number().optional()
|
|
68540
|
+
});
|
|
68541
|
+
var NpuInfoSchema = zod.z.object({
|
|
68542
|
+
type: zod.z.enum([
|
|
68543
|
+
"apple-ane",
|
|
68544
|
+
"intel-npu",
|
|
68545
|
+
"amd-xdna"
|
|
68546
|
+
]),
|
|
68547
|
+
name: zod.z.string().optional(),
|
|
68548
|
+
generation: zod.z.enum(["xdna1", "xdna2"]).optional()
|
|
68549
|
+
});
|
|
68550
|
+
var CoralBusSchema = zod.z.enum(["usb", "pci"]);
|
|
68551
|
+
var CoralInfoSchema = zod.z.object({
|
|
68552
|
+
type: zod.z.literal("coral-edgetpu"),
|
|
68553
|
+
/** Legacy single-bus report (older nodes) — read via `coralUsableUnits`. */
|
|
68554
|
+
bus: zod.z.string().optional(),
|
|
68555
|
+
/** Legacy USB unit count (older nodes) — read via `coralUsableUnits`. */
|
|
68556
|
+
count: zod.z.number().int().nonnegative().optional(),
|
|
68557
|
+
/** Usable units per bus — each one is its own `edgetpu:<bus>[:N]` device. */
|
|
68558
|
+
units: zod.z.array(zod.z.object({
|
|
68559
|
+
bus: CoralBusSchema,
|
|
68560
|
+
count: zod.z.number().int().nonnegative()
|
|
68561
|
+
})).readonly().optional(),
|
|
68562
|
+
/** Present-but-unusable units, each with its typed reason (never "absent"). */
|
|
68563
|
+
unavailable: zod.z.array(zod.z.object({
|
|
68564
|
+
bus: CoralBusSchema,
|
|
68565
|
+
count: zod.z.number().int().nonnegative(),
|
|
68566
|
+
reason: zod.z.enum(["driver-missing", "device-not-accessible"])
|
|
68567
|
+
})).readonly().optional()
|
|
68568
|
+
});
|
|
68569
|
+
var HardwareInfoSchema = zod.z.object({
|
|
68570
|
+
platform: HardwarePlatformSchema,
|
|
68571
|
+
arch: HardwareArchSchema,
|
|
68572
|
+
cpuModel: zod.z.string(),
|
|
68573
|
+
cpuCores: zod.z.number(),
|
|
68574
|
+
totalRAM_MB: zod.z.number(),
|
|
68575
|
+
availableRAM_MB: zod.z.number(),
|
|
68576
|
+
gpu: GpuInfoSchema.nullable(),
|
|
68577
|
+
npu: NpuInfoSchema.nullable(),
|
|
68578
|
+
coral: CoralInfoSchema.nullable().optional(),
|
|
68579
|
+
/**
|
|
68580
|
+
* Per-accelerator compute (D930). Omitted by a node that has not probed it.
|
|
68581
|
+
* `null` fields inside an entry are a failed query, never a zero.
|
|
68582
|
+
*/
|
|
68583
|
+
accelerators: zod.z.array(AcceleratorComputeSchema).readonly().optional()
|
|
68584
|
+
});
|
|
68585
|
+
var PlatformScoreSchema = zod.z.object({
|
|
68586
|
+
runtime: zod.z.literal("python"),
|
|
68587
|
+
backend: zod.z.string(),
|
|
68588
|
+
format: zod.z.enum([
|
|
68589
|
+
"onnx",
|
|
68590
|
+
"coreml",
|
|
68591
|
+
"openvino",
|
|
68592
|
+
"tflite"
|
|
68593
|
+
]),
|
|
68594
|
+
score: zod.z.number(),
|
|
68595
|
+
reason: zod.z.string(),
|
|
68596
|
+
available: zod.z.boolean()
|
|
68597
|
+
});
|
|
68598
|
+
var InferenceDeviceDescriptorSchema = zod.z.object({
|
|
68599
|
+
key: zod.z.string(),
|
|
68600
|
+
backend: zod.z.string(),
|
|
68601
|
+
device: zod.z.string(),
|
|
68602
|
+
format: ModelFormatSchema,
|
|
68603
|
+
runtime: zod.z.literal("python"),
|
|
68604
|
+
score: zod.z.number(),
|
|
68605
|
+
available: zod.z.boolean()
|
|
68606
|
+
});
|
|
68607
|
+
var PlatformCapabilitiesSchema = zod.z.object({
|
|
68608
|
+
hardware: HardwareInfoSchema,
|
|
68609
|
+
scores: zod.z.array(PlatformScoreSchema).readonly(),
|
|
68610
|
+
bestScore: PlatformScoreSchema,
|
|
68611
|
+
pythonPath: zod.z.string().nullable(),
|
|
68612
|
+
devices: zod.z.array(InferenceDeviceDescriptorSchema).readonly()
|
|
68613
|
+
});
|
|
68614
|
+
var ModelRequirementSchema = zod.z.object({
|
|
68615
|
+
modelId: zod.z.string(),
|
|
68616
|
+
name: zod.z.string(),
|
|
68617
|
+
minRAM_MB: zod.z.number(),
|
|
68618
|
+
accuracyScore: zod.z.number(),
|
|
68619
|
+
formats: zod.z.array(zod.z.string()).readonly()
|
|
68620
|
+
});
|
|
68621
|
+
var ResolvedInferenceConfigSchema = zod.z.object({
|
|
68622
|
+
modelId: zod.z.string(),
|
|
68623
|
+
runtime: zod.z.literal("python"),
|
|
68624
|
+
backend: zod.z.string(),
|
|
68625
|
+
format: ModelFormatSchema,
|
|
68626
|
+
reason: zod.z.string()
|
|
68627
|
+
});
|
|
68628
|
+
/**
|
|
68629
|
+
* The answer of an accelerator rescan (D919): the node's device list after
|
|
68630
|
+
* the re-probe, what changed, and the evidence that decided the Coral answer
|
|
68631
|
+
* (`usbBus` — could the USB bus be read at all; `decidedBy` — USB ids, the PCI
|
|
68632
|
+
* bus, the installed-delegate fallback, or nothing).
|
|
68633
|
+
*/
|
|
68634
|
+
var AcceleratorRescanSchema = zod.z.object({
|
|
68635
|
+
devices: zod.z.array(InferenceDeviceDescriptorSchema).readonly(),
|
|
68636
|
+
appeared: zod.z.array(zod.z.string()).readonly(),
|
|
68637
|
+
disappeared: zod.z.array(zod.z.string()).readonly(),
|
|
68638
|
+
usbBus: zod.z.enum(["observed", "unobservable"]),
|
|
68639
|
+
decidedBy: zod.z.enum([
|
|
68640
|
+
"usb-ids",
|
|
68641
|
+
"pci",
|
|
68642
|
+
"delegate-fallback",
|
|
68643
|
+
"none"
|
|
68644
|
+
])
|
|
68645
|
+
});
|
|
68646
|
+
var platformProbeCapability = {
|
|
68647
|
+
name: "platform-probe",
|
|
68648
|
+
scope: "system",
|
|
68649
|
+
mode: "singleton",
|
|
68650
|
+
methods: {
|
|
68651
|
+
getCapabilities: require_sleep.method(zod.z.void(), PlatformCapabilitiesSchema),
|
|
68652
|
+
getHardware: require_sleep.method(zod.z.void(), HardwareInfoSchema),
|
|
68653
|
+
resolveInferenceConfig: require_sleep.method(zod.z.object({ requirements: zod.z.array(ModelRequirementSchema).readonly() }), ResolvedInferenceConfigSchema),
|
|
68654
|
+
resolveHwAccel: require_sleep.method(zod.z.object({ prefer: HwAccelBackendInputSchema }), HwAccelResolutionSchema),
|
|
68655
|
+
/**
|
|
68656
|
+
* Re-probe this node's accelerators NOW (Coral USB ids + PCIe apex units;
|
|
68657
|
+
* `/sys` and `/dev` reads, no model load) and rebuild the device list that
|
|
68658
|
+
* `getCapabilities` serves. The same re-probe runs every 60 s on its own;
|
|
68659
|
+
* this is the operator's "rescan inference devices". Target a node with the
|
|
68660
|
+
* `{ nodeId }` routing input, like `getCapabilities`.
|
|
68661
|
+
*/
|
|
68662
|
+
rescanAccelerators: require_sleep.method(zod.z.void(), AcceleratorRescanSchema, {
|
|
68663
|
+
kind: "mutation",
|
|
68664
|
+
auth: "admin"
|
|
68665
|
+
})
|
|
68666
|
+
}
|
|
68667
|
+
};
|
|
68668
|
+
//#endregion
|
|
68669
|
+
//#region src/capabilities/power-meter.cap.ts
|
|
68670
|
+
/**
|
|
68671
|
+
* Multi-metric electrical meter. One slice can carry any combination
|
|
68672
|
+
* of instantaneous power (W), cumulative energy (kWh), voltage (V),
|
|
68673
|
+
* and current (A) — all fields optional so a single-metric source
|
|
68674
|
+
* (HA `sensor` with `device_class: power`) populates only what it
|
|
68675
|
+
* has and aggregators (energy dashboards, billing exports) compose
|
|
68676
|
+
* across providers without needing one cap per metric.
|
|
68677
|
+
*
|
|
68678
|
+
* Trade-off acknowledged: a HomeKit export that wants to surface
|
|
68679
|
+
* "power" and "energy" as separate services has to crack open the
|
|
68680
|
+
* slice and emit two characteristics from one cap. Worth it to avoid
|
|
68681
|
+
* exploding the cap catalog with `power-w` / `energy-kwh` /
|
|
68682
|
+
* `voltage-v` / `current-a` quartets that always travel together
|
|
68683
|
+
* in real-world devices.
|
|
68684
|
+
*/
|
|
68685
|
+
var PowerMeterStatusSchema = zod.z.object({
|
|
68686
|
+
/** Instantaneous power draw in watts. */
|
|
68687
|
+
watts: zod.z.number().optional(),
|
|
68688
|
+
/** Cumulative energy in kilowatt-hours since the meter was reset. */
|
|
68689
|
+
kwhTotal: zod.z.number().optional(),
|
|
68690
|
+
/** Voltage in volts. */
|
|
68691
|
+
volts: zod.z.number().optional(),
|
|
68692
|
+
/** Current in amperes. */
|
|
68693
|
+
amps: zod.z.number().optional(),
|
|
68694
|
+
/** Apparent power in volt-amperes (W × power-factor reciprocal). */
|
|
68695
|
+
va: zod.z.number().optional(),
|
|
68696
|
+
/** Power factor (0..1) if reported. */
|
|
68697
|
+
powerFactor: zod.z.number().min(0).max(1).optional(),
|
|
68698
|
+
/** Ms epoch when the slice was last updated. */
|
|
68699
|
+
lastFetchedAt: zod.z.number(),
|
|
68700
|
+
/** Live display unit of the single metric this slice carries (e.g. HA
|
|
68701
|
+
* `attributes.unit_of_measurement` → 'V' / 'A' / 'W' / 'kW' / 'kWh').
|
|
68702
|
+
* Each upstream `sensor.*` entity surfaces ONE device_class, so one
|
|
68703
|
+
* unit per slice is unambiguous. The UI prefers this over the role's
|
|
68704
|
+
* canonical unit so a 'kW' / 'Wh' feed renders verbatim. */
|
|
68705
|
+
unit: zod.z.string().optional(),
|
|
68706
|
+
/** Suggested decimal places for numeric display.
|
|
68707
|
+
* Populated live from the upstream source when provided (e.g. HA
|
|
68708
|
+
* `attributes.suggested_display_precision`). Falls back to
|
|
68709
|
+
* auto-formatting when absent. */
|
|
68710
|
+
precision: zod.z.number().int().min(0).max(10).optional()
|
|
68711
|
+
});
|
|
68712
|
+
var powerMeterCapability = {
|
|
68713
|
+
name: "power-meter",
|
|
68714
|
+
scope: "device",
|
|
68715
|
+
deviceNative: true,
|
|
68716
|
+
mode: "singleton",
|
|
68717
|
+
deviceTypes: [require_err_msg.DeviceType.Sensor],
|
|
68718
|
+
methods: {},
|
|
68719
|
+
status: {
|
|
68720
|
+
schema: PowerMeterStatusSchema,
|
|
68721
|
+
kind: "push"
|
|
68722
|
+
},
|
|
68723
|
+
runtimeState: PowerMeterStatusSchema,
|
|
68724
|
+
/**
|
|
68725
|
+
* Runtime-state durability: **restored** — as `numeric-sensor`; `kwhTotal` is an accumulator whose restored value is the baseline.
|
|
68726
|
+
*
|
|
68727
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
68728
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
68729
|
+
*/
|
|
68730
|
+
durability: "restored",
|
|
68731
|
+
/**
|
|
68732
|
+
* History (D850): recorded series. Gauges are floored at 30 s; `kwhTotal` is the one monotonic meter (counter).
|
|
68733
|
+
*/
|
|
68734
|
+
history: { fields: {
|
|
68735
|
+
watts: {
|
|
68736
|
+
kind: "gauge",
|
|
68737
|
+
unit: "W",
|
|
68738
|
+
deadband: 1,
|
|
68739
|
+
minIntervalMs: 3e4
|
|
68740
|
+
},
|
|
68741
|
+
kwhTotal: {
|
|
68742
|
+
kind: "counter",
|
|
68743
|
+
unit: "kWh",
|
|
68744
|
+
deadband: .01,
|
|
68745
|
+
minIntervalMs: 6e4
|
|
68746
|
+
},
|
|
68747
|
+
volts: {
|
|
68748
|
+
kind: "gauge",
|
|
68749
|
+
unit: "V",
|
|
68750
|
+
deadband: 1,
|
|
68751
|
+
minIntervalMs: 3e4
|
|
68752
|
+
},
|
|
68753
|
+
amps: {
|
|
68754
|
+
kind: "gauge",
|
|
68755
|
+
unit: "A",
|
|
68756
|
+
deadband: .05,
|
|
68757
|
+
minIntervalMs: 3e4
|
|
68758
|
+
},
|
|
68759
|
+
va: {
|
|
68760
|
+
kind: "gauge",
|
|
68761
|
+
unit: "VA",
|
|
68762
|
+
deadband: 1,
|
|
68763
|
+
minIntervalMs: 3e4
|
|
68764
|
+
},
|
|
68765
|
+
powerFactor: {
|
|
68766
|
+
kind: "gauge",
|
|
68767
|
+
unit: "ratio",
|
|
68768
|
+
deadband: .01,
|
|
68769
|
+
minIntervalMs: 6e4
|
|
68770
|
+
}
|
|
68771
|
+
} },
|
|
68772
|
+
/** Clock fields: written, but excluded from the compare that decides
|
|
68773
|
+
* whether persisting is worth a SQLite commit. */
|
|
68774
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
68775
|
+
};
|
|
68776
|
+
//#endregion
|
|
68777
|
+
//#region src/capabilities/presence.cap.ts
|
|
68778
|
+
/**
|
|
68779
|
+
* Presence cap. Models HA `person.*` and `device_tracker.*` entities
|
|
68780
|
+
* on `DeviceType.Presence`. Read-only — no setters: presence is
|
|
68781
|
+
* derived from upstream signals (mobile-app location, router DHCP
|
|
68782
|
+
* lease, Bluetooth beacon, manual zone change) and pushed into the
|
|
68783
|
+
* slice.
|
|
68784
|
+
*
|
|
68785
|
+
* `state` follows HA's canonical values — `home` / `not_home` /
|
|
68786
|
+
* `<zone-name>` for any other named zone. The zone name is freeform
|
|
68787
|
+
* because users define arbitrary zones (`work`, `gym`, `school`).
|
|
68788
|
+
*
|
|
68789
|
+
* GPS coordinates are nullable — tracker types without location
|
|
68790
|
+
* (Bluetooth scanner, MQTT presence) omit them. The presence of
|
|
68791
|
+
* GPS data is signaled by `DeviceFeature.PresenceGps` for fast
|
|
68792
|
+
* UI map-pin rendering.
|
|
68793
|
+
*/
|
|
68794
|
+
var GpsLocationSchema = zod.z.object({
|
|
68795
|
+
/** Latitude in decimal degrees, -90..90. */
|
|
68796
|
+
latitude: zod.z.number().min(-90).max(90),
|
|
68797
|
+
/** Longitude in decimal degrees, -180..180. */
|
|
68798
|
+
longitude: zod.z.number().min(-180).max(180),
|
|
68799
|
+
/** Reported accuracy in meters (lower = better). */
|
|
68800
|
+
accuracyMeters: zod.z.number().nonnegative()
|
|
68801
|
+
});
|
|
68802
|
+
var PresenceStatusSchema = zod.z.object({
|
|
68803
|
+
/** `home` / `not_home` / any user-defined zone name. */
|
|
68804
|
+
state: zod.z.string(),
|
|
68805
|
+
/** Optional textual location label (zone name, city, address). Null
|
|
68806
|
+
* when only the binary state is known. */
|
|
68807
|
+
location: zod.z.string().nullable(),
|
|
68808
|
+
/** GPS coordinates when available. Null otherwise. */
|
|
68809
|
+
gps: GpsLocationSchema.nullable(),
|
|
68810
|
+
/** Optional battery level of the tracking device (0..100). Null
|
|
68811
|
+
* when not reported. */
|
|
68812
|
+
batteryPercent: zod.z.number().min(0).max(100).nullable(),
|
|
68813
|
+
/** Ms epoch when the slice was last updated. */
|
|
68814
|
+
lastChangedAt: zod.z.number()
|
|
68815
|
+
});
|
|
68816
|
+
var presenceCapability = {
|
|
68817
|
+
name: "presence",
|
|
68818
|
+
scope: "device",
|
|
68819
|
+
deviceNative: true,
|
|
68820
|
+
mode: "singleton",
|
|
68821
|
+
deviceTypes: [require_err_msg.DeviceType.Presence],
|
|
68822
|
+
methods: {},
|
|
68823
|
+
status: {
|
|
68824
|
+
schema: PresenceStatusSchema,
|
|
68825
|
+
kind: "push"
|
|
68826
|
+
},
|
|
68827
|
+
/**
|
|
68828
|
+
* Runtime-state slice — mirrored by the kernel. UI presence card
|
|
68829
|
+
* reads `state` + `location` for the text label; iff `gps !== null`
|
|
68830
|
+
* the map pin is rendered (use `DeviceFeature.PresenceGps` for the
|
|
68831
|
+
* pre-fetch fast-path check).
|
|
68832
|
+
*/
|
|
68833
|
+
runtimeState: PresenceStatusSchema,
|
|
68834
|
+
/**
|
|
68835
|
+
* Runtime-state durability: **restored** — occupancy-relevant: the restored state is what an occupancy rule compares the first post-restart observation against.
|
|
68836
|
+
*
|
|
68837
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
68838
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
68839
|
+
*/
|
|
68840
|
+
durability: "restored",
|
|
68841
|
+
/**
|
|
68842
|
+
* History (D850): recorded series.
|
|
68843
|
+
*/
|
|
68844
|
+
history: { fields: { batteryPercent: {
|
|
68845
|
+
kind: "gauge",
|
|
68846
|
+
unit: "%",
|
|
68847
|
+
deadband: 1
|
|
68848
|
+
} } },
|
|
68849
|
+
/** Clock fields: written, but excluded from the compare that decides
|
|
68850
|
+
* whether persisting is worth a SQLite commit. */
|
|
68851
|
+
volatileStateFields: ["lastChangedAt"]
|
|
68852
|
+
};
|
|
68853
|
+
//#endregion
|
|
68854
|
+
//#region src/capabilities/pressure-sensor.cap.ts
|
|
68855
|
+
/**
|
|
68856
|
+
* Atmospheric pressure reading in hectopascals. Drives Home Assistant
|
|
68857
|
+
* `sensor` entries with `device_class: pressure`.
|
|
68858
|
+
*
|
|
68859
|
+
* Unit normalisation: hPa. The canonical display unit (`hPa`) is a
|
|
68860
|
+
* descriptor constant in the UI (ROLE_DESCRIPTOR), not stored in
|
|
68861
|
+
* `sourceInfo`.
|
|
68862
|
+
*/
|
|
68863
|
+
var PressureSensorStatusSchema = zod.z.object({
|
|
68864
|
+
/** Current pressure in hPa. */
|
|
68865
|
+
hpa: zod.z.number(),
|
|
68866
|
+
/** Ms epoch when the slice was last updated. */
|
|
68867
|
+
lastFetchedAt: zod.z.number(),
|
|
68868
|
+
/** Live display unit from the upstream source (e.g. HA
|
|
68869
|
+
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
68870
|
+
* role's canonical unit. Absent → fall back to the canonical unit. */
|
|
68871
|
+
unit: zod.z.string().optional(),
|
|
68872
|
+
/** Suggested decimal places for numeric display.
|
|
68873
|
+
* Populated live from the upstream source when provided (e.g. HA
|
|
68874
|
+
* `attributes.suggested_display_precision`). Falls back to
|
|
68875
|
+
* auto-formatting when absent. */
|
|
68876
|
+
precision: zod.z.number().int().min(0).max(10).optional()
|
|
68877
|
+
});
|
|
68878
|
+
var pressureSensorCapability = {
|
|
68879
|
+
name: "pressure-sensor",
|
|
68880
|
+
scope: "device",
|
|
68881
|
+
deviceNative: true,
|
|
68882
|
+
mode: "singleton",
|
|
68883
|
+
deviceTypes: [require_err_msg.DeviceType.Sensor],
|
|
68884
|
+
methods: {},
|
|
68885
|
+
status: {
|
|
68886
|
+
schema: PressureSensorStatusSchema,
|
|
68887
|
+
kind: "push"
|
|
68888
|
+
},
|
|
68086
68889
|
runtimeState: PressureSensorStatusSchema,
|
|
68087
68890
|
/**
|
|
68088
68891
|
* Runtime-state durability: **restored** — as `numeric-sensor`.
|
|
@@ -68786,6 +69589,43 @@ var ptzAutotrackCapability = {
|
|
|
68786
69589
|
history: "none"
|
|
68787
69590
|
};
|
|
68788
69591
|
//#endregion
|
|
69592
|
+
//#region src/capabilities/running-state.cap.ts
|
|
69593
|
+
var RunningStateSourceSchema = zod.z.enum(["native", "assigned"]);
|
|
69594
|
+
var RunningStateStatusSchema = zod.z.object({
|
|
69595
|
+
/** null = unknown — never coerced to false (D393). */
|
|
69596
|
+
running: zod.z.boolean().nullable(),
|
|
69597
|
+
/** Epoch ms the current boolean began; null while `running` is null. */
|
|
69598
|
+
since: zod.z.number().nullable(),
|
|
69599
|
+
source: RunningStateSourceSchema,
|
|
69600
|
+
phase: zod.z.string().nullable(),
|
|
69601
|
+
lastFetchedAt: zod.z.number()
|
|
69602
|
+
});
|
|
69603
|
+
var RunningFlipEventDataSchema = zod.z.object({
|
|
69604
|
+
deviceId: zod.z.number(),
|
|
69605
|
+
since: zod.z.number().nullable(),
|
|
69606
|
+
phase: zod.z.string().nullable()
|
|
69607
|
+
});
|
|
69608
|
+
var runningStateCapability = {
|
|
69609
|
+
name: "running-state",
|
|
69610
|
+
scope: "device",
|
|
69611
|
+
deviceNative: true,
|
|
69612
|
+
mode: "singleton",
|
|
69613
|
+
deviceTypes: Object.values(require_err_msg.DeviceType),
|
|
69614
|
+
methods: {},
|
|
69615
|
+
events: {
|
|
69616
|
+
onStarted: { data: RunningFlipEventDataSchema },
|
|
69617
|
+
onFinished: { data: RunningFlipEventDataSchema }
|
|
69618
|
+
},
|
|
69619
|
+
status: {
|
|
69620
|
+
schema: RunningStateStatusSchema,
|
|
69621
|
+
kind: "push"
|
|
69622
|
+
},
|
|
69623
|
+
runtimeState: RunningStateStatusSchema,
|
|
69624
|
+
durability: "restored",
|
|
69625
|
+
history: { fields: { running: { kind: "boolean" } } },
|
|
69626
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
69627
|
+
};
|
|
69628
|
+
//#endregion
|
|
68789
69629
|
//#region src/capabilities/reboot.cap.ts
|
|
68790
69630
|
/**
|
|
68791
69631
|
* reboot — device-scoped capability for "soft" device reboots (firmware
|
|
@@ -71686,7 +72526,20 @@ var ZoneRuleSchema = zod.z.object({
|
|
|
71686
72526
|
* Defaults to `true` so operators creating a rule via the UI
|
|
71687
72527
|
* see it active immediately.
|
|
71688
72528
|
*/
|
|
71689
|
-
enabled: zod.z.boolean().default(true)
|
|
72529
|
+
enabled: zod.z.boolean().default(true),
|
|
72530
|
+
/**
|
|
72531
|
+
* Package stage only. When true, a package candidate may open from a
|
|
72532
|
+
* scene difference in this rule's zone after a person leaves. The
|
|
72533
|
+
* package model, when it boxes something overlapping, is extra evidence
|
|
72534
|
+
* and is not the gate. Absent or false keeps today's model path.
|
|
72535
|
+
*/
|
|
72536
|
+
detectBySceneDifference: zod.z.boolean().optional(),
|
|
72537
|
+
/**
|
|
72538
|
+
* Package stage only. When true, a promoted package is shown to the
|
|
72539
|
+
* `llm` cap before the delivery event is emitted. Absent or false emits
|
|
72540
|
+
* as today. Usable with or without `detectBySceneDifference`.
|
|
72541
|
+
*/
|
|
72542
|
+
confirmWithLlm: zod.z.boolean().optional()
|
|
71690
72543
|
});
|
|
71691
72544
|
/**
|
|
71692
72545
|
* Convenience array schema — used by addon device-settings
|
|
@@ -74999,6 +75852,215 @@ var PIPELINE_OWNER_CAPABILITY_NAMES = [
|
|
|
74999
75852
|
"pipeline-runner"
|
|
75000
75853
|
];
|
|
75001
75854
|
//#endregion
|
|
75855
|
+
//#region src/running-state/assignment.ts
|
|
75856
|
+
function constant(literal) {
|
|
75857
|
+
return {
|
|
75858
|
+
kind: "expression",
|
|
75859
|
+
expr: literal,
|
|
75860
|
+
bindings: {}
|
|
75861
|
+
};
|
|
75862
|
+
}
|
|
75863
|
+
function fromRead(source, cap, fieldPath) {
|
|
75864
|
+
return {
|
|
75865
|
+
kind: "from",
|
|
75866
|
+
source,
|
|
75867
|
+
cap,
|
|
75868
|
+
fieldPath
|
|
75869
|
+
};
|
|
75870
|
+
}
|
|
75871
|
+
function sortKeys(value) {
|
|
75872
|
+
if (Array.isArray(value)) return value.map(sortKeys);
|
|
75873
|
+
if (typeof value !== "object" || value === null) return value;
|
|
75874
|
+
return Object.fromEntries(Object.entries(value).toSorted(([a], [b]) => a.localeCompare(b)).map(([key, inner]) => [key, sortKeys(inner)]));
|
|
75875
|
+
}
|
|
75876
|
+
function sameFeature(a, b) {
|
|
75877
|
+
return JSON.stringify(sortKeys(a)) === JSON.stringify(sortKeys(b));
|
|
75878
|
+
}
|
|
75879
|
+
function booleanRunningField(input) {
|
|
75880
|
+
if (input.source.kind !== "boolean") return null;
|
|
75881
|
+
const read = fromRead(input.source.source, input.source.cap, input.source.fieldPath);
|
|
75882
|
+
if (!input.invert) return read;
|
|
75883
|
+
return {
|
|
75884
|
+
kind: "expression",
|
|
75885
|
+
expr: "!x",
|
|
75886
|
+
bindings: { x: read }
|
|
75887
|
+
};
|
|
75888
|
+
}
|
|
75889
|
+
function sinceField(input) {
|
|
75890
|
+
if (input.source.kind === "boolean" && input.changedAtField !== null) return fromRead(input.source.source, input.source.cap, input.changedAtField);
|
|
75891
|
+
return {
|
|
75892
|
+
kind: "expression",
|
|
75893
|
+
expr: "latchedAt(running)",
|
|
75894
|
+
bindings: { running: fromRead(input.target, "running-state", "running") }
|
|
75895
|
+
};
|
|
75896
|
+
}
|
|
75897
|
+
function powerRunningField(input) {
|
|
75898
|
+
if (input.source.kind !== "number") return null;
|
|
75899
|
+
if (!Number.isFinite(input.thresholdWatts) || !Number.isFinite(input.startDelayMs) || !Number.isFinite(input.stopDelayMs) || input.thresholdWatts < 0 || input.startDelayMs < 0 || input.stopDelayMs < 0) return null;
|
|
75900
|
+
const read = fromRead(input.source.source, input.source.cap, input.source.fieldPath);
|
|
75901
|
+
return {
|
|
75902
|
+
kind: "expression",
|
|
75903
|
+
expr: `dwell(x >= ${input.thresholdWatts}, ${input.startDelayMs}, ${input.stopDelayMs})`,
|
|
75904
|
+
bindings: { x: read }
|
|
75905
|
+
};
|
|
75906
|
+
}
|
|
75907
|
+
function runningAssignmentFeature(input) {
|
|
75908
|
+
const running = input.source.kind === "number" ? powerRunningField(input) : booleanRunningField(input);
|
|
75909
|
+
if (running === null) return null;
|
|
75910
|
+
return {
|
|
75911
|
+
kind: "fields",
|
|
75912
|
+
cap: "running-state",
|
|
75913
|
+
fields: {
|
|
75914
|
+
source: constant("\"assigned\""),
|
|
75915
|
+
phase: constant("null"),
|
|
75916
|
+
lastFetchedAt: constant("now"),
|
|
75917
|
+
running,
|
|
75918
|
+
since: sinceField(input)
|
|
75919
|
+
}
|
|
75920
|
+
};
|
|
75921
|
+
}
|
|
75922
|
+
function booleanSourceFromRunningField(source) {
|
|
75923
|
+
if (source === void 0) return null;
|
|
75924
|
+
if (source.kind === "from") return {
|
|
75925
|
+
source: source.source,
|
|
75926
|
+
cap: source.cap,
|
|
75927
|
+
fieldPath: source.fieldPath,
|
|
75928
|
+
kind: "boolean"
|
|
75929
|
+
};
|
|
75930
|
+
if (source.kind !== "expression" || source.expr !== "!x") return null;
|
|
75931
|
+
const x = source.bindings["x"];
|
|
75932
|
+
if (x === void 0 || x.kind !== "from") return null;
|
|
75933
|
+
return {
|
|
75934
|
+
source: x.source,
|
|
75935
|
+
cap: x.cap,
|
|
75936
|
+
fieldPath: x.fieldPath,
|
|
75937
|
+
kind: "boolean"
|
|
75938
|
+
};
|
|
75939
|
+
}
|
|
75940
|
+
function recoverPowerRunning(running) {
|
|
75941
|
+
if (running === void 0 || running.kind !== "expression") return null;
|
|
75942
|
+
const match = /^dwell\(x >= ([\d.]+), (\d+), (\d+)\)$/.exec(running.expr);
|
|
75943
|
+
if (match === null) return null;
|
|
75944
|
+
const x = running.bindings["x"];
|
|
75945
|
+
if (x === void 0 || x.kind !== "from") return null;
|
|
75946
|
+
const thresholdWatts = Number(match[1]);
|
|
75947
|
+
const startDelayMs = Number(match[2]);
|
|
75948
|
+
const stopDelayMs = Number(match[3]);
|
|
75949
|
+
if (!Number.isFinite(thresholdWatts) || !Number.isFinite(startDelayMs) || !Number.isFinite(stopDelayMs)) return null;
|
|
75950
|
+
return {
|
|
75951
|
+
source: {
|
|
75952
|
+
...x,
|
|
75953
|
+
kind: "number"
|
|
75954
|
+
},
|
|
75955
|
+
invert: false,
|
|
75956
|
+
thresholdWatts,
|
|
75957
|
+
startDelayMs,
|
|
75958
|
+
stopDelayMs
|
|
75959
|
+
};
|
|
75960
|
+
}
|
|
75961
|
+
/** Recover what {@link runningAssignmentFeature} wrote, else null. */
|
|
75962
|
+
function runningAssignmentRecovered(stored, target) {
|
|
75963
|
+
if (stored.cap !== "running-state" || stored.kind !== "fields") return null;
|
|
75964
|
+
const power = recoverPowerRunning(stored.fields["running"]);
|
|
75965
|
+
if (power !== null) return power;
|
|
75966
|
+
const booleanSource = booleanSourceFromRunningField(stored.fields["running"]);
|
|
75967
|
+
if (booleanSource === null) return null;
|
|
75968
|
+
const since = stored.fields["since"];
|
|
75969
|
+
let changedAtField = null;
|
|
75970
|
+
if (since?.kind === "from" && since.cap === booleanSource.cap && since.source.addonId === booleanSource.source.addonId && since.source.stableId === booleanSource.source.stableId) changedAtField = since.fieldPath;
|
|
75971
|
+
const rebuilt = runningAssignmentFeature({
|
|
75972
|
+
mode: "replace",
|
|
75973
|
+
target,
|
|
75974
|
+
source: booleanSource,
|
|
75975
|
+
changedAtField,
|
|
75976
|
+
invert: stored.fields["running"]?.kind === "expression" && stored.fields["running"].expr === "!x",
|
|
75977
|
+
thresholdWatts: 0,
|
|
75978
|
+
startDelayMs: 0,
|
|
75979
|
+
stopDelayMs: 0
|
|
75980
|
+
});
|
|
75981
|
+
if (rebuilt === null) return null;
|
|
75982
|
+
const { enabled: _e, ...rest } = stored;
|
|
75983
|
+
if (!sameFeature(rest, rebuilt)) return null;
|
|
75984
|
+
return {
|
|
75985
|
+
source: booleanSource,
|
|
75986
|
+
invert: stored.fields["running"]?.kind === "expression" && stored.fields["running"].expr === "!x",
|
|
75987
|
+
thresholdWatts: 0,
|
|
75988
|
+
startDelayMs: 0,
|
|
75989
|
+
stopDelayMs: 0
|
|
75990
|
+
};
|
|
75991
|
+
}
|
|
75992
|
+
function isRunningAssignmentFeature(stored, mode, target) {
|
|
75993
|
+
const recovered = runningAssignmentRecovered(stored, target);
|
|
75994
|
+
if (recovered === null) return false;
|
|
75995
|
+
const rebuilt = recovered.source.kind === "number" ? runningAssignmentFeature({
|
|
75996
|
+
mode,
|
|
75997
|
+
target,
|
|
75998
|
+
source: recovered.source,
|
|
75999
|
+
changedAtField: null,
|
|
76000
|
+
invert: false,
|
|
76001
|
+
thresholdWatts: recovered.thresholdWatts,
|
|
76002
|
+
startDelayMs: recovered.startDelayMs,
|
|
76003
|
+
stopDelayMs: recovered.stopDelayMs
|
|
76004
|
+
}) : runningAssignmentFeature({
|
|
76005
|
+
mode,
|
|
76006
|
+
target,
|
|
76007
|
+
source: recovered.source,
|
|
76008
|
+
changedAtField: stored.fields["since"]?.kind === "from" ? stored.fields["since"].fieldPath : null,
|
|
76009
|
+
invert: recovered.invert,
|
|
76010
|
+
thresholdWatts: 0,
|
|
76011
|
+
startDelayMs: 0,
|
|
76012
|
+
stopDelayMs: 0
|
|
76013
|
+
});
|
|
76014
|
+
if (rebuilt === null) return false;
|
|
76015
|
+
const { enabled: _enabled, ...rest } = stored;
|
|
76016
|
+
return sameFeature(rest, rebuilt);
|
|
76017
|
+
}
|
|
76018
|
+
//#endregion
|
|
76019
|
+
//#region src/running-state/defaults.ts
|
|
76020
|
+
var RUNNING_POWER_THRESHOLD_WATTS = 5;
|
|
76021
|
+
var RUNNING_POWER_START_DELAY_MS = 3e4;
|
|
76022
|
+
var RUNNING_POWER_STOP_DELAY_MS = 18e4;
|
|
76023
|
+
//#endregion
|
|
76024
|
+
//#region src/running-state/from-operation.ts
|
|
76025
|
+
function resolveSince(running, previous, sinceMs) {
|
|
76026
|
+
if (running === null) return null;
|
|
76027
|
+
if ((previous?.running ?? null) !== running) return sinceMs;
|
|
76028
|
+
return previous?.since ?? sinceMs;
|
|
76029
|
+
}
|
|
76030
|
+
function nativeSlice(running, phase, previous, sinceMs, now) {
|
|
76031
|
+
return {
|
|
76032
|
+
running,
|
|
76033
|
+
since: resolveSince(running, previous, sinceMs),
|
|
76034
|
+
source: "native",
|
|
76035
|
+
phase: running === null ? null : phase,
|
|
76036
|
+
lastFetchedAt: now
|
|
76037
|
+
};
|
|
76038
|
+
}
|
|
76039
|
+
function runningFromDishwasherOperation(operation, program, previous, now) {
|
|
76040
|
+
if (operation === null) return nativeSlice(null, null, previous, now, now);
|
|
76041
|
+
if (operation === "Run" || operation === "Aborting") return nativeSlice(true, program ?? operation, previous, now, now);
|
|
76042
|
+
if (operation === "Pause") return nativeSlice(false, "Pause", previous, now, now);
|
|
76043
|
+
return nativeSlice(false, operation, previous, now, now);
|
|
76044
|
+
}
|
|
76045
|
+
function runningFromVacuumState(state, previous, now) {
|
|
76046
|
+
if (state === null) return nativeSlice(null, null, previous, now, now);
|
|
76047
|
+
if (state === "cleaning" || state === "returning" || state === "drying") return nativeSlice(true, state, previous, now, now);
|
|
76048
|
+
if (state === "paused") return nativeSlice(false, "paused", previous, now, now);
|
|
76049
|
+
return nativeSlice(false, state, previous, now, now);
|
|
76050
|
+
}
|
|
76051
|
+
function runningFromLawnMower(activity, previous, now) {
|
|
76052
|
+
if (activity === null) return nativeSlice(null, null, previous, now, now);
|
|
76053
|
+
if (activity === "mowing") return nativeSlice(true, activity, previous, now, now);
|
|
76054
|
+
if (activity === "paused") return nativeSlice(false, "paused", previous, now, now);
|
|
76055
|
+
return nativeSlice(false, activity, previous, now, now);
|
|
76056
|
+
}
|
|
76057
|
+
function runningFromMediaPlayer(state, previous, now) {
|
|
76058
|
+
if (state === null) return nativeSlice(null, null, previous, now, now);
|
|
76059
|
+
if (state === "playing" || state === "buffering") return nativeSlice(true, state, previous, now, now);
|
|
76060
|
+
if (state === "paused") return nativeSlice(false, "paused", previous, now, now);
|
|
76061
|
+
return nativeSlice(false, state, previous, now, now);
|
|
76062
|
+
}
|
|
76063
|
+
//#endregion
|
|
75002
76064
|
//#region src/clips/clip-event-labels.ts
|
|
75003
76065
|
/**
|
|
75004
76066
|
* Which of OUR object detections a clip window holds — the ONE overlap rule
|
|
@@ -75642,11 +76704,12 @@ function expressionOutcome(source, read, now, stateful) {
|
|
|
75642
76704
|
return unavailable;
|
|
75643
76705
|
}
|
|
75644
76706
|
const deadlines = [];
|
|
76707
|
+
const hooks = {
|
|
76708
|
+
noteDeadline: (at) => deadlines.push(at),
|
|
76709
|
+
convert: (value, from, to) => tryConvertUnit(value, normalizeUnit(from) ?? from, normalizeUnit(to) ?? to) ?? null
|
|
76710
|
+
};
|
|
75645
76711
|
const result = require_composition.evaluateExpressionSource(source.expr, values, now, {
|
|
75646
|
-
hooks
|
|
75647
|
-
noteDeadline: (at) => deadlines.push(at),
|
|
75648
|
-
convert: (value, from, to) => tryConvertUnit(value, normalizeUnit(from) ?? from, normalizeUnit(to) ?? to) ?? null
|
|
75649
|
-
},
|
|
76712
|
+
hooks,
|
|
75650
76713
|
...stateful === void 0 ? {} : { invokeStateful: (invocation) => require_composition.evaluateStatefulExpressionCall(stateful.callPathPrefix === void 0 ? invocation : {
|
|
75651
76714
|
...invocation,
|
|
75652
76715
|
path: `${stateful.callPathPrefix}@${invocation.path}`
|
|
@@ -75654,7 +76717,7 @@ function expressionOutcome(source, read, now, stateful) {
|
|
|
75654
76717
|
currentOutput: stateful.currentOutput,
|
|
75655
76718
|
startedAtMs: stateful.startedAtMs,
|
|
75656
76719
|
nowMs: now
|
|
75657
|
-
}) }
|
|
76720
|
+
}, hooks) }
|
|
75658
76721
|
});
|
|
75659
76722
|
if (!result.ok) return {
|
|
75660
76723
|
state: "unavailable",
|
|
@@ -76606,6 +77669,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
|
|
|
76606
77669
|
ptzAutotrack: ptzAutotrackCapability,
|
|
76607
77670
|
recordingOnboard: recordingOnboardCapability,
|
|
76608
77671
|
recordingSignal: recordingSignalCapability,
|
|
77672
|
+
runningState: runningStateCapability,
|
|
76609
77673
|
sceneMonitor: sceneMonitorCapability,
|
|
76610
77674
|
scriptRunner: scriptRunnerCapability,
|
|
76611
77675
|
smoke: smokeCapability,
|
|
@@ -86189,6 +87253,13 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
86189
87253
|
access: "create",
|
|
86190
87254
|
role: "admin"
|
|
86191
87255
|
},
|
|
87256
|
+
"runningState.getStatus": {
|
|
87257
|
+
capName: "running-state",
|
|
87258
|
+
capScope: "device",
|
|
87259
|
+
addonId: null,
|
|
87260
|
+
access: "view",
|
|
87261
|
+
role: "viewer"
|
|
87262
|
+
},
|
|
86192
87263
|
"sceneMonitor.captureReference": {
|
|
86193
87264
|
capName: "scene-monitor",
|
|
86194
87265
|
capScope: "device",
|
|
@@ -88478,6 +89549,7 @@ var KNOWN_CAP_NAMES = [
|
|
|
88478
89549
|
"recording-onboard",
|
|
88479
89550
|
"recording-signal",
|
|
88480
89551
|
"records",
|
|
89552
|
+
"running-state",
|
|
88481
89553
|
"scene-monitor",
|
|
88482
89554
|
"script-runner",
|
|
88483
89555
|
"server-management",
|
|
@@ -88592,6 +89664,7 @@ var DEVICE_CAP_NAMES = [
|
|
|
88592
89664
|
"recording",
|
|
88593
89665
|
"recording-onboard",
|
|
88594
89666
|
"recording-signal",
|
|
89667
|
+
"running-state",
|
|
88595
89668
|
"scene-monitor",
|
|
88596
89669
|
"script-runner",
|
|
88597
89670
|
"smoke",
|
|
@@ -91276,6 +92349,32 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
91276
92349
|
}],
|
|
91277
92350
|
events: { onSent: { notOperatorFacing: "a delivery receipt of a send this system made — not a fact about the world" } }
|
|
91278
92351
|
},
|
|
92352
|
+
"running-state": {
|
|
92353
|
+
scope: "device",
|
|
92354
|
+
noun: t("Running", "In funzione"),
|
|
92355
|
+
state: [{
|
|
92356
|
+
kind: "boolean",
|
|
92357
|
+
field: "running",
|
|
92358
|
+
label: t("Running", "In funzione"),
|
|
92359
|
+
whenTrue: t("When {device} starts", "Quando {device} inizia"),
|
|
92360
|
+
whenFalse: t("When {device} finishes", "Quando {device} finisce"),
|
|
92361
|
+
icon: "play"
|
|
92362
|
+
}],
|
|
92363
|
+
events: {
|
|
92364
|
+
onStarted: {
|
|
92365
|
+
category: require_event_category.EventCategory.RunningStarted,
|
|
92366
|
+
label: t("Started running", "Ha iniziato a funzionare"),
|
|
92367
|
+
phrase: t("When {device} starts running", "Quando {device} inizia a funzionare"),
|
|
92368
|
+
icon: "play"
|
|
92369
|
+
},
|
|
92370
|
+
onFinished: {
|
|
92371
|
+
category: require_event_category.EventCategory.RunningFinished,
|
|
92372
|
+
label: t("Finished", "Ha finito"),
|
|
92373
|
+
phrase: t("When {device} finishes", "Quando {device} finisce"),
|
|
92374
|
+
icon: "square"
|
|
92375
|
+
}
|
|
92376
|
+
}
|
|
92377
|
+
},
|
|
91279
92378
|
update: {
|
|
91280
92379
|
scope: "device",
|
|
91281
92380
|
noun: t("Firmware update", "Aggiornamento firmware"),
|
|
@@ -94216,6 +95315,7 @@ var CAP_NAMES_WITH_STATUS = [
|
|
|
94216
95315
|
"recording-archive",
|
|
94217
95316
|
"recording-onboard",
|
|
94218
95317
|
"recording-signal",
|
|
95318
|
+
"running-state",
|
|
94219
95319
|
"scene-monitor",
|
|
94220
95320
|
"script-runner",
|
|
94221
95321
|
"smoke",
|
|
@@ -94371,6 +95471,7 @@ var CAPABILITY_NAMES = {
|
|
|
94371
95471
|
recordingOnboard: "recording-onboard",
|
|
94372
95472
|
recordingSignal: "recording-signal",
|
|
94373
95473
|
records: "records",
|
|
95474
|
+
runningState: "running-state",
|
|
94374
95475
|
sceneMonitor: "scene-monitor",
|
|
94375
95476
|
scriptRunner: "script-runner",
|
|
94376
95477
|
serverManagement: "server-management",
|
|
@@ -94964,6 +96065,10 @@ var CAPABILITY_ROUTER_KEYS = [
|
|
|
94964
96065
|
key: "records",
|
|
94965
96066
|
name: "records"
|
|
94966
96067
|
},
|
|
96068
|
+
{
|
|
96069
|
+
key: "runningState",
|
|
96070
|
+
name: "running-state"
|
|
96071
|
+
},
|
|
94967
96072
|
{
|
|
94968
96073
|
key: "sceneMonitor",
|
|
94969
96074
|
name: "scene-monitor"
|
|
@@ -95280,6 +96385,7 @@ var ALL_CAPABILITY_DEFINITIONS = [
|
|
|
95280
96385
|
recordingOnboardCapability,
|
|
95281
96386
|
recordingSignalCapability,
|
|
95282
96387
|
recordsCapability,
|
|
96388
|
+
runningStateCapability,
|
|
95283
96389
|
sceneMonitorCapability,
|
|
95284
96390
|
scriptRunnerCapability,
|
|
95285
96391
|
serverManagementCapability,
|
|
@@ -97596,6 +98702,11 @@ var METHOD_DEVICE_SELECTORS = Object.freeze({
|
|
|
97596
98702
|
form: "single",
|
|
97597
98703
|
optional: true
|
|
97598
98704
|
}],
|
|
98705
|
+
"runningState.getStatus": [{
|
|
98706
|
+
name: "deviceId",
|
|
98707
|
+
form: "single",
|
|
98708
|
+
optional: false
|
|
98709
|
+
}],
|
|
97599
98710
|
"sceneMonitor.captureReference": [{
|
|
97600
98711
|
name: "deviceId",
|
|
97601
98712
|
form: "single",
|
|
@@ -98297,7 +99408,7 @@ function getCapsByProviderKind(kind) {
|
|
|
98297
99408
|
//#region src/generated/runtime-state-durability.ts
|
|
98298
99409
|
/**
|
|
98299
99410
|
* Per-cap runtime-state policy, projected from every `*.cap.ts` that declares
|
|
98300
|
-
* `runtimeState`.
|
|
99411
|
+
* `runtimeState`. 72 caps: 38 `restored`, 34 `session`.
|
|
98301
99412
|
*
|
|
98302
99413
|
* A cap absent from this map has no runtime-state slice at all. A cap PRESENT
|
|
98303
99414
|
* with `durability: 'session'` has one and has decided not to keep it.
|
|
@@ -98515,6 +99626,10 @@ var RUNTIME_STATE_POLICY = {
|
|
|
98515
99626
|
durability: "session",
|
|
98516
99627
|
volatileFields: []
|
|
98517
99628
|
},
|
|
99629
|
+
"running-state": {
|
|
99630
|
+
durability: "restored",
|
|
99631
|
+
volatileFields: ["lastFetchedAt"]
|
|
99632
|
+
},
|
|
98518
99633
|
"scene-monitor": {
|
|
98519
99634
|
durability: "session",
|
|
98520
99635
|
volatileFields: []
|
|
@@ -98637,6 +99752,7 @@ var RESTORED_CAP_NAMES = [
|
|
|
98637
99752
|
"pressure-sensor",
|
|
98638
99753
|
"privacy-mask",
|
|
98639
99754
|
"recording-onboard",
|
|
99755
|
+
"running-state",
|
|
98640
99756
|
"smoke",
|
|
98641
99757
|
"stream-catalog",
|
|
98642
99758
|
"stream-params",
|
|
@@ -98752,7 +99868,7 @@ var BucketAccumulator = class {
|
|
|
98752
99868
|
//#region src/generated/history-catalog.ts
|
|
98753
99869
|
/**
|
|
98754
99870
|
* Cap history catalog, projected from every `*.cap.ts` `history` block.
|
|
98755
|
-
*
|
|
99871
|
+
* 41 caps with series (74 series), 30 caps answered `'none'`.
|
|
98756
99872
|
* Field keys are `runtimeState` paths (`a.b` into fixed objects only).
|
|
98757
99873
|
*/
|
|
98758
99874
|
var HISTORY_CATALOG = {
|
|
@@ -99029,6 +100145,7 @@ var HISTORY_CATALOG = {
|
|
|
99029
100145
|
unit: "hPa",
|
|
99030
100146
|
deadband: .1
|
|
99031
100147
|
} },
|
|
100148
|
+
"running-state": { "running": { kind: "boolean" } },
|
|
99032
100149
|
"smoke": { "detected": { kind: "boolean" } },
|
|
99033
100150
|
"stream-params": {
|
|
99034
100151
|
"main.bitrate": {
|
|
@@ -100832,6 +101949,7 @@ function prepareNotification(caps, n) {
|
|
|
100832
101949
|
* narrows, and still fails CLOSED on a subject that cannot produce the identity
|
|
100833
101950
|
* it tests (a device the mirror cannot name is not evidence of a camera).
|
|
100834
101951
|
*/
|
|
101952
|
+
var LEGACY_KINDS_WITHOUT_FILTERS = new Set(NC_LEGACY_SYSTEM_EVENT_KINDS);
|
|
100835
101953
|
/**
|
|
100836
101954
|
* Every filter, for the callers that must cover all of them (guards, tests).
|
|
100837
101955
|
*
|
|
@@ -100855,6 +101973,7 @@ var NC_SYSTEM_EVENT_FILTER_KEYS = [
|
|
|
100855
101973
|
* ignores it and an editor hides its input. It never means "excluded".
|
|
100856
101974
|
*/
|
|
100857
101975
|
function systemEventFilterApplies(kind, filter) {
|
|
101976
|
+
if (LEGACY_KINDS_WITHOUT_FILTERS.has(kind)) return false;
|
|
100858
101977
|
switch (filter) {
|
|
100859
101978
|
case "deviceIds": return kind.startsWith("device-") || kind.startsWith("stream-") || kind === "detection-blind" || kind.startsWith("motion-") || kind.startsWith("recording-") || kind === "alarm-triggered";
|
|
100860
101979
|
case "deviceTypes": return kind.startsWith("device-") || kind === "detection-blind" || kind.startsWith("motion-");
|
|
@@ -100909,9 +102028,6 @@ var NC_SYSTEM_EVENT_FAMILY = {
|
|
|
100909
102028
|
"device-battery-normal": "device-events",
|
|
100910
102029
|
"device-consumable-low": "device-events",
|
|
100911
102030
|
"device-consumable-normal": "device-events",
|
|
100912
|
-
"device-tank-low": "device-events",
|
|
100913
|
-
"device-tank-full": "device-events",
|
|
100914
|
-
"device-tank-normal": "device-events",
|
|
100915
102031
|
"stream-online": "device-events",
|
|
100916
102032
|
"stream-offline": "device-events",
|
|
100917
102033
|
"detection-blind": "device-events",
|
|
@@ -101006,16 +102122,6 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
|
|
|
101006
102122
|
label: "Consumables",
|
|
101007
102123
|
kinds: ["device-consumable-low", "device-consumable-normal"]
|
|
101008
102124
|
},
|
|
101009
|
-
{
|
|
101010
|
-
id: "tanks",
|
|
101011
|
-
family: "device-events",
|
|
101012
|
-
label: "Tanks",
|
|
101013
|
-
kinds: [
|
|
101014
|
-
"device-tank-low",
|
|
101015
|
-
"device-tank-full",
|
|
101016
|
-
"device-tank-normal"
|
|
101017
|
-
]
|
|
101018
|
-
},
|
|
101019
102125
|
{
|
|
101020
102126
|
id: "detection-blind",
|
|
101021
102127
|
family: "device-events",
|
|
@@ -102912,11 +104018,6 @@ var RULE_BODY_ONLY = ["body"];
|
|
|
102912
104018
|
var RULE_TEXT = ["title", "body"];
|
|
102913
104019
|
var BATTERY = ["device-battery-low", "device-battery-normal"];
|
|
102914
104020
|
var CONSUMABLE = ["device-consumable-low", "device-consumable-normal"];
|
|
102915
|
-
var TANK = [
|
|
102916
|
-
"device-tank-low",
|
|
102917
|
-
"device-tank-full",
|
|
102918
|
-
"device-tank-normal"
|
|
102919
|
-
];
|
|
102920
104021
|
var ALERT_EPISODE = [
|
|
102921
104022
|
"alert-critical",
|
|
102922
104023
|
"alert-error",
|
|
@@ -102948,7 +104049,6 @@ var DEVICE_EVENTS = [
|
|
|
102948
104049
|
"device-enabled",
|
|
102949
104050
|
...BATTERY,
|
|
102950
104051
|
...CONSUMABLE,
|
|
102951
|
-
...TANK,
|
|
102952
104052
|
"stream-online",
|
|
102953
104053
|
"stream-offline",
|
|
102954
104054
|
"detection-blind",
|
|
@@ -102994,12 +104094,6 @@ var STATE_SUMMARY_SECTION_VARS = [
|
|
|
102994
104094
|
example: "Robot — filter 10 %",
|
|
102995
104095
|
kinds: CONSUMABLE
|
|
102996
104096
|
},
|
|
102997
|
-
{
|
|
102998
|
-
name: "summaryTanks",
|
|
102999
|
-
label: "Summary: tanks",
|
|
103000
|
-
example: "Robot — clean water low",
|
|
103001
|
-
kinds: TANK
|
|
103002
|
-
},
|
|
103003
104097
|
{
|
|
103004
104098
|
name: "summaryDetection",
|
|
103005
104099
|
label: "Summary: cameras detecting nothing",
|
|
@@ -103408,18 +104502,7 @@ var NC_TEMPLATE_VARS = [
|
|
|
103408
104502
|
families: ["rule"],
|
|
103409
104503
|
fields: [...RULE_TEXT],
|
|
103410
104504
|
kinds: ["system"],
|
|
103411
|
-
systemEventKinds: [...CONSUMABLE
|
|
103412
|
-
},
|
|
103413
|
-
{
|
|
103414
|
-
name: "tank",
|
|
103415
|
-
label: "Tank",
|
|
103416
|
-
description: "Which tank, as a stable token: cleanWater, dirtyWater, detergent, dustBin, or the device key of any other supply (salt, rinse aid).",
|
|
103417
|
-
example: "clean water",
|
|
103418
|
-
group: "system",
|
|
103419
|
-
families: ["rule"],
|
|
103420
|
-
fields: [...RULE_TEXT],
|
|
103421
|
-
kinds: ["system"],
|
|
103422
|
-
systemEventKinds: [...TANK]
|
|
104505
|
+
systemEventKinds: [...CONSUMABLE]
|
|
103423
104506
|
},
|
|
103424
104507
|
{
|
|
103425
104508
|
name: "profile",
|
|
@@ -104070,9 +105153,6 @@ var NC_TEMPLATE_VARS = [
|
|
|
104070
105153
|
* delete it from here then, and the served catalog names it again.
|
|
104071
105154
|
*/
|
|
104072
105155
|
var NC_TEMPLATE_HELD_BACK_SYSTEM_EVENT_KINDS = [
|
|
104073
|
-
"device-tank-low",
|
|
104074
|
-
"device-tank-full",
|
|
104075
|
-
"device-tank-normal",
|
|
104076
105156
|
"node-restarting",
|
|
104077
105157
|
"node-updating",
|
|
104078
105158
|
"alert-critical",
|
|
@@ -104234,15 +105314,6 @@ var NC_SYSTEM_EVENT_CLASS_SPECS = [
|
|
|
104234
105314
|
snapshotLabel: "Consumables low now",
|
|
104235
105315
|
snapshotSection: "consumables"
|
|
104236
105316
|
},
|
|
104237
|
-
{
|
|
104238
|
-
id: "tanks",
|
|
104239
|
-
family: "device-events",
|
|
104240
|
-
label: "Tanks (low or full / back to normal)",
|
|
104241
|
-
bad: ["device-tank-low", "device-tank-full"],
|
|
104242
|
-
recovery: ["device-tank-normal"],
|
|
104243
|
-
snapshotLabel: "Tanks low or full now",
|
|
104244
|
-
snapshotSection: "tanks"
|
|
104245
|
-
},
|
|
104246
105317
|
{
|
|
104247
105318
|
id: "motion-noise",
|
|
104248
105319
|
family: "device-events",
|
|
@@ -105603,898 +106674,341 @@ function stepModelRunsOnFormat(models, modelId, format) {
|
|
|
105603
106674
|
const named = models.find((m) => m.id === modelId);
|
|
105604
106675
|
if (named !== void 0 && hasBuild(named, format)) return true;
|
|
105605
106676
|
}
|
|
105606
|
-
return models.some((m) => m.legacy !== true && hasBuild(m, format));
|
|
105607
|
-
}
|
|
105608
|
-
//#endregion
|
|
105609
|
-
//#region src/pipeline/removed-model-ids.ts
|
|
105610
|
-
/**
|
|
105611
|
-
* Model ids DELETED from a step's catalog (D703, D710, D719), per step — and
|
|
105612
|
-
* the one question asked of them: is a stored pin to this id a pin to a model
|
|
105613
|
-
* that no longer exists? (D725)
|
|
105614
|
-
*
|
|
105615
|
-
* This is NOT a resolver input. `resolveModelForFormat` still returns an
|
|
105616
|
-
* unknown id unchanged (a custom model), and a bulk pass pinned to one still
|
|
105617
|
-
* refuses rather than substitutes (D54). The ONE reader is the orchestrator's
|
|
105618
|
-
* stored-pin reset (`removed-model-pin-reset.ts`), which clears such a pin from
|
|
105619
|
-
* the store so the step follows its default again — unless a registered custom
|
|
105620
|
-
* model carries the same id.
|
|
105621
|
-
*
|
|
105622
|
-
* `removed-model-ids.spec.ts` (addon-pipeline) pins that every id here is
|
|
105623
|
-
* really absent from its step's catalog. A model deleted from a catalog is
|
|
105624
|
-
* added here in the same change, or its stored pins stay behind as custom ids.
|
|
105625
|
-
*/
|
|
105626
|
-
/** `stepId → ids deleted from that step's catalog`. */
|
|
105627
|
-
var REMOVED_MODEL_IDS = {
|
|
105628
|
-
"object-detection": ["efficientdet-lite0-edgetpu", "ssdlite-mobiledet-coco-edgetpu"],
|
|
105629
|
-
"face-detection": [
|
|
105630
|
-
"scrypted-yolov9t-face",
|
|
105631
|
-
"scrfd-2.5g",
|
|
105632
|
-
"ssd-mobilenet-v2-face-edgetpu"
|
|
105633
|
-
],
|
|
105634
|
-
"face-embedding": ["arcface-r100", "inception-resnet-v1"],
|
|
105635
|
-
"plate-detection": ["yolov8n-plate"],
|
|
105636
|
-
"plate-ocr": ["vgg-english-g2"],
|
|
105637
|
-
"clip-embedding": [
|
|
105638
|
-
"mobileclip-s0",
|
|
105639
|
-
"mobileclip-s1",
|
|
105640
|
-
"mobileclip-s2"
|
|
105641
|
-
],
|
|
105642
|
-
"package-detection": ["yolov8n-package"],
|
|
105643
|
-
"bird-classifier": ["bird-classifier-aiy-edgetpu", "bird-nabirds-404"],
|
|
105644
|
-
"animal-classifier": ["animals-10"],
|
|
105645
|
-
"vehicle-classifier": ["vehicle-type-efficientnet"]
|
|
105646
|
-
};
|
|
105647
|
-
/** True when `modelId` was deleted from `stepId`'s catalog. */
|
|
105648
|
-
function isRemovedModelId(stepId, modelId) {
|
|
105649
|
-
return REMOVED_MODEL_IDS[stepId]?.includes(modelId) ?? false;
|
|
105650
|
-
}
|
|
105651
|
-
//#endregion
|
|
105652
|
-
//#region src/pipeline/model-bound-settings.ts
|
|
105653
|
-
/** Suffix of the sibling key that names the model a stored value was made for. */
|
|
105654
|
-
var MODEL_BINDING_SUFFIX = "ModelId";
|
|
105655
|
-
/** `minConfidencePerson` → `minConfidencePersonModelId`. */
|
|
105656
|
-
function modelBindingKey(key) {
|
|
105657
|
-
return `${key}${MODEL_BINDING_SUFFIX}`;
|
|
105658
|
-
}
|
|
105659
|
-
/** The model a stored value was made for, or `undefined` when unbound. */
|
|
105660
|
-
function storedBinding(settings, key) {
|
|
105661
|
-
const binding = settings?.[modelBindingKey(key)];
|
|
105662
|
-
return typeof binding === "string" && binding.length > 0 ? binding : void 0;
|
|
105663
|
-
}
|
|
105664
|
-
/**
|
|
105665
|
-
* The effective value of `key` for the model that runs (`runningModelId`,
|
|
105666
|
-
* `undefined` when unknown — it then matches no binding).
|
|
105667
|
-
*/
|
|
105668
|
-
function resolveModelBoundSetting(settings, key, runningModelId, modelDefault, globalDefault) {
|
|
105669
|
-
const stored = settings?.[key];
|
|
105670
|
-
const binding = storedBinding(settings, key);
|
|
105671
|
-
if (stored !== void 0 && binding === void 0) return {
|
|
105672
|
-
value: stored,
|
|
105673
|
-
source: "override"
|
|
105674
|
-
};
|
|
105675
|
-
if (stored !== void 0 && binding !== void 0 && binding === runningModelId) return {
|
|
105676
|
-
value: stored,
|
|
105677
|
-
source: "override",
|
|
105678
|
-
boundModelId: binding
|
|
105679
|
-
};
|
|
105680
|
-
const inapplicable = stored !== void 0 && binding !== void 0 ? { inapplicableOverride: {
|
|
105681
|
-
value: stored,
|
|
105682
|
-
modelId: binding
|
|
105683
|
-
} } : {};
|
|
105684
|
-
if (modelDefault !== void 0) return {
|
|
105685
|
-
value: modelDefault,
|
|
105686
|
-
source: "model-default",
|
|
105687
|
-
...inapplicable
|
|
105688
|
-
};
|
|
105689
|
-
return {
|
|
105690
|
-
value: globalDefault,
|
|
105691
|
-
source: "global-default",
|
|
105692
|
-
...inapplicable
|
|
105693
|
-
};
|
|
105694
|
-
}
|
|
105695
|
-
/**
|
|
105696
|
-
* The OPERATOR layer of a step's settings as the running model sees it: every
|
|
105697
|
-
* model-bound value made for another model is removed (so the default
|
|
105698
|
-
* underneath — the running model's — wins the merge), and every binding key is
|
|
105699
|
-
* stripped. Unbound values are kept. Returns a fresh object; `undefined` stays
|
|
105700
|
-
* `undefined`.
|
|
105701
|
-
*/
|
|
105702
|
-
function applyModelBoundSettings(settings, boundKeys, runningModelId) {
|
|
105703
|
-
if (settings === void 0) return void 0;
|
|
105704
|
-
const out = { ...settings };
|
|
105705
|
-
for (const key of boundKeys) {
|
|
105706
|
-
const binding = storedBinding(settings, key);
|
|
105707
|
-
if (binding !== void 0 && binding !== runningModelId) delete out[key];
|
|
105708
|
-
delete out[modelBindingKey(key)];
|
|
105709
|
-
}
|
|
105710
|
-
return out;
|
|
105711
|
-
}
|
|
105712
|
-
/** Store `value` for `key`, made for `modelId`. Returns a fresh object. */
|
|
105713
|
-
function writeModelBoundSetting(settings, key, value, modelId) {
|
|
105714
|
-
return {
|
|
105715
|
-
...settings,
|
|
105716
|
-
[key]: value,
|
|
105717
|
-
[modelBindingKey(key)]: modelId
|
|
105718
|
-
};
|
|
105719
|
-
}
|
|
105720
|
-
/** Remove an override and its binding — "reset to the model default". */
|
|
105721
|
-
function clearModelBoundSetting(settings, key) {
|
|
105722
|
-
const out = { ...settings };
|
|
105723
|
-
delete out[key];
|
|
105724
|
-
delete out[modelBindingKey(key)];
|
|
105725
|
-
return out;
|
|
105726
|
-
}
|
|
105727
|
-
/** True when any of `boundKeys` has an override IN FORCE for `modelId`. */
|
|
105728
|
-
function hasModelBoundOverrides(settings, boundKeys, modelId) {
|
|
105729
|
-
return boundKeys.some((key) => resolveModelBoundSetting(settings, key, modelId, void 0, void 0).source === "override");
|
|
105730
|
-
}
|
|
105731
|
-
/**
|
|
105732
|
-
* "Reset thresholds to model defaults": remove every override of `boundKeys`
|
|
105733
|
-
* that is in force for `modelId` (bound to it, or unbound), with its binding —
|
|
105734
|
-
* one result, so the editor writes it in one save. An override stored for
|
|
105735
|
-
* ANOTHER model is kept: it is not in force here, and it applies again when
|
|
105736
|
-
* that model runs.
|
|
105737
|
-
*/
|
|
105738
|
-
function clearModelBoundOverrides(settings, boundKeys, modelId) {
|
|
105739
|
-
return boundKeys.reduce((acc, key) => resolveModelBoundSetting(acc, key, modelId, void 0, void 0).source === "override" ? clearModelBoundSetting(acc, key) : acc, { ...settings });
|
|
105740
|
-
}
|
|
105741
|
-
/**
|
|
105742
|
-
* The keys of every `bindsToModel` field of a step's config schema, groups
|
|
105743
|
-
* flattened (D738). One definition for the runner (which applies the binding)
|
|
105744
|
-
* and the orchestrator's Auto tune (which clears those overrides, D745).
|
|
105745
|
-
*/
|
|
105746
|
-
function collectModelBoundKeys(fields) {
|
|
105747
|
-
return fields.flatMap((field) => {
|
|
105748
|
-
if (field.type === "group") return collectModelBoundKeys(field.fields);
|
|
105749
|
-
return "key" in field && "bindsToModel" in field && field.bindsToModel === true ? [field.key] : [];
|
|
105750
|
-
});
|
|
105751
|
-
}
|
|
105752
|
-
//#endregion
|
|
105753
|
-
//#region src/pipeline/step-settings-layers.ts
|
|
105754
|
-
/**
|
|
105755
|
-
* The layers of a detection step's settings, and the field-level edits on them
|
|
105756
|
-
* (D754, D927).
|
|
105757
|
-
*
|
|
105758
|
-
* A step's settings reach a session through two stored layers, merged key by
|
|
105759
|
-
* key — a key in the higher layer wins:
|
|
105760
|
-
*
|
|
105761
|
-
* node the node's base for the accelerator that runs the step
|
|
105762
|
-
* camera `cameraSettings[cam].stepSettings[step]` — camera-semantic
|
|
105763
|
-
* keys only, on every accelerator (never a model-bound field)
|
|
105764
|
-
*
|
|
105765
|
-
* Below them sits the default: the running model's (D738) or the field's own.
|
|
105766
|
-
*
|
|
105767
|
-
* Every layer is SPARSE and FIELD-LEVEL. A key present in a layer is an
|
|
105768
|
-
* override; an absent key inherits. Two consequences that every editor must
|
|
105769
|
-
* respect, and why this module exists:
|
|
105770
|
-
*
|
|
105771
|
-
* - A model-bound field (D738) occupies TWO keys, its value and
|
|
105772
|
-
* `<key>ModelId`. They are written, compared and removed as one unit — a
|
|
105773
|
-
* value without its binding over a lower layer's binding would be judged
|
|
105774
|
-
* against the wrong model.
|
|
105775
|
-
* - An editor writes a DIFF against the merged layers below, never a copy of
|
|
105776
|
-
* them. A copy pins every field at the value it had that day, so later
|
|
105777
|
-
* edits below never reach it (the full-copy "Override" this replaced).
|
|
105778
|
-
*/
|
|
105779
|
-
/** Top-down order of the stored layers. */
|
|
105780
|
-
var LAYER_ORDER = ["camera", "node"];
|
|
105781
|
-
/**
|
|
105782
|
-
* The keys one field occupies in a bag: its own, plus its binding key when it
|
|
105783
|
-
* binds to a model. `boundKeys` = `collectModelBoundKeys(schema)`.
|
|
105784
|
-
*/
|
|
105785
|
-
function fieldUnitKeys(key, boundKeys) {
|
|
105786
|
-
return boundKeys.includes(key) ? [key, modelBindingKey(key)] : [key];
|
|
105787
|
-
}
|
|
105788
|
-
/** The field a stored key belongs to (`minConfidencePersonModelId` → `minConfidencePerson`). */
|
|
105789
|
-
function unitOf(storedKey, boundKeys) {
|
|
105790
|
-
return boundKeys.find((k) => modelBindingKey(k) === storedKey) ?? storedKey;
|
|
105791
|
-
}
|
|
105792
|
-
/** Structural equality for stored values (numbers, strings, booleans, arrays, plain objects). */
|
|
105793
|
-
function settingValuesEqual(a, b) {
|
|
105794
|
-
if (Object.is(a, b)) return true;
|
|
105795
|
-
if (Array.isArray(a) && Array.isArray(b)) return a.length === b.length && a.every((v, i) => settingValuesEqual(v, b[i]));
|
|
105796
|
-
if (isPlainObject(a) && isPlainObject(b)) {
|
|
105797
|
-
const ka = Object.keys(a);
|
|
105798
|
-
const kb = Object.keys(b);
|
|
105799
|
-
return ka.length === kb.length && ka.every((k) => settingValuesEqual(a[k], b[k]));
|
|
105800
|
-
}
|
|
105801
|
-
return false;
|
|
105802
|
-
}
|
|
105803
|
-
function isPlainObject(v) {
|
|
105804
|
-
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
105805
|
-
}
|
|
105806
|
-
/** Merge bags bottom-up (later wins, per key). Fresh object. */
|
|
105807
|
-
function mergeSettingsBags(...bags) {
|
|
105808
|
-
return bags.reduce((acc, bag) => ({
|
|
105809
|
-
...acc,
|
|
105810
|
-
...bag
|
|
105811
|
-
}), {});
|
|
105812
|
-
}
|
|
105813
|
-
/** The merged value of the layers BELOW `layer` (what removing a key from `layer` falls back to). */
|
|
105814
|
-
function settingsBelow(layers, layer) {
|
|
105815
|
-
return mergeSettingsBags(...LAYER_ORDER.slice(LAYER_ORDER.indexOf(layer) + 1).toReversed().map((id) => layers[id]));
|
|
105816
|
-
}
|
|
105817
|
-
/** The effective bag: every stored layer merged. */
|
|
105818
|
-
function effectiveStepSettings(layers) {
|
|
105819
|
-
return mergeSettingsBags(layers.node, layers.camera);
|
|
105820
|
-
}
|
|
105821
|
-
/** The highest stored layer that holds any key of `key`'s unit, or `default`. */
|
|
105822
|
-
function stepSettingSource(layers, key, boundKeys) {
|
|
105823
|
-
const unit = fieldUnitKeys(key, boundKeys);
|
|
105824
|
-
for (const id of LAYER_ORDER) {
|
|
105825
|
-
const bag = layers[id];
|
|
105826
|
-
if (bag !== void 0 && unit.some((k) => bag[k] !== void 0)) return id;
|
|
105827
|
-
}
|
|
105828
|
-
return "default";
|
|
105829
|
-
}
|
|
105830
|
-
/**
|
|
105831
|
-
* `layer` with `key`'s unit set from `values` (a bag carrying the unit's new
|
|
105832
|
-
* value — e.g. the editor's whole next settings bag). A unit key that `values`
|
|
105833
|
-
* does not carry is removed from the layer. Fresh object.
|
|
105834
|
-
*/
|
|
105835
|
-
function withFieldFrom(layer, key, values, boundKeys) {
|
|
105836
|
-
const out = { ...layer };
|
|
105837
|
-
for (const k of fieldUnitKeys(key, boundKeys)) if (values[k] === void 0) delete out[k];
|
|
105838
|
-
else out[k] = values[k];
|
|
105839
|
-
return out;
|
|
105840
|
-
}
|
|
105841
|
-
/** `layer` without `key`'s unit — "reset to the next level". Fresh object. */
|
|
105842
|
-
function withoutField(layer, key, boundKeys) {
|
|
105843
|
-
const out = { ...layer };
|
|
105844
|
-
for (const k of fieldUnitKeys(key, boundKeys)) delete out[k];
|
|
105845
|
-
return out;
|
|
105846
|
-
}
|
|
105847
|
-
/** Every field unit stored in `layer`, by its field key. */
|
|
105848
|
-
function unitsIn(layer, boundKeys) {
|
|
105849
|
-
return [...new Set(Object.keys(layer).map((k) => unitOf(k, boundKeys)))];
|
|
105850
|
-
}
|
|
105851
|
-
/** True when every key of `key`'s unit has the same value in `layer` as in `below`. */
|
|
105852
|
-
function unitEqualsBelow(layer, below, key, boundKeys) {
|
|
105853
|
-
return fieldUnitKeys(key, boundKeys).every((k) => settingValuesEqual(layer[k], below[k]));
|
|
105854
|
-
}
|
|
105855
|
-
/**
|
|
105856
|
-
* `layer` reduced to the fields that DIFFER from `below` — what an editor
|
|
105857
|
-
* stores. A unit is kept or dropped whole. `undefined` when nothing is left.
|
|
105858
|
-
*/
|
|
105859
|
-
function diffStepSettings(layer, below, boundKeys) {
|
|
105860
|
-
if (layer === void 0) return void 0;
|
|
105861
|
-
const out = {};
|
|
105862
|
-
for (const unit of unitsIn(layer, boundKeys)) {
|
|
105863
|
-
if (unitEqualsBelow(layer, below, unit, boundKeys)) continue;
|
|
105864
|
-
for (const k of fieldUnitKeys(unit, boundKeys)) if (layer[k] !== void 0) out[k] = layer[k];
|
|
105865
|
-
}
|
|
105866
|
-
return Object.keys(out).length > 0 ? out : void 0;
|
|
105867
|
-
}
|
|
105868
|
-
/**
|
|
105869
|
-
* The fields `layer` stores at exactly the value of the layers below — a
|
|
105870
|
-
* legacy full copy's leftovers. They change nothing today and stop every
|
|
105871
|
-
* later edit below from reaching this layer. Sorted.
|
|
105872
|
-
*/
|
|
105873
|
-
function fieldsPinnedAtValueBelow(layer, below, boundKeys) {
|
|
105874
|
-
if (layer === void 0) return [];
|
|
105875
|
-
return unitsIn(layer, boundKeys).filter((unit) => unitEqualsBelow(layer, below, unit, boundKeys)).toSorted();
|
|
105876
|
-
}
|
|
105877
|
-
//#endregion
|
|
105878
|
-
//#region src/pipeline/auto-tune/hardware-class.ts
|
|
105879
|
-
var APPLE_CHIP = /^Apple M(\d+)(?:\s+(Pro|Max|Ultra))?\b/i;
|
|
105880
|
-
var INTEL_CORE_ULTRA = /Core\(TM\)\s+Ultra|Core Ultra/i;
|
|
105881
|
-
var INTEL_N_SERIES = /Intel\(R\)\s+N\d{2,3}\b|\bIntel\s+N\d{2,3}\b/i;
|
|
105882
|
-
/** Phoenix / Hawk Point mobile (7040/8040) and Ryzen 8000G desktop APUs. */
|
|
105883
|
-
var AMD_PHOENIX = /Ryzen \d+ (7[0-9]40|8[0-9]40|8[0-9]45)[HSU]|Ryzen \d+ 8[0-9]00G/i;
|
|
105884
|
-
function parseAppleChip(cpuModel) {
|
|
105885
|
-
const match = APPLE_CHIP.exec(cpuModel.trim());
|
|
105886
|
-
if (match === null) return null;
|
|
105887
|
-
const generation = Number(match[1]);
|
|
105888
|
-
const variantRaw = (match[2] ?? "base").toLowerCase();
|
|
105889
|
-
return {
|
|
105890
|
-
generation,
|
|
105891
|
-
variant: variantRaw === "pro" || variantRaw === "max" || variantRaw === "ultra" ? variantRaw : "base"
|
|
105892
|
-
};
|
|
105893
|
-
}
|
|
105894
|
-
/**
|
|
105895
|
-
* Classify a node. Apple base chips (M1–M4) map to the measured M2 column, the
|
|
105896
|
-
* Pro/Max/Ultra chips to the measured M3 Pro column — the M2 was measured at
|
|
105897
|
-
* 1.0–1.2× the M3 Pro on the Neural Engine, so the generation inside a variant
|
|
105898
|
-
* matters less than the column's own error.
|
|
105899
|
-
*/
|
|
105900
|
-
function classifyInferenceHardware(hw) {
|
|
105901
|
-
const cpuModel = hw.cpuModel;
|
|
105902
|
-
const apple = parseAppleChip(cpuModel);
|
|
105903
|
-
if (apple !== null) return {
|
|
105904
|
-
hardwareClass: apple.variant === "base" ? "apple-m-base" : "apple-m-pro",
|
|
105905
|
-
cpuModel,
|
|
105906
|
-
apple
|
|
105907
|
-
};
|
|
105908
|
-
if (INTEL_CORE_ULTRA.test(cpuModel)) return {
|
|
105909
|
-
hardwareClass: "intel-core-ultra",
|
|
105910
|
-
cpuModel
|
|
105911
|
-
};
|
|
105912
|
-
if (INTEL_N_SERIES.test(cpuModel)) return {
|
|
105913
|
-
hardwareClass: "intel-n-series",
|
|
105914
|
-
cpuModel
|
|
105915
|
-
};
|
|
105916
|
-
if (AMD_PHOENIX.test(cpuModel)) return {
|
|
105917
|
-
hardwareClass: "amd-phoenix",
|
|
105918
|
-
cpuModel
|
|
105919
|
-
};
|
|
105920
|
-
return {
|
|
105921
|
-
hardwareClass: "unknown",
|
|
105922
|
-
cpuModel
|
|
105923
|
-
};
|
|
105924
|
-
}
|
|
105925
|
-
/**
|
|
105926
|
-
* The accelerator kind of one of a node's device keys (`openvino:npu`,
|
|
105927
|
-
* `openvino:gpu`, `coreml:all`, `edgetpu:usb`, `cpu`, `onnx:cpu`, …). `null`
|
|
105928
|
-
* for a key this table has no column for.
|
|
105929
|
-
*/
|
|
105930
|
-
function acceleratorKindOf(deviceKey, hw) {
|
|
105931
|
-
const [backend = "", device = ""] = deviceKey.split(":");
|
|
105932
|
-
if (backend === "edgetpu") return "coral";
|
|
105933
|
-
if (backend === "coreml") return device === "cpu" ? "arm-cpu" : "apple-ane";
|
|
105934
|
-
if (backend === "openvino" && device === "npu") return "intel-npu";
|
|
105935
|
-
if (backend === "openvino" && device === "gpu") return "intel-igpu";
|
|
105936
|
-
if (backend === "onnx" && device === "webgpu") return "amd-igpu";
|
|
105937
|
-
if (backend === "vitisai" && device === "npu") return "amd-npu";
|
|
105938
|
-
if (backend === "cpu" || device === "cpu") return hw.arch === "arm64" ? "arm-cpu" : "x86-cpu";
|
|
105939
|
-
return null;
|
|
105940
|
-
}
|
|
105941
|
-
//#endregion
|
|
105942
|
-
//#region src/pipeline/auto-tune/detector-performance.ts
|
|
105943
|
-
var DETECTOR_PERF = {
|
|
105944
|
-
"intel-core-ultra": {
|
|
105945
|
-
"intel-npu": {
|
|
105946
|
-
"yolov9t-320": {
|
|
105947
|
-
p50Ms: 2.9,
|
|
105948
|
-
fps: 383,
|
|
105949
|
-
measured: true
|
|
105950
|
-
},
|
|
105951
|
-
"yolov9t-320-int8": {
|
|
105952
|
-
p50Ms: 3.8,
|
|
105953
|
-
fps: 333,
|
|
105954
|
-
measured: true
|
|
105955
|
-
},
|
|
105956
|
-
"yolov9t-640": {
|
|
105957
|
-
p50Ms: 7.4,
|
|
105958
|
-
fps: 139,
|
|
105959
|
-
measured: true
|
|
105960
|
-
},
|
|
105961
|
-
"yolov9t-640-int8": {
|
|
105962
|
-
p50Ms: 7.5,
|
|
105963
|
-
fps: 144,
|
|
105964
|
-
measured: true
|
|
105965
|
-
},
|
|
105966
|
-
"yolov9s-320": {
|
|
105967
|
-
p50Ms: 4.4,
|
|
105968
|
-
fps: 241,
|
|
105969
|
-
measured: true
|
|
105970
|
-
},
|
|
105971
|
-
"yolov9s-320-int8": {
|
|
105972
|
-
p50Ms: 4.1,
|
|
105973
|
-
fps: 264,
|
|
105974
|
-
measured: true
|
|
105975
|
-
},
|
|
105976
|
-
"yolov9s-640": {
|
|
105977
|
-
p50Ms: 12.7,
|
|
105978
|
-
fps: 82,
|
|
105979
|
-
measured: true
|
|
105980
|
-
},
|
|
105981
|
-
"yolov9s-640-int8": {
|
|
105982
|
-
p50Ms: 10.7,
|
|
105983
|
-
fps: 99,
|
|
105984
|
-
measured: true
|
|
105985
|
-
},
|
|
105986
|
-
"yolov9m-320": {
|
|
105987
|
-
p50Ms: 8.4,
|
|
105988
|
-
fps: 123,
|
|
105989
|
-
measured: true
|
|
105990
|
-
},
|
|
105991
|
-
"yolov9m-320-int8": {
|
|
105992
|
-
p50Ms: 6.5,
|
|
105993
|
-
fps: 165,
|
|
105994
|
-
measured: true
|
|
105995
|
-
},
|
|
105996
|
-
"yolov9m-640": {
|
|
105997
|
-
p50Ms: 29.5,
|
|
105998
|
-
fps: 32,
|
|
105999
|
-
measured: true
|
|
106000
|
-
},
|
|
106001
|
-
"yolov9m-640-int8": {
|
|
106002
|
-
p50Ms: 19.5,
|
|
106003
|
-
fps: 52,
|
|
106004
|
-
measured: true
|
|
106005
|
-
}
|
|
106006
|
-
},
|
|
106007
|
-
"intel-igpu": {
|
|
106008
|
-
"yolov9t-320": {
|
|
106009
|
-
p50Ms: 2.6,
|
|
106010
|
-
fps: 405,
|
|
106011
|
-
measured: true
|
|
106012
|
-
},
|
|
106013
|
-
"yolov9t-320-int8": {
|
|
106014
|
-
p50Ms: 2.8,
|
|
106015
|
-
fps: 324,
|
|
106016
|
-
measured: true
|
|
106017
|
-
},
|
|
106018
|
-
"yolov9t-640": {
|
|
106019
|
-
p50Ms: 6.5,
|
|
106020
|
-
fps: 157,
|
|
106021
|
-
measured: true
|
|
106022
|
-
},
|
|
106023
|
-
"yolov9t-640-int8": {
|
|
106024
|
-
p50Ms: 5.6,
|
|
106025
|
-
fps: 185,
|
|
106026
|
-
measured: true
|
|
106027
|
-
},
|
|
106028
|
-
"yolov9s-320": {
|
|
106029
|
-
p50Ms: 4.6,
|
|
106030
|
-
fps: 200,
|
|
106031
|
-
measured: true
|
|
106032
|
-
},
|
|
106033
|
-
"yolov9s-320-int8": {
|
|
106034
|
-
p50Ms: 4.1,
|
|
106035
|
-
fps: 239,
|
|
106036
|
-
measured: true
|
|
106037
|
-
},
|
|
106038
|
-
"yolov9s-640": {
|
|
106039
|
-
p50Ms: 15.2,
|
|
106040
|
-
fps: 69,
|
|
106041
|
-
measured: true
|
|
106042
|
-
},
|
|
106043
|
-
"yolov9s-640-int8": {
|
|
106044
|
-
p50Ms: 10.5,
|
|
106045
|
-
fps: 99,
|
|
106046
|
-
measured: true
|
|
106047
|
-
},
|
|
106048
|
-
"yolov9m-320": {
|
|
106049
|
-
p50Ms: 11.4,
|
|
106050
|
-
fps: 88,
|
|
106051
|
-
measured: true
|
|
106052
|
-
},
|
|
106053
|
-
"yolov9m-320-int8": {
|
|
106054
|
-
p50Ms: 7,
|
|
106055
|
-
fps: 127,
|
|
106056
|
-
measured: true
|
|
106057
|
-
},
|
|
106058
|
-
"yolov9m-640": {
|
|
106059
|
-
p50Ms: 40,
|
|
106060
|
-
fps: 25,
|
|
106061
|
-
measured: true
|
|
106062
|
-
},
|
|
106063
|
-
"yolov9m-640-int8": {
|
|
106064
|
-
p50Ms: 23.5,
|
|
106065
|
-
fps: 44,
|
|
106066
|
-
measured: true
|
|
106067
|
-
}
|
|
106068
|
-
},
|
|
106069
|
-
"x86-cpu": {
|
|
106070
|
-
"yolov9t-320": {
|
|
106071
|
-
p50Ms: 6.9,
|
|
106072
|
-
fps: 145,
|
|
106073
|
-
measured: true
|
|
106074
|
-
},
|
|
106075
|
-
"yolov9t-320-int8": {
|
|
106076
|
-
p50Ms: 2.7,
|
|
106077
|
-
fps: 405,
|
|
106078
|
-
measured: true
|
|
106079
|
-
},
|
|
106080
|
-
"yolov9t-640": {
|
|
106081
|
-
p50Ms: 23,
|
|
106082
|
-
fps: 44,
|
|
106083
|
-
measured: true
|
|
106084
|
-
},
|
|
106085
|
-
"yolov9t-640-int8": {
|
|
106086
|
-
p50Ms: 8.8,
|
|
106087
|
-
fps: 124,
|
|
106088
|
-
measured: true
|
|
106089
|
-
},
|
|
106090
|
-
"yolov9s-320": {
|
|
106091
|
-
p50Ms: 15.8,
|
|
106092
|
-
fps: 63,
|
|
106093
|
-
measured: true
|
|
106094
|
-
},
|
|
106095
|
-
"yolov9s-320-int8": {
|
|
106096
|
-
p50Ms: 6.5,
|
|
106097
|
-
fps: 180,
|
|
106098
|
-
measured: true
|
|
106099
|
-
},
|
|
106100
|
-
"yolov9s-640": {
|
|
106101
|
-
p50Ms: 58.2,
|
|
106102
|
-
fps: 17,
|
|
106103
|
-
measured: true
|
|
106104
|
-
},
|
|
106105
|
-
"yolov9s-640-int8": {
|
|
106106
|
-
p50Ms: 20.1,
|
|
106107
|
-
fps: 52,
|
|
106108
|
-
measured: true
|
|
106109
|
-
},
|
|
106110
|
-
"yolov9m-320": {
|
|
106111
|
-
p50Ms: 40,
|
|
106112
|
-
fps: 25,
|
|
106113
|
-
measured: true
|
|
106114
|
-
},
|
|
106115
|
-
"yolov9m-320-int8": {
|
|
106116
|
-
p50Ms: 14.2,
|
|
106117
|
-
fps: 74,
|
|
106118
|
-
measured: true
|
|
106119
|
-
},
|
|
106120
|
-
"yolov9m-640": {
|
|
106121
|
-
p50Ms: 155.8,
|
|
106122
|
-
fps: 6,
|
|
106123
|
-
measured: true
|
|
106124
|
-
},
|
|
106125
|
-
"yolov9m-640-int8": {
|
|
106126
|
-
p50Ms: 52.4,
|
|
106127
|
-
fps: 19,
|
|
106128
|
-
measured: true
|
|
106129
|
-
}
|
|
106130
|
-
},
|
|
106131
|
-
coral: { "yolov9s-320-edgetpu": {
|
|
106132
|
-
p50Ms: 32,
|
|
106133
|
-
fps: 31,
|
|
106134
|
-
measured: false
|
|
106135
|
-
} }
|
|
106136
|
-
},
|
|
106137
|
-
"intel-n-series": {
|
|
106138
|
-
"intel-igpu": {
|
|
106139
|
-
"yolov9t-320": {
|
|
106140
|
-
p50Ms: 15,
|
|
106141
|
-
fps: 48,
|
|
106142
|
-
measured: true
|
|
106143
|
-
},
|
|
106144
|
-
"yolov9t-320-int8": {
|
|
106145
|
-
p50Ms: 14.1,
|
|
106146
|
-
fps: 67,
|
|
106147
|
-
measured: true
|
|
106148
|
-
},
|
|
106149
|
-
"yolov9t-640": {
|
|
106150
|
-
p50Ms: 38.5,
|
|
106151
|
-
fps: 26,
|
|
106152
|
-
measured: true
|
|
106153
|
-
},
|
|
106154
|
-
"yolov9t-640-int8": {
|
|
106155
|
-
p50Ms: 33.1,
|
|
106156
|
-
fps: 33,
|
|
106157
|
-
measured: true
|
|
106158
|
-
},
|
|
106159
|
-
"yolov9s-320": {
|
|
106160
|
-
p50Ms: 41.4,
|
|
106161
|
-
fps: 24,
|
|
106162
|
-
measured: true
|
|
106163
|
-
},
|
|
106164
|
-
"yolov9s-320-int8": {
|
|
106165
|
-
p50Ms: 23.3,
|
|
106166
|
-
fps: 34,
|
|
106167
|
-
measured: true
|
|
106168
|
-
},
|
|
106169
|
-
"yolov9s-640": {
|
|
106170
|
-
p50Ms: 127.4,
|
|
106171
|
-
fps: 8,
|
|
106172
|
-
measured: true
|
|
106173
|
-
},
|
|
106174
|
-
"yolov9s-640-int8": {
|
|
106175
|
-
p50Ms: 92.9,
|
|
106176
|
-
fps: 11,
|
|
106177
|
-
measured: true
|
|
106178
|
-
},
|
|
106179
|
-
"yolov9m-320": {
|
|
106180
|
-
p50Ms: 63.5,
|
|
106181
|
-
fps: 16,
|
|
106182
|
-
measured: true
|
|
106183
|
-
},
|
|
106184
|
-
"yolov9m-320-int8": {
|
|
106185
|
-
p50Ms: 39.4,
|
|
106186
|
-
fps: 26,
|
|
106187
|
-
measured: true
|
|
106188
|
-
},
|
|
106189
|
-
"yolov9m-640": {
|
|
106190
|
-
p50Ms: 215.5,
|
|
106191
|
-
fps: 5,
|
|
106192
|
-
measured: true
|
|
106193
|
-
},
|
|
106194
|
-
"yolov9m-640-int8": {
|
|
106195
|
-
p50Ms: 139.1,
|
|
106196
|
-
fps: 7,
|
|
106197
|
-
measured: true
|
|
106198
|
-
}
|
|
106199
|
-
},
|
|
106200
|
-
"x86-cpu": {
|
|
106201
|
-
"yolov9t-320": {
|
|
106202
|
-
p50Ms: 86.2,
|
|
106203
|
-
fps: 12,
|
|
106204
|
-
measured: true
|
|
106205
|
-
},
|
|
106206
|
-
"yolov9t-320-int8": {
|
|
106207
|
-
p50Ms: 52.7,
|
|
106208
|
-
fps: 20,
|
|
106209
|
-
measured: true
|
|
106210
|
-
},
|
|
106211
|
-
"yolov9t-640": {
|
|
106212
|
-
p50Ms: 322.1,
|
|
106213
|
-
fps: 3,
|
|
106214
|
-
measured: true
|
|
106215
|
-
},
|
|
106216
|
-
"yolov9t-640-int8": {
|
|
106217
|
-
p50Ms: 205.6,
|
|
106218
|
-
fps: 5,
|
|
106219
|
-
measured: true
|
|
106220
|
-
},
|
|
106221
|
-
"yolov9s-320": {
|
|
106222
|
-
p50Ms: 164.4,
|
|
106223
|
-
fps: 6,
|
|
106224
|
-
measured: true
|
|
106225
|
-
},
|
|
106226
|
-
"yolov9s-320-int8": {
|
|
106227
|
-
p50Ms: 86.9,
|
|
106228
|
-
fps: 12,
|
|
106229
|
-
measured: true
|
|
106230
|
-
},
|
|
106231
|
-
"yolov9s-640": {
|
|
106232
|
-
p50Ms: 341.3,
|
|
106233
|
-
fps: 3,
|
|
106234
|
-
measured: true
|
|
106235
|
-
},
|
|
106236
|
-
"yolov9s-640-int8": {
|
|
106237
|
-
p50Ms: 185.3,
|
|
106238
|
-
fps: 5,
|
|
106239
|
-
measured: true
|
|
106240
|
-
},
|
|
106241
|
-
"yolov9m-320": {
|
|
106242
|
-
p50Ms: 166.8,
|
|
106243
|
-
fps: 6,
|
|
106244
|
-
measured: true
|
|
106245
|
-
},
|
|
106246
|
-
"yolov9m-320-int8": {
|
|
106247
|
-
p50Ms: 226.3,
|
|
106248
|
-
fps: 4,
|
|
106249
|
-
measured: true
|
|
106250
|
-
},
|
|
106251
|
-
"yolov9m-640": {
|
|
106252
|
-
p50Ms: 663.5,
|
|
106253
|
-
fps: 2,
|
|
106254
|
-
measured: true
|
|
106255
|
-
},
|
|
106256
|
-
"yolov9m-640-int8": {
|
|
106257
|
-
p50Ms: 324.4,
|
|
106258
|
-
fps: 3,
|
|
106259
|
-
measured: true
|
|
106260
|
-
}
|
|
106261
|
-
},
|
|
106262
|
-
coral: { "yolov9s-320-edgetpu": {
|
|
106263
|
-
p50Ms: 32,
|
|
106264
|
-
fps: 31,
|
|
106265
|
-
measured: false
|
|
106266
|
-
} }
|
|
106267
|
-
},
|
|
106268
|
-
"amd-phoenix": {
|
|
106269
|
-
"x86-cpu": {
|
|
106270
|
-
"yolov9t-320": {
|
|
106271
|
-
p50Ms: 24,
|
|
106272
|
-
fps: 42,
|
|
106273
|
-
measured: true
|
|
106274
|
-
},
|
|
106275
|
-
"yolov9t-320-int8": {
|
|
106276
|
-
p50Ms: 12,
|
|
106277
|
-
fps: 83,
|
|
106278
|
-
measured: true
|
|
106279
|
-
},
|
|
106280
|
-
"yolov9t-640": {
|
|
106281
|
-
p50Ms: 50,
|
|
106282
|
-
fps: 20,
|
|
106283
|
-
measured: true
|
|
106284
|
-
},
|
|
106285
|
-
"yolov9t-640-int8": {
|
|
106286
|
-
p50Ms: 20,
|
|
106287
|
-
fps: 50,
|
|
106288
|
-
measured: true
|
|
106289
|
-
},
|
|
106290
|
-
"yolov9s-320": {
|
|
106291
|
-
p50Ms: 30,
|
|
106292
|
-
fps: 33,
|
|
106293
|
-
measured: true
|
|
106294
|
-
},
|
|
106295
|
-
"yolov9s-320-int8": {
|
|
106296
|
-
p50Ms: 15,
|
|
106297
|
-
fps: 67,
|
|
106298
|
-
measured: true
|
|
106299
|
-
},
|
|
106300
|
-
"yolov9s-640": {
|
|
106301
|
-
p50Ms: 92,
|
|
106302
|
-
fps: 11,
|
|
106303
|
-
measured: true
|
|
106304
|
-
},
|
|
106305
|
-
"yolov9s-640-int8": {
|
|
106306
|
-
p50Ms: 27,
|
|
106307
|
-
fps: 37,
|
|
106308
|
-
measured: true
|
|
106309
|
-
},
|
|
106310
|
-
"yolov9m-320": {
|
|
106311
|
-
p50Ms: 67,
|
|
106312
|
-
fps: 15,
|
|
106313
|
-
measured: true
|
|
106314
|
-
},
|
|
106315
|
-
"yolov9m-320-int8": {
|
|
106316
|
-
p50Ms: 21,
|
|
106317
|
-
fps: 48,
|
|
106318
|
-
measured: true
|
|
106319
|
-
},
|
|
106320
|
-
"yolov9m-640": {
|
|
106321
|
-
p50Ms: 229,
|
|
106322
|
-
fps: 4,
|
|
106323
|
-
measured: true
|
|
106324
|
-
},
|
|
106325
|
-
"yolov9m-640-int8": {
|
|
106326
|
-
p50Ms: 48,
|
|
106327
|
-
fps: 21,
|
|
106328
|
-
measured: true
|
|
106329
|
-
}
|
|
106330
|
-
},
|
|
106331
|
-
"amd-igpu": {
|
|
106332
|
-
"yolov9t-320": {
|
|
106333
|
-
p50Ms: 40,
|
|
106334
|
-
fps: 25,
|
|
106335
|
-
measured: true
|
|
106336
|
-
},
|
|
106337
|
-
"yolov9t-640": {
|
|
106338
|
-
p50Ms: 89,
|
|
106339
|
-
fps: 11,
|
|
106340
|
-
measured: true
|
|
106341
|
-
},
|
|
106342
|
-
"yolov9s-320": {
|
|
106343
|
-
p50Ms: 65,
|
|
106344
|
-
fps: 15,
|
|
106345
|
-
measured: true
|
|
106346
|
-
},
|
|
106347
|
-
"yolov9s-640": {
|
|
106348
|
-
p50Ms: 125,
|
|
106349
|
-
fps: 8,
|
|
106350
|
-
measured: true
|
|
106351
|
-
},
|
|
106352
|
-
"yolov9m-320": {
|
|
106353
|
-
p50Ms: 85,
|
|
106354
|
-
fps: 12,
|
|
106355
|
-
measured: true
|
|
106356
|
-
},
|
|
106357
|
-
"yolov9m-640": {
|
|
106358
|
-
p50Ms: 228,
|
|
106359
|
-
fps: 4,
|
|
106360
|
-
measured: true
|
|
106361
|
-
}
|
|
106362
|
-
},
|
|
106363
|
-
coral: { "yolov9s-320-edgetpu": {
|
|
106364
|
-
p50Ms: 32,
|
|
106365
|
-
fps: 31,
|
|
106366
|
-
measured: true
|
|
106367
|
-
} }
|
|
106368
|
-
},
|
|
106369
|
-
"apple-m-pro": {
|
|
106370
|
-
"apple-ane": {
|
|
106371
|
-
"yolov9t-320": {
|
|
106372
|
-
p50Ms: 1.1,
|
|
106373
|
-
fps: 952,
|
|
106374
|
-
measured: true
|
|
106375
|
-
},
|
|
106376
|
-
"yolov9t-640": {
|
|
106377
|
-
p50Ms: 3,
|
|
106378
|
-
fps: 333,
|
|
106379
|
-
measured: true
|
|
106380
|
-
},
|
|
106381
|
-
"yolov9s-320": {
|
|
106382
|
-
p50Ms: 1.5,
|
|
106383
|
-
fps: 667,
|
|
106384
|
-
measured: true
|
|
106385
|
-
},
|
|
106386
|
-
"yolov9s-640": {
|
|
106387
|
-
p50Ms: 5.1,
|
|
106388
|
-
fps: 196,
|
|
106389
|
-
measured: true
|
|
106390
|
-
},
|
|
106391
|
-
"yolov9m-320": {
|
|
106392
|
-
p50Ms: 3.2,
|
|
106393
|
-
fps: 312,
|
|
106394
|
-
measured: true
|
|
106395
|
-
},
|
|
106396
|
-
"yolov9m-640": {
|
|
106397
|
-
p50Ms: 10.2,
|
|
106398
|
-
fps: 98,
|
|
106399
|
-
measured: true
|
|
106400
|
-
}
|
|
106401
|
-
},
|
|
106402
|
-
coral: { "yolov9s-320-edgetpu": {
|
|
106403
|
-
p50Ms: 32,
|
|
106404
|
-
fps: 31,
|
|
106405
|
-
measured: false
|
|
106406
|
-
} }
|
|
106407
|
-
},
|
|
106408
|
-
"apple-m-base": {
|
|
106409
|
-
"apple-ane": {
|
|
106410
|
-
"yolov9t-320": {
|
|
106411
|
-
p50Ms: 1.32,
|
|
106412
|
-
fps: 758,
|
|
106413
|
-
measured: false
|
|
106414
|
-
},
|
|
106415
|
-
"yolov9t-640": {
|
|
106416
|
-
p50Ms: 3.45,
|
|
106417
|
-
fps: 290,
|
|
106418
|
-
measured: true
|
|
106419
|
-
},
|
|
106420
|
-
"yolov9s-320": {
|
|
106421
|
-
p50Ms: 1.8,
|
|
106422
|
-
fps: 556,
|
|
106423
|
-
measured: false
|
|
106424
|
-
},
|
|
106425
|
-
"yolov9s-640": {
|
|
106426
|
-
p50Ms: 5.46,
|
|
106427
|
-
fps: 183,
|
|
106428
|
-
measured: true
|
|
106429
|
-
},
|
|
106430
|
-
"yolov9m-320": {
|
|
106431
|
-
p50Ms: 3.79,
|
|
106432
|
-
fps: 264,
|
|
106433
|
-
measured: true
|
|
106434
|
-
},
|
|
106435
|
-
"yolov9m-640": {
|
|
106436
|
-
p50Ms: 10.34,
|
|
106437
|
-
fps: 97,
|
|
106438
|
-
measured: true
|
|
106439
|
-
}
|
|
106440
|
-
},
|
|
106441
|
-
coral: { "yolov9s-320-edgetpu": {
|
|
106442
|
-
p50Ms: 32,
|
|
106443
|
-
fps: 31,
|
|
106444
|
-
measured: false
|
|
106445
|
-
} }
|
|
106446
|
-
}
|
|
106677
|
+
return models.some((m) => m.legacy !== true && hasBuild(m, format));
|
|
106678
|
+
}
|
|
106679
|
+
//#endregion
|
|
106680
|
+
//#region src/pipeline/removed-model-ids.ts
|
|
106681
|
+
/**
|
|
106682
|
+
* Model ids DELETED from a step's catalog (D703, D710, D719), per step — and
|
|
106683
|
+
* the one question asked of them: is a stored pin to this id a pin to a model
|
|
106684
|
+
* that no longer exists? (D725)
|
|
106685
|
+
*
|
|
106686
|
+
* This is NOT a resolver input. `resolveModelForFormat` still returns an
|
|
106687
|
+
* unknown id unchanged (a custom model), and a bulk pass pinned to one still
|
|
106688
|
+
* refuses rather than substitutes (D54). The ONE reader is the orchestrator's
|
|
106689
|
+
* stored-pin reset (`removed-model-pin-reset.ts`), which clears such a pin from
|
|
106690
|
+
* the store so the step follows its default again — unless a registered custom
|
|
106691
|
+
* model carries the same id.
|
|
106692
|
+
*
|
|
106693
|
+
* `removed-model-ids.spec.ts` (addon-pipeline) pins that every id here is
|
|
106694
|
+
* really absent from its step's catalog. A model deleted from a catalog is
|
|
106695
|
+
* added here in the same change, or its stored pins stay behind as custom ids.
|
|
106696
|
+
*/
|
|
106697
|
+
/** `stepId → ids deleted from that step's catalog`. */
|
|
106698
|
+
var REMOVED_MODEL_IDS = {
|
|
106699
|
+
"object-detection": ["efficientdet-lite0-edgetpu", "ssdlite-mobiledet-coco-edgetpu"],
|
|
106700
|
+
"face-detection": [
|
|
106701
|
+
"scrypted-yolov9t-face",
|
|
106702
|
+
"scrfd-2.5g",
|
|
106703
|
+
"ssd-mobilenet-v2-face-edgetpu"
|
|
106704
|
+
],
|
|
106705
|
+
"face-embedding": ["arcface-r100", "inception-resnet-v1"],
|
|
106706
|
+
"plate-detection": ["yolov8n-plate"],
|
|
106707
|
+
"plate-ocr": ["vgg-english-g2"],
|
|
106708
|
+
"clip-embedding": [
|
|
106709
|
+
"mobileclip-s0",
|
|
106710
|
+
"mobileclip-s1",
|
|
106711
|
+
"mobileclip-s2"
|
|
106712
|
+
],
|
|
106713
|
+
"package-detection": ["yolov8n-package"],
|
|
106714
|
+
"bird-classifier": ["bird-classifier-aiy-edgetpu", "bird-nabirds-404"],
|
|
106715
|
+
"animal-classifier": ["animals-10"],
|
|
106716
|
+
"vehicle-classifier": ["vehicle-type-efficientnet"]
|
|
106447
106717
|
};
|
|
106448
|
-
|
|
106449
|
-
|
|
106450
|
-
|
|
106451
|
-
|
|
106452
|
-
|
|
106453
|
-
|
|
106454
|
-
|
|
106455
|
-
|
|
106456
|
-
|
|
106457
|
-
|
|
106458
|
-
|
|
106459
|
-
|
|
106460
|
-
|
|
106461
|
-
|
|
106462
|
-
|
|
106463
|
-
|
|
106464
|
-
|
|
106465
|
-
|
|
106466
|
-
|
|
106467
|
-
|
|
106468
|
-
|
|
106469
|
-
|
|
106470
|
-
|
|
106471
|
-
|
|
106472
|
-
|
|
106473
|
-
|
|
106474
|
-
|
|
106475
|
-
|
|
106476
|
-
|
|
106477
|
-
|
|
106478
|
-
|
|
106479
|
-
|
|
106480
|
-
}
|
|
106481
|
-
|
|
106482
|
-
|
|
106483
|
-
|
|
106484
|
-
}
|
|
106485
|
-
|
|
106486
|
-
|
|
106487
|
-
|
|
106488
|
-
|
|
106489
|
-
|
|
106490
|
-
|
|
106491
|
-
|
|
106492
|
-
|
|
106493
|
-
|
|
106494
|
-
|
|
106495
|
-
|
|
106718
|
+
/** True when `modelId` was deleted from `stepId`'s catalog. */
|
|
106719
|
+
function isRemovedModelId(stepId, modelId) {
|
|
106720
|
+
return REMOVED_MODEL_IDS[stepId]?.includes(modelId) ?? false;
|
|
106721
|
+
}
|
|
106722
|
+
//#endregion
|
|
106723
|
+
//#region src/pipeline/model-bound-settings.ts
|
|
106724
|
+
/** Suffix of the sibling key that names the model a stored value was made for. */
|
|
106725
|
+
var MODEL_BINDING_SUFFIX = "ModelId";
|
|
106726
|
+
/** `minConfidencePerson` → `minConfidencePersonModelId`. */
|
|
106727
|
+
function modelBindingKey(key) {
|
|
106728
|
+
return `${key}${MODEL_BINDING_SUFFIX}`;
|
|
106729
|
+
}
|
|
106730
|
+
/** The model a stored value was made for, or `undefined` when unbound. */
|
|
106731
|
+
function storedBinding(settings, key) {
|
|
106732
|
+
const binding = settings?.[modelBindingKey(key)];
|
|
106733
|
+
return typeof binding === "string" && binding.length > 0 ? binding : void 0;
|
|
106734
|
+
}
|
|
106735
|
+
/**
|
|
106736
|
+
* The effective value of `key` for the model that runs (`runningModelId`,
|
|
106737
|
+
* `undefined` when unknown — it then matches no binding).
|
|
106738
|
+
*/
|
|
106739
|
+
function resolveModelBoundSetting(settings, key, runningModelId, modelDefault, globalDefault) {
|
|
106740
|
+
const stored = settings?.[key];
|
|
106741
|
+
const binding = storedBinding(settings, key);
|
|
106742
|
+
if (stored !== void 0 && binding === void 0) return {
|
|
106743
|
+
value: stored,
|
|
106744
|
+
source: "override"
|
|
106745
|
+
};
|
|
106746
|
+
if (stored !== void 0 && binding !== void 0 && binding === runningModelId) return {
|
|
106747
|
+
value: stored,
|
|
106748
|
+
source: "override",
|
|
106749
|
+
boundModelId: binding
|
|
106750
|
+
};
|
|
106751
|
+
const inapplicable = stored !== void 0 && binding !== void 0 ? { inapplicableOverride: {
|
|
106752
|
+
value: stored,
|
|
106753
|
+
modelId: binding
|
|
106754
|
+
} } : {};
|
|
106755
|
+
if (modelDefault !== void 0) return {
|
|
106756
|
+
value: modelDefault,
|
|
106757
|
+
source: "model-default",
|
|
106758
|
+
...inapplicable
|
|
106759
|
+
};
|
|
106760
|
+
return {
|
|
106761
|
+
value: globalDefault,
|
|
106762
|
+
source: "global-default",
|
|
106763
|
+
...inapplicable
|
|
106764
|
+
};
|
|
106765
|
+
}
|
|
106766
|
+
/**
|
|
106767
|
+
* The OPERATOR layer of a step's settings as the running model sees it: every
|
|
106768
|
+
* model-bound value made for another model is removed (so the default
|
|
106769
|
+
* underneath — the running model's — wins the merge), and every binding key is
|
|
106770
|
+
* stripped. Unbound values are kept. Returns a fresh object; `undefined` stays
|
|
106771
|
+
* `undefined`.
|
|
106772
|
+
*/
|
|
106773
|
+
function applyModelBoundSettings(settings, boundKeys, runningModelId) {
|
|
106774
|
+
if (settings === void 0) return void 0;
|
|
106775
|
+
const out = { ...settings };
|
|
106776
|
+
for (const key of boundKeys) {
|
|
106777
|
+
const binding = storedBinding(settings, key);
|
|
106778
|
+
if (binding !== void 0 && binding !== runningModelId) delete out[key];
|
|
106779
|
+
delete out[modelBindingKey(key)];
|
|
106496
106780
|
}
|
|
106497
|
-
|
|
106781
|
+
return out;
|
|
106782
|
+
}
|
|
106783
|
+
/** Store `value` for `key`, made for `modelId`. Returns a fresh object. */
|
|
106784
|
+
function writeModelBoundSetting(settings, key, value, modelId) {
|
|
106785
|
+
return {
|
|
106786
|
+
...settings,
|
|
106787
|
+
[key]: value,
|
|
106788
|
+
[modelBindingKey(key)]: modelId
|
|
106789
|
+
};
|
|
106790
|
+
}
|
|
106791
|
+
/** Remove an override and its binding — "reset to the model default". */
|
|
106792
|
+
function clearModelBoundSetting(settings, key) {
|
|
106793
|
+
const out = { ...settings };
|
|
106794
|
+
delete out[key];
|
|
106795
|
+
delete out[modelBindingKey(key)];
|
|
106796
|
+
return out;
|
|
106797
|
+
}
|
|
106798
|
+
/** True when any of `boundKeys` has an override IN FORCE for `modelId`. */
|
|
106799
|
+
function hasModelBoundOverrides(settings, boundKeys, modelId) {
|
|
106800
|
+
return boundKeys.some((key) => resolveModelBoundSetting(settings, key, modelId, void 0, void 0).source === "override");
|
|
106801
|
+
}
|
|
106802
|
+
/**
|
|
106803
|
+
* "Reset thresholds to model defaults": remove every override of `boundKeys`
|
|
106804
|
+
* that is in force for `modelId` (bound to it, or unbound), with its binding —
|
|
106805
|
+
* one result, so the editor writes it in one save. An override stored for
|
|
106806
|
+
* ANOTHER model is kept: it is not in force here, and it applies again when
|
|
106807
|
+
* that model runs.
|
|
106808
|
+
*/
|
|
106809
|
+
function clearModelBoundOverrides(settings, boundKeys, modelId) {
|
|
106810
|
+
return boundKeys.reduce((acc, key) => resolveModelBoundSetting(acc, key, modelId, void 0, void 0).source === "override" ? clearModelBoundSetting(acc, key) : acc, { ...settings });
|
|
106811
|
+
}
|
|
106812
|
+
/**
|
|
106813
|
+
* The keys of every `bindsToModel` field of a step's config schema, groups
|
|
106814
|
+
* flattened (D738). One definition for the runner (which applies the binding)
|
|
106815
|
+
* and the orchestrator's Auto tune (which clears those overrides, D745).
|
|
106816
|
+
*/
|
|
106817
|
+
function collectModelBoundKeys(fields) {
|
|
106818
|
+
return fields.flatMap((field) => {
|
|
106819
|
+
if (field.type === "group") return collectModelBoundKeys(field.fields);
|
|
106820
|
+
return "key" in field && "bindsToModel" in field && field.bindsToModel === true ? [field.key] : [];
|
|
106821
|
+
});
|
|
106822
|
+
}
|
|
106823
|
+
//#endregion
|
|
106824
|
+
//#region src/pipeline/step-settings-layers.ts
|
|
106825
|
+
/**
|
|
106826
|
+
* The layers of a detection step's settings, and the field-level edits on them
|
|
106827
|
+
* (D754, D927).
|
|
106828
|
+
*
|
|
106829
|
+
* A step's settings reach a session through two stored layers, merged key by
|
|
106830
|
+
* key — a key in the higher layer wins:
|
|
106831
|
+
*
|
|
106832
|
+
* node the node's base for the accelerator that runs the step
|
|
106833
|
+
* camera `cameraSettings[cam].stepSettings[step]` — camera-semantic
|
|
106834
|
+
* keys only, on every accelerator (never a model-bound field)
|
|
106835
|
+
*
|
|
106836
|
+
* Below them sits the default: the running model's (D738) or the field's own.
|
|
106837
|
+
*
|
|
106838
|
+
* Every layer is SPARSE and FIELD-LEVEL. A key present in a layer is an
|
|
106839
|
+
* override; an absent key inherits. Two consequences that every editor must
|
|
106840
|
+
* respect, and why this module exists:
|
|
106841
|
+
*
|
|
106842
|
+
* - A model-bound field (D738) occupies TWO keys, its value and
|
|
106843
|
+
* `<key>ModelId`. They are written, compared and removed as one unit — a
|
|
106844
|
+
* value without its binding over a lower layer's binding would be judged
|
|
106845
|
+
* against the wrong model.
|
|
106846
|
+
* - An editor writes a DIFF against the merged layers below, never a copy of
|
|
106847
|
+
* them. A copy pins every field at the value it had that day, so later
|
|
106848
|
+
* edits below never reach it (the full-copy "Override" this replaced).
|
|
106849
|
+
*/
|
|
106850
|
+
/** Top-down order of the stored layers. */
|
|
106851
|
+
var LAYER_ORDER = ["camera", "node"];
|
|
106852
|
+
/**
|
|
106853
|
+
* The keys one field occupies in a bag: its own, plus its binding key when it
|
|
106854
|
+
* binds to a model. `boundKeys` = `collectModelBoundKeys(schema)`.
|
|
106855
|
+
*/
|
|
106856
|
+
function fieldUnitKeys(key, boundKeys) {
|
|
106857
|
+
return boundKeys.includes(key) ? [key, modelBindingKey(key)] : [key];
|
|
106858
|
+
}
|
|
106859
|
+
/** The field a stored key belongs to (`minConfidencePersonModelId` → `minConfidencePerson`). */
|
|
106860
|
+
function unitOf(storedKey, boundKeys) {
|
|
106861
|
+
return boundKeys.find((k) => modelBindingKey(k) === storedKey) ?? storedKey;
|
|
106862
|
+
}
|
|
106863
|
+
/** Structural equality for stored values (numbers, strings, booleans, arrays, plain objects). */
|
|
106864
|
+
function settingValuesEqual(a, b) {
|
|
106865
|
+
if (Object.is(a, b)) return true;
|
|
106866
|
+
if (Array.isArray(a) && Array.isArray(b)) return a.length === b.length && a.every((v, i) => settingValuesEqual(v, b[i]));
|
|
106867
|
+
if (isPlainObject(a) && isPlainObject(b)) {
|
|
106868
|
+
const ka = Object.keys(a);
|
|
106869
|
+
const kb = Object.keys(b);
|
|
106870
|
+
return ka.length === kb.length && ka.every((k) => settingValuesEqual(a[k], b[k]));
|
|
106871
|
+
}
|
|
106872
|
+
return false;
|
|
106873
|
+
}
|
|
106874
|
+
function isPlainObject(v) {
|
|
106875
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
106876
|
+
}
|
|
106877
|
+
/** Merge bags bottom-up (later wins, per key). Fresh object. */
|
|
106878
|
+
function mergeSettingsBags(...bags) {
|
|
106879
|
+
return bags.reduce((acc, bag) => ({
|
|
106880
|
+
...acc,
|
|
106881
|
+
...bag
|
|
106882
|
+
}), {});
|
|
106883
|
+
}
|
|
106884
|
+
/** The merged value of the layers BELOW `layer` (what removing a key from `layer` falls back to). */
|
|
106885
|
+
function settingsBelow(layers, layer) {
|
|
106886
|
+
return mergeSettingsBags(...LAYER_ORDER.slice(LAYER_ORDER.indexOf(layer) + 1).toReversed().map((id) => layers[id]));
|
|
106887
|
+
}
|
|
106888
|
+
/** The effective bag: every stored layer merged. */
|
|
106889
|
+
function effectiveStepSettings(layers) {
|
|
106890
|
+
return mergeSettingsBags(layers.node, layers.camera);
|
|
106891
|
+
}
|
|
106892
|
+
/** The highest stored layer that holds any key of `key`'s unit, or `default`. */
|
|
106893
|
+
function stepSettingSource(layers, key, boundKeys) {
|
|
106894
|
+
const unit = fieldUnitKeys(key, boundKeys);
|
|
106895
|
+
for (const id of LAYER_ORDER) {
|
|
106896
|
+
const bag = layers[id];
|
|
106897
|
+
if (bag !== void 0 && unit.some((k) => bag[k] !== void 0)) return id;
|
|
106898
|
+
}
|
|
106899
|
+
return "default";
|
|
106900
|
+
}
|
|
106901
|
+
/**
|
|
106902
|
+
* `layer` with `key`'s unit set from `values` (a bag carrying the unit's new
|
|
106903
|
+
* value — e.g. the editor's whole next settings bag). A unit key that `values`
|
|
106904
|
+
* does not carry is removed from the layer. Fresh object.
|
|
106905
|
+
*/
|
|
106906
|
+
function withFieldFrom(layer, key, values, boundKeys) {
|
|
106907
|
+
const out = { ...layer };
|
|
106908
|
+
for (const k of fieldUnitKeys(key, boundKeys)) if (values[k] === void 0) delete out[k];
|
|
106909
|
+
else out[k] = values[k];
|
|
106910
|
+
return out;
|
|
106911
|
+
}
|
|
106912
|
+
/** `layer` without `key`'s unit — "reset to the next level". Fresh object. */
|
|
106913
|
+
function withoutField(layer, key, boundKeys) {
|
|
106914
|
+
const out = { ...layer };
|
|
106915
|
+
for (const k of fieldUnitKeys(key, boundKeys)) delete out[k];
|
|
106916
|
+
return out;
|
|
106917
|
+
}
|
|
106918
|
+
/** Every field unit stored in `layer`, by its field key. */
|
|
106919
|
+
function unitsIn(layer, boundKeys) {
|
|
106920
|
+
return [...new Set(Object.keys(layer).map((k) => unitOf(k, boundKeys)))];
|
|
106921
|
+
}
|
|
106922
|
+
/** True when every key of `key`'s unit has the same value in `layer` as in `below`. */
|
|
106923
|
+
function unitEqualsBelow(layer, below, key, boundKeys) {
|
|
106924
|
+
return fieldUnitKeys(key, boundKeys).every((k) => settingValuesEqual(layer[k], below[k]));
|
|
106925
|
+
}
|
|
106926
|
+
/**
|
|
106927
|
+
* `layer` reduced to the fields that DIFFER from `below` — what an editor
|
|
106928
|
+
* stores. A unit is kept or dropped whole. `undefined` when nothing is left.
|
|
106929
|
+
*/
|
|
106930
|
+
function diffStepSettings(layer, below, boundKeys) {
|
|
106931
|
+
if (layer === void 0) return void 0;
|
|
106932
|
+
const out = {};
|
|
106933
|
+
for (const unit of unitsIn(layer, boundKeys)) {
|
|
106934
|
+
if (unitEqualsBelow(layer, below, unit, boundKeys)) continue;
|
|
106935
|
+
for (const k of fieldUnitKeys(unit, boundKeys)) if (layer[k] !== void 0) out[k] = layer[k];
|
|
106936
|
+
}
|
|
106937
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
106938
|
+
}
|
|
106939
|
+
/**
|
|
106940
|
+
* The fields `layer` stores at exactly the value of the layers below — a
|
|
106941
|
+
* legacy full copy's leftovers. They change nothing today and stop every
|
|
106942
|
+
* later edit below from reaching this layer. Sorted.
|
|
106943
|
+
*/
|
|
106944
|
+
function fieldsPinnedAtValueBelow(layer, below, boundKeys) {
|
|
106945
|
+
if (layer === void 0) return [];
|
|
106946
|
+
return unitsIn(layer, boundKeys).filter((unit) => unitEqualsBelow(layer, below, unit, boundKeys)).toSorted();
|
|
106947
|
+
}
|
|
106948
|
+
//#endregion
|
|
106949
|
+
//#region src/pipeline/auto-tune/hardware-class.ts
|
|
106950
|
+
var APPLE_CHIP = /^Apple M(\d+)(?:\s+(Pro|Max|Ultra))?\b/i;
|
|
106951
|
+
var INTEL_CORE_ULTRA = /Core\(TM\)\s+Ultra|Core Ultra/i;
|
|
106952
|
+
var INTEL_N_SERIES = /Intel\(R\)\s+N\d{2,3}\b|\bIntel\s+N\d{2,3}\b/i;
|
|
106953
|
+
/** Phoenix / Hawk Point mobile (7040/8040) and Ryzen 8000G desktop APUs. */
|
|
106954
|
+
var AMD_PHOENIX = /Ryzen \d+ (7[0-9]40|8[0-9]40|8[0-9]45)[HSU]|Ryzen \d+ 8[0-9]00G/i;
|
|
106955
|
+
function parseAppleChip(cpuModel) {
|
|
106956
|
+
const match = APPLE_CHIP.exec(cpuModel.trim());
|
|
106957
|
+
if (match === null) return null;
|
|
106958
|
+
const generation = Number(match[1]);
|
|
106959
|
+
const variantRaw = (match[2] ?? "base").toLowerCase();
|
|
106960
|
+
return {
|
|
106961
|
+
generation,
|
|
106962
|
+
variant: variantRaw === "pro" || variantRaw === "max" || variantRaw === "ultra" ? variantRaw : "base"
|
|
106963
|
+
};
|
|
106964
|
+
}
|
|
106965
|
+
/**
|
|
106966
|
+
* Classify a node. Apple base chips (M1–M4) map to the measured M2 column, the
|
|
106967
|
+
* Pro/Max/Ultra chips to the measured M3 Pro column — the M2 was measured at
|
|
106968
|
+
* 1.0–1.2× the M3 Pro on the Neural Engine, so the generation inside a variant
|
|
106969
|
+
* matters less than the column's own error.
|
|
106970
|
+
*/
|
|
106971
|
+
function classifyInferenceHardware(hw) {
|
|
106972
|
+
const cpuModel = hw.cpuModel;
|
|
106973
|
+
const apple = parseAppleChip(cpuModel);
|
|
106974
|
+
if (apple !== null) return {
|
|
106975
|
+
hardwareClass: apple.variant === "base" ? "apple-m-base" : "apple-m-pro",
|
|
106976
|
+
cpuModel,
|
|
106977
|
+
apple
|
|
106978
|
+
};
|
|
106979
|
+
if (INTEL_CORE_ULTRA.test(cpuModel)) return {
|
|
106980
|
+
hardwareClass: "intel-core-ultra",
|
|
106981
|
+
cpuModel
|
|
106982
|
+
};
|
|
106983
|
+
if (INTEL_N_SERIES.test(cpuModel)) return {
|
|
106984
|
+
hardwareClass: "intel-n-series",
|
|
106985
|
+
cpuModel
|
|
106986
|
+
};
|
|
106987
|
+
if (AMD_PHOENIX.test(cpuModel)) return {
|
|
106988
|
+
hardwareClass: "amd-phoenix",
|
|
106989
|
+
cpuModel
|
|
106990
|
+
};
|
|
106991
|
+
return {
|
|
106992
|
+
hardwareClass: "unknown",
|
|
106993
|
+
cpuModel
|
|
106994
|
+
};
|
|
106995
|
+
}
|
|
106996
|
+
/**
|
|
106997
|
+
* The accelerator kind of one of a node's device keys (`openvino:npu`,
|
|
106998
|
+
* `openvino:gpu`, `coreml:all`, `edgetpu:usb`, `cpu`, `onnx:cpu`, …). `null`
|
|
106999
|
+
* for a key this table has no column for.
|
|
107000
|
+
*/
|
|
107001
|
+
function acceleratorKindOf(deviceKey, hw) {
|
|
107002
|
+
const [backend = "", device = ""] = deviceKey.split(":");
|
|
107003
|
+
if (backend === "edgetpu") return "coral";
|
|
107004
|
+
if (backend === "coreml") return device === "cpu" ? "arm-cpu" : "apple-ane";
|
|
107005
|
+
if (backend === "openvino" && device === "npu") return "intel-npu";
|
|
107006
|
+
if (backend === "openvino" && device === "gpu") return "intel-igpu";
|
|
107007
|
+
if (backend === "onnx" && device === "webgpu") return "amd-igpu";
|
|
107008
|
+
if (backend === "vitisai" && device === "npu") return "amd-npu";
|
|
107009
|
+
if (backend === "cpu" || device === "cpu") return hw.arch === "arm64" ? "arm-cpu" : "x86-cpu";
|
|
107010
|
+
return null;
|
|
107011
|
+
}
|
|
106498
107012
|
//#endregion
|
|
106499
107013
|
//#region src/pipeline/auto-tune/recommend.ts
|
|
106500
107014
|
/** The family the cluster runs. */
|
|
@@ -106510,10 +107024,35 @@ var RECALL_TIE = .02;
|
|
|
106510
107024
|
var DEFAULT_HEADROOM = .4;
|
|
106511
107025
|
/** Detection rate a session is counted at when no camera says otherwise. */
|
|
106512
107026
|
var DEFAULT_CAMERA_FPS = 10;
|
|
106513
|
-
/**
|
|
106514
|
-
|
|
106515
|
-
|
|
106516
|
-
|
|
107027
|
+
/**
|
|
107028
|
+
* Camera rate at which serial `1000/p50` matched the cameras that held
|
|
107029
|
+
* (N100 iGPU, 2026-10-24). Below this the second InferRequest's overlap
|
|
107030
|
+
* becomes extra sessions; at and above it the serial ceiling is the count.
|
|
107031
|
+
*/
|
|
107032
|
+
var SERIAL_CAPACITY_CAMERA_FPS = 10;
|
|
107033
|
+
/**
|
|
107034
|
+
* Aggregate throughput ÷ serial throughput when the frame interval is wide
|
|
107035
|
+
* enough to hide the second request. intel-igpu is the N100 measurement
|
|
107036
|
+
* (~1.4× under LATENCY with 2 InferRequests). Every other kind stays 1
|
|
107037
|
+
* until it is measured the same way.
|
|
107038
|
+
*/
|
|
107039
|
+
var AGGREGATE_THROUGHPUT_BY_KIND = { "intel-igpu": 1.4 };
|
|
107040
|
+
/** The overlap factor for this kind at this camera rate. 1 at {@link SERIAL_CAPACITY_CAMERA_FPS} and above. */
|
|
107041
|
+
function aggregateThroughputFactor(kind, cameraFps) {
|
|
107042
|
+
if (!(cameraFps > 0) || cameraFps >= 10) return 1;
|
|
107043
|
+
return AGGREGATE_THROUGHPUT_BY_KIND[kind] ?? 1;
|
|
107044
|
+
}
|
|
107045
|
+
/**
|
|
107046
|
+
* Parallel sessions one device sustains at `fps` with `headroom` kept free.
|
|
107047
|
+
* `aggregateFactor` is the kind's overlap (1 unless the caller measured
|
|
107048
|
+
* extra throughput at this camera rate).
|
|
107049
|
+
*/
|
|
107050
|
+
function capacitySessionsOf(fps, headroom, cameraFps, aggregateFactor = 1) {
|
|
107051
|
+
if (!(fps > 0) || !(cameraFps > 0) || !(aggregateFactor > 0)) return 0;
|
|
107052
|
+
return Math.max(0, Math.floor(fps * aggregateFactor * (1 - headroom) / cameraFps + 1e-9));
|
|
107053
|
+
}
|
|
107054
|
+
function reportFor(hardware, deviceKey) {
|
|
107055
|
+
return hardware.accelerators?.find((report) => report.deviceKey === deviceKey) ?? null;
|
|
106517
107056
|
}
|
|
106518
107057
|
/** The table column with the node's own measurements laid over it. */
|
|
106519
107058
|
function overlayMeasured(table, measured) {
|
|
@@ -106535,11 +107074,12 @@ function round(value, digits = 3) {
|
|
|
106535
107074
|
const f = 10 ** digits;
|
|
106536
107075
|
return Math.round(value * f) / f;
|
|
106537
107076
|
}
|
|
106538
|
-
function sessionsOn(cell, sizing) {
|
|
106539
|
-
|
|
107077
|
+
function sessionsOn(cell, sizing, kind) {
|
|
107078
|
+
if (cell === void 0) return 0;
|
|
107079
|
+
return capacitySessionsOf(cell.fps, sizing.headroom, sizing.cameraFps, aggregateThroughputFactor(kind, sizing.cameraFps));
|
|
106540
107080
|
}
|
|
106541
|
-
function fitsOn(
|
|
106542
|
-
return sessionsOn(
|
|
107081
|
+
function fitsOn(ctx, modelId, sizing) {
|
|
107082
|
+
return sessionsOn(ctx.column[modelId], sizing, ctx.accelerator) >= sizing.minSessions;
|
|
106543
107083
|
}
|
|
106544
107084
|
function candidatesFor(contexts, formatsByModel) {
|
|
106545
107085
|
return Object.keys(DETECTOR_ACCURACY).filter((id) => id.startsWith(AUTO_TUNE_FAMILY)).filter((id) => contexts.every((c) => c.column[id] !== void 0 && (formatsByModel[id] ?? []).includes(c.device.format))).map((id) => ({
|
|
@@ -106552,13 +107092,13 @@ function totalLatency(contexts, modelId) {
|
|
|
106552
107092
|
}
|
|
106553
107093
|
/** The fewest sessions any device of the group sustains with this model. */
|
|
106554
107094
|
function worstSessions(contexts, modelId, sizing) {
|
|
106555
|
-
return contexts.reduce((worst, c) => Math.min(worst, sessionsOn(c.column[modelId], sizing)), Number.POSITIVE_INFINITY);
|
|
107095
|
+
return contexts.reduce((worst, c) => Math.min(worst, sessionsOn(c.column[modelId], sizing, c.accelerator)), Number.POSITIVE_INFINITY);
|
|
106556
107096
|
}
|
|
106557
107097
|
/** One model for a group of devices that must agree (step 2–4 of the header). */
|
|
106558
107098
|
function pickForGroup(contexts, formatsByModel, sizing) {
|
|
106559
107099
|
const candidates = candidatesFor(contexts, formatsByModel);
|
|
106560
107100
|
if (candidates.length === 0) return null;
|
|
106561
|
-
const fitting = candidates.filter((c) => contexts.every((ctx) => fitsOn(ctx
|
|
107101
|
+
const fitting = candidates.filter((c) => contexts.every((ctx) => fitsOn(ctx, c.modelId, sizing)));
|
|
106562
107102
|
if (fitting.length === 0) {
|
|
106563
107103
|
const lightest = candidates.toSorted((a, b) => worstSessions(contexts, b.modelId, sizing) - worstSessions(contexts, a.modelId, sizing) || totalLatency(contexts, a.modelId) - totalLatency(contexts, b.modelId))[0];
|
|
106564
107104
|
if (lightest === void 0) return null;
|
|
@@ -106577,7 +107117,7 @@ function pickForGroup(contexts, formatsByModel, sizing) {
|
|
|
106577
107117
|
const reasons = [];
|
|
106578
107118
|
const mostAccurate = candidates[0];
|
|
106579
107119
|
if (mostAccurate !== void 0 && mostAccurate.modelId !== best.modelId) {
|
|
106580
|
-
const limitedBy = contexts.find((ctx) => !fitsOn(ctx
|
|
107120
|
+
const limitedBy = contexts.find((ctx) => !fitsOn(ctx, mostAccurate.modelId, sizing))?.device.key ?? "";
|
|
106581
107121
|
reasons.push({
|
|
106582
107122
|
kind: "stepped-down",
|
|
106583
107123
|
from: mostAccurate.modelId,
|
|
@@ -106598,13 +107138,19 @@ function pickForGroup(contexts, formatsByModel, sizing) {
|
|
|
106598
107138
|
function choiceOn(ctx, modelId, sizing) {
|
|
106599
107139
|
const cell = ctx.column[modelId];
|
|
106600
107140
|
if (cell === void 0) return null;
|
|
107141
|
+
const estimatedFrom = cell.source?.kind === "node-benchmark" ? void 0 : cell.estimatedFrom;
|
|
107142
|
+
const source = cell.source ?? (estimatedFrom !== void 0 ? {
|
|
107143
|
+
kind: "estimated",
|
|
107144
|
+
estimatedFrom
|
|
107145
|
+
} : { kind: "table" });
|
|
106601
107146
|
return {
|
|
106602
107147
|
modelId,
|
|
106603
107148
|
p50Ms: cell.p50Ms,
|
|
106604
107149
|
fps: cell.fps,
|
|
106605
107150
|
measured: cell.measured,
|
|
106606
|
-
source
|
|
106607
|
-
|
|
107151
|
+
source,
|
|
107152
|
+
...estimatedFrom !== void 0 ? { estimatedFrom } : {},
|
|
107153
|
+
capacitySessions: sessionsOn(cell, sizing, ctx.accelerator),
|
|
106608
107154
|
personRecall: DETECTOR_ACCURACY[modelId]?.personRecall ?? 0
|
|
106609
107155
|
};
|
|
106610
107156
|
}
|
|
@@ -106655,7 +107201,11 @@ function recommendNode(input) {
|
|
|
106655
107201
|
const measured = input.measured ?? {};
|
|
106656
107202
|
const hasMeasured = Object.values(measured).some((m) => Object.keys(m).length > 0);
|
|
106657
107203
|
const knownTable = hardwareClass === "unknown" ? void 0 : perf[hardwareClass];
|
|
106658
|
-
|
|
107204
|
+
const canEstimate = (hardware.accelerators ?? []).some((report) => {
|
|
107205
|
+
const kind = acceleratorKindOf(report.deviceKey, hardware);
|
|
107206
|
+
return kind !== null && reportIsScalable(report, kind);
|
|
107207
|
+
});
|
|
107208
|
+
if (knownTable === void 0 && !hasMeasured && !canEstimate) return skippedNode(node, {
|
|
106659
107209
|
kind: "no-measured-data",
|
|
106660
107210
|
hardwareClass
|
|
106661
107211
|
}, hardwareClass);
|
|
@@ -106691,7 +107241,12 @@ function recommendNode(input) {
|
|
|
106691
107241
|
});
|
|
106692
107242
|
continue;
|
|
106693
107243
|
}
|
|
106694
|
-
const column = accelerator === null ? void 0 : overlayMeasured(
|
|
107244
|
+
const column = accelerator === null ? void 0 : overlayMeasured(resolveLatencyColumn({
|
|
107245
|
+
hardwareClass,
|
|
107246
|
+
accelerator,
|
|
107247
|
+
report: reportFor(hardware, device.key),
|
|
107248
|
+
perf
|
|
107249
|
+
})?.column, measured[device.key]);
|
|
106695
107250
|
if (accelerator === null || column === void 0) {
|
|
106696
107251
|
early.set(device.key, [{
|
|
106697
107252
|
kind: "no-family-build",
|
|
@@ -107439,6 +107994,507 @@ function buildBenchStepTree(schema, rootModelId, enabledStepIds) {
|
|
|
107439
107994
|
}];
|
|
107440
107995
|
}
|
|
107441
107996
|
//#endregion
|
|
107997
|
+
//#region src/pipeline/second-pass/small-subject-recovery.ts
|
|
107998
|
+
/** The object-detection step id — the first-level detector both recipes run. */
|
|
107999
|
+
var OBJECT_DETECTION_STEP_ID = "object-detection";
|
|
108000
|
+
/**
|
|
108001
|
+
* Smallest crop side, native pixels. Below this the model no longer
|
|
108002
|
+
* magnifies enough to matter on a 4K frame, and on a 1080p frame 640 is
|
|
108003
|
+
* already a third of the picture. Measured: 480–800 px all recover the dog.
|
|
108004
|
+
*/
|
|
108005
|
+
var RECOVERY_CROP_MIN_SIDE_PX = 640;
|
|
108006
|
+
/**
|
|
108007
|
+
* Largest crop side. A 320 model on a 1024 px square is a 3.2× reduction and
|
|
108008
|
+
* scored the dog at 0.52–0.76; at 1280 px it was absent again. A region wider
|
|
108009
|
+
* than this is tiled, never zoomed out.
|
|
108010
|
+
*/
|
|
108011
|
+
var RECOVERY_CROP_MAX_SIDE_PX = 800;
|
|
108012
|
+
/** Context around the region before squaring, as a fraction of its larger side. */
|
|
108013
|
+
var RECOVERY_CROP_PADDING_RATIO = .25;
|
|
108014
|
+
/**
|
|
108015
|
+
* The view the crop is resolved at, compressed. Never larger than the crop
|
|
108016
|
+
* (the floor is {@link RECOVERY_CROP_MIN_SIDE_PX}), so it never upscales, and
|
|
108017
|
+
* the model letterboxes it to its own input from here as it does the frame.
|
|
108018
|
+
*/
|
|
108019
|
+
var RECOVERY_VIEW_PX = 640;
|
|
108020
|
+
/**
|
|
108021
|
+
* Crops per frame. Each is one more inference on the same pool (15–25 ms on
|
|
108022
|
+
* CoreML for a 640 view); two cover a person-and-dog blob and a second mover.
|
|
108023
|
+
*/
|
|
108024
|
+
var RECOVERY_MAX_CROPS_PER_FRAME = 2;
|
|
108025
|
+
/**
|
|
108026
|
+
* The pass runs at most this often per camera — at 10 fps that is one frame
|
|
108027
|
+
* in five. A subject the full pass misses is missed for seconds, not for one
|
|
108028
|
+
* frame, so the recovery does not need every frame to find it.
|
|
108029
|
+
*
|
|
108030
|
+
* The floor under this number is the tracker: a track coasts for
|
|
108031
|
+
* `maxMissedMs` (4 000 ms by default) before it is dropped, so 2 recoveries a
|
|
108032
|
+
* second keep a subject the full-frame pass has lost ALIVE with 8× margin,
|
|
108033
|
+
* and reach `minHits` (3) in 1.5 s. Raising it past ~1 300 ms would start to
|
|
108034
|
+
* lose tracks between passes.
|
|
108035
|
+
*/
|
|
108036
|
+
var RECOVERY_MIN_INTERVAL_MS = 500;
|
|
108037
|
+
/**
|
|
108038
|
+
* A region larger than this share of the frame is not a small subject: a
|
|
108039
|
+
* lighting change, a car filling the picture, a person at the lens. The
|
|
108040
|
+
* full-frame pass already sees those at scale.
|
|
108041
|
+
*/
|
|
108042
|
+
var RECOVERY_MAX_REGION_FRAME_FRACTION = .25;
|
|
108043
|
+
/**
|
|
108044
|
+
* How much of a region must lie OUTSIDE every full-frame detection for the
|
|
108045
|
+
* pass to run on it. A person's own blob (dilated, with shadow) is covered
|
|
108046
|
+
* 70–90% by the person box; a person-and-dog blob is covered ~55% by it.
|
|
108047
|
+
*/
|
|
108048
|
+
var RECOVERY_MIN_UNCOVERED_FRACTION = .35;
|
|
108049
|
+
/** Motion regions older than this describe a different frame. */
|
|
108050
|
+
var RECOVERY_MOTION_MAX_AGE_MS = 2e3;
|
|
108051
|
+
/** A recovered box overlapping a same-class full-frame box this much is the same subject. */
|
|
108052
|
+
var RECOVERY_FUSE_IOU = .5;
|
|
108053
|
+
/**
|
|
108054
|
+
* A recovered box CONTAINED this much in a same-class full-frame box is a part
|
|
108055
|
+
* of it — a torso the crop cut out of a person the full pass already has.
|
|
108056
|
+
*/
|
|
108057
|
+
var RECOVERY_FUSE_CONTAINMENT = .7;
|
|
108058
|
+
/** A box within this many crop-view pixels of the crop edge was clipped by it. */
|
|
108059
|
+
var RECOVERY_EDGE_MARGIN_PX = 2;
|
|
108060
|
+
/** Area of a normalised rectangle, never negative. Shared with the D396 recipe. */
|
|
108061
|
+
function normalizedArea(r) {
|
|
108062
|
+
return Math.max(0, r.w) * Math.max(0, r.h);
|
|
108063
|
+
}
|
|
108064
|
+
var area = normalizedArea;
|
|
108065
|
+
function intersection(a, b) {
|
|
108066
|
+
return Math.max(0, Math.min(a.x + a.w, b.x + b.w) - Math.max(a.x, b.x)) * Math.max(0, Math.min(a.y + a.h, b.y + b.h) - Math.max(a.y, b.y));
|
|
108067
|
+
}
|
|
108068
|
+
/**
|
|
108069
|
+
* Share of `region` not covered by any detection box. The sum of pairwise
|
|
108070
|
+
* intersections over-counts where detections overlap each other, which only
|
|
108071
|
+
* makes the pass MORE conservative; clipped at 1.
|
|
108072
|
+
*/
|
|
108073
|
+
function uncoveredFraction(region, detections) {
|
|
108074
|
+
const regionArea = area(region);
|
|
108075
|
+
if (regionArea <= 0) return 0;
|
|
108076
|
+
let covered = 0;
|
|
108077
|
+
for (const detection of detections) covered += intersection(region, detection);
|
|
108078
|
+
return 1 - Math.min(1, covered / regionArea);
|
|
108079
|
+
}
|
|
108080
|
+
/**
|
|
108081
|
+
* THE square-placement rule of this pass — one owner, shared by both recipes.
|
|
108082
|
+
*
|
|
108083
|
+
* A crop is a square of `side` native pixels centred where it can be and slid
|
|
108084
|
+
* inside the frame where it cannot. Both region rules place their squares
|
|
108085
|
+
* through this function so a crop means the same rectangle whichever rule
|
|
108086
|
+
* chose the region.
|
|
108087
|
+
*/
|
|
108088
|
+
function squareAround(centreX, centreY, side, native) {
|
|
108089
|
+
return {
|
|
108090
|
+
left: Math.round(Math.max(0, Math.min(native.width - side, centreX - side / 2))),
|
|
108091
|
+
top: Math.round(Math.max(0, Math.min(native.height - side, centreY - side / 2))),
|
|
108092
|
+
side
|
|
108093
|
+
};
|
|
108094
|
+
}
|
|
108095
|
+
function contains(crop, x, y) {
|
|
108096
|
+
return x >= crop.left && x <= crop.left + crop.side && y >= crop.top && y <= crop.top + crop.side;
|
|
108097
|
+
}
|
|
108098
|
+
/**
|
|
108099
|
+
* Squares for one region: one when it fits the largest side, otherwise up to
|
|
108100
|
+
* two tiles along its longer axis (a person-sized blob with something small
|
|
108101
|
+
* at either end). The padded region decides the side, never the frame.
|
|
108102
|
+
*/
|
|
108103
|
+
function cropsForRegion(region, native) {
|
|
108104
|
+
const widthPx = region.w * native.width;
|
|
108105
|
+
const heightPx = region.h * native.height;
|
|
108106
|
+
const longest = Math.max(widthPx, heightPx);
|
|
108107
|
+
const padded = longest * 1.5;
|
|
108108
|
+
const side = Math.round(Math.min(Math.max(padded, 640), 800));
|
|
108109
|
+
const centreX = (region.x + region.w / 2) * native.width;
|
|
108110
|
+
const centreY = (region.y + region.h / 2) * native.height;
|
|
108111
|
+
if (padded <= 800) return [squareAround(centreX, centreY, side, native)];
|
|
108112
|
+
const alongX = widthPx >= heightPx;
|
|
108113
|
+
const quarter = longest / 4;
|
|
108114
|
+
return [squareAround(alongX ? centreX - quarter : centreX, alongX ? centreY : centreY - quarter, side, native), squareAround(alongX ? centreX + quarter : centreX, alongX ? centreY : centreY + quarter, side, native)];
|
|
108115
|
+
}
|
|
108116
|
+
/**
|
|
108117
|
+
* The ONE place the two region gates are decided. `planRecoveryCrops` filters
|
|
108118
|
+
* on it and {@link countRecoverySkips} counts it, so the report can never
|
|
108119
|
+
* describe a gate the planner does not actually apply.
|
|
108120
|
+
*/
|
|
108121
|
+
function classifyRegion(region, uncovered) {
|
|
108122
|
+
if (area(region) > .25) return "too-large";
|
|
108123
|
+
if (uncovered < .35) return "covered";
|
|
108124
|
+
return "eligible";
|
|
108125
|
+
}
|
|
108126
|
+
/**
|
|
108127
|
+
* WHY A FRAME PLANNED NO CROP, AS A NUMBER (D378).
|
|
108128
|
+
*
|
|
108129
|
+
* The pass report counts the passes that RAN, so "the second pass stops running
|
|
108130
|
+
* when a person is beside the dog" — a plausible reading of
|
|
108131
|
+
* {@link RECOVERY_MAX_REGION_FRAME_FRACTION} and
|
|
108132
|
+
* {@link RECOVERY_MIN_UNCOVERED_FRACTION}, and a live hypothesis on camera 615
|
|
108133
|
+
* — could not be confirmed or killed from the logs at all. This classifies the
|
|
108134
|
+
* regions a frame offered, against the same predicate the planner filters on.
|
|
108135
|
+
*/
|
|
108136
|
+
function countRecoverySkips(input) {
|
|
108137
|
+
let tooLarge = 0;
|
|
108138
|
+
let covered = 0;
|
|
108139
|
+
let eligible = 0;
|
|
108140
|
+
for (const region of input.regions) switch (classifyRegion(region, uncoveredFraction(region, input.detections))) {
|
|
108141
|
+
case "too-large":
|
|
108142
|
+
tooLarge += 1;
|
|
108143
|
+
break;
|
|
108144
|
+
case "covered":
|
|
108145
|
+
covered += 1;
|
|
108146
|
+
break;
|
|
108147
|
+
case "eligible":
|
|
108148
|
+
eligible += 1;
|
|
108149
|
+
break;
|
|
108150
|
+
}
|
|
108151
|
+
return {
|
|
108152
|
+
tooLarge,
|
|
108153
|
+
covered,
|
|
108154
|
+
eligible
|
|
108155
|
+
};
|
|
108156
|
+
}
|
|
108157
|
+
function planRecoveryCrops(input) {
|
|
108158
|
+
const { native } = input;
|
|
108159
|
+
if (Math.min(native.width, native.height) < 640) return [];
|
|
108160
|
+
const ranked = input.regions.map((region) => ({
|
|
108161
|
+
region,
|
|
108162
|
+
uncovered: uncoveredFraction(region, input.detections)
|
|
108163
|
+
})).filter(({ region, uncovered }) => classifyRegion(region, uncovered) === "eligible").toSorted((a, b) => area(b.region) * b.uncovered - area(a.region) * a.uncovered);
|
|
108164
|
+
const planned = [];
|
|
108165
|
+
for (const { region } of ranked) for (const crop of cropsForRegion(region, native)) {
|
|
108166
|
+
if (planned.length >= 2) return planned;
|
|
108167
|
+
const centreX = crop.left + crop.side / 2;
|
|
108168
|
+
const centreY = crop.top + crop.side / 2;
|
|
108169
|
+
if (planned.some((existing) => contains(existing, centreX, centreY))) continue;
|
|
108170
|
+
planned.push(crop);
|
|
108171
|
+
}
|
|
108172
|
+
return planned;
|
|
108173
|
+
}
|
|
108174
|
+
/**
|
|
108175
|
+
* Boxes the executor produced on the crop VIEW → the result's own space.
|
|
108176
|
+
*
|
|
108177
|
+
* Three spaces meet here: the view the model saw (`view`, ≤640 px), the native
|
|
108178
|
+
* square it was cut from (`crop`, native px) and the space the full-frame
|
|
108179
|
+
* result reports in (`result`, native px on the `frameRef` path, the inline
|
|
108180
|
+
* ≤640 view otherwise). A box touching the view edge was clipped by the crop
|
|
108181
|
+
* and is dropped: half a person is not a person, and the tracker would take
|
|
108182
|
+
* the half as the whole.
|
|
108183
|
+
*/
|
|
108184
|
+
function remapCropDetections(detections, crop, view, result, native) {
|
|
108185
|
+
const viewToNative = crop.side / Math.max(1, view.width);
|
|
108186
|
+
const nativeToResultX = result.width / Math.max(1, native.width);
|
|
108187
|
+
const nativeToResultY = result.height / Math.max(1, native.height);
|
|
108188
|
+
const out = [];
|
|
108189
|
+
for (const detection of detections) {
|
|
108190
|
+
const { x, y, width, height } = detection.bbox;
|
|
108191
|
+
if (x <= 2 || y <= 2 || x + width >= view.width - 2 || y + height >= view.height - 2) continue;
|
|
108192
|
+
out.push({
|
|
108193
|
+
...detection,
|
|
108194
|
+
nativeRecovery: {
|
|
108195
|
+
score: detection.score,
|
|
108196
|
+
cropSidePx: crop.side,
|
|
108197
|
+
viewPx: view.width
|
|
108198
|
+
},
|
|
108199
|
+
bbox: {
|
|
108200
|
+
x: (crop.left + x * viewToNative) * nativeToResultX,
|
|
108201
|
+
y: (crop.top + y * viewToNative) * nativeToResultY,
|
|
108202
|
+
width: width * viewToNative * nativeToResultX,
|
|
108203
|
+
height: height * viewToNative * nativeToResultY
|
|
108204
|
+
}
|
|
108205
|
+
});
|
|
108206
|
+
}
|
|
108207
|
+
return out;
|
|
108208
|
+
}
|
|
108209
|
+
function boxIntersection(a, b) {
|
|
108210
|
+
return Math.max(0, Math.min(a.x + a.width, b.x + b.width) - Math.max(a.x, b.x)) * Math.max(0, Math.min(a.y + a.height, b.y + b.height) - Math.max(a.y, b.y));
|
|
108211
|
+
}
|
|
108212
|
+
function sameSubject(recovered, existing) {
|
|
108213
|
+
const inter = boxIntersection(recovered, existing);
|
|
108214
|
+
if (inter <= 0) return false;
|
|
108215
|
+
const areaR = recovered.width * recovered.height;
|
|
108216
|
+
const union = areaR + existing.width * existing.height - inter;
|
|
108217
|
+
if (union > 0 && inter / union >= .5) return true;
|
|
108218
|
+
return areaR > 0 && inter / areaR >= .7;
|
|
108219
|
+
}
|
|
108220
|
+
/**
|
|
108221
|
+
* Class-aware NMS of the recovered boxes against the full-frame ones. Every
|
|
108222
|
+
* full-frame box is kept as it is — the second pass only ADDS. A recovered box
|
|
108223
|
+
* is added when no full-frame box of the same macro class is the same subject
|
|
108224
|
+
* (`RECOVERY_FUSE_IOU`) or contains it (`RECOVERY_FUSE_CONTAINMENT`). Ids are
|
|
108225
|
+
* re-minted so a crop's `d0` cannot collide with the frame's.
|
|
108226
|
+
*/
|
|
108227
|
+
function fuseRecoveredDetections(full, recovered) {
|
|
108228
|
+
const accepted = [];
|
|
108229
|
+
for (const [index, candidate] of recovered.entries()) {
|
|
108230
|
+
if ([...full, ...accepted].some((existing) => existing.macroClass === candidate.macroClass && sameSubject(candidate.bbox, existing.bbox))) continue;
|
|
108231
|
+
accepted.push({
|
|
108232
|
+
...candidate,
|
|
108233
|
+
id: `recovered-${index}-${candidate.id}`
|
|
108234
|
+
});
|
|
108235
|
+
}
|
|
108236
|
+
return [...full, ...accepted];
|
|
108237
|
+
}
|
|
108238
|
+
/**
|
|
108239
|
+
* The step the second pass runs: the enabled first-level object detector of
|
|
108240
|
+
* the camera's own tree, with its own model and no children. Same model as
|
|
108241
|
+
* the full pass so a recovered box is comparable to a full-frame one.
|
|
108242
|
+
*/
|
|
108243
|
+
function selectRecoveryStep(steps) {
|
|
108244
|
+
const detector = steps.find((step) => step.addonId === "object-detection" && step.enabled !== false);
|
|
108245
|
+
if (!detector) return null;
|
|
108246
|
+
return {
|
|
108247
|
+
addonId: detector.addonId,
|
|
108248
|
+
...detector.modelId !== void 0 ? { modelId: detector.modelId } : {},
|
|
108249
|
+
enabled: true
|
|
108250
|
+
};
|
|
108251
|
+
}
|
|
108252
|
+
//#endregion
|
|
108253
|
+
//#region src/pipeline/second-pass/motion-crop-plan.ts
|
|
108254
|
+
/**
|
|
108255
|
+
* Step-settings key of the opt-in, on the `object-detection` step — so it is
|
|
108256
|
+
* per camera through `pipelineOrchestrator.setCameraStepSettings` (a
|
|
108257
|
+
* camera-semantic key, D927), and it rides the same
|
|
108258
|
+
* `PipelineStepInputOutput.settings` the per-macro confidence floors ride.
|
|
108259
|
+
*/
|
|
108260
|
+
var MOTION_CROP_DISPATCH_KEY = "motionCropDispatch";
|
|
108261
|
+
/**
|
|
108262
|
+
* Read the opt-in. Anything that is not the boolean `true` is the shipped
|
|
108263
|
+
* rule: a stored string or a number is a mis-write (this repo has shipped a
|
|
108264
|
+
* setting written into the wrong sqlite table before), and a mis-write must
|
|
108265
|
+
* not silently change what a camera detects on.
|
|
108266
|
+
*/
|
|
108267
|
+
function readRecoveryRecipe(settings) {
|
|
108268
|
+
return settings?.["motionCropDispatch"] === true ? "motion-crop" : "uncovered-region";
|
|
108269
|
+
}
|
|
108270
|
+
/**
|
|
108271
|
+
* The recipe this camera's resolved step tree selects.
|
|
108272
|
+
*
|
|
108273
|
+
* Reads the ENABLED first-level detector's own settings, which is where
|
|
108274
|
+
* `pipelineOrchestrator.setCameraStepSettings` writes a per-camera value. A
|
|
108275
|
+
* tree with no enabled detector runs no second pass at all, so the answer
|
|
108276
|
+
* there is the shipped rule and it is never used.
|
|
108277
|
+
*/
|
|
108278
|
+
function readRecoveryRecipeForSteps(steps) {
|
|
108279
|
+
return readRecoveryRecipe(steps.find((step) => step.addonId === "object-detection" && step.enabled !== false)?.settings);
|
|
108280
|
+
}
|
|
108281
|
+
/**
|
|
108282
|
+
* A motion box is grown ×1.5 about its centre before anything else.
|
|
108283
|
+
*
|
|
108284
|
+
* The motion step's box is the CHANGED pixels, which on a moving animal is the
|
|
108285
|
+
* leading edge plus its shadow, not the animal. The detector needs the subject
|
|
108286
|
+
* and the context it was trained on.
|
|
108287
|
+
*/
|
|
108288
|
+
var MOTION_CROP_MOTION_PAD = 1.5;
|
|
108289
|
+
/**
|
|
108290
|
+
* An intersecting tracked box is grown ×1.25 — less, because it is already a
|
|
108291
|
+
* subject rectangle rather than a change mask.
|
|
108292
|
+
*/
|
|
108293
|
+
var MOTION_CROP_TRACKED_PAD = 1.25;
|
|
108294
|
+
/**
|
|
108295
|
+
* Regions per frame, largest area first.
|
|
108296
|
+
*
|
|
108297
|
+
* Measured as NON-BINDING: over the 156 frames of the recall harness the cap
|
|
108298
|
+
* bound on 0 of them. It is the bound on a storm, not a tuning knob, and the
|
|
108299
|
+
* runner reports when it binds so a camera on which it starts to is visible.
|
|
108300
|
+
*/
|
|
108301
|
+
var MOTION_CROP_MAX_REGIONS = 3;
|
|
108302
|
+
/**
|
|
108303
|
+
* The floor a `person` or `vehicle` box must clear when it came out of a CROP
|
|
108304
|
+
* rather than out of the full frame.
|
|
108305
|
+
*
|
|
108306
|
+
* Collected, not assumed: 3 085 quiet frames produced 27 motion-positive
|
|
108307
|
+
* frames and 28 crop invocations. The full-frame arm produced 0 boxes above
|
|
108308
|
+
* any gate; the crop arm produced 3, of which two are genuine false positives,
|
|
108309
|
+
* and both are night-IR corners upscaled into a 320 model — `vehicle 0.749`
|
|
108310
|
+
* and `person 0.701`. Zero false `animal` at any gate, which is why `animal`
|
|
108311
|
+
* is deliberately absent from this table: the class the pass exists to recover
|
|
108312
|
+
* is the class that never produced a false positive, and gating it would cost
|
|
108313
|
+
* the whole measured gain.
|
|
108314
|
+
*
|
|
108315
|
+
* This is a CROP-ONLY floor, applied on top of the per-macro floors the
|
|
108316
|
+
* executor already enforced. A full-frame `person` at 0.70 is untouched — the
|
|
108317
|
+
* full-frame arm is the one arm with ground truth behind its floor
|
|
108318
|
+
* (`minConfidencePerson` 0.65, raised on 2026-07-30 from a measured
|
|
108319
|
+
* distribution).
|
|
108320
|
+
*
|
|
108321
|
+
* The cost of this floor is stated rather than assumed away: it can also
|
|
108322
|
+
* remove a real distant person or vehicle recovered from a crop at 0.70–0.80.
|
|
108323
|
+
* Nothing in the quiet-footage sample measures that, so the floor is a
|
|
108324
|
+
* constant here rather than a slider, and the runner reports every drop it
|
|
108325
|
+
* makes per camera.
|
|
108326
|
+
*/
|
|
108327
|
+
var CROP_CLASS_FLOORS = Object.freeze({
|
|
108328
|
+
person: .8,
|
|
108329
|
+
vehicle: .8
|
|
108330
|
+
});
|
|
108331
|
+
/**
|
|
108332
|
+
* Apply the crop-only class floors, REPORTING every drop.
|
|
108333
|
+
*
|
|
108334
|
+
* A class with no entry in {@link CROP_CLASS_FLOORS} is kept: a floor this
|
|
108335
|
+
* table cannot express is not a floor of zero, and silently dropping a class
|
|
108336
|
+
* nobody measured is the exact shape of silent drop this repo has paid for
|
|
108337
|
+
* three separate times.
|
|
108338
|
+
*/
|
|
108339
|
+
function applyCropClassFloors(detections) {
|
|
108340
|
+
const kept = [];
|
|
108341
|
+
const dropped = [];
|
|
108342
|
+
for (const detection of detections) {
|
|
108343
|
+
const floor = CROP_CLASS_FLOORS[detection.macroClass];
|
|
108344
|
+
if (floor !== void 0 && detection.score < floor) {
|
|
108345
|
+
dropped.push({
|
|
108346
|
+
macroClass: detection.macroClass,
|
|
108347
|
+
score: detection.score,
|
|
108348
|
+
floor
|
|
108349
|
+
});
|
|
108350
|
+
continue;
|
|
108351
|
+
}
|
|
108352
|
+
kept.push(detection);
|
|
108353
|
+
}
|
|
108354
|
+
return {
|
|
108355
|
+
kept,
|
|
108356
|
+
dropped
|
|
108357
|
+
};
|
|
108358
|
+
}
|
|
108359
|
+
function scaleAbout(rect, factor) {
|
|
108360
|
+
const w = rect.w * factor;
|
|
108361
|
+
const h = rect.h * factor;
|
|
108362
|
+
return {
|
|
108363
|
+
x: rect.x + rect.w / 2 - w / 2,
|
|
108364
|
+
y: rect.y + rect.h / 2 - h / 2,
|
|
108365
|
+
w,
|
|
108366
|
+
h
|
|
108367
|
+
};
|
|
108368
|
+
}
|
|
108369
|
+
function overlaps(a, b) {
|
|
108370
|
+
return Math.min(a.x + a.w, b.x + b.w) > Math.max(a.x, b.x) && Math.min(a.y + a.h, b.y + b.h) > Math.max(a.y, b.y);
|
|
108371
|
+
}
|
|
108372
|
+
function union(a, b) {
|
|
108373
|
+
const x = Math.min(a.x, b.x);
|
|
108374
|
+
const y = Math.min(a.y, b.y);
|
|
108375
|
+
return {
|
|
108376
|
+
x,
|
|
108377
|
+
y,
|
|
108378
|
+
w: Math.max(a.x + a.w, b.x + b.w) - x,
|
|
108379
|
+
h: Math.max(a.y + a.h, b.y + b.h) - y
|
|
108380
|
+
};
|
|
108381
|
+
}
|
|
108382
|
+
function clampToFrame(rect) {
|
|
108383
|
+
const x = Math.max(0, rect.x);
|
|
108384
|
+
const y = Math.max(0, rect.y);
|
|
108385
|
+
return {
|
|
108386
|
+
x,
|
|
108387
|
+
y,
|
|
108388
|
+
w: Math.min(1, rect.x + rect.w) - x,
|
|
108389
|
+
h: Math.min(1, rect.y + rect.h) - y
|
|
108390
|
+
};
|
|
108391
|
+
}
|
|
108392
|
+
/**
|
|
108393
|
+
* Merge transitively every seed whose padded box overlaps another, and answer
|
|
108394
|
+
* the union rectangles.
|
|
108395
|
+
*
|
|
108396
|
+
* Transitive on purpose: a person, the dog beside them and the dog's shadow
|
|
108397
|
+
* are three components of one subject, and cutting three crops for them spends
|
|
108398
|
+
* two inferences on pixels the first one already showed the detector.
|
|
108399
|
+
*/
|
|
108400
|
+
function mergeSeeds(seeds) {
|
|
108401
|
+
const merged = [];
|
|
108402
|
+
for (const seed of seeds) {
|
|
108403
|
+
let current = seed;
|
|
108404
|
+
let changed = true;
|
|
108405
|
+
while (changed) {
|
|
108406
|
+
changed = false;
|
|
108407
|
+
for (let i = merged.length - 1; i >= 0; i -= 1) {
|
|
108408
|
+
const other = merged[i];
|
|
108409
|
+
if (other === void 0 || !overlaps(current, other)) continue;
|
|
108410
|
+
current = union(current, other);
|
|
108411
|
+
merged.splice(i, 1);
|
|
108412
|
+
changed = true;
|
|
108413
|
+
}
|
|
108414
|
+
}
|
|
108415
|
+
merged.push(current);
|
|
108416
|
+
}
|
|
108417
|
+
return merged;
|
|
108418
|
+
}
|
|
108419
|
+
/**
|
|
108420
|
+
* The regions the measured recipe judges this frame on.
|
|
108421
|
+
*
|
|
108422
|
+
* Motion boxes padded {@link MOTION_CROP_MOTION_PAD}; every full-frame box
|
|
108423
|
+
* that intersects a RAW motion box padded {@link MOTION_CROP_TRACKED_PAD} and
|
|
108424
|
+
* added as a seed of its own; everything that overlaps merged; the result
|
|
108425
|
+
* clamped to the frame, ordered largest first and capped at
|
|
108426
|
+
* {@link MOTION_CROP_MAX_REGIONS}.
|
|
108427
|
+
*
|
|
108428
|
+
* There is deliberately no coverage gate and no size gate here. Those belong
|
|
108429
|
+
* to the shipped rule, and they are what makes it a rule about a subject
|
|
108430
|
+
* BESIDE a detected one; the measurement says the pass is worth running on the
|
|
108431
|
+
* detected subject's own region too.
|
|
108432
|
+
*/
|
|
108433
|
+
function planMotionCropRegions(input) {
|
|
108434
|
+
if (input.regions.length === 0) return [];
|
|
108435
|
+
const seeds = input.regions.map((r) => scaleAbout(r, MOTION_CROP_MOTION_PAD));
|
|
108436
|
+
for (const detection of input.detections) {
|
|
108437
|
+
if (!input.regions.some((region) => overlaps(detection, region))) continue;
|
|
108438
|
+
seeds.push(scaleAbout(detection, MOTION_CROP_TRACKED_PAD));
|
|
108439
|
+
}
|
|
108440
|
+
return mergeSeeds(seeds).map(clampToFrame).filter((r) => r.w > 0 && r.h > 0).toSorted((a, b) => normalizedArea(b) - normalizedArea(a)).slice(0, 3);
|
|
108441
|
+
}
|
|
108442
|
+
/**
|
|
108443
|
+
* The square the detector is fed for one measured region.
|
|
108444
|
+
*
|
|
108445
|
+
* "Grow to the detector's input aspect ratio without ever shrinking" is a
|
|
108446
|
+
* SQUARE here, and that is a fact about the production detector rather than a
|
|
108447
|
+
* simplification: `yolov9m-320-int8` takes 320×320, as does every hardware
|
|
108448
|
+
* default in the catalog.
|
|
108449
|
+
*
|
|
108450
|
+
* The side is the region's longer side, floored at
|
|
108451
|
+
* {@link RECOVERY_CROP_MIN_SIDE_PX} — the growth is what magnifies the subject
|
|
108452
|
+
* — and ceilinged at {@link RECOVERY_CROP_MAX_SIDE_PX}. The ceiling is NOT a
|
|
108453
|
+
* simplification either: at 1024 px the same 320 model scored the reference
|
|
108454
|
+
* dog 0.52–0.76, and at 1280 px lost it entirely
|
|
108455
|
+
* (`docs/benchmarks/pipeline-frame-model-eval.md`, case `miss-dog-615`). A
|
|
108456
|
+
* region longer than the ceiling is TILED along its longer axis, never zoomed
|
|
108457
|
+
* out.
|
|
108458
|
+
*/
|
|
108459
|
+
function motionCropsForRegion(region, native) {
|
|
108460
|
+
const widthPx = region.w * native.width;
|
|
108461
|
+
const heightPx = region.h * native.height;
|
|
108462
|
+
const longest = Math.max(widthPx, heightPx);
|
|
108463
|
+
const side = Math.round(Math.min(Math.max(longest, 640), 800));
|
|
108464
|
+
const centreX = (region.x + region.w / 2) * native.width;
|
|
108465
|
+
const centreY = (region.y + region.h / 2) * native.height;
|
|
108466
|
+
if (longest <= 800) return [squareAround(centreX, centreY, side, native)];
|
|
108467
|
+
const alongX = widthPx >= heightPx;
|
|
108468
|
+
const quarter = longest / 4;
|
|
108469
|
+
return [squareAround(alongX ? centreX - quarter : centreX, alongX ? centreY : centreY - quarter, side, native), squareAround(alongX ? centreX + quarter : centreX, alongX ? centreY : centreY + quarter, side, native)];
|
|
108470
|
+
}
|
|
108471
|
+
/** `true` when `inner` lies entirely inside `outer`. */
|
|
108472
|
+
function cropContains(outer, inner) {
|
|
108473
|
+
return inner.left >= outer.left && inner.top >= outer.top && inner.left + inner.side <= outer.left + outer.side && inner.top + inner.side <= outer.top + outer.side;
|
|
108474
|
+
}
|
|
108475
|
+
/**
|
|
108476
|
+
* The crops the measured recipe runs on this frame.
|
|
108477
|
+
*
|
|
108478
|
+
* Dedupe is by CONTAINMENT only. Two squares that merely overlap both run: the
|
|
108479
|
+
* pixels the second adds are pixels no other crop shows the detector, and the
|
|
108480
|
+
* fusion step downstream already refuses a duplicate BOX. Dropping a crop
|
|
108481
|
+
* because it overlapped would drop the subject sitting at its edge, which is
|
|
108482
|
+
* the whole population this pass exists for.
|
|
108483
|
+
*
|
|
108484
|
+
* A frame that cannot hold the smallest crop yields nothing: it is already
|
|
108485
|
+
* seen at scale.
|
|
108486
|
+
*/
|
|
108487
|
+
function planMotionCrops(input) {
|
|
108488
|
+
const { native } = input;
|
|
108489
|
+
if (Math.min(native.width, native.height) < 640) return [];
|
|
108490
|
+
const planned = [];
|
|
108491
|
+
for (const region of planMotionCropRegions(input)) for (const crop of motionCropsForRegion(region, native)) {
|
|
108492
|
+
if (planned.some((existing) => cropContains(existing, crop))) continue;
|
|
108493
|
+
planned.push(crop);
|
|
108494
|
+
}
|
|
108495
|
+
return planned;
|
|
108496
|
+
}
|
|
108497
|
+
//#endregion
|
|
107442
108498
|
//#region src/pipeline/root-face-crop.ts
|
|
107443
108499
|
/** Side of the square, as a multiple of the face box's longer side. */
|
|
107444
108500
|
var ROOT_FACE_REFINE_SCALE = 1.6;
|
|
@@ -109793,6 +110849,7 @@ exports.ADMIN_SCOPE = ADMIN_SCOPE;
|
|
|
109793
110849
|
exports.AGENT_MDNS_QUERY_NAME = AGENT_MDNS_QUERY_NAME;
|
|
109794
110850
|
exports.AGENT_MDNS_SERVICE_TYPE = AGENT_MDNS_SERVICE_TYPE;
|
|
109795
110851
|
exports.AGENT_MDNS_TXT_KEYS = AGENT_MDNS_TXT_KEYS;
|
|
110852
|
+
exports.AGGREGATE_THROUGHPUT_BY_KIND = AGGREGATE_THROUGHPUT_BY_KIND;
|
|
109796
110853
|
exports.ALEXA_EGRESS_PROFILE = ALEXA_EGRESS_PROFILE;
|
|
109797
110854
|
exports.ALL_CAPABILITY_DEFINITIONS = ALL_CAPABILITY_DEFINITIONS;
|
|
109798
110855
|
exports.ALL_METHOD_ACCESS = ALL_METHOD_ACCESS;
|
|
@@ -109820,6 +110877,7 @@ exports.AUDIO_SCALE_EPOCH_MS = AUDIO_SCALE_EPOCH_MS;
|
|
|
109820
110877
|
exports.AUTOMATIC_HUB_ADDRESS_FLAGS = AUTOMATIC_HUB_ADDRESS_FLAGS;
|
|
109821
110878
|
exports.AUTO_SHARE_HEADROOM = AUTO_SHARE_HEADROOM;
|
|
109822
110879
|
exports.AUTO_TUNE_FAMILY = AUTO_TUNE_FAMILY;
|
|
110880
|
+
exports.AcceleratorComputeSchema = AcceleratorComputeSchema;
|
|
109823
110881
|
exports.AcceleratorKindSchema = AcceleratorKindSchema;
|
|
109824
110882
|
exports.AccessoriesStatusSchema = AccessoriesStatusSchema;
|
|
109825
110883
|
exports.AccessoryKind = AccessoryKind;
|
|
@@ -110006,8 +111064,11 @@ exports.CONTAINER_CHILD_PRIORITY = CONTAINER_CHILD_PRIORITY;
|
|
|
110006
111064
|
exports.CORAL_DEFAULT_MODEL_ID = CORAL_DEFAULT_MODEL_ID;
|
|
110007
111065
|
exports.CORE_BLOCKS_ADDON_ID = CORE_BLOCKS_ADDON_ID;
|
|
110008
111066
|
exports.CORE_BLOCK_ADDON_PREFIX = CORE_BLOCK_ADDON_PREFIX;
|
|
111067
|
+
exports.CORE_ULTRA_265K_IGPU_CLOCK_MHZ = CORE_ULTRA_265K_IGPU_CLOCK_MHZ;
|
|
111068
|
+
exports.CORE_ULTRA_265K_IGPU_EU = CORE_ULTRA_265K_IGPU_EU;
|
|
110009
111069
|
exports.CREDENTIAL_TOKEN_AUDIENCE = CREDENTIAL_TOKEN_AUDIENCE;
|
|
110010
111070
|
exports.CREDENTIAL_TOKEN_KIND = CREDENTIAL_TOKEN_KIND;
|
|
111071
|
+
exports.CROP_CLASS_FLOORS = CROP_CLASS_FLOORS;
|
|
110011
111072
|
exports.CROP_SUPPLY_SOURCES = CROP_SUPPLY_SOURCES;
|
|
110012
111073
|
exports.CROP_SUPPLY_STEP_IDS = CROP_SUPPLY_STEP_IDS;
|
|
110013
111074
|
exports.CUSTOMIZATION_BLOCK_NAME_PREFIX = require_composition.CUSTOMIZATION_BLOCK_NAME_PREFIX;
|
|
@@ -110358,6 +111419,8 @@ exports.DoorbellStatusSchema = DoorbellStatusSchema;
|
|
|
110358
111419
|
exports.EMBEDDING_MODEL_FAILED = EMBEDDING_MODEL_FAILED;
|
|
110359
111420
|
exports.EMBEDDING_REQUIREMENTS_FAILED = EMBEDDING_REQUIREMENTS_FAILED;
|
|
110360
111421
|
exports.EMBEDDING_REQUIREMENTS_UNAVAILABLE = EMBEDDING_REQUIREMENTS_UNAVAILABLE;
|
|
111422
|
+
exports.ESTIMATE_COMPUTE_RATIO_MAX = ESTIMATE_COMPUTE_RATIO_MAX;
|
|
111423
|
+
exports.ESTIMATE_COMPUTE_RATIO_MIN = ESTIMATE_COMPUTE_RATIO_MIN;
|
|
110361
111424
|
exports.EVENTFUL_CAP_NAMES = EVENTFUL_CAP_NAMES;
|
|
110362
111425
|
exports.EVENT_DENSITY_BATCH_MAX = EVENT_DENSITY_BATCH_MAX;
|
|
110363
111426
|
exports.EVENT_KIND_BY_CAP = EVENT_KIND_BY_CAP;
|
|
@@ -110708,6 +111771,10 @@ exports.MODEL_FORMATS = require_sleep.MODEL_FORMATS;
|
|
|
110708
111771
|
exports.MODEL_HOSTING = MODEL_HOSTING;
|
|
110709
111772
|
exports.MODEL_PROVIDER_IDS = MODEL_PROVIDER_IDS;
|
|
110710
111773
|
exports.MOTION_CLOSE_AFTER_MS = MOTION_CLOSE_AFTER_MS;
|
|
111774
|
+
exports.MOTION_CROP_DISPATCH_KEY = MOTION_CROP_DISPATCH_KEY;
|
|
111775
|
+
exports.MOTION_CROP_MAX_REGIONS = MOTION_CROP_MAX_REGIONS;
|
|
111776
|
+
exports.MOTION_CROP_MOTION_PAD = MOTION_CROP_MOTION_PAD;
|
|
111777
|
+
exports.MOTION_CROP_TRACKED_PAD = MOTION_CROP_TRACKED_PAD;
|
|
110711
111778
|
exports.MOTION_SOURCE_LABELS = MOTION_SOURCE_LABELS;
|
|
110712
111779
|
exports.MOTION_TRIGGER_FEATURE = MOTION_TRIGGER_FEATURE;
|
|
110713
111780
|
exports.ManagedModelCatalogEntrySchema = ManagedModelCatalogEntrySchema;
|
|
@@ -110772,6 +111839,8 @@ exports.MotionZoneStatusSchema = MotionZoneStatusSchema;
|
|
|
110772
111839
|
exports.MqttBrokerInfoSchema = BrokerInfoSchema$1;
|
|
110773
111840
|
exports.MqttBrokerStatusSchema = StatusSchema;
|
|
110774
111841
|
exports.MutationFilterSchema = MutationFilterSchema;
|
|
111842
|
+
exports.N100_IGPU_CLOCK_MHZ = N100_IGPU_CLOCK_MHZ;
|
|
111843
|
+
exports.N100_IGPU_EU = N100_IGPU_EU;
|
|
110775
111844
|
exports.NATIVE_LEASE_ACTIVITY_FIELD = NATIVE_LEASE_ACTIVITY_FIELD;
|
|
110776
111845
|
exports.NATIVE_LEASE_ACTIVITY_KEY = NATIVE_LEASE_ACTIVITY_KEY;
|
|
110777
111846
|
exports.NATIVE_LEASE_ADMISSION_FIELD = NATIVE_LEASE_ADMISSION_FIELD;
|
|
@@ -110839,6 +111908,7 @@ exports.NC_GROUPING_MAX_SEC = NC_GROUPING_MAX_SEC;
|
|
|
110839
111908
|
exports.NC_GROUPING_MIN_SEC = NC_GROUPING_MIN_SEC;
|
|
110840
111909
|
exports.NC_HISTORY_LIMIT_DEFAULT = NC_HISTORY_LIMIT_DEFAULT;
|
|
110841
111910
|
exports.NC_HISTORY_LIMIT_MAX = NC_HISTORY_LIMIT_MAX;
|
|
111911
|
+
exports.NC_LEGACY_SYSTEM_EVENT_KINDS = NC_LEGACY_SYSTEM_EVENT_KINDS;
|
|
110842
111912
|
exports.NC_MAX_PER_TRACK_IMMEDIATE = NC_MAX_PER_TRACK_IMMEDIATE;
|
|
110843
111913
|
exports.NC_MEDIA_IMAGE_MODES = NC_MEDIA_IMAGE_MODES;
|
|
110844
111914
|
exports.NC_OCCUPANCY_DEFAULTS = NC_OCCUPANCY_DEFAULTS;
|
|
@@ -110853,6 +111923,7 @@ exports.NC_SENSOR_TRANSITION_NEVER_FIRES_WARNING = NC_SENSOR_TRANSITION_NEVER_FI
|
|
|
110853
111923
|
exports.NC_SENSOR_TRANSITION_UNSUPPORTED_REASON = NC_SENSOR_TRANSITION_UNSUPPORTED_REASON;
|
|
110854
111924
|
exports.NC_SERVER_PACKAGE_NAME = NC_SERVER_PACKAGE_NAME;
|
|
110855
111925
|
exports.NC_SNOOZE_MAX_MINUTES = NC_SNOOZE_MAX_MINUTES;
|
|
111926
|
+
exports.NC_STATE_SUMMARY_SECTION_IDS = NC_STATE_SUMMARY_SECTION_IDS;
|
|
110856
111927
|
exports.NC_SYSTEM_DELIVERY = NC_SYSTEM_DELIVERY;
|
|
110857
111928
|
exports.NC_SYSTEM_EVENT_CLASS_SPECS = NC_SYSTEM_EVENT_CLASS_SPECS;
|
|
110858
111929
|
exports.NC_SYSTEM_EVENT_CONDITION_FILTER_KEYS = NC_SYSTEM_EVENT_CONDITION_FILTER_KEYS;
|
|
@@ -110863,7 +111934,6 @@ exports.NC_SYSTEM_EVENT_FILTER_KEYS = NC_SYSTEM_EVENT_FILTER_KEYS;
|
|
|
110863
111934
|
exports.NC_SYSTEM_EVENT_KIND_GROUP_SPECS = NC_SYSTEM_EVENT_KIND_GROUP_SPECS;
|
|
110864
111935
|
exports.NC_SYSTEM_EVENT_SUMMARY_FAMILIES = NC_SYSTEM_EVENT_SUMMARY_FAMILIES;
|
|
110865
111936
|
exports.NC_SYSTEM_RULE_KINDS = NC_SYSTEM_RULE_KINDS;
|
|
110866
|
-
exports.NC_TANK_SYSTEM_EVENT_KINDS = NC_TANK_SYSTEM_EVENT_KINDS;
|
|
110867
111937
|
exports.NC_TAXONOMY = NC_TAXONOMY;
|
|
110868
111938
|
exports.NC_TEMPLATE_HELD_BACK_SYSTEM_EVENT_KINDS = NC_TEMPLATE_HELD_BACK_SYSTEM_EVENT_KINDS;
|
|
110869
111939
|
exports.NC_TEMPLATE_VARS = NC_TEMPLATE_VARS;
|
|
@@ -111037,6 +112107,7 @@ exports.NotifierStatusSchema = NotifierStatusSchema;
|
|
|
111037
112107
|
exports.NumericSensorStatusSchema = NumericSensorStatusSchema;
|
|
111038
112108
|
exports.OAUTH_LANDING_PATH = OAUTH_LANDING_PATH;
|
|
111039
112109
|
exports.OAUTH_LANDING_REASONS = OAUTH_LANDING_REASONS;
|
|
112110
|
+
exports.OBJECT_DETECTION_STEP_ID = OBJECT_DETECTION_STEP_ID;
|
|
111040
112111
|
exports.OPERATOR_WRITTEN_STALE_MS = OPERATOR_WRITTEN_STALE_MS;
|
|
111041
112112
|
exports.OPS_LOG_DEFAULT_LIMIT = OPS_LOG_DEFAULT_LIMIT;
|
|
111042
112113
|
exports.OPS_LOG_RING_DEFAULT_MAX = OPS_LOG_RING_DEFAULT_MAX;
|
|
@@ -111087,6 +112158,7 @@ exports.PEER_BYTES_TICKET_TTL_MS = require_sleep.PEER_BYTES_TICKET_TTL_MS;
|
|
|
111087
112158
|
exports.PENDING_BLOCK_ID = PENDING_BLOCK_ID;
|
|
111088
112159
|
exports.PET_FEEDER_MANUAL_FEED_MAX = PET_FEEDER_MANUAL_FEED_MAX;
|
|
111089
112160
|
exports.PET_FEEDER_MANUAL_FEED_MIN = PET_FEEDER_MANUAL_FEED_MIN;
|
|
112161
|
+
exports.PHOENIX_780M_CU = PHOENIX_780M_CU;
|
|
111090
112162
|
exports.PIPELINE_ANALYTICS_CAP_NAME = PIPELINE_ANALYTICS_CAP_NAME;
|
|
111091
112163
|
exports.PIPELINE_FLOW_CAPABILITY_NAMES = PIPELINE_FLOW_CAPABILITY_NAMES;
|
|
111092
112164
|
exports.PIPELINE_OWNER_CAPABILITY_NAMES = PIPELINE_OWNER_CAPABILITY_NAMES;
|
|
@@ -111103,6 +112175,7 @@ exports.PUBLIC_ENDPOINT_HTTP_REASON = PUBLIC_ENDPOINT_HTTP_REASON;
|
|
|
111103
112175
|
exports.PYTHON_SCRIPT = PYTHON_SCRIPT;
|
|
111104
112176
|
exports.PackageInstallationSchema = PackageInstallationSchema;
|
|
111105
112177
|
exports.PackageInstallationsSchema = PackageInstallationsSchema;
|
|
112178
|
+
exports.PackageLlmVerdictSchema = PackageLlmVerdictSchema;
|
|
111106
112179
|
exports.PackageUpdateSchema = PackageUpdateSchema;
|
|
111107
112180
|
exports.PackageVersionInfoSchema = PackageVersionInfoSchema;
|
|
111108
112181
|
exports.ParkRefusalSchema = ParkRefusalSchema;
|
|
@@ -111135,6 +112208,7 @@ exports.PlaintextSecretError = require_sleep.PlaintextSecretError;
|
|
|
111135
112208
|
exports.PolygonPointSchema = PolygonPointSchema;
|
|
111136
112209
|
exports.PoolMemoryWatchdog = PoolMemoryWatchdog;
|
|
111137
112210
|
exports.PowerMeterStatusSchema = PowerMeterStatusSchema;
|
|
112211
|
+
exports.PrecisionGopsSchema = PrecisionGopsSchema;
|
|
111138
112212
|
exports.PreferenceDeviceIdSchema = PreferenceDeviceIdSchema;
|
|
111139
112213
|
exports.PreferenceNamespaceSchema = PreferenceNamespaceSchema;
|
|
111140
112214
|
exports.PreferenceRowSchema = PreferenceRowSchema;
|
|
@@ -111203,7 +112277,21 @@ exports.RECORD_QUERY_MAX = require_sleep.RECORD_QUERY_MAX;
|
|
|
111203
112277
|
exports.RECORD_SECRET_COLLECTION_PREFIX = require_sleep.RECORD_SECRET_COLLECTION_PREFIX;
|
|
111204
112278
|
exports.RECORD_SETTINGS_MAX_ROWS = require_sleep.RECORD_SETTINGS_MAX_ROWS;
|
|
111205
112279
|
exports.RECORD_SWEEP_INTERVAL_MS = require_sleep.RECORD_SWEEP_INTERVAL_MS;
|
|
112280
|
+
exports.RECOVERY_CROP_MAX_SIDE_PX = RECOVERY_CROP_MAX_SIDE_PX;
|
|
112281
|
+
exports.RECOVERY_CROP_MIN_SIDE_PX = RECOVERY_CROP_MIN_SIDE_PX;
|
|
112282
|
+
exports.RECOVERY_CROP_PADDING_RATIO = RECOVERY_CROP_PADDING_RATIO;
|
|
112283
|
+
exports.RECOVERY_EDGE_MARGIN_PX = RECOVERY_EDGE_MARGIN_PX;
|
|
112284
|
+
exports.RECOVERY_FUSE_CONTAINMENT = RECOVERY_FUSE_CONTAINMENT;
|
|
112285
|
+
exports.RECOVERY_FUSE_IOU = RECOVERY_FUSE_IOU;
|
|
112286
|
+
exports.RECOVERY_MAX_CROPS_PER_FRAME = RECOVERY_MAX_CROPS_PER_FRAME;
|
|
112287
|
+
exports.RECOVERY_MAX_REGION_FRAME_FRACTION = RECOVERY_MAX_REGION_FRAME_FRACTION;
|
|
112288
|
+
exports.RECOVERY_MIN_INTERVAL_MS = RECOVERY_MIN_INTERVAL_MS;
|
|
112289
|
+
exports.RECOVERY_MIN_UNCOVERED_FRACTION = RECOVERY_MIN_UNCOVERED_FRACTION;
|
|
112290
|
+
exports.RECOVERY_MOTION_MAX_AGE_MS = RECOVERY_MOTION_MAX_AGE_MS;
|
|
112291
|
+
exports.RECOVERY_VIEW_PX = RECOVERY_VIEW_PX;
|
|
111206
112292
|
exports.REDACTED_SECRET = require_sleep.REDACTED_SECRET;
|
|
112293
|
+
exports.REFERENCE_COMPUTE = REFERENCE_COMPUTE;
|
|
112294
|
+
exports.REFERENCE_MATCH_FRACTION = REFERENCE_MATCH_FRACTION;
|
|
111207
112295
|
exports.REMOVAL_RECONCILE_INTERVAL_MS = REMOVAL_RECONCILE_INTERVAL_MS;
|
|
111208
112296
|
exports.REMOVED_MODEL_IDS = REMOVED_MODEL_IDS;
|
|
111209
112297
|
exports.REMOVE_LEAF = require_sleep.REMOVE_LEAF;
|
|
@@ -111223,6 +112311,9 @@ exports.ROOT_CHILD_CONTAINMENT_MIN = ROOT_CHILD_CONTAINMENT_MIN;
|
|
|
111223
112311
|
exports.ROOT_FACE_REFINE_INPUT_PX = ROOT_FACE_REFINE_INPUT_PX;
|
|
111224
112312
|
exports.ROOT_FACE_REFINE_SCALE = ROOT_FACE_REFINE_SCALE;
|
|
111225
112313
|
exports.ROOT_SUPPLIABLE_STEP_IDS = ROOT_SUPPLIABLE_STEP_IDS;
|
|
112314
|
+
exports.RUNNING_POWER_START_DELAY_MS = RUNNING_POWER_START_DELAY_MS;
|
|
112315
|
+
exports.RUNNING_POWER_STOP_DELAY_MS = RUNNING_POWER_STOP_DELAY_MS;
|
|
112316
|
+
exports.RUNNING_POWER_THRESHOLD_WATTS = RUNNING_POWER_THRESHOLD_WATTS;
|
|
111226
112317
|
exports.RUNTIME_DEFAULTS = require_sleep.RUNTIME_DEFAULTS;
|
|
111227
112318
|
exports.RUNTIME_STATE_POLICY = RUNTIME_STATE_POLICY;
|
|
111228
112319
|
exports.RUNTIME_STATE_REFRESH_MISS_COOLDOWN_MS = RUNTIME_STATE_REFRESH_MISS_COOLDOWN_MS;
|
|
@@ -111374,6 +112465,8 @@ exports.RunnerJumpStepProvisioningSchema = RunnerJumpStepProvisioningSchema;
|
|
|
111374
112465
|
exports.RunnerLocalLoadSchema = RunnerLocalLoadSchema;
|
|
111375
112466
|
exports.RunnerLocalMetricsSchema = RunnerLocalMetricsSchema;
|
|
111376
112467
|
exports.RunnerRefusalSchema = RunnerRefusalSchema;
|
|
112468
|
+
exports.RunningStateSourceSchema = RunningStateSourceSchema;
|
|
112469
|
+
exports.RunningStateStatusSchema = RunningStateStatusSchema;
|
|
111377
112470
|
exports.SCENE_CONDITIONS = SCENE_CONDITIONS;
|
|
111378
112471
|
exports.SCENE_CONFIRM_DEFAULT_MAX_IMAGE_PX = SCENE_CONFIRM_DEFAULT_MAX_IMAGE_PX;
|
|
111379
112472
|
exports.SCENE_CONFIRM_DEFAULT_TIMEOUT_MS = SCENE_CONFIRM_DEFAULT_TIMEOUT_MS;
|
|
@@ -111390,6 +112483,7 @@ exports.SECRET_META = require_sleep.SECRET_META;
|
|
|
111390
112483
|
exports.SECRET_REF_SCHEME = require_sleep.SECRET_REF_SCHEME;
|
|
111391
112484
|
exports.SENSOR_FEATURES = SENSOR_FEATURES;
|
|
111392
112485
|
exports.SENSOR_MAP = SENSOR_MAP;
|
|
112486
|
+
exports.SERIAL_CAPACITY_CAMERA_FPS = SERIAL_CAPACITY_CAMERA_FPS;
|
|
111393
112487
|
exports.SERVER_NAME_MAX_LENGTH = SERVER_NAME_MAX_LENGTH;
|
|
111394
112488
|
exports.SERVER_NAME_SETTING_KEY = SERVER_NAME_SETTING_KEY;
|
|
111395
112489
|
exports.SESSION_PLACEMENT_SETTING_KEY = SESSION_PLACEMENT_SETTING_KEY;
|
|
@@ -111745,6 +112839,7 @@ exports.addonWidgetsSourceCapability = addonWidgetsSourceCapability;
|
|
|
111745
112839
|
exports.addonsCapability = addonsCapability;
|
|
111746
112840
|
exports.adminUiCapability = require_sleep.adminUiCapability;
|
|
111747
112841
|
exports.advertisedHubOrigins = advertisedHubOrigins;
|
|
112842
|
+
exports.aggregateThroughputFactor = aggregateThroughputFactor;
|
|
111748
112843
|
exports.airQualitySensorCapability = airQualitySensorCapability;
|
|
111749
112844
|
exports.alarmDeviceBlocksArming = alarmDeviceBlocksArming;
|
|
111750
112845
|
exports.alarmDeviceSecureOnArm = alarmDeviceSecureOnArm;
|
|
@@ -111762,6 +112857,7 @@ exports.ambientLightSensorCapability = ambientLightSensorCapability;
|
|
|
111762
112857
|
exports.annotateClipsWithEventLabels = annotateClipsWithEventLabels;
|
|
111763
112858
|
exports.appFlowLocation = appFlowLocation;
|
|
111764
112859
|
exports.applyBlockingEdit = applyBlockingEdit;
|
|
112860
|
+
exports.applyCropClassFloors = applyCropClassFloors;
|
|
111765
112861
|
exports.applyModeDelaysEdits = applyModeDelaysEdits;
|
|
111766
112862
|
exports.applyModeDeviceListEdit = applyModeDeviceListEdit;
|
|
111767
112863
|
exports.applyModelBoundSettings = applyModelBoundSettings;
|
|
@@ -111897,6 +112993,7 @@ exports.compositionSliceKey = require_composition.compositionSliceKey;
|
|
|
111897
112993
|
exports.compositionSourceKey = require_composition.compositionSourceKey;
|
|
111898
112994
|
exports.compositionSourceRefs = compositionSourceRefs;
|
|
111899
112995
|
exports.computeCapacityShares = computeCapacityShares;
|
|
112996
|
+
exports.computeScores = computeScores;
|
|
111900
112997
|
exports.conditionDepth = conditionDepth;
|
|
111901
112998
|
exports.conditionExclusionReason = conditionExclusionReason;
|
|
111902
112999
|
exports.conditionVisibleForKind = conditionVisibleForKind;
|
|
@@ -111919,6 +113016,7 @@ exports.coreBlockSourceKind = coreBlockSourceKind;
|
|
|
111919
113016
|
exports.coreBlocksCapability = coreBlocksCapability;
|
|
111920
113017
|
exports.cosineSimilarity = cosineSimilarity;
|
|
111921
113018
|
exports.countConditionLeaves = countConditionLeaves;
|
|
113019
|
+
exports.countRecoverySkips = countRecoverySkips;
|
|
111922
113020
|
exports.coverCapability = coverCapability;
|
|
111923
113021
|
exports.createAlarmProfile = createAlarmProfile;
|
|
111924
113022
|
exports.createDeviceExtensionProvider = createDeviceExtensionProvider;
|
|
@@ -112089,6 +113187,7 @@ exports.formatSecretScope = require_sleep.formatSecretScope;
|
|
|
112089
113187
|
exports.freshResults = freshResults;
|
|
112090
113188
|
exports.frigateDefaultClassFloors = frigateDefaultClassFloors;
|
|
112091
113189
|
exports.frigateLabelFields = frigateLabelFields;
|
|
113190
|
+
exports.fuseRecoveredDetections = fuseRecoveredDetections;
|
|
112092
113191
|
exports.gasCapability = gasCapability;
|
|
112093
113192
|
exports.gbToBytes = gbToBytes;
|
|
112094
113193
|
exports.generateAutomationBlock = generateAutomationBlock;
|
|
@@ -112181,6 +113280,7 @@ exports.isRemovedModelId = isRemovedModelId;
|
|
|
112181
113280
|
exports.isRestoredCap = isRestoredCap;
|
|
112182
113281
|
exports.isRetiredClearKey = isRetiredClearKey;
|
|
112183
113282
|
exports.isRootSuppliableStepId = isRootSuppliableStepId;
|
|
113283
|
+
exports.isRunningAssignmentFeature = isRunningAssignmentFeature;
|
|
112184
113284
|
exports.isSameAddonId = isSameAddonId;
|
|
112185
113285
|
exports.isScheduleActive = isScheduleActive;
|
|
112186
113286
|
exports.isSecretClear = require_sleep.isSecretClear;
|
|
@@ -112299,6 +113399,7 @@ exports.normalizeOrigin = normalizeOrigin;
|
|
|
112299
113399
|
exports.normalizeSecretOwner = require_sleep.normalizeSecretOwner;
|
|
112300
113400
|
exports.normalizeTokenScopes = normalizeTokenScopes;
|
|
112301
113401
|
exports.normalizeUnit = normalizeUnit;
|
|
113402
|
+
exports.normalizedArea = normalizedArea;
|
|
112302
113403
|
exports.notificationOutputCapability = notificationOutputCapability;
|
|
112303
113404
|
exports.notificationRulesCapability = notificationRulesCapability;
|
|
112304
113405
|
exports.notifierCapability = notifierCapability;
|
|
@@ -112316,6 +113417,7 @@ exports.osdCapability = osdCapability;
|
|
|
112316
113417
|
exports.osdManagerCapability = osdManagerCapability;
|
|
112317
113418
|
exports.overlayClusterStepSettings = overlayClusterStepSettings;
|
|
112318
113419
|
exports.packagePickerItems = packagePickerItems;
|
|
113420
|
+
exports.parseAcceleratorFactsJson = parseAcceleratorFactsJson;
|
|
112319
113421
|
exports.parseAdminBase = parseAdminBase;
|
|
112320
113422
|
exports.parseAlertEpisodePayload = parseAlertEpisodePayload;
|
|
112321
113423
|
exports.parseAppAuthCallback = parseAppAuthCallback;
|
|
@@ -112366,6 +113468,9 @@ exports.pipelineRunnerCapability = pipelineRunnerCapability;
|
|
|
112366
113468
|
exports.planComposition = planComposition;
|
|
112367
113469
|
exports.planCompositionEvents = planCompositionEvents;
|
|
112368
113470
|
exports.planItems = planItems;
|
|
113471
|
+
exports.planMotionCropRegions = planMotionCropRegions;
|
|
113472
|
+
exports.planMotionCrops = planMotionCrops;
|
|
113473
|
+
exports.planRecoveryCrops = planRecoveryCrops;
|
|
112369
113474
|
exports.plateGalleryCapability = plateGalleryCapability;
|
|
112370
113475
|
exports.platformProbeCapability = platformProbeCapability;
|
|
112371
113476
|
exports.poolMemoryThreshold = poolMemoryThreshold;
|
|
@@ -112394,6 +113499,8 @@ exports.readHubMotionStatus = readHubMotionStatus;
|
|
|
112394
113499
|
exports.readMotionTriggeredBy = readMotionTriggeredBy;
|
|
112395
113500
|
exports.readNativeLeaseOverride = readNativeLeaseOverride;
|
|
112396
113501
|
exports.readNodePin = require_sleep.readNodePin;
|
|
113502
|
+
exports.readRecoveryRecipe = readRecoveryRecipe;
|
|
113503
|
+
exports.readRecoveryRecipeForSteps = readRecoveryRecipeForSteps;
|
|
112397
113504
|
exports.readTimelapseGeneratedAt = readTimelapseGeneratedAt;
|
|
112398
113505
|
exports.readUseRootModel = readUseRootModel;
|
|
112399
113506
|
exports.readinessKey = require_sleep.readinessKey;
|
|
@@ -112415,9 +113522,11 @@ exports.recordsErrorFromMessage = require_sleep.recordsErrorFromMessage;
|
|
|
112415
113522
|
exports.rectsToCells = rectsToCells;
|
|
112416
113523
|
exports.reducePoints = reducePoints;
|
|
112417
113524
|
exports.registerKnownSecret = require_sleep.registerKnownSecret;
|
|
113525
|
+
exports.remapCropDetections = remapCropDetections;
|
|
112418
113526
|
exports.renameAlarmProfile = renameAlarmProfile;
|
|
112419
113527
|
exports.renderVocabPhrase = renderVocabPhrase;
|
|
112420
113528
|
exports.replacedFieldKeys = replacedFieldKeys;
|
|
113529
|
+
exports.reportIsScalable = reportIsScalable;
|
|
112421
113530
|
exports.requestMountPrefix = requestMountPrefix;
|
|
112422
113531
|
exports.requiredTopLevelKeys = requiredTopLevelKeys;
|
|
112423
113532
|
exports.requiresPython = requiresPython;
|
|
@@ -112445,6 +113554,7 @@ exports.resolveHubAddressRoles = resolveHubAddressRoles;
|
|
|
112445
113554
|
exports.resolveHydratedFieldValue = require_sleep.resolveHydratedFieldValue;
|
|
112446
113555
|
exports.resolveIconKey = resolveIconKey;
|
|
112447
113556
|
exports.resolveInternalAddress = resolveInternalAddress;
|
|
113557
|
+
exports.resolveLatencyColumn = resolveLatencyColumn;
|
|
112448
113558
|
exports.resolveLocationMode = resolveLocationMode;
|
|
112449
113559
|
exports.resolveMethodAuth = resolveMethodAuth;
|
|
112450
113560
|
exports.resolveMethodRole = resolveMethodRole;
|
|
@@ -112488,6 +113598,13 @@ exports.ruleSectionOf = ruleSectionOf;
|
|
|
112488
113598
|
exports.ruleSeedForSection = ruleSeedForSection;
|
|
112489
113599
|
exports.ruleTriggerZoneIds = ruleTriggerZoneIds;
|
|
112490
113600
|
exports.runInferenceStep = runInferenceStep;
|
|
113601
|
+
exports.runningAssignmentFeature = runningAssignmentFeature;
|
|
113602
|
+
exports.runningAssignmentRecovered = runningAssignmentRecovered;
|
|
113603
|
+
exports.runningFromDishwasherOperation = runningFromDishwasherOperation;
|
|
113604
|
+
exports.runningFromLawnMower = runningFromLawnMower;
|
|
113605
|
+
exports.runningFromMediaPlayer = runningFromMediaPlayer;
|
|
113606
|
+
exports.runningFromVacuumState = runningFromVacuumState;
|
|
113607
|
+
exports.runningStateCapability = runningStateCapability;
|
|
112491
113608
|
exports.runtimeDevices = runtimeDevices;
|
|
112492
113609
|
exports.runtimeStatePolicyFor = runtimeStatePolicyFor;
|
|
112493
113610
|
exports.sameDoFamily = sameDoFamily;
|
|
@@ -112511,6 +113628,7 @@ exports.secretTreeFromSchema = require_sleep.secretTreeFromSchema;
|
|
|
112511
113628
|
exports.secureActuationFor = secureActuationFor;
|
|
112512
113629
|
exports.selectAppConnectionEndpoints = selectAppConnectionEndpoints;
|
|
112513
113630
|
exports.selectAssignedProfileSlots = require_sleep.selectAssignedProfileSlots;
|
|
113631
|
+
exports.selectRecoveryStep = selectRecoveryStep;
|
|
112514
113632
|
exports.sendTimesForEditor = sendTimesForEditor;
|
|
112515
113633
|
exports.sendTimesOfSchedule = sendTimesOfSchedule;
|
|
112516
113634
|
exports.sensorRaisedOf = sensorRaisedOf;
|
|
@@ -112537,6 +113655,7 @@ exports.smokeCapability = smokeCapability;
|
|
|
112537
113655
|
exports.smtpProviderCapability = smtpProviderCapability;
|
|
112538
113656
|
exports.snapshotCapability = snapshotCapability;
|
|
112539
113657
|
exports.splitTranslationRef = splitTranslationRef;
|
|
113658
|
+
exports.squareAround = squareAround;
|
|
112540
113659
|
exports.ssoBridgeCapability = ssoBridgeCapability;
|
|
112541
113660
|
exports.startMoverUnderClearance = startMoverUnderClearance;
|
|
112542
113661
|
exports.startReachabilityPoll = startReachabilityPoll;
|