@camstack/types 1.2.316 → 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.
@@ -0,0 +1,54 @@
1
+ /**
2
+ * The Best settings 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.
5
+ *
6
+ * Per node:
7
+ * 1. Classify the hardware (`classifyInferenceHardware`). An unknown class,
8
+ * an offline node or a node with nothing to recommend is left untouched.
9
+ * 2. The node's ACCELERATORS (NPU, iGPU, Neural Engine) that are enabled and
10
+ * available share ONE model — so a camera's floors never flip with the
11
+ * device the balancer rotated it onto. A candidate must have a build for
12
+ * 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.
15
+ * 4. Among candidates that fit everywhere, the most accurate wins (person
16
+ * recall at the shipped floor); within `RECALL_TIE` of the best, the
17
+ * 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.
19
+ * 5. An enabled CPU device (the fallback pool) gets its own pick the same
20
+ * way; a Coral is never touched (no YOLOv9 build), only reported.
21
+ */
22
+ import type { HardwareInfo } from '../../interfaces/platform.js';
23
+ import type { ModelFormat } from '../../types/models.js';
24
+ import { type DetectorPerfTable } from './detector-performance.js';
25
+ import type { DeviceDemand, NodeRecommendation } from './plan-schemas.js';
26
+ /** The family the cluster runs. */
27
+ export declare const BEST_SETTINGS_FAMILY = "yolov9";
28
+ /** Recall within this of the best counts as a tie — the faster one wins. */
29
+ export declare const RECALL_TIE = 0.02;
30
+ /** Default share of an accelerator's throughput kept free. */
31
+ export declare const DEFAULT_HEADROOM = 0.4;
32
+ /** One device of a node, as the recommender reads it. */
33
+ export interface BestSettingsDeviceInput {
34
+ readonly key: string;
35
+ readonly format: ModelFormat;
36
+ readonly enabled: boolean;
37
+ readonly available: boolean;
38
+ }
39
+ /** One node, as the recommender reads it. */
40
+ export interface BestSettingsNodeInput {
41
+ readonly nodeId: string;
42
+ readonly reachable: boolean;
43
+ readonly hardware: HardwareInfo | null;
44
+ readonly devices: readonly BestSettingsDeviceInput[];
45
+ /** Object-detection model id → the formats it has a build for, from the node's catalog. */
46
+ readonly formatsByModel: Readonly<Record<string, readonly ModelFormat[]>>;
47
+ }
48
+ /** Recommend one node. See the module header. */
49
+ export declare function recommendNode(input: {
50
+ readonly node: BestSettingsNodeInput;
51
+ readonly demand: readonly DeviceDemand[];
52
+ readonly headroom?: number;
53
+ readonly perf?: DetectorPerfTable;
54
+ }): NodeRecommendation;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Load balancing (D748): the share placement policy for detection sessions
3
+ * and the shapes of its admin surface. See `share-election.ts`.
4
+ */
5
+ export * from './share-election.js';
6
+ export * from './schemas.js';
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Wire shapes of the Load balancing surface (D748): the session placement
3
+ * policy, the atomic share write, the observed placement statistics, the
4
+ * hardware share suggestion and the stale device pins.
5
+ */
6
+ import { z } from 'zod';
7
+ /**
8
+ * How a new detection session picks its node and device.
9
+ * - `least-loaded` — the historical rule: lowest `attached / weight`, a
10
+ * constant node order at a tie (D37). An idle node always ties at 0, so a
11
+ * weight only bites once a node already holds a session.
12
+ * - `share` — the share of PARALLEL sessions is held to the weights, and a
13
+ * full or drowning node yields to a free one (D748).
14
+ */
15
+ export declare const SessionPlacementPolicySchema: z.ZodEnum<{
16
+ "least-loaded": "least-loaded";
17
+ share: "share";
18
+ }>;
19
+ export type SessionPlacementPolicy = z.infer<typeof SessionPlacementPolicySchema>;
20
+ export declare const DEFAULT_SESSION_PLACEMENT_POLICY: SessionPlacementPolicy;
21
+ /** The global-settings key the policy is stored under (orchestrator `load` section). */
22
+ export declare const SESSION_PLACEMENT_SETTING_KEY = "sessionPlacement";
23
+ /** Parse a stored value; anything unknown is the default (nothing changes on deploy). */
24
+ export declare function parseSessionPlacementPolicy(value: unknown): SessionPlacementPolicy;
25
+ export declare const LoadBalancingSharesInputSchema: z.ZodObject<{
26
+ nodes: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodNumber>>;
27
+ devices: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodNumber>>>;
28
+ }, z.core.$strip>;
29
+ export type LoadBalancingSharesInput = z.infer<typeof LoadBalancingSharesInputSchema>;
30
+ export declare const PlacementStatsInputSchema: z.ZodObject<{
31
+ windowMs: z.ZodDefault<z.ZodNumber>;
32
+ }, z.core.$strip>;
33
+ export type PlacementStatsInput = z.infer<typeof PlacementStatsInputSchema>;
34
+ export declare const PlacementDeviceStatsSchema: z.ZodObject<{
35
+ deviceKey: z.ZodString;
36
+ sessionsNow: z.ZodNumber;
37
+ sessionsStarted: z.ZodNumber;
38
+ observedShare: z.ZodNullable<z.ZodNumber>;
39
+ }, z.core.$strip>;
40
+ export type PlacementDeviceStats = z.infer<typeof PlacementDeviceStatsSchema>;
41
+ export declare const PlacementNodeStatsSchema: z.ZodObject<{
42
+ nodeId: z.ZodString;
43
+ sessionsNow: z.ZodNumber;
44
+ sessionsStarted: z.ZodNumber;
45
+ observedShare: z.ZodNullable<z.ZodNumber>;
46
+ peakSessions: z.ZodNumber;
47
+ yieldsAway: z.ZodNumber;
48
+ devices: z.ZodReadonly<z.ZodArray<z.ZodObject<{
49
+ deviceKey: z.ZodString;
50
+ sessionsNow: z.ZodNumber;
51
+ sessionsStarted: z.ZodNumber;
52
+ observedShare: z.ZodNullable<z.ZodNumber>;
53
+ }, z.core.$strip>>>;
54
+ }, z.core.$strip>;
55
+ export type PlacementNodeStats = z.infer<typeof PlacementNodeStatsSchema>;
56
+ /** A camera's device pin that the session path cannot honour. */
57
+ export declare const StaleDevicePinSchema: z.ZodObject<{
58
+ deviceId: z.ZodNumber;
59
+ deviceKey: z.ZodString;
60
+ reason: z.ZodEnum<{
61
+ absent: "absent";
62
+ disabled: "disabled";
63
+ }>;
64
+ }, z.core.$strip>;
65
+ export type StaleDevicePin = z.infer<typeof StaleDevicePinSchema>;
66
+ export declare const PlacementStatsSchema: z.ZodObject<{
67
+ policy: z.ZodEnum<{
68
+ "least-loaded": "least-loaded";
69
+ share: "share";
70
+ }>;
71
+ windowMs: z.ZodNumber;
72
+ observedForMs: z.ZodNumber;
73
+ sessionsNow: z.ZodNumber;
74
+ clusterPeakSessions: z.ZodNumber;
75
+ nodes: z.ZodReadonly<z.ZodArray<z.ZodObject<{
76
+ nodeId: z.ZodString;
77
+ sessionsNow: z.ZodNumber;
78
+ sessionsStarted: z.ZodNumber;
79
+ observedShare: z.ZodNullable<z.ZodNumber>;
80
+ peakSessions: z.ZodNumber;
81
+ yieldsAway: z.ZodNumber;
82
+ devices: z.ZodReadonly<z.ZodArray<z.ZodObject<{
83
+ deviceKey: z.ZodString;
84
+ sessionsNow: z.ZodNumber;
85
+ sessionsStarted: z.ZodNumber;
86
+ observedShare: z.ZodNullable<z.ZodNumber>;
87
+ }, z.core.$strip>>>;
88
+ }, z.core.$strip>>>;
89
+ stalePins: z.ZodReadonly<z.ZodArray<z.ZodObject<{
90
+ deviceId: z.ZodNumber;
91
+ deviceKey: z.ZodString;
92
+ reason: z.ZodEnum<{
93
+ absent: "absent";
94
+ disabled: "disabled";
95
+ }>;
96
+ }, z.core.$strip>>>;
97
+ }, z.core.$strip>;
98
+ export type PlacementStats = z.infer<typeof PlacementStatsSchema>;
99
+ export declare const SuggestSharesInputSchema: z.ZodObject<{
100
+ headroom: z.ZodDefault<z.ZodNumber>;
101
+ }, z.core.$strip>;
102
+ export type SuggestSharesInput = z.infer<typeof SuggestSharesInputSchema>;
103
+ export declare const SuggestedDeviceShareSchema: z.ZodObject<{
104
+ deviceKey: z.ZodString;
105
+ share: z.ZodNumber;
106
+ modelId: z.ZodNullable<z.ZodString>;
107
+ fps: z.ZodNullable<z.ZodNumber>;
108
+ measured: z.ZodBoolean;
109
+ suggestedMaxSessions: z.ZodNullable<z.ZodNumber>;
110
+ }, z.core.$strip>;
111
+ export type SuggestedDeviceShare = z.infer<typeof SuggestedDeviceShareSchema>;
112
+ export declare const SuggestedNodeShareSchema: z.ZodObject<{
113
+ nodeId: z.ZodString;
114
+ share: z.ZodNullable<z.ZodNumber>;
115
+ capacitySessions: z.ZodNumber;
116
+ devices: z.ZodReadonly<z.ZodArray<z.ZodObject<{
117
+ deviceKey: z.ZodString;
118
+ share: z.ZodNumber;
119
+ modelId: z.ZodNullable<z.ZodString>;
120
+ fps: z.ZodNullable<z.ZodNumber>;
121
+ measured: z.ZodBoolean;
122
+ suggestedMaxSessions: z.ZodNullable<z.ZodNumber>;
123
+ }, z.core.$strip>>>;
124
+ skippedReason: z.ZodNullable<z.ZodString>;
125
+ }, z.core.$strip>;
126
+ export type SuggestedNodeShare = z.infer<typeof SuggestedNodeShareSchema>;
127
+ export declare const SuggestSharesResultSchema: z.ZodObject<{
128
+ headroom: z.ZodNumber;
129
+ cameraFps: z.ZodNumber;
130
+ iterations: z.ZodNumber;
131
+ nodes: z.ZodReadonly<z.ZodArray<z.ZodObject<{
132
+ nodeId: z.ZodString;
133
+ share: z.ZodNullable<z.ZodNumber>;
134
+ capacitySessions: z.ZodNumber;
135
+ devices: z.ZodReadonly<z.ZodArray<z.ZodObject<{
136
+ deviceKey: z.ZodString;
137
+ share: z.ZodNumber;
138
+ modelId: z.ZodNullable<z.ZodString>;
139
+ fps: z.ZodNullable<z.ZodNumber>;
140
+ measured: z.ZodBoolean;
141
+ suggestedMaxSessions: z.ZodNullable<z.ZodNumber>;
142
+ }, z.core.$strip>>>;
143
+ skippedReason: z.ZodNullable<z.ZodString>;
144
+ }, z.core.$strip>>>;
145
+ }, z.core.$strip>;
146
+ export type SuggestSharesResult = z.infer<typeof SuggestSharesResultSchema>;
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Share placement (D748): which node — or which device of a node — a NEW
3
+ * detection session goes to when the cluster's `sessionPlacement` policy is
4
+ * `share`. Pure.
5
+ *
6
+ * A share is a share of PARALLEL sessions. At each session start the
7
+ * candidate furthest below its share of the sessions running once this one
8
+ * starts wins:
9
+ *
10
+ * deficit_i = share_i / Σshare × (Σrunning + 1) − running_i
11
+ *
12
+ * so with 70/20/10 and an empty cluster the first session goes to the 70,
13
+ * and ten concurrent sessions settle at 7/2/1. Sessions a pin placed count
14
+ * toward their candidate like any other — a pinned node is not owed extra.
15
+ *
16
+ * Work-conserving: when the turn holder has no room — at an EXPLICIT cap
17
+ * (`maxSessions` / `maxCameras`) or drowning (its pool at the shed cap) — the
18
+ * session goes to the free candidate with the most spare explicit capacity,
19
+ * then the largest deficit. Never onto a saturated one. A capacity derived
20
+ * from the measured latency table is advisory only and never enters here: it
21
+ * would turn a suggestion into a de-facto cap.
22
+ *
23
+ * When every uncapped candidate is saturated the turn holder keeps the
24
+ * session: excluding the whole set would silently disable placement.
25
+ */
26
+ export interface ShareCandidate {
27
+ readonly key: string;
28
+ /** Relative share (any scale; ≤ 0 = wanted last). */
29
+ readonly share: number;
30
+ /** Sessions running on this candidate now. */
31
+ readonly running: number;
32
+ /** At an explicit cap — never elected. */
33
+ readonly capped: boolean;
34
+ /** Drowning (pool queue at its shed cap) — stepped over while another is free. */
35
+ readonly saturated: boolean;
36
+ /** Spare sessions under an explicit cap; absent = no explicit cap. */
37
+ readonly spare?: number;
38
+ }
39
+ export type ShareYieldReason = 'at-cap' | 'saturated';
40
+ export type ShareElection = {
41
+ readonly kind: 'elected';
42
+ readonly key: string;
43
+ readonly reason: 'share';
44
+ } | {
45
+ readonly kind: 'elected';
46
+ readonly key: string;
47
+ readonly reason: 'yield';
48
+ /** The candidate whose turn it was. */
49
+ readonly turnKey: string;
50
+ readonly yieldReason: ShareYieldReason;
51
+ } | {
52
+ readonly kind: 'none';
53
+ };
54
+ export declare function electByShare(candidates: readonly ShareCandidate[]): ShareElection;
@@ -33,6 +33,7 @@
33
33
  * runner (`addon-pipeline`) and the editors (`ui-library`, `addon-admin-ui`)
34
34
  * apply the same rule.
35
35
  */
36
+ import type { ConfigField } from '../interfaces/config-ui.js';
36
37
  /** `minConfidencePerson` → `minConfidencePersonModelId`. */
37
38
  export declare function modelBindingKey(key: string): string;
38
39
  /** A loose step-settings bag, as it is stored and travels. */
@@ -80,3 +81,9 @@ export declare function hasModelBoundOverrides(settings: ModelBoundSettingsBag |
80
81
  * that model runs.
81
82
  */
82
83
  export declare function clearModelBoundOverrides(settings: ModelBoundSettingsBag | undefined, boundKeys: readonly string[], modelId: string | undefined): Record<string, unknown>;
84
+ /**
85
+ * The keys of every `bindsToModel` field of a step's config schema, groups
86
+ * flattened (D738). One definition for the runner (which applies the binding)
87
+ * and the orchestrator's Best settings (which clears those overrides, D745).
88
+ */
89
+ export declare function collectModelBoundKeys(fields: readonly ConfigField[]): string[];
@@ -157,7 +157,6 @@ export interface AgentPipelineSettings {
157
157
  * resolved tree.
158
158
  */
159
159
  export interface CameraPipelineSettings {
160
- readonly pinnedAgentNodeId?: string;
161
160
  readonly stepToggles?: Readonly<Record<string, boolean>>;
162
161
  /**
163
162
  * Per-(camera,node,device) model/settings override. Addressed by
@@ -27,6 +27,51 @@ export declare const ModelFormatEntrySchema: z.ZodObject<{
27
27
  revision: z.ZodOptional<z.ZodString>;
28
28
  }, z.core.$strip>;
29
29
  export type ModelFormatEntry = z.infer<typeof ModelFormatEntrySchema>;
30
+ /**
31
+ * What a node holds for one (model, format) (D746). `current` is a complete
32
+ * copy at its catalog revision — exactly what `downloaded: true` means.
33
+ * `stale` is bytes on disk that are not (yet) proven current under D743/D744:
34
+ * no or a different revision record, or a missing sibling. `missing` is no
35
+ * copy. `downloaded` alone folds `stale` into "not downloaded", which hid
36
+ * reclaimable bytes and a copy about to be replaced.
37
+ */
38
+ export declare const ModelOnDiskStateSchema: z.ZodEnum<{
39
+ missing: "missing";
40
+ current: "current";
41
+ stale: "stale";
42
+ }>;
43
+ export type ModelOnDiskState = z.infer<typeof ModelOnDiskStateSchema>;
44
+ /**
45
+ * The numeric precision of one format's BUILD (D746) — reported only when the
46
+ * build states it (file-name token, an INT8 variant, an EdgeTPU compile, a
47
+ * CamStack Core ML export). Absent means "not stated", never "fp32".
48
+ */
49
+ export declare const ModelFormatPrecisionSchema: z.ZodEnum<{
50
+ fp32: "fp32";
51
+ fp16: "fp16";
52
+ int8: "int8";
53
+ }>;
54
+ export type ModelFormatPrecision = z.infer<typeof ModelFormatPrecisionSchema>;
55
+ /**
56
+ * One format of one model as a node's `pipelineExecutor.getSchema` reports it.
57
+ * `onDisk` / `precision` are optional for version skew: a runner that predates
58
+ * them omits them, and a reader falls back to `downloaded`.
59
+ */
60
+ export declare const PipelineModelFormatStatusSchema: z.ZodObject<{
61
+ downloaded: z.ZodBoolean;
62
+ sizeMB: z.ZodNumber;
63
+ onDisk: z.ZodOptional<z.ZodEnum<{
64
+ missing: "missing";
65
+ current: "current";
66
+ stale: "stale";
67
+ }>>;
68
+ precision: z.ZodOptional<z.ZodEnum<{
69
+ fp32: "fp32";
70
+ fp16: "fp16";
71
+ int8: "int8";
72
+ }>>;
73
+ }, z.core.$strip>;
74
+ export type PipelineModelFormatStatus = z.infer<typeof PipelineModelFormatStatusSchema>;
30
75
  /**
31
76
  * Extra file that must be downloaded alongside the model (e.g., labels JSON, dict.txt).
32
77
  * The downloader fetches from `url` and saves to `{modelsDir}/{filename}`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/types",
3
- "version": "1.2.316",
3
+ "version": "1.2.318",
4
4
  "description": "Shared types, interfaces, and model catalogs for the CamStack detection ecosystem",
5
5
  "keywords": [
6
6
  "camstack",