@telora/daemon 0.18.61 → 0.18.67
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/build-info.json +2 -2
- package/dist/config.d.ts +16 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +69 -13
- package/dist/config.js.map +1 -1
- package/dist/discovery-poll.d.ts.map +1 -1
- package/dist/discovery-poll.js +14 -2
- package/dist/discovery-poll.js.map +1 -1
- package/dist/focus-executor.d.ts +26 -3
- package/dist/focus-executor.d.ts.map +1 -1
- package/dist/focus-executor.js +79 -0
- package/dist/focus-executor.js.map +1 -1
- package/dist/git-fetch.d.ts +123 -0
- package/dist/git-fetch.d.ts.map +1 -0
- package/dist/git-fetch.js +164 -0
- package/dist/git-fetch.js.map +1 -0
- package/dist/git-state-detector.d.ts.map +1 -1
- package/dist/git-state-detector.js +63 -26
- package/dist/git-state-detector.js.map +1 -1
- package/dist/host-control/capability.d.ts +31 -0
- package/dist/host-control/capability.d.ts.map +1 -0
- package/dist/host-control/capability.js +50 -0
- package/dist/host-control/capability.js.map +1 -0
- package/dist/host-control/catalog.d.ts +48 -0
- package/dist/host-control/catalog.d.ts.map +1 -0
- package/dist/host-control/catalog.js +149 -0
- package/dist/host-control/catalog.js.map +1 -0
- package/dist/host-control/client.d.ts +76 -0
- package/dist/host-control/client.d.ts.map +1 -0
- package/dist/host-control/client.js +203 -0
- package/dist/host-control/client.js.map +1 -0
- package/dist/host-control/index.d.ts +18 -0
- package/dist/host-control/index.d.ts.map +1 -0
- package/dist/host-control/index.js +14 -0
- package/dist/host-control/index.js.map +1 -0
- package/dist/host-control/load.d.ts +136 -0
- package/dist/host-control/load.d.ts.map +1 -0
- package/dist/host-control/load.js +241 -0
- package/dist/host-control/load.js.map +1 -0
- package/dist/host-control/sizing.d.ts +207 -0
- package/dist/host-control/sizing.d.ts.map +1 -0
- package/dist/host-control/sizing.js +464 -0
- package/dist/host-control/sizing.js.map +1 -0
- package/dist/host-control/types.d.ts +174 -0
- package/dist/host-control/types.d.ts.map +1 -0
- package/dist/host-control/types.js +16 -0
- package/dist/host-control/types.js.map +1 -0
- package/dist/prd-controller.d.ts +10 -12
- package/dist/prd-controller.d.ts.map +1 -1
- package/dist/prd-controller.js +24 -54
- package/dist/prd-controller.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hardware-aware context sizing for the host-control plane (Local model host
|
|
3
|
+
* control, D3).
|
|
4
|
+
*
|
|
5
|
+
* The problem this closes: `max_seq_length` (the load-time context window) was a
|
|
6
|
+
* MANUAL, hardware-blind input. Set it too SMALL and codex's turn-1 prompt does
|
|
7
|
+
* not fit -- the load silently fails turn-1 with an instant `response.failed`
|
|
8
|
+
* (live: a 4096-ctx load failed; a manual reload to >=16k fixed it). Set it too
|
|
9
|
+
* LARGE and the KV-cache (which scales with context) OOMs a near-full GPU (live:
|
|
10
|
+
* a 12GB card sat ~93% used with a model resident). The safe value is bounded
|
|
11
|
+
* BELOW by codex's ~8k turn-1 floor and ABOVE by remaining VRAM headroom.
|
|
12
|
+
*
|
|
13
|
+
* This module adds two things:
|
|
14
|
+
* 1. defensive client reads over the host's hardware + kv-cache endpoints
|
|
15
|
+
* (`getHardware`, `getKvCacheEstimate`) -- the live JSON shapes are NOT
|
|
16
|
+
* pinned, so the normalizers tolerate reasonable field-name variants and
|
|
17
|
+
* are flagged for runbook reconciliation;
|
|
18
|
+
* 2. a PURE `computeSafeMaxSeqLength` (no HTTP) -- the heart: pick the largest
|
|
19
|
+
* context that clears the codex floor without exceeding VRAM headroom,
|
|
20
|
+
* reaching for optional KV-cache quantization when it lets a context fit.
|
|
21
|
+
*
|
|
22
|
+
* The hardware/kv-cache endpoints live under the host ROOT (NOT `/v1`), so we
|
|
23
|
+
* derive the root with `hostRootFromBaseUrl` (strips a trailing `/v1`). All
|
|
24
|
+
* errors are address-free (TEL-5): the load path treats any read failure as
|
|
25
|
+
* "no sizing data" and falls back to the fixed floor default, never breaking a
|
|
26
|
+
* swap that previously worked.
|
|
27
|
+
*/
|
|
28
|
+
import { hostGet, hostRootFromBaseUrl } from './client.js';
|
|
29
|
+
import { DEFAULT_MAX_SEQ_LENGTH } from './load.js';
|
|
30
|
+
/**
|
|
31
|
+
* The control-surface path (under the host ROOT, not `/v1`) for GPU hardware
|
|
32
|
+
* info: GPU name + total/used VRAM. Reconcile field names per the runbook.
|
|
33
|
+
*/
|
|
34
|
+
export const HOST_CONTROL_HARDWARE_PATH = '/api/system/hardware';
|
|
35
|
+
/**
|
|
36
|
+
* The control-surface path (under the host ROOT, not `/v1`) for a KV-cache fit
|
|
37
|
+
* prediction at a candidate context. Reconcile field names per the runbook.
|
|
38
|
+
*/
|
|
39
|
+
export const HOST_CONTROL_KV_CACHE_PATH = '/api/models/kv-cache-estimate';
|
|
40
|
+
/**
|
|
41
|
+
* Codex's turn-1 context floor. A load BELOW this silently fails the very first
|
|
42
|
+
* codex turn (instant `response.failed`) -- so we treat it as a HARD lower
|
|
43
|
+
* bound and never return a `maxSeqLength` under it, even when VRAM headroom is
|
|
44
|
+
* tight (we signal quant-required/marginal instead of silently going lower).
|
|
45
|
+
*
|
|
46
|
+
* The live datapoint: a 4096-ctx load failed turn-1; a reload to >=16384
|
|
47
|
+
* worked. The exact floor is ~8k; 8192 is the documented conservative pin.
|
|
48
|
+
*/
|
|
49
|
+
export const CODEX_TURN1_FLOOR = 8192;
|
|
50
|
+
/**
|
|
51
|
+
* VRAM safety margin (MB) held back from raw headroom before sizing the KV
|
|
52
|
+
* cache. A near-full GPU (live: 12GB card ~93% used with a model resident)
|
|
53
|
+
* leaves little slack; this guards against the estimate being slightly optimistic,
|
|
54
|
+
* driver/allocator overhead, and fragmentation. Conservative on purpose --
|
|
55
|
+
* an OOM costs a whole swap, a slightly-smaller context costs nothing.
|
|
56
|
+
*/
|
|
57
|
+
export const VRAM_SAFETY_MARGIN_MB = 512;
|
|
58
|
+
/**
|
|
59
|
+
* Per-token KV-cache cost heuristic (MB per 1 token of context) used as the
|
|
60
|
+
* FALLBACK growth model when the host's kv-cache-estimate endpoint is not
|
|
61
|
+
* consulted (the pure path takes only static inputs). This is intentionally a
|
|
62
|
+
* coarse upper-ish estimate for a ~7-8B class model at fp16 KV; the actual
|
|
63
|
+
* footprint is model-architecture-specific. The host's `getKvCacheEstimate`
|
|
64
|
+
* endpoint, when reachable, gives a far better per-context number and SHOULD be
|
|
65
|
+
* preferred by the orchestrator; this constant only bounds the pure computation
|
|
66
|
+
* when no live estimate is supplied.
|
|
67
|
+
*
|
|
68
|
+
* Derivation sketch (documented so the runbook can refine it): KV bytes ~=
|
|
69
|
+
* 2 (K and V) * layers * 2 (kv heads grouping varies) * head_dim * bytes/elem.
|
|
70
|
+
* For a 7B-class model at fp16 this lands on the order of ~0.12-0.20 MB/token;
|
|
71
|
+
* we pin a conservative 0.16 MB/token. Quantized KV (int8/fp8) roughly halves
|
|
72
|
+
* this -- see `KV_QUANT_FOOTPRINT_FACTOR`.
|
|
73
|
+
*/
|
|
74
|
+
export const KV_CACHE_MB_PER_TOKEN = 0.16;
|
|
75
|
+
/**
|
|
76
|
+
* Footprint multiplier when KV-cache quantization is requested. int8/fp8 KV
|
|
77
|
+
* roughly halves the per-token cost vs fp16. Conservative 0.5 -- a real host
|
|
78
|
+
* may do better; the kv-cache-estimate endpoint, when consulted, supersedes
|
|
79
|
+
* this heuristic.
|
|
80
|
+
*/
|
|
81
|
+
export const KV_QUANT_FOOTPRINT_FACTOR = 0.5;
|
|
82
|
+
/**
|
|
83
|
+
* The KV-cache dtype token sent as `cache_type_kv` on the load wire when the
|
|
84
|
+
* sizer elects to quantize to fit a context. The exact accepted token is
|
|
85
|
+
* host-specific; `q8_0` is the common llama.cpp-family int8 KV token. Flag for
|
|
86
|
+
* the runbook to confirm against the live host's accepted values.
|
|
87
|
+
*/
|
|
88
|
+
export const KV_QUANT_CACHE_TYPE = 'q8_0';
|
|
89
|
+
/**
|
|
90
|
+
* Hard ceiling on the context we will ever request, independent of headroom,
|
|
91
|
+
* when no native-context bound is known. A sane upper clamp so a wildly large
|
|
92
|
+
* estimate cannot produce an absurd request. Native context (when known) clamps
|
|
93
|
+
* tighter than this.
|
|
94
|
+
*/
|
|
95
|
+
export const MAX_SEQ_LENGTH_CEILING = 131072;
|
|
96
|
+
// ── Defensive normalizers (live shapes NOT pinned -- runbook reconciles) ──────
|
|
97
|
+
/** Read the first finite, non-negative number-valued key present; null if none. */
|
|
98
|
+
function firstNumberOrNull(obj, keys) {
|
|
99
|
+
for (const key of keys) {
|
|
100
|
+
const v = obj[key];
|
|
101
|
+
if (typeof v === 'number' && Number.isFinite(v) && v >= 0)
|
|
102
|
+
return v;
|
|
103
|
+
// Some hosts stringify numbers; accept a clean numeric string defensively.
|
|
104
|
+
if (typeof v === 'string') {
|
|
105
|
+
const n = Number(v.trim());
|
|
106
|
+
if (Number.isFinite(n) && n >= 0)
|
|
107
|
+
return n;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
/** Read the first non-blank string-valued key present, trimmed; null if none. */
|
|
113
|
+
function firstStringOrNull(obj, keys) {
|
|
114
|
+
for (const key of keys) {
|
|
115
|
+
const v = obj[key];
|
|
116
|
+
if (typeof v === 'string') {
|
|
117
|
+
const trimmed = v.trim();
|
|
118
|
+
if (trimmed)
|
|
119
|
+
return trimmed;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
/** Candidate keys for the GPU product name on a hardware payload. */
|
|
125
|
+
const HW_GPU_NAME_KEYS = ['gpu_name', 'gpuName', 'name', 'gpu', 'device_name', 'product_name'];
|
|
126
|
+
/**
|
|
127
|
+
* Candidate keys for TOTAL VRAM in MB. We tolerate both MB-named and bytes-named
|
|
128
|
+
* fields; a bytes field is converted in `normalizeHardware`. The MB keys win
|
|
129
|
+
* when present (no conversion ambiguity).
|
|
130
|
+
*/
|
|
131
|
+
const HW_VRAM_TOTAL_MB_KEYS = ['vram_total_mb', 'vramTotalMb', 'memory_total_mb', 'total_memory_mb'];
|
|
132
|
+
const HW_VRAM_TOTAL_BYTES_KEYS = ['vram_total_bytes', 'vram_total', 'memory_total', 'total_memory_bytes'];
|
|
133
|
+
/** Candidate keys for USED VRAM in MB / bytes (same MB-preferred convention). */
|
|
134
|
+
const HW_VRAM_USED_MB_KEYS = ['vram_used_mb', 'vramUsedMb', 'memory_used_mb', 'used_memory_mb'];
|
|
135
|
+
const HW_VRAM_USED_BYTES_KEYS = ['vram_used_bytes', 'vram_used', 'memory_used', 'used_memory_bytes'];
|
|
136
|
+
/** Bytes -> MB (binary MiB), rounded down so we never overstate available memory. */
|
|
137
|
+
function bytesToMb(bytes) {
|
|
138
|
+
return Math.floor(bytes / (1024 * 1024));
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Resolve a VRAM quantity in MB from a payload, preferring an MB-named field and
|
|
142
|
+
* falling back to a bytes-named field (converted). Some hosts nest VRAM under a
|
|
143
|
+
* `gpu`/`gpus[0]`/`memory` sub-object; we also probe a single nested object.
|
|
144
|
+
* Returns null when nothing usable is present.
|
|
145
|
+
*/
|
|
146
|
+
function vramMb(obj, mbKeys, bytesKeys) {
|
|
147
|
+
const mb = firstNumberOrNull(obj, mbKeys);
|
|
148
|
+
if (mb !== null)
|
|
149
|
+
return Math.floor(mb);
|
|
150
|
+
const bytes = firstNumberOrNull(obj, bytesKeys);
|
|
151
|
+
if (bytes !== null)
|
|
152
|
+
return bytesToMb(bytes);
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Probe for a nested hardware sub-object so a payload like
|
|
157
|
+
* `{ gpu: { name, vram_total_mb, ... } }` or `{ gpus: [ {...} ] }` still yields
|
|
158
|
+
* fields. Returns the merged top-level + nested view (top-level wins).
|
|
159
|
+
*/
|
|
160
|
+
function flattenHardwarePayload(obj) {
|
|
161
|
+
const nestedKeys = ['gpu', 'memory', 'device'];
|
|
162
|
+
let merged = { ...obj };
|
|
163
|
+
for (const key of nestedKeys) {
|
|
164
|
+
const v = obj[key];
|
|
165
|
+
if (v && typeof v === 'object' && !Array.isArray(v)) {
|
|
166
|
+
merged = { ...v, ...merged };
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// A `gpus: [ {...} ]` array -- take the first entry as the active device.
|
|
170
|
+
const gpus = obj['gpus'];
|
|
171
|
+
if (Array.isArray(gpus) && gpus.length > 0 && gpus[0] && typeof gpus[0] === 'object') {
|
|
172
|
+
merged = { ...gpus[0], ...merged };
|
|
173
|
+
}
|
|
174
|
+
return merged;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Normalize a `/api/system/hardware` payload into a typed `HostHardware`.
|
|
178
|
+
* DEFENSIVE: the live shape is NOT pinned. Tolerates MB- or bytes-named VRAM
|
|
179
|
+
* fields, a flat or a `{ gpu: {...} }` / `{ gpus: [...] }` nesting, and missing
|
|
180
|
+
* fields (-> null). Pure + exported for unit testing.
|
|
181
|
+
*
|
|
182
|
+
* Runbook flag: confirm the live `/api/system/hardware` field names + units
|
|
183
|
+
* (MB vs bytes) and tighten `HW_*_KEYS` once observed.
|
|
184
|
+
*/
|
|
185
|
+
export function normalizeHardware(payload) {
|
|
186
|
+
if (!payload || typeof payload !== 'object') {
|
|
187
|
+
return { gpuName: null, vramTotalMb: null, vramUsedMb: null };
|
|
188
|
+
}
|
|
189
|
+
const obj = flattenHardwarePayload(payload);
|
|
190
|
+
return {
|
|
191
|
+
gpuName: firstStringOrNull(obj, HW_GPU_NAME_KEYS),
|
|
192
|
+
vramTotalMb: vramMb(obj, HW_VRAM_TOTAL_MB_KEYS, HW_VRAM_TOTAL_BYTES_KEYS),
|
|
193
|
+
vramUsedMb: vramMb(obj, HW_VRAM_USED_MB_KEYS, HW_VRAM_USED_BYTES_KEYS),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
/** Candidate keys for the predicted KV-cache footprint (MB) on an estimate payload. */
|
|
197
|
+
const KV_FOOTPRINT_MB_KEYS = ['kv_cache_mb', 'kvCacheMb', 'footprint_mb', 'estimate_mb', 'mb'];
|
|
198
|
+
const KV_FOOTPRINT_BYTES_KEYS = ['kv_cache_bytes', 'footprint_bytes', 'bytes'];
|
|
199
|
+
/** Candidate keys for a boolean "does it fit" verdict on an estimate payload. */
|
|
200
|
+
const KV_FITS_KEYS = ['fits', 'ok', 'will_fit'];
|
|
201
|
+
/**
|
|
202
|
+
* Normalize a `/api/models/kv-cache-estimate` payload into a typed
|
|
203
|
+
* `KvCacheEstimate`. DEFENSIVE: footprint under MB- or bytes-named fields, an
|
|
204
|
+
* optional boolean fit verdict, both null/undefined when absent. Pure + exported.
|
|
205
|
+
*
|
|
206
|
+
* Runbook flag: confirm the live estimate field names + whether the endpoint
|
|
207
|
+
* echoes the candidate context; tighten `KV_*_KEYS` once observed.
|
|
208
|
+
*/
|
|
209
|
+
export function normalizeKvCacheEstimate(payload) {
|
|
210
|
+
if (!payload || typeof payload !== 'object') {
|
|
211
|
+
return { footprintMb: null, fits: null };
|
|
212
|
+
}
|
|
213
|
+
const obj = payload;
|
|
214
|
+
let footprintMb = firstNumberOrNull(obj, KV_FOOTPRINT_MB_KEYS);
|
|
215
|
+
if (footprintMb === null) {
|
|
216
|
+
const bytes = firstNumberOrNull(obj, KV_FOOTPRINT_BYTES_KEYS);
|
|
217
|
+
if (bytes !== null)
|
|
218
|
+
footprintMb = bytesToMb(bytes);
|
|
219
|
+
}
|
|
220
|
+
let fits = null;
|
|
221
|
+
for (const key of KV_FITS_KEYS) {
|
|
222
|
+
const v = obj[key];
|
|
223
|
+
if (typeof v === 'boolean') {
|
|
224
|
+
fits = v;
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return { footprintMb, fits };
|
|
229
|
+
}
|
|
230
|
+
// ── Client reads ──────────────────────────────────────────────────────────────
|
|
231
|
+
/**
|
|
232
|
+
* Read the host's GPU + VRAM info (`GET <root>/api/system/hardware`), normalized
|
|
233
|
+
* to a typed `HostHardware`. The hardware endpoint lives under the host ROOT,
|
|
234
|
+
* NOT `/v1`, so we derive the root from the stored baseUrl. Throws
|
|
235
|
+
* `HostControlError` (address-free) on any transport failure -- the caller
|
|
236
|
+
* fail-safes to the floor default.
|
|
237
|
+
*/
|
|
238
|
+
export async function getHardware(baseUrl, apiKey) {
|
|
239
|
+
const root = hostRootFromBaseUrl(baseUrl);
|
|
240
|
+
const payload = await hostGet(`${root}${HOST_CONTROL_HARDWARE_PATH}`, { apiKey });
|
|
241
|
+
return normalizeHardware(payload);
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Read a KV-cache fit prediction for a candidate context
|
|
245
|
+
* (`GET <root>/api/models/kv-cache-estimate`). The candidate context (and an
|
|
246
|
+
* optional model path + kv quant) are passed as query params; the live host's
|
|
247
|
+
* exact param names are NOT pinned, so we send the common-sense set and the
|
|
248
|
+
* normalizer is defensive about the response. Throws `HostControlError` on any
|
|
249
|
+
* transport failure -- the caller treats it as "no estimate" and uses the pure
|
|
250
|
+
* heuristic instead.
|
|
251
|
+
*
|
|
252
|
+
* Runbook flag: confirm the live query-param names (`max_seq_length`,
|
|
253
|
+
* `model_path`, `cache_type_kv`) the endpoint expects.
|
|
254
|
+
*/
|
|
255
|
+
export async function getKvCacheEstimate(baseUrl, params, apiKey) {
|
|
256
|
+
const root = hostRootFromBaseUrl(baseUrl);
|
|
257
|
+
const qs = new URLSearchParams();
|
|
258
|
+
// snake_case ONLY at the wire boundary (query string).
|
|
259
|
+
qs.set('max_seq_length', String(params.maxSeqLength));
|
|
260
|
+
if (params.modelPath)
|
|
261
|
+
qs.set('model_path', params.modelPath);
|
|
262
|
+
if (params.cacheTypeKv)
|
|
263
|
+
qs.set('cache_type_kv', params.cacheTypeKv);
|
|
264
|
+
const payload = await hostGet(`${root}${HOST_CONTROL_KV_CACHE_PATH}?${qs.toString()}`, { apiKey });
|
|
265
|
+
return normalizeKvCacheEstimate(payload);
|
|
266
|
+
}
|
|
267
|
+
// ── The pure heart: computeSafeMaxSeqLength ──────────────────────────────────
|
|
268
|
+
/**
|
|
269
|
+
* Round a context length down to a power-of-two-ish, clean step. Hosts size KV
|
|
270
|
+
* buffers in chunks; a clean step avoids requesting an awkward non-aligned
|
|
271
|
+
* context. We step in 1024-token units (a context is always a multiple of 1024)
|
|
272
|
+
* and never round a value below the floor up-and-over -- callers clamp the floor
|
|
273
|
+
* separately. Returns at least 0.
|
|
274
|
+
*/
|
|
275
|
+
function roundDownToStep(value, step = 1024) {
|
|
276
|
+
if (value <= 0)
|
|
277
|
+
return 0;
|
|
278
|
+
return Math.floor(value / step) * step;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* The per-token KV-cache cost for the chosen quant mode (fp16 vs quantized).
|
|
282
|
+
*/
|
|
283
|
+
function perTokenCost(kvQuant) {
|
|
284
|
+
return kvQuant ? KV_CACHE_MB_PER_TOKEN * KV_QUANT_FOOTPRINT_FACTOR : KV_CACHE_MB_PER_TOKEN;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* The largest context (rounded to the step) whose KV cache fits within
|
|
288
|
+
* `availableMb` at the given per-token cost, clamped to `nativeCap`. May return
|
|
289
|
+
* a value BELOW the floor -- the caller decides what to do with that.
|
|
290
|
+
*/
|
|
291
|
+
function largestFittingContext(availableMb, kvQuant, nativeCap) {
|
|
292
|
+
if (availableMb <= 0)
|
|
293
|
+
return 0;
|
|
294
|
+
const rawTokens = availableMb / perTokenCost(kvQuant);
|
|
295
|
+
const stepped = roundDownToStep(rawTokens);
|
|
296
|
+
return Math.min(stepped, nativeCap);
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* PURE: compute the safe `max_seq_length` (and whether to quantize the KV cache)
|
|
300
|
+
* for a load, given VRAM totals/usage, the resident model size, and the model's
|
|
301
|
+
* native context. NO HTTP -- the orchestrator gathers the inputs and (optionally)
|
|
302
|
+
* validates the result against the live kv-cache endpoint.
|
|
303
|
+
*
|
|
304
|
+
* The arithmetic, with the live constraints in mind:
|
|
305
|
+
*
|
|
306
|
+
* - headroom = vramTotal - vramUsed - VRAM_SAFETY_MARGIN_MB. This is the VRAM
|
|
307
|
+
* available for the KV cache AFTER the model weights are resident. (Live: a
|
|
308
|
+
* 12GB card sat ~93% used with a model loaded -- headroom is small, so the
|
|
309
|
+
* KV cache, which scales with context, is the binding constraint.) When
|
|
310
|
+
* `vramUsed` already accounts for the weights, `modelSizeMb` is informational;
|
|
311
|
+
* when it does not, we subtract `modelSizeMb` too (see below).
|
|
312
|
+
*
|
|
313
|
+
* - The KV cache grows ~linearly with context (`KV_CACHE_MB_PER_TOKEN`). We pick
|
|
314
|
+
* the LARGEST context whose KV cache fits headroom, clamped to the model's
|
|
315
|
+
* native context (`nativeContext`) and the global ceiling.
|
|
316
|
+
*
|
|
317
|
+
* - HARD LOWER BOUND (codex floor): a context below `CODEX_TURN1_FLOOR` silently
|
|
318
|
+
* fails turn-1, so we NEVER return below it. If headroom can't fit the floor
|
|
319
|
+
* at fp16 KV, we try quantization (`cacheTypeKv`), which ~halves the footprint.
|
|
320
|
+
* If even quantized KV can't fit the floor, we return the floor ANYWAY with
|
|
321
|
+
* `quantRequired`/`marginal` set -- going below is not an option; better to
|
|
322
|
+
* attempt the floor (and let the load fail loudly / the operator add VRAM)
|
|
323
|
+
* than to silently load a too-small context that fails turn-1 with no signal.
|
|
324
|
+
*
|
|
325
|
+
* - When inputs are missing/degenerate (no VRAM data, zero/negative headroom),
|
|
326
|
+
* we degrade SAFELY to the floor default -- never throw, never go below floor.
|
|
327
|
+
*/
|
|
328
|
+
export function computeSafeMaxSeqLength(input) {
|
|
329
|
+
const floor = input.codexFloor ?? CODEX_TURN1_FLOOR;
|
|
330
|
+
// Native context bounds everything; when unknown, fall back to the global
|
|
331
|
+
// ceiling so headroom (not an unknown native cap) is the binding constraint.
|
|
332
|
+
const nativeCap = typeof input.nativeContext === 'number' && input.nativeContext >= floor
|
|
333
|
+
? input.nativeContext
|
|
334
|
+
: MAX_SEQ_LENGTH_CEILING;
|
|
335
|
+
// Degenerate VRAM inputs -> we cannot size against headroom. Return the floor
|
|
336
|
+
// (clamped to native) and signal marginal so the orchestrator logs it. NEVER
|
|
337
|
+
// go below the floor; NEVER throw.
|
|
338
|
+
const vramTotal = input.vramTotalMb;
|
|
339
|
+
const vramUsed = input.vramUsedMb;
|
|
340
|
+
if (vramTotal === null ||
|
|
341
|
+
vramTotal === undefined ||
|
|
342
|
+
vramUsed === null ||
|
|
343
|
+
vramUsed === undefined ||
|
|
344
|
+
!Number.isFinite(vramTotal) ||
|
|
345
|
+
!Number.isFinite(vramUsed)) {
|
|
346
|
+
return { maxSeqLength: Math.min(floor, nativeCap === floor ? floor : Math.max(floor, nativeCap)), marginal: true };
|
|
347
|
+
}
|
|
348
|
+
// Headroom = total - used - safety margin. If the host's `vramUsed` does NOT
|
|
349
|
+
// already include the candidate model's weights (i.e. nothing resident yet),
|
|
350
|
+
// also subtract the model size so we size the KV cache against memory left
|
|
351
|
+
// AFTER the weights load. We can't know which the host reports, so we take the
|
|
352
|
+
// conservative interpretation: subtract modelSize only when it would not already
|
|
353
|
+
// be reflected -- i.e. when used is small relative to the model size, treat the
|
|
354
|
+
// model as not-yet-resident and reserve its weights. This keeps us safe on a
|
|
355
|
+
// cold GPU (subtract weights) and on a warm GPU (used already high -> no double
|
|
356
|
+
// subtract because we cap headroom at >= 0).
|
|
357
|
+
const modelSize = typeof input.modelSizeMb === 'number' && input.modelSizeMb > 0 ? input.modelSizeMb : 0;
|
|
358
|
+
const modelNotYetResident = modelSize > 0 && vramUsed < modelSize;
|
|
359
|
+
const reservedForWeights = modelNotYetResident ? modelSize : 0;
|
|
360
|
+
const headroom = vramTotal - vramUsed - VRAM_SAFETY_MARGIN_MB - reservedForWeights;
|
|
361
|
+
const requestedQuant = input.kvQuant === true;
|
|
362
|
+
// First try fp16 KV (no quant) unless the caller pre-requested quant.
|
|
363
|
+
const fp16Fit = largestFittingContext(headroom, false, nativeCap);
|
|
364
|
+
if (!requestedQuant && fp16Fit >= floor) {
|
|
365
|
+
// Comfortable: the largest fp16 context that fits clears the floor.
|
|
366
|
+
return { maxSeqLength: fp16Fit };
|
|
367
|
+
}
|
|
368
|
+
// fp16 can't clear the floor (or quant was requested) -> try quantized KV,
|
|
369
|
+
// which ~halves the per-token cost and so fits a larger context.
|
|
370
|
+
const quantFit = largestFittingContext(headroom, true, nativeCap);
|
|
371
|
+
if (quantFit >= floor) {
|
|
372
|
+
// Quantization buys us the floor (or more). Take the larger of the two paths
|
|
373
|
+
// that clears the floor; if fp16 already cleared it (requestedQuant case),
|
|
374
|
+
// honor the requested quant but never drop below what fp16 would have given.
|
|
375
|
+
const best = Math.max(quantFit, requestedQuant && fp16Fit >= floor ? fp16Fit : 0);
|
|
376
|
+
return { maxSeqLength: Math.min(best, nativeCap), cacheTypeKv: KV_QUANT_CACHE_TYPE };
|
|
377
|
+
}
|
|
378
|
+
// Even quantized KV can't fit the floor in the available headroom. Do NOT go
|
|
379
|
+
// below the floor (that silently fails turn-1). Return the floor clamped to
|
|
380
|
+
// native, flag quant-required + marginal so the load path logs the risk; the
|
|
381
|
+
// load may fail loudly / the operator must free VRAM. Better a loud floor
|
|
382
|
+
// attempt than a silent sub-floor failure.
|
|
383
|
+
return {
|
|
384
|
+
maxSeqLength: Math.min(floor, nativeCap),
|
|
385
|
+
cacheTypeKv: KV_QUANT_CACHE_TYPE,
|
|
386
|
+
quantRequired: true,
|
|
387
|
+
marginal: true,
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Resolve the safe `EnsureLoadedOptions` sizing bits (`maxSeqLength` + optional
|
|
392
|
+
* `cacheTypeKv`) for a load by reading the host's hardware, running the PURE
|
|
393
|
+
* `computeSafeMaxSeqLength`, and OPTIONALLY validating the result against the
|
|
394
|
+
* live kv-cache-estimate endpoint. This is the seam the focus-executor calls in
|
|
395
|
+
* place of D2's fixed `DEFAULT_MAX_SEQ_LENGTH`.
|
|
396
|
+
*
|
|
397
|
+
* FAIL-SAFE: if the hardware endpoint is missing / unreachable / returns no VRAM
|
|
398
|
+
* totals, we DO NOT compute against headroom -- we return the fixed
|
|
399
|
+
* `DEFAULT_MAX_SEQ_LENGTH` (>= the codex floor) exactly as D2 did, and report the
|
|
400
|
+
* fallback so the caller logs it. A sizing failure must NEVER break a swap that
|
|
401
|
+
* previously worked with the fixed default.
|
|
402
|
+
*
|
|
403
|
+
* The kv-cache-estimate validation is best-effort: if it predicts the computed
|
|
404
|
+
* context will NOT fit (and we are not already quantizing), we re-run the sizer
|
|
405
|
+
* forcing quantization; if the endpoint is unreachable we keep the pure result.
|
|
406
|
+
* The estimate is never allowed to push the context BELOW the floor.
|
|
407
|
+
*
|
|
408
|
+
* Returns `{ maxSeqLength, cacheTypeKv?, source, marginal?, quantRequired? }`.
|
|
409
|
+
* `source` is `'computed'` when hardware drove the value, `'fallback'` when we
|
|
410
|
+
* fell back to the fixed default -- the caller logs which path it took.
|
|
411
|
+
*/
|
|
412
|
+
export async function resolveSafeLoadOptions(baseUrl, modelPath, hints = {}, apiKey) {
|
|
413
|
+
let hardware;
|
|
414
|
+
try {
|
|
415
|
+
hardware = await getHardware(baseUrl, apiKey);
|
|
416
|
+
}
|
|
417
|
+
catch {
|
|
418
|
+
// Hardware endpoint missing / unreachable -> fail-safe to the fixed default.
|
|
419
|
+
// Address-free: the rich error was already categorized inside the client; we
|
|
420
|
+
// do not surface or rethrow it -- a sizing read failure is non-fatal.
|
|
421
|
+
return { maxSeqLength: DEFAULT_MAX_SEQ_LENGTH, source: 'fallback' };
|
|
422
|
+
}
|
|
423
|
+
// No usable VRAM totals -> we cannot size against headroom. Fail-safe to the
|
|
424
|
+
// fixed default (which clears the floor), rather than guessing.
|
|
425
|
+
if (hardware.vramTotalMb === null) {
|
|
426
|
+
return { maxSeqLength: DEFAULT_MAX_SEQ_LENGTH, source: 'fallback' };
|
|
427
|
+
}
|
|
428
|
+
const computed = computeSafeMaxSeqLength({
|
|
429
|
+
vramTotalMb: hardware.vramTotalMb,
|
|
430
|
+
vramUsedMb: hardware.vramUsedMb,
|
|
431
|
+
modelSizeMb: hints.modelSizeMb ?? null,
|
|
432
|
+
nativeContext: hints.nativeContext ?? null,
|
|
433
|
+
});
|
|
434
|
+
// Best-effort validation against the live kv-cache estimate. If the host says
|
|
435
|
+
// the computed context will NOT fit and we are not already quantizing, re-run
|
|
436
|
+
// the sizer forcing quantization. A failed estimate read is ignored (keep the
|
|
437
|
+
// pure result). The estimate can never push us below the floor.
|
|
438
|
+
let result = computed;
|
|
439
|
+
if (!computed.cacheTypeKv) {
|
|
440
|
+
try {
|
|
441
|
+
const estimate = await getKvCacheEstimate(baseUrl, { maxSeqLength: computed.maxSeqLength, modelPath: modelPath ?? undefined }, apiKey);
|
|
442
|
+
if (estimate.fits === false) {
|
|
443
|
+
result = computeSafeMaxSeqLength({
|
|
444
|
+
vramTotalMb: hardware.vramTotalMb,
|
|
445
|
+
vramUsedMb: hardware.vramUsedMb,
|
|
446
|
+
modelSizeMb: hints.modelSizeMb ?? null,
|
|
447
|
+
nativeContext: hints.nativeContext ?? null,
|
|
448
|
+
kvQuant: true,
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
catch {
|
|
453
|
+
// Estimate endpoint missing / unreachable -> keep the pure computed result.
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
return {
|
|
457
|
+
maxSeqLength: result.maxSeqLength,
|
|
458
|
+
...(result.cacheTypeKv !== undefined ? { cacheTypeKv: result.cacheTypeKv } : {}),
|
|
459
|
+
source: 'computed',
|
|
460
|
+
...(result.marginal !== undefined ? { marginal: result.marginal } : {}),
|
|
461
|
+
...(result.quantRequired !== undefined ? { quantRequired: result.quantRequired } : {}),
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
//# sourceMappingURL=sizing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sizing.js","sourceRoot":"","sources":["../../src/host-control/sizing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for the host-control plane (Local model host control, D1).
|
|
3
|
+
*
|
|
4
|
+
* The host-control plane sits ABOVE the OpenAI-compatible `/v1/models` floor: a
|
|
5
|
+
* controllable model host (e.g. Unsloth Studio) exposes its FULL downloaded
|
|
6
|
+
* catalog -- including models that are downloaded-but-not-loaded -- under a
|
|
7
|
+
* control surface (`/api/models/local`). Detection is per-endpoint and
|
|
8
|
+
* fail-safe: a plain OpenAI-compatible endpoint that lacks the control surface
|
|
9
|
+
* degrades cleanly to the load-then-discover (`/v1/models`) behavior.
|
|
10
|
+
*
|
|
11
|
+
* Delivery 1 covers detection + catalog read. Later deliveries (`load.ts`,
|
|
12
|
+
* `sizing.ts`) add load/unload + sizing on top of these types; keep this module
|
|
13
|
+
* additive-friendly.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* One loadable model from a controllable host's catalog. `id` is the served
|
|
17
|
+
* model identifier (the value the picklist offers); `modelPath` is the host's
|
|
18
|
+
* own load path (`model_path`) captured so a later delivery can resolve
|
|
19
|
+
* id -> path to load the model. Optional richer fields (size, family) are added
|
|
20
|
+
* by later deliveries -- keep this interface open to extension.
|
|
21
|
+
*/
|
|
22
|
+
export interface LoadableModel {
|
|
23
|
+
/** Served model id -- the value offered in the add-model picklist. */
|
|
24
|
+
id: string;
|
|
25
|
+
/**
|
|
26
|
+
* The host's load path for this model (`model_path` on the wire). Null when
|
|
27
|
+
* the catalog entry did not carry one (a degraded/floor entry, or a host that
|
|
28
|
+
* omits the field). Delivery 2 consumes id -> modelPath to load.
|
|
29
|
+
*/
|
|
30
|
+
modelPath: string | null;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The result of reading an endpoint's loadable-model catalog. `controllable`
|
|
34
|
+
* records whether the host-control surface answered (true) or we fell back to
|
|
35
|
+
* the `/v1/models` floor (false). `ids` is the flat string[] for the picklist;
|
|
36
|
+
* `models` carries the richer entries for downstream load resolution. On the
|
|
37
|
+
* floor path `models` mirrors `ids` with `modelPath: null`.
|
|
38
|
+
*/
|
|
39
|
+
export interface LoadableCatalog {
|
|
40
|
+
/** True when the controllable host catalog answered; false on the floor path. */
|
|
41
|
+
controllable: boolean;
|
|
42
|
+
/** De-duplicated served model ids -- the picklist payload. */
|
|
43
|
+
ids: string[];
|
|
44
|
+
/** Richer per-model entries (id + modelPath) for downstream load resolution. */
|
|
45
|
+
models: LoadableModel[];
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The outcome of a cheap, fail-safe capability probe against an endpoint's host
|
|
49
|
+
* root. `controllable: true` means the control surface answered a well-formed
|
|
50
|
+
* catalog; anything else (error / non-2xx / parse failure / timeout) yields
|
|
51
|
+
* `false` so the caller falls back to the `/v1/models` floor.
|
|
52
|
+
*/
|
|
53
|
+
export interface HostCapability {
|
|
54
|
+
controllable: boolean;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Normalized `/v1/status` snapshot (Local model host control, D2). The host's
|
|
58
|
+
* live status shape is NOT pinned, so the normalizer is defensive; this is the
|
|
59
|
+
* typed result it yields. `loadedModelId` is the currently-resident served id
|
|
60
|
+
* (null when nothing is loaded); `ready` is whether that model is up + serving.
|
|
61
|
+
*/
|
|
62
|
+
export interface HostStatus {
|
|
63
|
+
/** Currently-loaded served model id, or null when nothing is resident. */
|
|
64
|
+
loadedModelId: string | null;
|
|
65
|
+
/** True when the loaded model is up and ready to serve. */
|
|
66
|
+
ready: boolean;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Parameters for a `POST /v1/load` (Local model host control, D2). camelCase app
|
|
70
|
+
* types here; the load client maps them to the host's snake_case wire field
|
|
71
|
+
* names (`model_path`, `max_seq_length`, `cache_type_kv`, `gpu_ids`) at the
|
|
72
|
+
* boundary. `maxSeqLength` is REQUIRED + injected so Delivery 3 can pass a
|
|
73
|
+
* computed safe value rather than the fixed `DEFAULT_MAX_SEQ_LENGTH` floor.
|
|
74
|
+
*/
|
|
75
|
+
export interface LoadModelParams {
|
|
76
|
+
/** The host's load path for the target model (`model_path` on the wire). */
|
|
77
|
+
modelPath: string;
|
|
78
|
+
/** Context window to load with (`max_seq_length` on the wire). */
|
|
79
|
+
maxSeqLength: number;
|
|
80
|
+
/** Optional KV cache dtype (`cache_type_kv` on the wire). */
|
|
81
|
+
cacheTypeKv?: string;
|
|
82
|
+
/** Optional GPU id selection (`gpu_ids` on the wire). */
|
|
83
|
+
gpuIds?: number[];
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The action a single-GPU swap must take to make a target model resident, given
|
|
87
|
+
* the current resident model (Local model host control, D2). One model fits the
|
|
88
|
+
* GPU at a time, so a different resident must be evicted before the load.
|
|
89
|
+
*/
|
|
90
|
+
export type SwapAction = 'noop' | 'load' | 'evict_then_load';
|
|
91
|
+
/** The pure swap decision -- the action `ensureLoaded` will perform. */
|
|
92
|
+
export interface SwapDecision {
|
|
93
|
+
action: SwapAction;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Normalized `/api/system/hardware` snapshot (Local model host control, D3). The
|
|
97
|
+
* host's live shape is NOT pinned, so the normalizer is defensive; this is the
|
|
98
|
+
* typed result it yields. VRAM is carried in MB (the normalizer converts a
|
|
99
|
+
* bytes-named source). Any field the host omits is null -- the sizer degrades
|
|
100
|
+
* safely to the floor default when VRAM totals are unavailable.
|
|
101
|
+
*/
|
|
102
|
+
export interface HostHardware {
|
|
103
|
+
/** GPU product name (e.g. "NVIDIA GeForce RTX 3060"), or null when absent. */
|
|
104
|
+
gpuName: string | null;
|
|
105
|
+
/** Total VRAM in MB, or null when the host did not report it. */
|
|
106
|
+
vramTotalMb: number | null;
|
|
107
|
+
/** Currently-used VRAM in MB, or null when the host did not report it. */
|
|
108
|
+
vramUsedMb: number | null;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Query params for a `GET /api/models/kv-cache-estimate` (Local model host
|
|
112
|
+
* control, D3). camelCase here; the client maps them to the host's snake_case
|
|
113
|
+
* query-string field names (`max_seq_length`, `model_path`, `cache_type_kv`) at
|
|
114
|
+
* the boundary. The candidate context is required; model path + kv quant are
|
|
115
|
+
* optional refiners.
|
|
116
|
+
*/
|
|
117
|
+
export interface KvCacheEstimateParams {
|
|
118
|
+
/** Candidate context to estimate the KV-cache footprint for. */
|
|
119
|
+
maxSeqLength: number;
|
|
120
|
+
/** Optional model path so the host can size against the specific model. */
|
|
121
|
+
modelPath?: string;
|
|
122
|
+
/** Optional KV-cache dtype (e.g. quantized) to estimate the smaller footprint. */
|
|
123
|
+
cacheTypeKv?: string;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Normalized `/api/models/kv-cache-estimate` result (Local model host control,
|
|
127
|
+
* D3). DEFENSIVE: the live shape is NOT pinned. `footprintMb` is the predicted
|
|
128
|
+
* KV-cache memory (MB) for the candidate context (null when not reported);
|
|
129
|
+
* `fits` is an optional host verdict on whether it fits available VRAM.
|
|
130
|
+
*/
|
|
131
|
+
export interface KvCacheEstimate {
|
|
132
|
+
/** Predicted KV-cache footprint in MB for the candidate context, or null. */
|
|
133
|
+
footprintMb: number | null;
|
|
134
|
+
/** Optional host verdict on whether the candidate context fits, or null. */
|
|
135
|
+
fits: boolean | null;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Input to the PURE `computeSafeMaxSeqLength` (Local model host control, D3). All
|
|
139
|
+
* static -- gathered by the orchestrator from the hardware endpoint + catalog so
|
|
140
|
+
* the core sizing arithmetic is unit-testable with NO HTTP. Unknown numeric
|
|
141
|
+
* inputs (null/undefined) degrade safely to the floor default.
|
|
142
|
+
*/
|
|
143
|
+
export interface SafeMaxSeqInput {
|
|
144
|
+
/** Total VRAM in MB (null/undefined -> degrade to floor). */
|
|
145
|
+
vramTotalMb: number | null | undefined;
|
|
146
|
+
/** Currently-used VRAM in MB (null/undefined -> degrade to floor). */
|
|
147
|
+
vramUsedMb: number | null | undefined;
|
|
148
|
+
/** The candidate model's on-disk/weights size in MB, when known. */
|
|
149
|
+
modelSizeMb?: number | null;
|
|
150
|
+
/** The model's native context window (clamps the result), when known. */
|
|
151
|
+
nativeContext?: number | null;
|
|
152
|
+
/** Pre-request KV quantization (forces the quantized footprint path). */
|
|
153
|
+
kvQuant?: boolean;
|
|
154
|
+
/** Override the codex turn-1 floor (defaults to `CODEX_TURN1_FLOOR`). */
|
|
155
|
+
codexFloor?: number;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Output of `computeSafeMaxSeqLength` (Local model host control, D3). The chosen
|
|
159
|
+
* context (never below the codex floor) and, when quantization was elected to
|
|
160
|
+
* fit a context, the `cacheTypeKv` to pass on the load. `quantRequired` +
|
|
161
|
+
* `marginal` are advisory signals for the load path to log -- they do NOT change
|
|
162
|
+
* the returned `maxSeqLength`, which is always at least the floor.
|
|
163
|
+
*/
|
|
164
|
+
export interface SafeMaxSeqResult {
|
|
165
|
+
/** The safe context to load with (always >= the codex floor, <= native cap). */
|
|
166
|
+
maxSeqLength: number;
|
|
167
|
+
/** KV-cache dtype to quantize with, set only when quantization was elected. */
|
|
168
|
+
cacheTypeKv?: string;
|
|
169
|
+
/** True when even quantized KV could not fit the floor -- a tight, risky load. */
|
|
170
|
+
quantRequired?: boolean;
|
|
171
|
+
/** True when the result rests at the floor under tight/unknown headroom. */
|
|
172
|
+
marginal?: boolean;
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/host-control/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC5B,sEAAsE;IACtE,EAAE,EAAE,MAAM,CAAC;IACX;;;;OAIG;IACH,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B,iFAAiF;IACjF,YAAY,EAAE,OAAO,CAAC;IACtB,8DAA8D;IAC9D,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,gFAAgF;IAChF,MAAM,EAAE,aAAa,EAAE,CAAC;CACzB;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,OAAO,CAAC;CACvB;AAED;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,0EAA0E;IAC1E,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,2DAA2D;IAC3D,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B,4EAA4E;IAC5E,SAAS,EAAE,MAAM,CAAC;IAClB,kEAAkE;IAClE,YAAY,EAAE,MAAM,CAAC;IACrB,6DAA6D;IAC7D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yDAAyD;IACzD,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,iBAAiB,CAAC;AAE7D,wEAAwE;AACxE,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,UAAU,CAAC;CACpB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B,8EAA8E;IAC9E,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,iEAAiE;IACjE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,0EAA0E;IAC1E,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACpC,gEAAgE;IAChE,YAAY,EAAE,MAAM,CAAC;IACrB,2EAA2E;IAC3E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kFAAkF;IAClF,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,6EAA6E;IAC7E,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,4EAA4E;IAC5E,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;CACtB;AAED;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,6DAA6D;IAC7D,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACvC,sEAAsE;IACtE,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACtC,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,yEAAyE;IACzE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,yEAAyE;IACzE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,gFAAgF;IAChF,YAAY,EAAE,MAAM,CAAC;IACrB,+EAA+E;IAC/E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kFAAkF;IAClF,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB"}
|