@camstack/types 1.2.319 → 1.2.321

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.
@@ -80,7 +80,7 @@ export interface ConfigSection {
80
80
  readonly immediate?: boolean;
81
81
  readonly fields: readonly ConfigField[];
82
82
  }
83
- export type ConfigField = ConfigTextField | ConfigNumberField | ConfigBooleanField | ConfigSelectField | ConfigMultiSelectField | ConfigTimezoneField | ConfigDateTimeField | ConfigColorField | ConfigPasswordField | ConfigTextAreaField | ConfigSliderField | ConfigTagsField | ConfigScheduleField | ConfigGroupField | ConfigSubTabsField | ConfigSeparatorField | ConfigInfoField | ConfigQrCodeField | ConfigObjectArrayField | ConfigEditableArrayField | ConfigModelSelectorField | ConfigStorageLocationField | ConfigProbeField | ConfigButtonField | ConfigPipelineEditorField | ConfigNodeSelectField | ConfigNodeMultiSelectField | ConfigDeviceSelectField | ConfigDeviceMultiSelectField | ConfigNotificationTargetSelectField | ConfigNotificationTargetMultiSelectField | ConfigWidgetField | ConfigAddonActionSelectField | ConfigAddonActionButtonField | ConfigDeviceActionButtonField;
83
+ export type ConfigField = ConfigTextField | ConfigNumberField | ConfigBooleanField | ConfigSelectField | ConfigMultiSelectField | ConfigTimezoneField | ConfigDateTimeField | ConfigColorField | ConfigPasswordField | ConfigTextAreaField | ConfigSliderField | ConfigTagsField | ConfigScheduleField | ConfigGroupField | ConfigSubTabsField | ConfigSeparatorField | ConfigInfoField | ConfigQrCodeField | ConfigObjectArrayField | ConfigEditableArrayField | ConfigModelSelectorField | ConfigStorageLocationField | ConfigProbeField | ConfigButtonField | ConfigNodeSelectField | ConfigNodeMultiSelectField | ConfigDeviceSelectField | ConfigDeviceMultiSelectField | ConfigNotificationTargetSelectField | ConfigNotificationTargetMultiSelectField | ConfigWidgetField | ConfigAddonActionSelectField | ConfigAddonActionButtonField | ConfigDeviceActionButtonField;
84
84
  /**
85
85
  * Per-MODEL applicability of a pipeline-step setting (D214).
86
86
  *
@@ -900,61 +900,6 @@ export interface ConfigDeviceActionButtonField extends ConfigFieldBase {
900
900
  successMessage?: string;
901
901
  confirmMessage?: string;
902
902
  }
903
- /**
904
- * Describes an async resolver that the form renderer should call to
905
- * fetch deferred-load data a field depends on (e.g. the addon catalog
906
- * for a pipeline-editor, a dynamic options list for a select, …).
907
- *
908
- * The renderer dispatches through `client.trpc[capName][methodName]
909
- * .query(input)` — the field declares WHICH cap + method + input to
910
- * call, and the renderer stays oblivious to the endpoint naming.
911
- * Results are cached client-side keyed by `(capName, methodName,
912
- * input)` so two fields pointing at the same resolver deduplicate
913
- * automatically.
914
- *
915
- * Not an endpoint to arbitrary HTTP — the tRPC proxy gates it to the
916
- * server's declared cap surface + each cap's auth rules, same as
917
- * every other `.query()` call the UI makes.
918
- */
919
- export interface AsyncResolverRef {
920
- /** tRPC cap namespace (e.g. `pipelineExecutor`, `pipelineOrchestrator`). */
921
- readonly capName: string;
922
- /** Method on that cap — must be a `.query()` (no mutations via resolvers). */
923
- readonly methodName: string;
924
- /** Input payload passed verbatim; serialised into the React Query key. */
925
- readonly input?: Readonly<Record<string, unknown>>;
926
- }
927
- /**
928
- * Camera pipeline editor field (stateless-pipeline Phase 6, option B).
929
- *
930
- * The FormBuilder renders this as the rich `PipelineBuilder` composite
931
- * (step tree + engine picker + template picker) instead of a flat
932
- * input. `value` is always a fully-resolved `CameraPipelineConfig` —
933
- * when the camera has no persisted pipeline, the contributor
934
- * (orchestrator) fills it with the cluster default server-side so
935
- * the renderer stays dumb about nullability.
936
- *
937
- * `nodeId` names the runner node whose addon catalog powers the
938
- * editor. It's exposed explicitly (rather than buried inside the
939
- * resolver) for diagnostics, UI labels, and cache keying. The
940
- * catalog itself isn't embedded — `catalogResolver` tells the
941
- * renderer how to lazy-load it via the shared resolver hook, which
942
- * React Query caches once per `(capName, methodName, input)`. That
943
- * way 50 cameras assigned to the same node don't each trigger 50
944
- * catalog fetches.
945
- *
946
- * Templates are NOT described by a resolver — they're a hub-wide
947
- * library fetched in one place by the editor directly.
948
- */
949
- export interface ConfigPipelineEditorField extends ConfigFieldBase {
950
- type: 'pipeline-editor';
951
- /** Runner node whose catalog powers the editor. */
952
- readonly nodeId: string;
953
- /** The camera this field edits — used by the renderer to fetch per-camera settings. */
954
- readonly deviceId: number;
955
- /** How to fetch the addon catalog for this node. */
956
- readonly catalogResolver: AsyncResolverRef;
957
- }
958
903
  /**
959
904
  * Generic widget field — addon-contributed widget mounted by the
960
905
  * shared `<WidgetSlot>` runtime via the `WidgetRegistry`.
@@ -1016,9 +961,7 @@ export type ConfigFieldWithValue = (ConfigTextField & {
1016
961
  readonly value: unknown;
1017
962
  }) | (ConfigProbeField & {
1018
963
  readonly value: unknown;
1019
- }) | ConfigButtonField | (ConfigPipelineEditorField & {
1020
- readonly value: unknown;
1021
- }) | (ConfigNodeSelectField & {
964
+ }) | ConfigButtonField | (ConfigNodeSelectField & {
1022
965
  readonly value: unknown;
1023
966
  }) | (ConfigNodeMultiSelectField & {
1024
967
  readonly value: unknown;
@@ -1,10 +1,9 @@
1
1
  /**
2
- * Auto tune (D745): the static-hardware × measured-latency × demand
3
- * recommender of ONE detector family for the whole cluster, and the shapes of
4
- * its plan. See `recommend.ts`.
2
+ * Auto tune (D745, D755): the static-hardware × measured-latency recommender
3
+ * of ONE detector family for the whole cluster, sized by a capacity floor
4
+ * (no load input), and the shapes of its plan. See `recommend.ts`.
5
5
  */
6
6
  export * from './hardware-class.js';
7
7
  export * from './detector-performance.js';
8
- export * from './demand.js';
9
8
  export * from './recommend.js';
10
9
  export * from './plan-schemas.js';
@@ -1,10 +1,17 @@
1
1
  /**
2
2
  * The wire shapes of the Auto tune plan and apply (D745), used by the
3
3
  * `pipeline-orchestrator` cap. The recommender's own types
4
- * (`recommend.ts`, `demand.ts`) are the TS face of the same data; these
4
+ * (`recommend.ts`) are the TS face of the same data; these
5
5
  * schemas are what crosses the cap boundary.
6
6
  */
7
7
  import { z } from 'zod';
8
+ /** Default least number of parallel sessions per device Auto tune keeps (D755). */
9
+ export declare const DEFAULT_MIN_SESSIONS_PER_DEVICE = 2;
10
+ /** The operator's range for it. */
11
+ export declare const MIN_SESSIONS_PER_DEVICE_RANGE: {
12
+ readonly min: 1;
13
+ readonly max: 8;
14
+ };
8
15
  export declare const HardwareClassIdSchema: z.ZodEnum<{
9
16
  unknown: "unknown";
10
17
  "intel-core-ultra": "intel-core-ultra";
@@ -21,8 +28,9 @@ export declare const AcceleratorKindSchema: z.ZodEnum<{
21
28
  coral: "coral";
22
29
  }>;
23
30
  export declare const RecommendationReasonSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
24
- kind: z.ZodLiteral<"fits">;
25
- utilisation: z.ZodNumber;
31
+ kind: z.ZodLiteral<"capacity">;
32
+ sessions: z.ZodNumber;
33
+ minSessions: z.ZodNumber;
26
34
  }, z.core.$strip>, z.ZodObject<{
27
35
  kind: z.ZodLiteral<"tie-faster">;
28
36
  over: z.ZodString;
@@ -34,13 +42,11 @@ export declare const RecommendationReasonSchema: z.ZodDiscriminatedUnion<[z.ZodO
34
42
  limitedBy: z.ZodString;
35
43
  }, z.core.$strip>, z.ZodObject<{
36
44
  kind: z.ZodLiteral<"no-fit-lightest">;
37
- utilisation: z.ZodNumber;
45
+ sessions: z.ZodNumber;
46
+ minSessions: z.ZodNumber;
38
47
  }, z.core.$strip>, z.ZodObject<{
39
48
  kind: z.ZodLiteral<"derived-latency">;
40
49
  modelId: z.ZodString;
41
- }, z.core.$strip>, z.ZodObject<{
42
- kind: z.ZodLiteral<"estimated-load">;
43
- observedForMs: z.ZodNullable<z.ZodNumber>;
44
50
  }, z.core.$strip>, z.ZodObject<{
45
51
  kind: z.ZodLiteral<"cpu-device">;
46
52
  }, z.core.$strip>, z.ZodObject<{
@@ -73,30 +79,12 @@ export declare const RecommendationReasonSchema: z.ZodDiscriminatedUnion<[z.ZodO
73
79
  gguf: "gguf";
74
80
  }>;
75
81
  }, z.core.$strip>], "kind">;
76
- export declare const DeviceDemandSchema: z.ZodObject<{
77
- nodeId: z.ZodString;
78
- deviceKey: z.ZodString;
79
- demandFps: z.ZodNumber;
80
- maxCameraFps: z.ZodNumber;
81
- alwaysOnCameras: z.ZodNumber;
82
- onMotionSessions: z.ZodNumber;
83
- onMotionSource: z.ZodEnum<{
84
- none: "none";
85
- "observed-24h": "observed-24h";
86
- estimate: "estimate";
87
- }>;
88
- observedForMs: z.ZodNullable<z.ZodNumber>;
89
- placement: z.ZodEnum<{
90
- "least-loaded": "least-loaded";
91
- share: "share";
92
- }>;
93
- }, z.core.$strip>;
94
82
  export declare const DeviceModelChoiceSchema: z.ZodObject<{
95
83
  modelId: z.ZodString;
96
84
  p50Ms: z.ZodNumber;
97
85
  fps: z.ZodNumber;
98
86
  measured: z.ZodBoolean;
99
- utilisation: z.ZodNumber;
87
+ capacitySessions: z.ZodNumber;
100
88
  personRecall: z.ZodNumber;
101
89
  }, z.core.$strip>;
102
90
  export declare const DeviceRecommendationSchema: z.ZodObject<{
@@ -111,35 +99,18 @@ export declare const DeviceRecommendationSchema: z.ZodObject<{
111
99
  }>>;
112
100
  enabled: z.ZodBoolean;
113
101
  available: z.ZodBoolean;
114
- demand: z.ZodNullable<z.ZodObject<{
115
- nodeId: z.ZodString;
116
- deviceKey: z.ZodString;
117
- demandFps: z.ZodNumber;
118
- maxCameraFps: z.ZodNumber;
119
- alwaysOnCameras: z.ZodNumber;
120
- onMotionSessions: z.ZodNumber;
121
- onMotionSource: z.ZodEnum<{
122
- none: "none";
123
- "observed-24h": "observed-24h";
124
- estimate: "estimate";
125
- }>;
126
- observedForMs: z.ZodNullable<z.ZodNumber>;
127
- placement: z.ZodEnum<{
128
- "least-loaded": "least-loaded";
129
- share: "share";
130
- }>;
131
- }, z.core.$strip>>;
132
102
  chosen: z.ZodNullable<z.ZodObject<{
133
103
  modelId: z.ZodString;
134
104
  p50Ms: z.ZodNumber;
135
105
  fps: z.ZodNumber;
136
106
  measured: z.ZodBoolean;
137
- utilisation: z.ZodNumber;
107
+ capacitySessions: z.ZodNumber;
138
108
  personRecall: z.ZodNumber;
139
109
  }, z.core.$strip>>;
140
110
  reasons: z.ZodReadonly<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
141
- kind: z.ZodLiteral<"fits">;
142
- utilisation: z.ZodNumber;
111
+ kind: z.ZodLiteral<"capacity">;
112
+ sessions: z.ZodNumber;
113
+ minSessions: z.ZodNumber;
143
114
  }, z.core.$strip>, z.ZodObject<{
144
115
  kind: z.ZodLiteral<"tie-faster">;
145
116
  over: z.ZodString;
@@ -151,13 +122,11 @@ export declare const DeviceRecommendationSchema: z.ZodObject<{
151
122
  limitedBy: z.ZodString;
152
123
  }, z.core.$strip>, z.ZodObject<{
153
124
  kind: z.ZodLiteral<"no-fit-lightest">;
154
- utilisation: z.ZodNumber;
125
+ sessions: z.ZodNumber;
126
+ minSessions: z.ZodNumber;
155
127
  }, z.core.$strip>, z.ZodObject<{
156
128
  kind: z.ZodLiteral<"derived-latency">;
157
129
  modelId: z.ZodString;
158
- }, z.core.$strip>, z.ZodObject<{
159
- kind: z.ZodLiteral<"estimated-load">;
160
- observedForMs: z.ZodNullable<z.ZodNumber>;
161
130
  }, z.core.$strip>, z.ZodObject<{
162
131
  kind: z.ZodLiteral<"cpu-device">;
163
132
  }, z.core.$strip>, z.ZodObject<{
@@ -216,35 +185,18 @@ export declare const NodeRecommendationSchema: z.ZodObject<{
216
185
  }>>;
217
186
  enabled: z.ZodBoolean;
218
187
  available: z.ZodBoolean;
219
- demand: z.ZodNullable<z.ZodObject<{
220
- nodeId: z.ZodString;
221
- deviceKey: z.ZodString;
222
- demandFps: z.ZodNumber;
223
- maxCameraFps: z.ZodNumber;
224
- alwaysOnCameras: z.ZodNumber;
225
- onMotionSessions: z.ZodNumber;
226
- onMotionSource: z.ZodEnum<{
227
- none: "none";
228
- "observed-24h": "observed-24h";
229
- estimate: "estimate";
230
- }>;
231
- observedForMs: z.ZodNullable<z.ZodNumber>;
232
- placement: z.ZodEnum<{
233
- "least-loaded": "least-loaded";
234
- share: "share";
235
- }>;
236
- }, z.core.$strip>>;
237
188
  chosen: z.ZodNullable<z.ZodObject<{
238
189
  modelId: z.ZodString;
239
190
  p50Ms: z.ZodNumber;
240
191
  fps: z.ZodNumber;
241
192
  measured: z.ZodBoolean;
242
- utilisation: z.ZodNumber;
193
+ capacitySessions: z.ZodNumber;
243
194
  personRecall: z.ZodNumber;
244
195
  }, z.core.$strip>>;
245
196
  reasons: z.ZodReadonly<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
246
- kind: z.ZodLiteral<"fits">;
247
- utilisation: z.ZodNumber;
197
+ kind: z.ZodLiteral<"capacity">;
198
+ sessions: z.ZodNumber;
199
+ minSessions: z.ZodNumber;
248
200
  }, z.core.$strip>, z.ZodObject<{
249
201
  kind: z.ZodLiteral<"tie-faster">;
250
202
  over: z.ZodString;
@@ -256,13 +208,11 @@ export declare const NodeRecommendationSchema: z.ZodObject<{
256
208
  limitedBy: z.ZodString;
257
209
  }, z.core.$strip>, z.ZodObject<{
258
210
  kind: z.ZodLiteral<"no-fit-lightest">;
259
- utilisation: z.ZodNumber;
211
+ sessions: z.ZodNumber;
212
+ minSessions: z.ZodNumber;
260
213
  }, z.core.$strip>, z.ZodObject<{
261
214
  kind: z.ZodLiteral<"derived-latency">;
262
215
  modelId: z.ZodString;
263
- }, z.core.$strip>, z.ZodObject<{
264
- kind: z.ZodLiteral<"estimated-load">;
265
- observedForMs: z.ZodNullable<z.ZodNumber>;
266
216
  }, z.core.$strip>, z.ZodObject<{
267
217
  kind: z.ZodLiteral<"cpu-device">;
268
218
  }, z.core.$strip>, z.ZodObject<{
@@ -297,8 +247,9 @@ export declare const NodeRecommendationSchema: z.ZodObject<{
297
247
  }, z.core.$strip>], "kind">>>;
298
248
  }, z.core.$strip>>>;
299
249
  reasons: z.ZodReadonly<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
300
- kind: z.ZodLiteral<"fits">;
301
- utilisation: z.ZodNumber;
250
+ kind: z.ZodLiteral<"capacity">;
251
+ sessions: z.ZodNumber;
252
+ minSessions: z.ZodNumber;
302
253
  }, z.core.$strip>, z.ZodObject<{
303
254
  kind: z.ZodLiteral<"tie-faster">;
304
255
  over: z.ZodString;
@@ -310,13 +261,11 @@ export declare const NodeRecommendationSchema: z.ZodObject<{
310
261
  limitedBy: z.ZodString;
311
262
  }, z.core.$strip>, z.ZodObject<{
312
263
  kind: z.ZodLiteral<"no-fit-lightest">;
313
- utilisation: z.ZodNumber;
264
+ sessions: z.ZodNumber;
265
+ minSessions: z.ZodNumber;
314
266
  }, z.core.$strip>, z.ZodObject<{
315
267
  kind: z.ZodLiteral<"derived-latency">;
316
268
  modelId: z.ZodString;
317
- }, z.core.$strip>, z.ZodObject<{
318
- kind: z.ZodLiteral<"estimated-load">;
319
- observedForMs: z.ZodNullable<z.ZodNumber>;
320
269
  }, z.core.$strip>, z.ZodObject<{
321
270
  kind: z.ZodLiteral<"cpu-device">;
322
271
  }, z.core.$strip>, z.ZodObject<{
@@ -398,16 +347,14 @@ export declare const AutoTuneWarningSchema: z.ZodDiscriminatedUnion<[z.ZodObject
398
347
  }, z.core.$strip>], "kind">;
399
348
  export declare const AutoTuneOptionsSchema: z.ZodObject<{
400
349
  headroom: z.ZodDefault<z.ZodNumber>;
401
- shares: z.ZodOptional<z.ZodObject<{
402
- nodes: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodNumber>>;
403
- devices: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodNumber>>>;
404
- }, z.core.$strip>>;
350
+ minSessionsPerDevice: z.ZodDefault<z.ZodNumber>;
405
351
  }, z.core.$strip>;
406
352
  export declare const AutoTunePlanSchema: z.ZodObject<{
407
353
  planHash: z.ZodString;
408
354
  family: z.ZodLiteral<"yolov9">;
409
355
  headroom: z.ZodNumber;
410
- whatIf: z.ZodBoolean;
356
+ minSessionsPerDevice: z.ZodNumber;
357
+ cameraFps: z.ZodNumber;
411
358
  generatedAt: z.ZodNumber;
412
359
  nodes: z.ZodReadonly<z.ZodArray<z.ZodObject<{
413
360
  nodeId: z.ZodString;
@@ -434,35 +381,18 @@ export declare const AutoTunePlanSchema: z.ZodObject<{
434
381
  }>>;
435
382
  enabled: z.ZodBoolean;
436
383
  available: z.ZodBoolean;
437
- demand: z.ZodNullable<z.ZodObject<{
438
- nodeId: z.ZodString;
439
- deviceKey: z.ZodString;
440
- demandFps: z.ZodNumber;
441
- maxCameraFps: z.ZodNumber;
442
- alwaysOnCameras: z.ZodNumber;
443
- onMotionSessions: z.ZodNumber;
444
- onMotionSource: z.ZodEnum<{
445
- none: "none";
446
- "observed-24h": "observed-24h";
447
- estimate: "estimate";
448
- }>;
449
- observedForMs: z.ZodNullable<z.ZodNumber>;
450
- placement: z.ZodEnum<{
451
- "least-loaded": "least-loaded";
452
- share: "share";
453
- }>;
454
- }, z.core.$strip>>;
455
384
  chosen: z.ZodNullable<z.ZodObject<{
456
385
  modelId: z.ZodString;
457
386
  p50Ms: z.ZodNumber;
458
387
  fps: z.ZodNumber;
459
388
  measured: z.ZodBoolean;
460
- utilisation: z.ZodNumber;
389
+ capacitySessions: z.ZodNumber;
461
390
  personRecall: z.ZodNumber;
462
391
  }, z.core.$strip>>;
463
392
  reasons: z.ZodReadonly<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
464
- kind: z.ZodLiteral<"fits">;
465
- utilisation: z.ZodNumber;
393
+ kind: z.ZodLiteral<"capacity">;
394
+ sessions: z.ZodNumber;
395
+ minSessions: z.ZodNumber;
466
396
  }, z.core.$strip>, z.ZodObject<{
467
397
  kind: z.ZodLiteral<"tie-faster">;
468
398
  over: z.ZodString;
@@ -474,13 +404,11 @@ export declare const AutoTunePlanSchema: z.ZodObject<{
474
404
  limitedBy: z.ZodString;
475
405
  }, z.core.$strip>, z.ZodObject<{
476
406
  kind: z.ZodLiteral<"no-fit-lightest">;
477
- utilisation: z.ZodNumber;
407
+ sessions: z.ZodNumber;
408
+ minSessions: z.ZodNumber;
478
409
  }, z.core.$strip>, z.ZodObject<{
479
410
  kind: z.ZodLiteral<"derived-latency">;
480
411
  modelId: z.ZodString;
481
- }, z.core.$strip>, z.ZodObject<{
482
- kind: z.ZodLiteral<"estimated-load">;
483
- observedForMs: z.ZodNullable<z.ZodNumber>;
484
412
  }, z.core.$strip>, z.ZodObject<{
485
413
  kind: z.ZodLiteral<"cpu-device">;
486
414
  }, z.core.$strip>, z.ZodObject<{
@@ -515,8 +443,9 @@ export declare const AutoTunePlanSchema: z.ZodObject<{
515
443
  }, z.core.$strip>], "kind">>>;
516
444
  }, z.core.$strip>>>;
517
445
  reasons: z.ZodReadonly<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
518
- kind: z.ZodLiteral<"fits">;
519
- utilisation: z.ZodNumber;
446
+ kind: z.ZodLiteral<"capacity">;
447
+ sessions: z.ZodNumber;
448
+ minSessions: z.ZodNumber;
520
449
  }, z.core.$strip>, z.ZodObject<{
521
450
  kind: z.ZodLiteral<"tie-faster">;
522
451
  over: z.ZodString;
@@ -528,13 +457,11 @@ export declare const AutoTunePlanSchema: z.ZodObject<{
528
457
  limitedBy: z.ZodString;
529
458
  }, z.core.$strip>, z.ZodObject<{
530
459
  kind: z.ZodLiteral<"no-fit-lightest">;
531
- utilisation: z.ZodNumber;
460
+ sessions: z.ZodNumber;
461
+ minSessions: z.ZodNumber;
532
462
  }, z.core.$strip>, z.ZodObject<{
533
463
  kind: z.ZodLiteral<"derived-latency">;
534
464
  modelId: z.ZodString;
535
- }, z.core.$strip>, z.ZodObject<{
536
- kind: z.ZodLiteral<"estimated-load">;
537
- observedForMs: z.ZodNullable<z.ZodNumber>;
538
465
  }, z.core.$strip>, z.ZodObject<{
539
466
  kind: z.ZodLiteral<"cpu-device">;
540
467
  }, z.core.$strip>, z.ZodObject<{
@@ -613,6 +540,7 @@ export declare const AutoTunePlanSchema: z.ZodObject<{
613
540
  export declare const AutoTuneApplyInputSchema: z.ZodObject<{
614
541
  planHash: z.ZodString;
615
542
  headroom: z.ZodDefault<z.ZodNumber>;
543
+ minSessionsPerDevice: z.ZodDefault<z.ZodNumber>;
616
544
  }, z.core.$strip>;
617
545
  export declare const AutoTunePhaseResultSchema: z.ZodObject<{
618
546
  phase: z.ZodEnum<{
@@ -658,7 +586,8 @@ export declare const AutoTuneApplyResultSchema: z.ZodDiscriminatedUnion<[z.ZodOb
658
586
  planHash: z.ZodString;
659
587
  family: z.ZodLiteral<"yolov9">;
660
588
  headroom: z.ZodNumber;
661
- whatIf: z.ZodBoolean;
589
+ minSessionsPerDevice: z.ZodNumber;
590
+ cameraFps: z.ZodNumber;
662
591
  generatedAt: z.ZodNumber;
663
592
  nodes: z.ZodReadonly<z.ZodArray<z.ZodObject<{
664
593
  nodeId: z.ZodString;
@@ -685,35 +614,18 @@ export declare const AutoTuneApplyResultSchema: z.ZodDiscriminatedUnion<[z.ZodOb
685
614
  }>>;
686
615
  enabled: z.ZodBoolean;
687
616
  available: z.ZodBoolean;
688
- demand: z.ZodNullable<z.ZodObject<{
689
- nodeId: z.ZodString;
690
- deviceKey: z.ZodString;
691
- demandFps: z.ZodNumber;
692
- maxCameraFps: z.ZodNumber;
693
- alwaysOnCameras: z.ZodNumber;
694
- onMotionSessions: z.ZodNumber;
695
- onMotionSource: z.ZodEnum<{
696
- none: "none";
697
- "observed-24h": "observed-24h";
698
- estimate: "estimate";
699
- }>;
700
- observedForMs: z.ZodNullable<z.ZodNumber>;
701
- placement: z.ZodEnum<{
702
- "least-loaded": "least-loaded";
703
- share: "share";
704
- }>;
705
- }, z.core.$strip>>;
706
617
  chosen: z.ZodNullable<z.ZodObject<{
707
618
  modelId: z.ZodString;
708
619
  p50Ms: z.ZodNumber;
709
620
  fps: z.ZodNumber;
710
621
  measured: z.ZodBoolean;
711
- utilisation: z.ZodNumber;
622
+ capacitySessions: z.ZodNumber;
712
623
  personRecall: z.ZodNumber;
713
624
  }, z.core.$strip>>;
714
625
  reasons: z.ZodReadonly<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
715
- kind: z.ZodLiteral<"fits">;
716
- utilisation: z.ZodNumber;
626
+ kind: z.ZodLiteral<"capacity">;
627
+ sessions: z.ZodNumber;
628
+ minSessions: z.ZodNumber;
717
629
  }, z.core.$strip>, z.ZodObject<{
718
630
  kind: z.ZodLiteral<"tie-faster">;
719
631
  over: z.ZodString;
@@ -725,13 +637,11 @@ export declare const AutoTuneApplyResultSchema: z.ZodDiscriminatedUnion<[z.ZodOb
725
637
  limitedBy: z.ZodString;
726
638
  }, z.core.$strip>, z.ZodObject<{
727
639
  kind: z.ZodLiteral<"no-fit-lightest">;
728
- utilisation: z.ZodNumber;
640
+ sessions: z.ZodNumber;
641
+ minSessions: z.ZodNumber;
729
642
  }, z.core.$strip>, z.ZodObject<{
730
643
  kind: z.ZodLiteral<"derived-latency">;
731
644
  modelId: z.ZodString;
732
- }, z.core.$strip>, z.ZodObject<{
733
- kind: z.ZodLiteral<"estimated-load">;
734
- observedForMs: z.ZodNullable<z.ZodNumber>;
735
645
  }, z.core.$strip>, z.ZodObject<{
736
646
  kind: z.ZodLiteral<"cpu-device">;
737
647
  }, z.core.$strip>, z.ZodObject<{
@@ -766,8 +676,9 @@ export declare const AutoTuneApplyResultSchema: z.ZodDiscriminatedUnion<[z.ZodOb
766
676
  }, z.core.$strip>], "kind">>>;
767
677
  }, z.core.$strip>>>;
768
678
  reasons: z.ZodReadonly<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
769
- kind: z.ZodLiteral<"fits">;
770
- utilisation: z.ZodNumber;
679
+ kind: z.ZodLiteral<"capacity">;
680
+ sessions: z.ZodNumber;
681
+ minSessions: z.ZodNumber;
771
682
  }, z.core.$strip>, z.ZodObject<{
772
683
  kind: z.ZodLiteral<"tie-faster">;
773
684
  over: z.ZodString;
@@ -779,13 +690,11 @@ export declare const AutoTuneApplyResultSchema: z.ZodDiscriminatedUnion<[z.ZodOb
779
690
  limitedBy: z.ZodString;
780
691
  }, z.core.$strip>, z.ZodObject<{
781
692
  kind: z.ZodLiteral<"no-fit-lightest">;
782
- utilisation: z.ZodNumber;
693
+ sessions: z.ZodNumber;
694
+ minSessions: z.ZodNumber;
783
695
  }, z.core.$strip>, z.ZodObject<{
784
696
  kind: z.ZodLiteral<"derived-latency">;
785
697
  modelId: z.ZodString;
786
- }, z.core.$strip>, z.ZodObject<{
787
- kind: z.ZodLiteral<"estimated-load">;
788
- observedForMs: z.ZodNullable<z.ZodNumber>;
789
698
  }, z.core.$strip>, z.ZodObject<{
790
699
  kind: z.ZodLiteral<"cpu-device">;
791
700
  }, z.core.$strip>, z.ZodObject<{
@@ -879,7 +788,6 @@ export type AutoTuneNodeDeviceChange = z.infer<typeof NodeDeviceChangeSchema>;
879
788
  export type HardwareClassId = z.infer<typeof HardwareClassIdSchema>;
880
789
  export type AcceleratorKind = z.infer<typeof AcceleratorKindSchema>;
881
790
  export type RecommendationReason = z.infer<typeof RecommendationReasonSchema>;
882
- export type DeviceDemand = z.infer<typeof DeviceDemandSchema>;
883
791
  export type DeviceModelChoice = z.infer<typeof DeviceModelChoiceSchema>;
884
792
  export type DeviceRecommendation = z.infer<typeof DeviceRecommendationSchema>;
885
793
  export type NodeRecommendation = z.infer<typeof NodeRecommendationSchema>;
@@ -1,7 +1,8 @@
1
1
  /**
2
- * The Auto tune recommender (D745): one detector per node, from ONE family
3
- * (YOLOv9) for the whole cluster, chosen from static hardware facts × the
4
- * measured latency table × the node's detection demand. Pure; no benchmark.
2
+ * The Auto tune recommender (D745, D755): one detector per node, from ONE
3
+ * family (YOLOv9) for the whole cluster, chosen from static hardware facts ×
4
+ * the measured latency table × a capacity floor. Pure; no benchmark, and no
5
+ * load: nothing here depends on how many sessions ran yesterday.
5
6
  *
6
7
  * Per node:
7
8
  * 1. Classify the hardware (`classifyInferenceHardware`). An unknown class,
@@ -10,25 +11,31 @@
10
11
  * available share ONE model — so a camera's floors never flip with the
11
12
  * device the balancer rotated it onto. A candidate must have a build for
12
13
  * every accelerator's format and a latency cell for each.
13
- * 3. A candidate FITS a device when its throughput, less the headroom, covers
14
- * the device's demand and the single fastest camera that can land on it.
14
+ * 3. A candidate FITS a device when the device sustains at least
15
+ * `minSessionsPerDevice` parallel sessions with it:
16
+ * ⌊fps × (1 − headroom) / camera fps⌋ ≥ minSessionsPerDevice.
15
17
  * 4. Among candidates that fit everywhere, the most accurate wins (person
16
18
  * recall at the shipped floor); within `RECALL_TIE` of the best, the
17
19
  * fastest wins. That IS the step-down order the data gives: the first
18
- * variant down from m-640 is s-640-int8, not m-320.
20
+ * variant down from m-640 is s-640-int8, not m-320. When nothing fits,
21
+ * the candidate with the most capacity is used and the plan says so.
19
22
  * 5. An enabled CPU device (the fallback pool) gets its own pick the same
20
23
  * way; a Coral is never touched (no YOLOv9 build), only reported.
21
24
  */
22
25
  import type { HardwareInfo } from '../../interfaces/platform.js';
23
26
  import type { ModelFormat } from '../../types/models.js';
24
27
  import { type DetectorPerfTable } from './detector-performance.js';
25
- import type { DeviceDemand, NodeRecommendation } from './plan-schemas.js';
28
+ import { type NodeRecommendation } from './plan-schemas.js';
26
29
  /** The family the cluster runs. */
27
30
  export declare const AUTO_TUNE_FAMILY = "yolov9";
28
31
  /** Recall within this of the best counts as a tie — the faster one wins. */
29
32
  export declare const RECALL_TIE = 0.02;
30
33
  /** Default share of an accelerator's throughput kept free. */
31
34
  export declare const DEFAULT_HEADROOM = 0.4;
35
+ /** Detection rate a session is counted at when no camera says otherwise. */
36
+ export declare const DEFAULT_CAMERA_FPS = 10;
37
+ /** Parallel sessions one device sustains at `fps` with `headroom` kept free. */
38
+ export declare function capacitySessionsOf(fps: number, headroom: number, cameraFps: number): number;
32
39
  /** One device of a node, as the recommender reads it. */
33
40
  export interface AutoTuneDeviceInput {
34
41
  readonly key: string;
@@ -48,7 +55,10 @@ export interface AutoTuneNodeInput {
48
55
  /** Recommend one node. See the module header. */
49
56
  export declare function recommendNode(input: {
50
57
  readonly node: AutoTuneNodeInput;
51
- readonly demand: readonly DeviceDemand[];
52
58
  readonly headroom?: number;
59
+ /** Least parallel sessions per device (default {@link DEFAULT_MIN_SESSIONS_PER_DEVICE}). */
60
+ readonly minSessionsPerDevice?: number;
61
+ /** The rate a session is counted at (default {@link DEFAULT_CAMERA_FPS}). */
62
+ readonly cameraFps?: number;
53
63
  readonly perf?: DetectorPerfTable;
54
64
  }): NodeRecommendation;
@@ -0,0 +1,37 @@
1
+ import type { NodeRecommendation } from '../auto-tune/plan-schemas.js';
2
+ import type { CapacitySource, SuggestSharesResult } from './schemas.js';
3
+ /** One device's measured capacity (a node benchmark result, reduced to what shares need). */
4
+ export interface DeviceMeasuredCapacity {
5
+ readonly deviceKey: string;
6
+ /** Parallel sessions the device sustained at the camera rate. */
7
+ readonly maxSessions: number;
8
+ readonly modelId: string | null;
9
+ readonly measuredAtMs: number;
10
+ }
11
+ /** One node's measured capacity. */
12
+ export interface NodeMeasuredCapacity {
13
+ readonly nodeId: string;
14
+ readonly devices: readonly DeviceMeasuredCapacity[];
15
+ }
16
+ /**
17
+ * Where measured node capacity is read from. The node-benchmark results
18
+ * store implements it; until it exists the orchestrator wires one that
19
+ * answers nothing, and every node falls back to `hardware`.
20
+ */
21
+ export interface NodeCapacitySource {
22
+ /** The usable (fresh) measured capacity of each asked node that has any. */
23
+ readMeasuredCapacity(nodeIds: readonly string[]): Promise<ReadonlyMap<string, NodeMeasuredCapacity>>;
24
+ }
25
+ /** A source that has no results — the benchmark store before it exists. */
26
+ export declare const NO_MEASURED_CAPACITY: NodeCapacitySource;
27
+ /** Integer shares summing to 100 (largest remainder); ties broken by key. */
28
+ export declare function toPercentShares(weights: ReadonlyMap<string, number>): Map<string, number>;
29
+ /** Shares proportional to capacity — the automatic shares and the suggestion are this. */
30
+ export declare function computeCapacityShares(input: {
31
+ readonly recommendations: readonly NodeRecommendation[];
32
+ readonly source: CapacitySource;
33
+ readonly measured: ReadonlyMap<string, NodeMeasuredCapacity>;
34
+ readonly headroom: number;
35
+ readonly cameraFps: number;
36
+ readonly minSessionsPerDevice: number;
37
+ }): SuggestSharesResult;
@@ -2,15 +2,15 @@
2
2
  * The shares a session election actually reads, and the view the Load
3
3
  * balancing tab shows (D750). Pure.
4
4
  *
5
- * The SOURCE decides the configured weights: the automatic capacity shares,
6
- * or the operator's stored sliders. Eligibility decides the effective ones: a
5
+ * The SOURCE decides the configured weights: the automatic capacity shares
6
+ * (`hardware` or `benchmark`, D755), or the operator's stored sliders. Eligibility decides the effective ones: a
7
7
  * node or device that cannot take a session now (offline, switched off, no
8
8
  * detect capability, unable to run the root) contributes nothing, and the
9
9
  * others split its share in proportion to their own. That redistribution is
10
10
  * exactly what `electByShare` does over its candidate set, so the view and
11
11
  * the election cannot disagree; the stored values are never rewritten for it.
12
12
  */
13
- import type { EffectiveShares, SessionPlacementPolicy, ShareSource, ShareWeights } from './schemas.js';
13
+ import { type CapacityFallback, type CapacitySource, type EffectiveShares, type SessionPlacementPolicy, type ShareSource, type ShareWeights } from './schemas.js';
14
14
  export interface EffectiveSharesInput {
15
15
  readonly source: ShareSource;
16
16
  /** Every node the view covers, eligible or not. */
@@ -18,6 +18,11 @@ export interface EffectiveSharesInput {
18
18
  readonly stored: ShareWeights;
19
19
  /** The last automatic computation; null = none yet. */
20
20
  readonly auto: ShareWeights | null;
21
+ /** Per node, what the automatic answer was computed from. */
22
+ readonly autoCapacity?: Readonly<Record<string, {
23
+ readonly source: CapacitySource;
24
+ readonly fallback: CapacityFallback | null;
25
+ }>>;
21
26
  readonly eligibleNodes: readonly string[];
22
27
  /** nodeId → device keys that can take sessions; a node absent here lists none. */
23
28
  readonly eligibleDevices: Readonly<Record<string, readonly string[]>>;
@@ -1,8 +1,9 @@
1
1
  /**
2
- * Load balancing (D748, D750): the share placement policy for detection
2
+ * Load balancing (D748, D750, D755): the share placement policy for detection
3
3
  * sessions, where its shares come from, and the shapes of its admin surface.
4
- * See `share-election.ts` and `effective-shares.ts`.
4
+ * See `share-election.ts`, `effective-shares.ts` and `capacity-shares.ts`.
5
5
  */
6
6
  export * from './share-election.js';
7
7
  export * from './schemas.js';
8
8
  export * from './effective-shares.js';
9
+ export * from './capacity-shares.js';