@camstack/types 1.2.317 → 1.2.318
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 +8 -2
- package/dist/addon.mjs +8 -2
- package/dist/capabilities/pipeline-orchestrator.cap.d.ts +96 -12
- package/dist/client.js +3 -1
- package/dist/client.mjs +3 -1
- package/dist/generated/addon-api.d.ts +21 -7
- package/dist/generated/cap-input-defaults.d.ts +1 -1
- package/dist/generated/method-access-map.d.ts +1 -1
- package/dist/generated/system-proxy.d.ts +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +250 -31
- package/dist/index.mjs +236 -32
- package/dist/pipeline/best-settings/demand.d.ts +21 -24
- package/dist/pipeline/best-settings/plan-schemas.d.ts +26 -0
- package/dist/pipeline/load-balancing/index.d.ts +6 -0
- package/dist/pipeline/load-balancing/schemas.d.ts +146 -0
- package/dist/pipeline/load-balancing/share-election.d.ts +54 -0
- package/dist/types/agent-pipeline-settings.d.ts +0 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -27242,6 +27242,107 @@ var pipelineExecutorCapability = {
|
|
|
27242
27242
|
}
|
|
27243
27243
|
};
|
|
27244
27244
|
//#endregion
|
|
27245
|
+
//#region src/pipeline/load-balancing/schemas.ts
|
|
27246
|
+
/**
|
|
27247
|
+
* Wire shapes of the Load balancing surface (D748): the session placement
|
|
27248
|
+
* policy, the atomic share write, the observed placement statistics, the
|
|
27249
|
+
* hardware share suggestion and the stale device pins.
|
|
27250
|
+
*/
|
|
27251
|
+
/**
|
|
27252
|
+
* How a new detection session picks its node and device.
|
|
27253
|
+
* - `least-loaded` — the historical rule: lowest `attached / weight`, a
|
|
27254
|
+
* constant node order at a tie (D37). An idle node always ties at 0, so a
|
|
27255
|
+
* weight only bites once a node already holds a session.
|
|
27256
|
+
* - `share` — the share of PARALLEL sessions is held to the weights, and a
|
|
27257
|
+
* full or drowning node yields to a free one (D748).
|
|
27258
|
+
*/
|
|
27259
|
+
var SessionPlacementPolicySchema = zod.z.enum(["least-loaded", "share"]);
|
|
27260
|
+
var DEFAULT_SESSION_PLACEMENT_POLICY = "least-loaded";
|
|
27261
|
+
/** The global-settings key the policy is stored under (orchestrator `load` section). */
|
|
27262
|
+
var SESSION_PLACEMENT_SETTING_KEY = "sessionPlacement";
|
|
27263
|
+
/** Parse a stored value; anything unknown is the default (nothing changes on deploy). */
|
|
27264
|
+
function parseSessionPlacementPolicy(value) {
|
|
27265
|
+
const parsed = SessionPlacementPolicySchema.safeParse(value);
|
|
27266
|
+
return parsed.success ? parsed.data : DEFAULT_SESSION_PLACEMENT_POLICY;
|
|
27267
|
+
}
|
|
27268
|
+
var LoadBalancingSharesInputSchema = zod.z.object({
|
|
27269
|
+
/** nodeId → share (> 0). A node left out keeps its weight. */
|
|
27270
|
+
nodes: zod.z.record(zod.z.string(), zod.z.number().positive()).default({}),
|
|
27271
|
+
/** nodeId → deviceKey → share (> 0). A device left out keeps its weight. */
|
|
27272
|
+
devices: zod.z.record(zod.z.string(), zod.z.record(zod.z.string(), zod.z.number().positive())).default({})
|
|
27273
|
+
});
|
|
27274
|
+
var PlacementStatsInputSchema = zod.z.object({
|
|
27275
|
+
/** Window the observed shares are averaged over (default 1 h, max 24 h). */
|
|
27276
|
+
windowMs: zod.z.number().int().min(6e4).max(1440 * 60 * 1e3).default(3600 * 1e3) });
|
|
27277
|
+
var PlacementDeviceStatsSchema = zod.z.object({
|
|
27278
|
+
deviceKey: zod.z.string(),
|
|
27279
|
+
/** Sessions running on this device now. */
|
|
27280
|
+
sessionsNow: zod.z.number(),
|
|
27281
|
+
/** Sessions started on this device inside the window. */
|
|
27282
|
+
sessionsStarted: zod.z.number(),
|
|
27283
|
+
/** Time-weighted share of the node's parallel sessions over the window (0–1); null = no session ran. */
|
|
27284
|
+
observedShare: zod.z.number().nullable()
|
|
27285
|
+
});
|
|
27286
|
+
var PlacementNodeStatsSchema = zod.z.object({
|
|
27287
|
+
nodeId: zod.z.string(),
|
|
27288
|
+
sessionsNow: zod.z.number(),
|
|
27289
|
+
sessionsStarted: zod.z.number(),
|
|
27290
|
+
/** Time-weighted share of the cluster's parallel sessions over the window (0–1); null = none ran. */
|
|
27291
|
+
observedShare: zod.z.number().nullable(),
|
|
27292
|
+
/** Most sessions this node ran at once inside the window. */
|
|
27293
|
+
peakSessions: zod.z.number(),
|
|
27294
|
+
/** Sessions a share election handed elsewhere because this node was full or drowning. */
|
|
27295
|
+
yieldsAway: zod.z.number(),
|
|
27296
|
+
devices: zod.z.array(PlacementDeviceStatsSchema).readonly()
|
|
27297
|
+
});
|
|
27298
|
+
/** A camera's device pin that the session path cannot honour. */
|
|
27299
|
+
var StaleDevicePinSchema = zod.z.object({
|
|
27300
|
+
deviceId: zod.z.number(),
|
|
27301
|
+
deviceKey: zod.z.string(),
|
|
27302
|
+
/** `disabled` — the key exists on some node but is not enabled anywhere; `absent` — no node has it. */
|
|
27303
|
+
reason: zod.z.enum(["disabled", "absent"])
|
|
27304
|
+
});
|
|
27305
|
+
var PlacementStatsSchema = zod.z.object({
|
|
27306
|
+
policy: SessionPlacementPolicySchema,
|
|
27307
|
+
windowMs: zod.z.number(),
|
|
27308
|
+
/** How much of the window was actually observed (the hub keeps it in memory). */
|
|
27309
|
+
observedForMs: zod.z.number(),
|
|
27310
|
+
sessionsNow: zod.z.number(),
|
|
27311
|
+
/** Most parallel sessions across the cluster inside the window. */
|
|
27312
|
+
clusterPeakSessions: zod.z.number(),
|
|
27313
|
+
nodes: zod.z.array(PlacementNodeStatsSchema).readonly(),
|
|
27314
|
+
stalePins: zod.z.array(StaleDevicePinSchema).readonly()
|
|
27315
|
+
});
|
|
27316
|
+
var SuggestSharesInputSchema = zod.z.object({ headroom: zod.z.number().min(.3).max(.5).default(.4) });
|
|
27317
|
+
var SuggestedDeviceShareSchema = zod.z.object({
|
|
27318
|
+
deviceKey: zod.z.string(),
|
|
27319
|
+
/** Share of the node (0–100, rounded). */
|
|
27320
|
+
share: zod.z.number(),
|
|
27321
|
+
modelId: zod.z.string().nullable(),
|
|
27322
|
+
/** Inferences per second at that model, from the measured table. */
|
|
27323
|
+
fps: zod.z.number().nullable(),
|
|
27324
|
+
measured: zod.z.boolean(),
|
|
27325
|
+
/** Advisory session cap: ⌊fps × (1 − headroom) / camera fps⌋. Never enforced. */
|
|
27326
|
+
suggestedMaxSessions: zod.z.number().nullable()
|
|
27327
|
+
});
|
|
27328
|
+
var SuggestedNodeShareSchema = zod.z.object({
|
|
27329
|
+
nodeId: zod.z.string(),
|
|
27330
|
+
/** Share of the cluster (0–100, rounded); null = node skipped (offline, unknown hardware). */
|
|
27331
|
+
share: zod.z.number().nullable(),
|
|
27332
|
+
/** Parallel sessions the node sustains at the suggested models with the headroom kept free. */
|
|
27333
|
+
capacitySessions: zod.z.number(),
|
|
27334
|
+
devices: zod.z.array(SuggestedDeviceShareSchema).readonly(),
|
|
27335
|
+
skippedReason: zod.z.string().nullable()
|
|
27336
|
+
});
|
|
27337
|
+
var SuggestSharesResultSchema = zod.z.object({
|
|
27338
|
+
headroom: zod.z.number(),
|
|
27339
|
+
/** Camera detection rate the capacity is counted in (the fastest camera's fps). */
|
|
27340
|
+
cameraFps: zod.z.number(),
|
|
27341
|
+
/** How many share → model → capacity rounds ran before the shares stopped moving (≤ 3). */
|
|
27342
|
+
iterations: zod.z.number(),
|
|
27343
|
+
nodes: zod.z.array(SuggestedNodeShareSchema).readonly()
|
|
27344
|
+
});
|
|
27345
|
+
//#endregion
|
|
27245
27346
|
//#region src/pipeline/best-settings/plan-schemas.ts
|
|
27246
27347
|
/**
|
|
27247
27348
|
* The wire shapes of the Best settings plan and apply (D745), used by the
|
|
@@ -27320,7 +27421,9 @@ var DeviceDemandSchema = zod.z.object({
|
|
|
27320
27421
|
"estimate",
|
|
27321
27422
|
"none"
|
|
27322
27423
|
]),
|
|
27323
|
-
observedForMs: zod.z.number().nullable()
|
|
27424
|
+
observedForMs: zod.z.number().nullable(),
|
|
27425
|
+
/** How on-motion sessions were charged: per-device observation, or split by share (D748). */
|
|
27426
|
+
placement: SessionPlacementPolicySchema
|
|
27324
27427
|
});
|
|
27325
27428
|
var DeviceModelChoiceSchema = zod.z.object({
|
|
27326
27429
|
modelId: zod.z.string(),
|
|
@@ -27392,14 +27495,24 @@ var BestSettingsWarningSchema = zod.z.discriminatedUnion("kind", [
|
|
|
27392
27495
|
deviceKey: zod.z.string()
|
|
27393
27496
|
})
|
|
27394
27497
|
]);
|
|
27395
|
-
var BestSettingsOptionsSchema = zod.z.object({
|
|
27396
|
-
/** Share of each accelerator's throughput kept free (0.3–0.5). */
|
|
27397
|
-
headroom: zod.z.number().min(.3).max(.5).default(.4)
|
|
27498
|
+
var BestSettingsOptionsSchema = zod.z.object({
|
|
27499
|
+
/** Share of each accelerator's throughput kept free (0.3–0.5). */
|
|
27500
|
+
headroom: zod.z.number().min(.3).max(.5).default(.4),
|
|
27501
|
+
/**
|
|
27502
|
+
* What-if shares (D748): plan as if these node/device shares were stored
|
|
27503
|
+
* and the policy were `share`. Absent = the stored shares under the stored
|
|
27504
|
+
* policy. A what-if plan is for display; apply always re-plans from what
|
|
27505
|
+
* is stored.
|
|
27506
|
+
*/
|
|
27507
|
+
shares: LoadBalancingSharesInputSchema.optional()
|
|
27508
|
+
});
|
|
27398
27509
|
var BestSettingsPlanSchema = zod.z.object({
|
|
27399
27510
|
/** Hash of everything the apply will write — the apply refuses a different one. */
|
|
27400
27511
|
planHash: zod.z.string(),
|
|
27401
27512
|
family: zod.z.literal("yolov9"),
|
|
27402
27513
|
headroom: zod.z.number(),
|
|
27514
|
+
/** Planned under what-if shares (D748): for display — apply refuses it as stale. */
|
|
27515
|
+
whatIf: zod.z.boolean(),
|
|
27403
27516
|
generatedAt: zod.z.number(),
|
|
27404
27517
|
nodes: zod.z.array(NodeRecommendationSchema).readonly(),
|
|
27405
27518
|
nodeChanges: zod.z.array(NodeDeviceChangeSchema).readonly(),
|
|
@@ -29055,7 +29168,6 @@ var CameraStepOverridePatchSchema = zod.z.object({
|
|
|
29055
29168
|
settings: zod.z.record(zod.z.string(), zod.z.unknown()).readonly().optional()
|
|
29056
29169
|
});
|
|
29057
29170
|
var CameraPipelineSettingsSchema = zod.z.object({
|
|
29058
|
-
pinnedAgentNodeId: zod.z.string().optional(),
|
|
29059
29171
|
stepToggles: zod.z.record(zod.z.string(), zod.z.boolean()).optional(),
|
|
29060
29172
|
stepOverridesByDevice: zod.z.record(zod.z.string(), zod.z.record(zod.z.string(), zod.z.record(zod.z.string(), CameraStepOverridePatchSchema))).optional(),
|
|
29061
29173
|
pipelineByAgent: zod.z.record(zod.z.string(), CameraPipelineForAgentSchema).optional()
|
|
@@ -29749,18 +29861,6 @@ var pipelineOrchestratorCapability = {
|
|
|
29749
29861
|
auth: "admin"
|
|
29750
29862
|
}),
|
|
29751
29863
|
/**
|
|
29752
|
-
* Set a node's detection WEIGHT (relative share for the quota balancer).
|
|
29753
|
-
* `null` clears it (back to the implicit weight 1). Unpinned cameras
|
|
29754
|
-
* distribute proportionally to weight; `maxCameras` still clamps on top.
|
|
29755
|
-
*/
|
|
29756
|
-
setAgentDetectWeight: require_sleep.method(zod.z.object({
|
|
29757
|
-
agentNodeId: zod.z.string(),
|
|
29758
|
-
detectWeight: zod.z.number().positive().nullable()
|
|
29759
|
-
}), zod.z.object({ success: zod.z.literal(true) }), {
|
|
29760
|
-
kind: "mutation",
|
|
29761
|
-
auth: "admin"
|
|
29762
|
-
}),
|
|
29763
|
-
/**
|
|
29764
29864
|
* Set one node's placement CAPABILITIES — the per-node record that
|
|
29765
29865
|
* replaced the four flat orchestrator lists (`enabledNodes`,
|
|
29766
29866
|
* `enabledDecoderNodes`, `enabledAudioNodes`, `remoteSourcingNodes`).
|
|
@@ -29900,6 +30000,33 @@ var pipelineOrchestratorCapability = {
|
|
|
29900
30000
|
kind: "mutation",
|
|
29901
30001
|
auth: "admin"
|
|
29902
30002
|
}),
|
|
30003
|
+
/**
|
|
30004
|
+
* Load balancing (D748) — write node shares (`detectWeight`) and per-node
|
|
30005
|
+
* device shares (inference-device `weight`) in ONE atomic read-modify-write
|
|
30006
|
+
* of the agent settings — the only writer of node shares (the per-node
|
|
30007
|
+
* `setAgentDetectWeight` it replaced raced its siblings, one RMW each).
|
|
30008
|
+
* A node or device left out keeps its weight. Never changes the
|
|
30009
|
+
* `sessionPlacement` policy.
|
|
30010
|
+
*/
|
|
30011
|
+
setLoadBalancingShares: require_sleep.method(LoadBalancingSharesInputSchema, zod.z.object({ success: zod.z.literal(true) }), {
|
|
30012
|
+
kind: "mutation",
|
|
30013
|
+
auth: "admin"
|
|
30014
|
+
}),
|
|
30015
|
+
/**
|
|
30016
|
+
* Load balancing (D748) — what placement actually did: per node and
|
|
30017
|
+
* device, sessions now, sessions started and the time-weighted share of
|
|
30018
|
+
* PARALLEL sessions over `windowMs`; the cluster's parallel peak; yields
|
|
30019
|
+
* a share election made; and every device pin the session path cannot
|
|
30020
|
+
* honour. In memory on the hub — a restart starts a new window.
|
|
30021
|
+
*/
|
|
30022
|
+
getPlacementStats: require_sleep.method(PlacementStatsInputSchema, PlacementStatsSchema, { auth: "admin" }),
|
|
30023
|
+
/**
|
|
30024
|
+
* Load balancing (D748) — shares proportional to each node's measured
|
|
30025
|
+
* capacity at the models Best settings would pick, iterated (≤ 3 rounds)
|
|
30026
|
+
* until the shares stop moving; per device, an ADVISORY session cap.
|
|
30027
|
+
* Writes nothing.
|
|
30028
|
+
*/
|
|
30029
|
+
suggestShares: require_sleep.method(SuggestSharesInputSchema, SuggestSharesResultSchema, { auth: "admin" }),
|
|
29903
30030
|
/** Read one camera's settings. Null when never touched (inherits agent defaults fully). */
|
|
29904
30031
|
getCameraSettings: require_sleep.method(zod.z.object({ deviceId: zod.z.number() }), CameraPipelineSettingsSchema.nullable()),
|
|
29905
30032
|
/** Set or clear the 3-state toggle for one (camera, addonId). Pass `enabled: null` to clear and revert to agent default. */
|
|
@@ -56360,6 +56487,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
56360
56487
|
addonId: null,
|
|
56361
56488
|
access: "view"
|
|
56362
56489
|
},
|
|
56490
|
+
"pipelineOrchestrator.getPlacementStats": {
|
|
56491
|
+
capName: "pipeline-orchestrator",
|
|
56492
|
+
capScope: "system",
|
|
56493
|
+
addonId: null,
|
|
56494
|
+
access: "view"
|
|
56495
|
+
},
|
|
56363
56496
|
"pipelineOrchestrator.getReconcileFromDiskStatus": {
|
|
56364
56497
|
capName: "pipeline-orchestrator",
|
|
56365
56498
|
capScope: "system",
|
|
@@ -56450,12 +56583,6 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
56450
56583
|
addonId: null,
|
|
56451
56584
|
access: "create"
|
|
56452
56585
|
},
|
|
56453
|
-
"pipelineOrchestrator.setAgentDetectWeight": {
|
|
56454
|
-
capName: "pipeline-orchestrator",
|
|
56455
|
-
capScope: "system",
|
|
56456
|
-
addonId: null,
|
|
56457
|
-
access: "create"
|
|
56458
|
-
},
|
|
56459
56586
|
"pipelineOrchestrator.setAgentInferenceDevices": {
|
|
56460
56587
|
capName: "pipeline-orchestrator",
|
|
56461
56588
|
capScope: "system",
|
|
@@ -56510,12 +56637,24 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
56510
56637
|
addonId: null,
|
|
56511
56638
|
access: "create"
|
|
56512
56639
|
},
|
|
56640
|
+
"pipelineOrchestrator.setLoadBalancingShares": {
|
|
56641
|
+
capName: "pipeline-orchestrator",
|
|
56642
|
+
capScope: "system",
|
|
56643
|
+
addonId: null,
|
|
56644
|
+
access: "create"
|
|
56645
|
+
},
|
|
56513
56646
|
"pipelineOrchestrator.setPipelineDevicePin": {
|
|
56514
56647
|
capName: "pipeline-orchestrator",
|
|
56515
56648
|
capScope: "system",
|
|
56516
56649
|
addonId: null,
|
|
56517
56650
|
access: "create"
|
|
56518
56651
|
},
|
|
56652
|
+
"pipelineOrchestrator.suggestShares": {
|
|
56653
|
+
capName: "pipeline-orchestrator",
|
|
56654
|
+
capScope: "system",
|
|
56655
|
+
addonId: null,
|
|
56656
|
+
access: "view"
|
|
56657
|
+
},
|
|
56519
56658
|
"pipelineOrchestrator.unassignAudio": {
|
|
56520
56659
|
capName: "pipeline-orchestrator",
|
|
56521
56660
|
capScope: "system",
|
|
@@ -67270,7 +67409,6 @@ function createSystemProxy(api) {
|
|
|
67270
67409
|
listAgentSettings: (input) => dispatch("pipelineOrchestrator", "listAgentSettings", "query", input),
|
|
67271
67410
|
removeAgentSettings: (input) => dispatch("pipelineOrchestrator", "removeAgentSettings", "mutation", input),
|
|
67272
67411
|
setAgentMaxCameras: (input) => dispatch("pipelineOrchestrator", "setAgentMaxCameras", "mutation", input),
|
|
67273
|
-
setAgentDetectWeight: (input) => dispatch("pipelineOrchestrator", "setAgentDetectWeight", "mutation", input),
|
|
67274
67412
|
setAgentCapabilities: (input) => dispatch("pipelineOrchestrator", "setAgentCapabilities", "mutation", input),
|
|
67275
67413
|
setAgentDecodeLimits: (input) => dispatch("pipelineOrchestrator", "setAgentDecodeLimits", "mutation", input),
|
|
67276
67414
|
setAgentReachableHost: (input) => dispatch("pipelineOrchestrator", "setAgentReachableHost", "mutation", input),
|
|
@@ -67279,6 +67417,9 @@ function createSystemProxy(api) {
|
|
|
67279
67417
|
resetNodePipelineDefaults: (input) => dispatch("pipelineOrchestrator", "resetNodePipelineDefaults", "mutation", input),
|
|
67280
67418
|
planBestSettings: (input) => dispatch("pipelineOrchestrator", "planBestSettings", "query", input),
|
|
67281
67419
|
applyBestSettings: (input) => dispatch("pipelineOrchestrator", "applyBestSettings", "mutation", input),
|
|
67420
|
+
setLoadBalancingShares: (input) => dispatch("pipelineOrchestrator", "setLoadBalancingShares", "mutation", input),
|
|
67421
|
+
getPlacementStats: (input) => dispatch("pipelineOrchestrator", "getPlacementStats", "query", input),
|
|
67422
|
+
suggestShares: (input) => dispatch("pipelineOrchestrator", "suggestShares", "query", input),
|
|
67282
67423
|
getCameraStatuses: (input) => dispatch("pipelineOrchestrator", "getCameraStatuses", "query", input),
|
|
67283
67424
|
setCameraSensors: (input) => dispatch("pipelineOrchestrator", "setCameraSensors", "mutation", input),
|
|
67284
67425
|
reconcileFromDisk: (input) => dispatch("pipelineOrchestrator", "reconcileFromDisk", "mutation", input),
|
|
@@ -72767,6 +72908,7 @@ function estimatedConcurrentSessions(cameraCount, options) {
|
|
|
72767
72908
|
*/
|
|
72768
72909
|
function estimateDeviceDemand(input) {
|
|
72769
72910
|
const options = input.options ?? DEFAULT_DEMAND_OPTIONS;
|
|
72911
|
+
const placement = input.shares ? "share" : "least-loaded";
|
|
72770
72912
|
const eligibleById = new Map(input.eligible.map((d) => [deviceId(d.nodeId, d.deviceKey), d]));
|
|
72771
72913
|
const peakById = new Map(input.peaks.map((p) => [deviceId(p.nodeId, p.deviceKey), p]));
|
|
72772
72914
|
const weightByNode = /* @__PURE__ */ new Map();
|
|
@@ -72786,19 +72928,28 @@ function estimateDeviceDemand(input) {
|
|
|
72786
72928
|
const noteMax = (key, fps) => {
|
|
72787
72929
|
maxFps.set(key, Math.max(maxFps.get(key) ?? 0, fps));
|
|
72788
72930
|
};
|
|
72931
|
+
const pinNodeOf = (pin) => {
|
|
72932
|
+
if (pin.nodeId !== null) return pin.nodeId;
|
|
72933
|
+
const holders = input.eligible.filter((d) => d.deviceKey === pin.deviceKey);
|
|
72934
|
+
return holders.length === 1 ? holders[0].nodeId : null;
|
|
72935
|
+
};
|
|
72936
|
+
let alwaysOnTotal = 0;
|
|
72789
72937
|
for (const cam of input.cameras) {
|
|
72790
72938
|
if (!charges(cam)) continue;
|
|
72791
|
-
|
|
72792
|
-
|
|
72939
|
+
const pinNode = cam.devicePin !== null ? pinNodeOf(cam.devicePin) : null;
|
|
72940
|
+
if (cam.devicePin !== null && pinNode !== null) {
|
|
72941
|
+
const key = deviceId(pinNode, cam.devicePin.deviceKey);
|
|
72793
72942
|
if (!eligibleById.has(key)) continue;
|
|
72794
72943
|
bump(pinnedFps, key, cam.fps);
|
|
72795
72944
|
noteMax(key, cam.fps);
|
|
72796
72945
|
continue;
|
|
72797
72946
|
}
|
|
72798
|
-
|
|
72799
|
-
|
|
72947
|
+
const standing = cam.mode === "always-on" && cam.standingNodeId !== null ? cam.standingNodeId : cam.nodePinId;
|
|
72948
|
+
if (standing !== null) {
|
|
72949
|
+
if (cam.mode === "always-on") alwaysOnTotal += 1;
|
|
72950
|
+
const nodeWeight = weightByNode.get(standing) ?? 0;
|
|
72800
72951
|
for (const d of input.eligible) {
|
|
72801
|
-
if (d.nodeId !==
|
|
72952
|
+
if (d.nodeId !== standing || nodeWeight <= 0) continue;
|
|
72802
72953
|
const key = deviceId(d.nodeId, d.deviceKey);
|
|
72803
72954
|
const share = Math.max(d.weight, 0) / nodeWeight;
|
|
72804
72955
|
bump(alwaysOnFps, key, cam.fps * share);
|
|
@@ -72812,13 +72963,23 @@ function estimateDeviceDemand(input) {
|
|
|
72812
72963
|
const fastestOnMotion = onMotionRates.reduce((m, r) => Math.max(m, r), 0);
|
|
72813
72964
|
const nodeCount = weightByNode.size;
|
|
72814
72965
|
const estimatedTotal = estimatedConcurrentSessions(onMotionRates.length, options);
|
|
72966
|
+
const clusterPeak = input.shares?.clusterPeak ?? null;
|
|
72967
|
+
const clusterObserved = clusterPeak !== null && clusterPeak.observedForMs >= 864e5;
|
|
72968
|
+
const clusterSessions = clusterObserved ? Math.max(0, clusterPeak.peakSessions - alwaysOnTotal) : estimatedTotal;
|
|
72969
|
+
const shareSum = [...weightByNode.keys()].reduce((sum, nodeId) => sum + Math.max(input.shares?.nodes[nodeId] ?? 0, 0), 0);
|
|
72815
72970
|
return input.eligible.map((d) => {
|
|
72816
72971
|
const key = deviceId(d.nodeId, d.deviceKey);
|
|
72817
72972
|
const peak = peakById.get(key) ?? (eligibleCountByNode.get(d.nodeId) === 1 ? peakById.get(deviceId(d.nodeId, "")) : void 0);
|
|
72818
72973
|
const alwaysOn = alwaysOnCount.get(key) ?? 0;
|
|
72819
72974
|
let onMotionSessions = 0;
|
|
72820
72975
|
let onMotionSource = "none";
|
|
72821
|
-
if (onMotionRates.length > 0
|
|
72976
|
+
if (onMotionRates.length > 0 && input.shares !== void 0) {
|
|
72977
|
+
const nodeWeight = weightByNode.get(d.nodeId) ?? 0;
|
|
72978
|
+
const nodeShare = shareSum > 0 ? Math.max(input.shares.nodes[d.nodeId] ?? 0, 0) / shareSum : 0;
|
|
72979
|
+
const deviceShare = nodeWeight > 0 ? Math.max(d.weight, 0) / nodeWeight : 0;
|
|
72980
|
+
onMotionSessions = Math.max(0, Math.ceil(clusterSessions * nodeShare * deviceShare - 1e-9));
|
|
72981
|
+
onMotionSource = clusterObserved ? "observed-24h" : "estimate";
|
|
72982
|
+
} else if (onMotionRates.length > 0) if (peak !== void 0 && peak.observedForMs >= 864e5) {
|
|
72822
72983
|
onMotionSessions = Math.max(0, Math.round(peak.peakSessions - alwaysOn));
|
|
72823
72984
|
onMotionSource = "observed-24h";
|
|
72824
72985
|
} else {
|
|
@@ -72836,7 +72997,8 @@ function estimateDeviceDemand(input) {
|
|
|
72836
72997
|
alwaysOnCameras: Math.round(alwaysOn * 100) / 100,
|
|
72837
72998
|
onMotionSessions,
|
|
72838
72999
|
onMotionSource,
|
|
72839
|
-
observedForMs: peak?.observedForMs ?? null
|
|
73000
|
+
observedForMs: input.shares !== void 0 ? clusterPeak?.observedForMs ?? null : peak?.observedForMs ?? null,
|
|
73001
|
+
placement
|
|
72840
73002
|
};
|
|
72841
73003
|
}).toSorted((a, b) => a.nodeId === b.nodeId ? a.deviceKey.localeCompare(b.deviceKey) : a.nodeId.localeCompare(b.nodeId));
|
|
72842
73004
|
}
|
|
@@ -73072,6 +73234,48 @@ function recommendNode(input) {
|
|
|
73072
73234
|
};
|
|
73073
73235
|
}
|
|
73074
73236
|
//#endregion
|
|
73237
|
+
//#region src/pipeline/load-balancing/share-election.ts
|
|
73238
|
+
/** Deficits equal up to float noise (0.7 × 2 − 1 is 0.3999…, not 0.4). */
|
|
73239
|
+
var DEFICIT_EPSILON = 1e-9;
|
|
73240
|
+
function byDeficit(a, b) {
|
|
73241
|
+
const diff = b.deficit - a.deficit;
|
|
73242
|
+
return (Math.abs(diff) < DEFICIT_EPSILON ? 0 : diff) || b.candidate.share - a.candidate.share || a.candidate.key.localeCompare(b.candidate.key);
|
|
73243
|
+
}
|
|
73244
|
+
function spareOf(c) {
|
|
73245
|
+
return c.spare ?? Number.POSITIVE_INFINITY;
|
|
73246
|
+
}
|
|
73247
|
+
function electByShare(candidates) {
|
|
73248
|
+
if (candidates.length === 0) return { kind: "none" };
|
|
73249
|
+
const shareSum = candidates.reduce((s, c) => s + Math.max(c.share, 0), 0);
|
|
73250
|
+
const running = candidates.reduce((s, c) => s + Math.max(c.running, 0), 0);
|
|
73251
|
+
const scored = candidates.map((candidate) => ({
|
|
73252
|
+
candidate,
|
|
73253
|
+
deficit: (shareSum > 0 ? Math.max(candidate.share, 0) / shareSum : 1 / candidates.length) * (running + 1) - candidate.running
|
|
73254
|
+
})).toSorted(byDeficit);
|
|
73255
|
+
const turn = scored[0].candidate;
|
|
73256
|
+
if (!turn.capped && !turn.saturated) return {
|
|
73257
|
+
kind: "elected",
|
|
73258
|
+
key: turn.key,
|
|
73259
|
+
reason: "share"
|
|
73260
|
+
};
|
|
73261
|
+
const free = scored.filter((s) => !s.candidate.capped && !s.candidate.saturated).toSorted((a, b) => spareOf(b.candidate) - spareOf(a.candidate) || byDeficit(a, b));
|
|
73262
|
+
const yieldReason = turn.capped ? "at-cap" : "saturated";
|
|
73263
|
+
if (free.length > 0) return {
|
|
73264
|
+
kind: "elected",
|
|
73265
|
+
key: free[0].candidate.key,
|
|
73266
|
+
reason: "yield",
|
|
73267
|
+
turnKey: turn.key,
|
|
73268
|
+
yieldReason
|
|
73269
|
+
};
|
|
73270
|
+
const uncapped = scored.find((s) => !s.candidate.capped);
|
|
73271
|
+
if (uncapped === void 0) return { kind: "none" };
|
|
73272
|
+
return {
|
|
73273
|
+
kind: "elected",
|
|
73274
|
+
key: uncapped.candidate.key,
|
|
73275
|
+
reason: "share"
|
|
73276
|
+
};
|
|
73277
|
+
}
|
|
73278
|
+
//#endregion
|
|
73075
73279
|
//#region src/pipeline/root-face-crop.ts
|
|
73076
73280
|
/** Side of the square, as a multiple of the face box's longer side. */
|
|
73077
73281
|
var ROOT_FACE_REFINE_SCALE = 1.6;
|
|
@@ -75591,6 +75795,7 @@ exports.DEFAULT_POOL_MEMORY_POLICY = DEFAULT_POOL_MEMORY_POLICY;
|
|
|
75591
75795
|
exports.DEFAULT_RECORDING_PROFILES = DEFAULT_RECORDING_PROFILES;
|
|
75592
75796
|
exports.DEFAULT_RETENTION = DEFAULT_RETENTION;
|
|
75593
75797
|
exports.DEFAULT_RUNTIME_STATE_DURABILITY = require_sleep.DEFAULT_RUNTIME_STATE_DURABILITY;
|
|
75798
|
+
exports.DEFAULT_SESSION_PLACEMENT_POLICY = DEFAULT_SESSION_PLACEMENT_POLICY;
|
|
75594
75799
|
exports.DEFAULT_TIMELAPSE_PREVIEW_TEXT = DEFAULT_TIMELAPSE_PREVIEW_TEXT;
|
|
75595
75800
|
exports.DEFAULT_TOKEN_EXPIRY = DEFAULT_TOKEN_EXPIRY;
|
|
75596
75801
|
exports.DERIVED_CAMERA_SILENCED_CAP_NAMES = DERIVED_CAMERA_SILENCED_CAP_NAMES;
|
|
@@ -75887,6 +76092,7 @@ exports.LlmRuntimeStatusSchema = LlmRuntimeStatusSchema;
|
|
|
75887
76092
|
exports.LlmTimeoutDefaults = LlmTimeoutDefaults;
|
|
75888
76093
|
exports.LlmUsageRollupSchema = LlmUsageRollupSchema;
|
|
75889
76094
|
exports.LlmUsageSchema = LlmUsageSchema;
|
|
76095
|
+
exports.LoadBalancingSharesInputSchema = LoadBalancingSharesInputSchema;
|
|
75890
76096
|
exports.LoadContributionSchema = LoadContributionSchema;
|
|
75891
76097
|
exports.LocateSegmentResultSchema = LocateSegmentResultSchema;
|
|
75892
76098
|
exports.LocationDescriptionAssignmentSchema = LocationDescriptionAssignmentSchema;
|
|
@@ -76319,6 +76525,10 @@ exports.PipelineStepInputSchema = PipelineStepInputSchema;
|
|
|
76319
76525
|
exports.PipelineValidationIssueSchema = PipelineValidationIssueSchema;
|
|
76320
76526
|
exports.PipelineValidationResultSchema = PipelineValidationResultSchema;
|
|
76321
76527
|
exports.PlaceholderReasonSchema = PlaceholderReasonSchema;
|
|
76528
|
+
exports.PlacementDeviceStatsSchema = PlacementDeviceStatsSchema;
|
|
76529
|
+
exports.PlacementNodeStatsSchema = PlacementNodeStatsSchema;
|
|
76530
|
+
exports.PlacementStatsInputSchema = PlacementStatsInputSchema;
|
|
76531
|
+
exports.PlacementStatsSchema = PlacementStatsSchema;
|
|
76322
76532
|
exports.PolygonPointSchema = PolygonPointSchema;
|
|
76323
76533
|
exports.PoolMemoryWatchdog = PoolMemoryWatchdog;
|
|
76324
76534
|
exports.PowerMeterStatusSchema = PowerMeterStatusSchema;
|
|
@@ -76481,6 +76691,7 @@ exports.SCENE_RESET_RECAPTURES = SCENE_RESET_RECAPTURES;
|
|
|
76481
76691
|
exports.SCOPE_PRESETS = SCOPE_PRESETS;
|
|
76482
76692
|
exports.SENSOR_FEATURES = SENSOR_FEATURES;
|
|
76483
76693
|
exports.SENSOR_MAP = SENSOR_MAP;
|
|
76694
|
+
exports.SESSION_PLACEMENT_SETTING_KEY = SESSION_PLACEMENT_SETTING_KEY;
|
|
76484
76695
|
exports.SHARE_VIEW_KINDS = require_sleep.SHARE_VIEW_KINDS;
|
|
76485
76696
|
exports.SINGLE_HUB_ADDRESS_ROLES = SINGLE_HUB_ADDRESS_ROLES;
|
|
76486
76697
|
exports.SOURCE_CAPS = require_composition.SOURCE_CAPS;
|
|
@@ -76526,6 +76737,7 @@ exports.ServerUpdateActionResultSchema = ServerUpdateActionResultSchema;
|
|
|
76526
76737
|
exports.ServerUpdateCheckResultSchema = ServerUpdateCheckResultSchema;
|
|
76527
76738
|
exports.ServerUpdateOutcomeSchema = ServerUpdateOutcomeSchema;
|
|
76528
76739
|
exports.ServerUpdateStateSchema = ServerUpdateStateSchema;
|
|
76740
|
+
exports.SessionPlacementPolicySchema = SessionPlacementPolicySchema;
|
|
76529
76741
|
exports.SessionTriggerSchema = SessionTriggerSchema;
|
|
76530
76742
|
exports.SetCameraSensorsInputSchema = SetCameraSensorsInputSchema;
|
|
76531
76743
|
exports.SetLoggingSettingsInputSchema = SetLoggingSettingsInputSchema;
|
|
@@ -76548,6 +76760,7 @@ exports.SnapshotImageSchema = SnapshotImageSchema;
|
|
|
76548
76760
|
exports.SourceInfoSchema = SourceInfoSchema;
|
|
76549
76761
|
exports.SpatialDetectionSchema = SpatialDetectionSchema;
|
|
76550
76762
|
exports.SsoBridgeClaimsSchema = SsoBridgeClaimsSchema;
|
|
76763
|
+
exports.StaleDevicePinSchema = StaleDevicePinSchema;
|
|
76551
76764
|
exports.StartEmbeddedInputSchema = StartEmbeddedInputSchema;
|
|
76552
76765
|
exports.StationaryObjectSchema = StationaryObjectSchema;
|
|
76553
76766
|
exports.StorageAbortUploadInputSchema = AbortUploadInputSchema;
|
|
@@ -76611,6 +76824,10 @@ exports.SubscribeAudioChunksInputSchema = require_sleep.SubscribeAudioChunksInpu
|
|
|
76611
76824
|
exports.SubscribeAudioChunksResultSchema = require_sleep.SubscribeAudioChunksResultSchema;
|
|
76612
76825
|
exports.SubscribeFramesInputSchema = require_sleep.SubscribeFramesInputSchema;
|
|
76613
76826
|
exports.SubscribeFramesResultSchema = require_sleep.SubscribeFramesResultSchema;
|
|
76827
|
+
exports.SuggestSharesInputSchema = SuggestSharesInputSchema;
|
|
76828
|
+
exports.SuggestSharesResultSchema = SuggestSharesResultSchema;
|
|
76829
|
+
exports.SuggestedDeviceShareSchema = SuggestedDeviceShareSchema;
|
|
76830
|
+
exports.SuggestedNodeShareSchema = SuggestedNodeShareSchema;
|
|
76614
76831
|
exports.SuppliedRootOutputSchema = SuppliedRootOutputSchema;
|
|
76615
76832
|
exports.SwitchStatusSchema = SwitchStatusSchema;
|
|
76616
76833
|
exports.SystemMetricsSchema = SystemMetricsSchema;
|
|
@@ -76966,6 +77183,7 @@ exports.doorbellFeatureSources = doorbellFeatureSources;
|
|
|
76966
77183
|
exports.droppedConditionsForKind = droppedConditionsForKind;
|
|
76967
77184
|
exports.egressTranscodeSharingKey = egressTranscodeSharingKey;
|
|
76968
77185
|
exports.egressTransportFromRequest = egressTransportFromRequest;
|
|
77186
|
+
exports.electByShare = electByShare;
|
|
76969
77187
|
exports.embeddingEncoderCapability = embeddingEncoderCapability;
|
|
76970
77188
|
exports.embeddingModelFailedError = embeddingModelFailedError;
|
|
76971
77189
|
exports.embeddingModelFailedOf = embeddingModelFailedOf;
|
|
@@ -77216,6 +77434,7 @@ exports.parseOccupancyScope = parseOccupancyScope;
|
|
|
77216
77434
|
exports.parseProcStatus = parseProcStatus;
|
|
77217
77435
|
exports.parseProfileBrokerId = require_sleep.parseProfileBrokerId;
|
|
77218
77436
|
exports.parseRuleSection = parseRuleSection;
|
|
77437
|
+
exports.parseSessionPlacementPolicy = parseSessionPlacementPolicy;
|
|
77219
77438
|
exports.parseStreamParamsFormPatch = parseStreamParamsFormPatch;
|
|
77220
77439
|
exports.patchAudio = patchAudio;
|
|
77221
77440
|
exports.petFeederCapability = petFeederCapability;
|