@miller-tech/uap 2.21.3 → 2.36.4
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/README.md +22 -10
- package/config/model-profiles/qwen38-coder390.json +46 -0
- package/config/model-profiles/qwen38.json +3 -2
- package/config/model-registry.json +110 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/benchmarks/paired/types.d.ts +2 -2
- package/dist/bin/cli.js +315 -20
- package/dist/bin/cli.js.map +1 -1
- package/dist/cli/attention.d.ts +6 -0
- package/dist/cli/attention.d.ts.map +1 -0
- package/dist/cli/attention.js +115 -0
- package/dist/cli/attention.js.map +1 -0
- package/dist/cli/bench-report.d.ts +82 -0
- package/dist/cli/bench-report.d.ts.map +1 -0
- package/dist/cli/bench-report.js +237 -0
- package/dist/cli/bench-report.js.map +1 -0
- package/dist/cli/clients.d.ts +6 -0
- package/dist/cli/clients.d.ts.map +1 -0
- package/dist/cli/clients.js +142 -0
- package/dist/cli/clients.js.map +1 -0
- package/dist/cli/dashboard-grants.d.ts +26 -0
- package/dist/cli/dashboard-grants.d.ts.map +1 -0
- package/dist/cli/dashboard-grants.js +69 -0
- package/dist/cli/dashboard-grants.js.map +1 -0
- package/dist/cli/dashboard-pace.d.ts +31 -0
- package/dist/cli/dashboard-pace.d.ts.map +1 -0
- package/dist/cli/dashboard-pace.js +42 -0
- package/dist/cli/dashboard-pace.js.map +1 -0
- package/dist/cli/dashboard-session.d.ts +25 -0
- package/dist/cli/dashboard-session.d.ts.map +1 -0
- package/dist/cli/dashboard-session.js +814 -0
- package/dist/cli/dashboard-session.js.map +1 -0
- package/dist/cli/dashboard-types.d.ts +18 -0
- package/dist/cli/dashboard-types.d.ts.map +1 -0
- package/dist/cli/dashboard-types.js +9 -0
- package/dist/cli/dashboard-types.js.map +1 -0
- package/dist/cli/dashboard.d.ts +2 -18
- package/dist/cli/dashboard.d.ts.map +1 -1
- package/dist/cli/dashboard.js +43 -793
- package/dist/cli/dashboard.js.map +1 -1
- package/dist/cli/deliver.d.ts +35 -0
- package/dist/cli/deliver.d.ts.map +1 -1
- package/dist/cli/deliver.js +93 -9
- package/dist/cli/deliver.js.map +1 -1
- package/dist/cli/diff.d.ts +85 -0
- package/dist/cli/diff.d.ts.map +1 -0
- package/dist/cli/diff.js +293 -0
- package/dist/cli/diff.js.map +1 -0
- package/dist/cli/eta.d.ts +35 -0
- package/dist/cli/eta.d.ts.map +1 -0
- package/dist/cli/eta.js +101 -0
- package/dist/cli/eta.js.map +1 -0
- package/dist/cli/handsfree.d.ts +7 -0
- package/dist/cli/handsfree.d.ts.map +1 -1
- package/dist/cli/handsfree.js +27 -0
- package/dist/cli/handsfree.js.map +1 -1
- package/dist/cli/hooks.d.ts.map +1 -1
- package/dist/cli/hooks.js +126 -5
- package/dist/cli/hooks.js.map +1 -1
- package/dist/cli/loops.d.ts +24 -5
- package/dist/cli/loops.d.ts.map +1 -1
- package/dist/cli/loops.js +92 -12
- package/dist/cli/loops.js.map +1 -1
- package/dist/cli/models.d.ts +62 -0
- package/dist/cli/models.d.ts.map +1 -0
- package/dist/cli/models.js +567 -0
- package/dist/cli/models.js.map +1 -0
- package/dist/cli/plan.d.ts +9 -0
- package/dist/cli/plan.d.ts.map +1 -1
- package/dist/cli/plan.js +57 -4
- package/dist/cli/plan.js.map +1 -1
- package/dist/cli/proxy-relay.d.ts +55 -0
- package/dist/cli/proxy-relay.d.ts.map +1 -0
- package/dist/cli/proxy-relay.js +116 -0
- package/dist/cli/proxy-relay.js.map +1 -0
- package/dist/cli/proxy.d.ts +4 -0
- package/dist/cli/proxy.d.ts.map +1 -1
- package/dist/cli/proxy.js +26 -1
- package/dist/cli/proxy.js.map +1 -1
- package/dist/cli/sandbox.d.ts +101 -4
- package/dist/cli/sandbox.d.ts.map +1 -1
- package/dist/cli/sandbox.js +393 -44
- package/dist/cli/sandbox.js.map +1 -1
- package/dist/cli/self.d.ts +13 -0
- package/dist/cli/self.d.ts.map +1 -0
- package/dist/cli/self.js +94 -0
- package/dist/cli/self.js.map +1 -0
- package/dist/cli/systemd-services.d.ts +2 -8
- package/dist/cli/systemd-services.d.ts.map +1 -1
- package/dist/cli/systemd-services.js +5 -20
- package/dist/cli/systemd-services.js.map +1 -1
- package/dist/cli/task.js +1 -1
- package/dist/cli/task.js.map +1 -1
- package/dist/cli/wizard-config.d.ts.map +1 -1
- package/dist/cli/wizard-config.js +31 -2
- package/dist/cli/wizard-config.js.map +1 -1
- package/dist/config/settings-registry.d.ts +1 -1
- package/dist/config/settings-registry.d.ts.map +1 -1
- package/dist/config/settings-registry.js +108 -0
- package/dist/config/settings-registry.js.map +1 -1
- package/dist/coordination/capability-router.js +2 -2
- package/dist/coordination/capability-router.js.map +1 -1
- package/dist/coordination/database.d.ts +13 -0
- package/dist/coordination/database.d.ts.map +1 -1
- package/dist/coordination/database.js +63 -7
- package/dist/coordination/database.js.map +1 -1
- package/dist/coordination/eta-inject.d.ts +32 -0
- package/dist/coordination/eta-inject.d.ts.map +1 -0
- package/dist/coordination/eta-inject.js +64 -0
- package/dist/coordination/eta-inject.js.map +1 -0
- package/dist/coordination/reactor.d.ts.map +1 -1
- package/dist/coordination/reactor.js +14 -0
- package/dist/coordination/reactor.js.map +1 -1
- package/dist/coordination/service.d.ts +18 -8
- package/dist/coordination/service.d.ts.map +1 -1
- package/dist/coordination/service.js +38 -28
- package/dist/coordination/service.js.map +1 -1
- package/dist/dashboard/client-registry.d.ts +163 -0
- package/dist/dashboard/client-registry.d.ts.map +1 -0
- package/dist/dashboard/client-registry.js +549 -0
- package/dist/dashboard/client-registry.js.map +1 -0
- package/dist/dashboard/controls.d.ts.map +1 -1
- package/dist/dashboard/controls.js +33 -4
- package/dist/dashboard/controls.js.map +1 -1
- package/dist/dashboard/data-seeder.d.ts +10 -7
- package/dist/dashboard/data-seeder.d.ts.map +1 -1
- package/dist/dashboard/data-seeder.js +31 -75
- package/dist/dashboard/data-seeder.js.map +1 -1
- package/dist/dashboard/data-service.d.ts +9 -0
- package/dist/dashboard/data-service.d.ts.map +1 -1
- package/dist/dashboard/data-service.js +32 -3
- package/dist/dashboard/data-service.js.map +1 -1
- package/dist/dashboard/placement-routes.d.ts +119 -0
- package/dist/dashboard/placement-routes.d.ts.map +1 -0
- package/dist/dashboard/placement-routes.js +331 -0
- package/dist/dashboard/placement-routes.js.map +1 -0
- package/dist/dashboard/server.d.ts.map +1 -1
- package/dist/dashboard/server.js +408 -2
- package/dist/dashboard/server.js.map +1 -1
- package/dist/delivery/attention.d.ts +130 -0
- package/dist/delivery/attention.d.ts.map +1 -0
- package/dist/delivery/attention.js +341 -0
- package/dist/delivery/attention.js.map +1 -0
- package/dist/delivery/decompose.d.ts +18 -0
- package/dist/delivery/decompose.d.ts.map +1 -1
- package/dist/delivery/decompose.js +22 -1
- package/dist/delivery/decompose.js.map +1 -1
- package/dist/delivery/eta-estimate.d.ts +111 -0
- package/dist/delivery/eta-estimate.d.ts.map +1 -0
- package/dist/delivery/eta-estimate.js +171 -0
- package/dist/delivery/eta-estimate.js.map +1 -0
- package/dist/delivery/eta.d.ts +23 -0
- package/dist/delivery/eta.d.ts.map +1 -0
- package/dist/delivery/eta.js +59 -0
- package/dist/delivery/eta.js.map +1 -0
- package/dist/delivery/orchestrated-mission.d.ts +10 -0
- package/dist/delivery/orchestrated-mission.d.ts.map +1 -1
- package/dist/delivery/orchestrated-mission.js +15 -1
- package/dist/delivery/orchestrated-mission.js.map +1 -1
- package/dist/delivery/plan-check.d.ts +7 -1
- package/dist/delivery/plan-check.d.ts.map +1 -1
- package/dist/delivery/plan-check.js +23 -2
- package/dist/delivery/plan-check.js.map +1 -1
- package/dist/delivery/run-state.d.ts +1 -1
- package/dist/delivery/run-state.d.ts.map +1 -1
- package/dist/delivery/run-state.js +8 -1
- package/dist/delivery/run-state.js.map +1 -1
- package/dist/delivery/task-orchestrator.d.ts +8 -3
- package/dist/delivery/task-orchestrator.d.ts.map +1 -1
- package/dist/delivery/task-orchestrator.js +75 -43
- package/dist/delivery/task-orchestrator.js.map +1 -1
- package/dist/delivery/task-sync.d.ts +21 -0
- package/dist/delivery/task-sync.d.ts.map +1 -1
- package/dist/delivery/task-sync.js +44 -8
- package/dist/delivery/task-sync.js.map +1 -1
- package/dist/delivery/task-workspace.d.ts +3 -3
- package/dist/delivery/task-workspace.js +3 -3
- package/dist/generators/self-card.d.ts +65 -0
- package/dist/generators/self-card.d.ts.map +1 -0
- package/dist/generators/self-card.js +256 -0
- package/dist/generators/self-card.js.map +1 -0
- package/dist/inference/probe.js +1 -1
- package/dist/inference/probe.js.map +1 -1
- package/dist/memory/prepopulate.d.ts.map +1 -1
- package/dist/memory/prepopulate.js +2 -0
- package/dist/memory/prepopulate.js.map +1 -1
- package/dist/models/analytics.d.ts +32 -0
- package/dist/models/analytics.d.ts.map +1 -1
- package/dist/models/analytics.js +68 -5
- package/dist/models/analytics.js.map +1 -1
- package/dist/models/plan-readability.d.ts +55 -0
- package/dist/models/plan-readability.d.ts.map +1 -0
- package/dist/models/plan-readability.js +339 -0
- package/dist/models/plan-readability.js.map +1 -0
- package/dist/placement/admission.d.ts +59 -0
- package/dist/placement/admission.d.ts.map +1 -0
- package/dist/placement/admission.js +197 -0
- package/dist/placement/admission.js.map +1 -0
- package/dist/placement/auto.d.ts +44 -0
- package/dist/placement/auto.d.ts.map +1 -0
- package/dist/placement/auto.js +212 -0
- package/dist/placement/auto.js.map +1 -0
- package/dist/placement/enforce.d.ts +128 -0
- package/dist/placement/enforce.d.ts.map +1 -0
- package/dist/placement/enforce.js +655 -0
- package/dist/placement/enforce.js.map +1 -0
- package/dist/placement/idle.d.ts +50 -0
- package/dist/placement/idle.d.ts.map +1 -0
- package/dist/placement/idle.js +213 -0
- package/dist/placement/idle.js.map +1 -0
- package/dist/placement/ledger.d.ts +323 -0
- package/dist/placement/ledger.d.ts.map +1 -0
- package/dist/placement/ledger.js +263 -0
- package/dist/placement/ledger.js.map +1 -0
- package/dist/placement/preview.d.ts +51 -0
- package/dist/placement/preview.d.ts.map +1 -0
- package/dist/placement/preview.js +61 -0
- package/dist/placement/preview.js.map +1 -0
- package/dist/placement/probes.d.ts +47 -0
- package/dist/placement/probes.d.ts.map +1 -0
- package/dist/placement/probes.js +207 -0
- package/dist/placement/probes.js.map +1 -0
- package/dist/placement/proxy-env.d.ts +21 -0
- package/dist/placement/proxy-env.d.ts.map +1 -0
- package/dist/placement/proxy-env.js +42 -0
- package/dist/placement/proxy-env.js.map +1 -0
- package/dist/placement/registry.d.ts +1038 -0
- package/dist/placement/registry.d.ts.map +1 -0
- package/dist/placement/registry.js +274 -0
- package/dist/placement/registry.js.map +1 -0
- package/dist/policies/liveness.d.ts +6 -8
- package/dist/policies/liveness.d.ts.map +1 -1
- package/dist/policies/liveness.js +6 -25
- package/dist/policies/liveness.js.map +1 -1
- package/dist/policies/sandbox-anchor.d.ts +91 -0
- package/dist/policies/sandbox-anchor.d.ts.map +1 -0
- package/dist/policies/sandbox-anchor.js +187 -0
- package/dist/policies/sandbox-anchor.js.map +1 -0
- package/dist/policies/sandbox-grants.d.ts +131 -0
- package/dist/policies/sandbox-grants.d.ts.map +1 -0
- package/dist/policies/sandbox-grants.js +562 -0
- package/dist/policies/sandbox-grants.js.map +1 -0
- package/dist/state/manifest.d.ts +4 -2
- package/dist/state/manifest.d.ts.map +1 -1
- package/dist/state/manifest.js +32 -16
- package/dist/state/manifest.js.map +1 -1
- package/dist/utils/checkout-root.d.ts +2 -0
- package/dist/utils/checkout-root.d.ts.map +1 -0
- package/dist/utils/checkout-root.js +43 -0
- package/dist/utils/checkout-root.js.map +1 -0
- package/dist/utils/resolve-uap-bin.d.ts +5 -0
- package/dist/utils/resolve-uap-bin.d.ts.map +1 -0
- package/dist/utils/resolve-uap-bin.js +33 -0
- package/dist/utils/resolve-uap-bin.js.map +1 -0
- package/dist/utils/terminal-safe.d.ts +12 -0
- package/dist/utils/terminal-safe.d.ts.map +1 -0
- package/dist/utils/terminal-safe.js +53 -0
- package/dist/utils/terminal-safe.js.map +1 -0
- package/docs/ATTRIBUTION.md +1 -0
- package/docs/DASHBOARD_UPLIFT_SPEC.md +8 -8
- package/docs/INDEX.md +6 -5
- package/docs/architecture/OVERVIEW.md +15 -3
- package/docs/architecture/PROTOCOL.md +7 -1
- package/docs/architecture/adr/0006-operator-directed-model-placement.md +86 -0
- package/docs/architecture/adr/0007-auto-displacement-allowlist-as-standing-consent.md +79 -0
- package/docs/architecture/adr/0008-fleet-clients-link-out.md +92 -0
- package/docs/architecture/adr/0009-completion-triggered-dispatch-and-per-leaf-model-dials.md +71 -0
- package/docs/architecture/adr/0010-attention-queue-and-afk-auto-decider.md +159 -0
- package/docs/architecture/adr/0011-sandbox-grants-and-network-egress-boundary.md +219 -0
- package/docs/architecture/adr/0012-operator-legibility-surfaces.md +175 -0
- package/docs/getting-started/INSTALLATION.md +1 -1
- package/docs/guides/AUTOMATIC_FEATURES.md +8 -0
- package/docs/guides/LOCAL_MODELS.md +1 -1
- package/docs/guides/PROXY.md +2 -1
- package/docs/guides/SANDBOX.md +91 -4
- package/docs/integrations/MCP_ROUTER.md +1 -1
- package/docs/plans/model-performance-proof-options.md +176 -0
- package/docs/reference/API.md +1 -1
- package/docs/reference/CLI.md +169 -4
- package/docs/reference/COMPARISON.md +1 -1
- package/docs/reference/CONFIGURATION.md +12 -3
- package/docs/reference/CONFIGURATION_REFERENCE.md +269 -14
- package/docs/reference/DATABASE_SCHEMA.md +1 -1
- package/docs/reference/FEATURES.md +15 -3
- package/docs/reference/PATTERNS.md +1 -1
- package/docs/reference/PLATFORMS.md +14 -1
- package/docs/specs/operator-model-placement.md +1314 -0
- package/docs/specs/operator-model-placement.review.md +339 -0
- package/package.json +2 -2
- package/scripts/maintenance/publish-npm.sh +23 -11
- package/scripts/version-bump.sh +6 -51
- package/src/policies/enforcers/__pycache__/_common.cpython-311.pyc +0 -0
- package/src/policies/enforcers/__pycache__/expert_review_required.cpython-311.pyc +0 -0
- package/src/policies/enforcers/__pycache__/quality_metrics_gate.cpython-311.pyc +0 -0
- package/src/policies/schemas/policies/mandatory-testing-deployment.md +2 -5
- package/templates/hooks/__pycache__/deliver_autoroute.cpython-311.pyc +0 -0
- package/templates/hooks/session-start.sh +44 -0
- package/tools/agents/UAP/cli.py +14 -3
- package/tools/agents/UAP/paths.py +50 -0
- package/tools/agents/scripts/__pycache__/toolcall_path_normalizer.cpython-311.pyc +0 -0
- package/tools/agents/scripts/anthropic_proxy.py +1420 -100
- package/tools/agents/scripts/toolcall_path_normalizer.py +46 -1
- package/tools/agents/tests/test_anthropic_proxy_streaming.py +29 -6
- package/tools/agents/tests/test_doubling_break.py +64 -5
- package/tools/agents/tests/test_error_loop_break.py +10 -4
- package/tools/agents/tests/test_error_loop_ignores_correctives.py +13 -2
- package/tools/agents/tests/test_lockstep_escalation.py +280 -9
- package/tools/agents/tests/test_path_containment.py +94 -0
- package/tools/agents/tests/test_proxy_unix_socket.py +162 -0
- package/tools/agents/tests/test_signature_quality.py +400 -0
- package/tools/agents/tests/test_uap_compliance.py +11 -1
- package/web/dash/core.js +1 -0
- package/web/dash/tab-clients.js +196 -0
- package/web/dash/tab-models.js +387 -8
- package/web/dash/tab-policies.js +87 -4
- package/web/dash/tabs.js +77 -73
- package/web/dashboard.html +6 -0
- package/src/policies/enforcers/__pycache__/_common.cpython-312.pyc +0 -0
- package/src/policies/enforcers/__pycache__/expert_review_required.cpython-312.pyc +0 -0
- package/src/policies/enforcers/__pycache__/quality_metrics_gate.cpython-312.pyc +0 -0
- package/templates/hooks/__pycache__/deliver_autoroute.cpython-312.pyc +0 -0
- package/tools/agents/scripts/__pycache__/toolcall_path_normalizer.cpython-312.pyc +0 -0
- package/web/dash/tab-agents.js +0 -21
- package/web/dash/tab-deliver.js +0 -12
- package/web/dash/tab-memory.js +0 -12
- package/web/dash/tab-orchestration.js +0 -21
- package/web/dash/tab-overview.js +0 -21
- package/web/dash/tab-tasks.js +0 -21
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { type ModelRegistry } from './registry.js';
|
|
3
|
+
export declare const LEDGER_VERSION = 1;
|
|
4
|
+
export declare const LedgerDeviceStateSchema: z.ZodObject<{
|
|
5
|
+
kind: z.ZodEnum<["gpu", "cpu"]>;
|
|
6
|
+
total_mib: z.ZodOptional<z.ZodNumber>;
|
|
7
|
+
free_mib: z.ZodOptional<z.ZodNumber>;
|
|
8
|
+
reserved_mib: z.ZodOptional<z.ZodNumber>;
|
|
9
|
+
source: z.ZodOptional<z.ZodString>;
|
|
10
|
+
}, "strip", z.ZodTypeAny, {
|
|
11
|
+
kind: "cpu" | "gpu";
|
|
12
|
+
source?: string | undefined;
|
|
13
|
+
total_mib?: number | undefined;
|
|
14
|
+
reserved_mib?: number | undefined;
|
|
15
|
+
free_mib?: number | undefined;
|
|
16
|
+
}, {
|
|
17
|
+
kind: "cpu" | "gpu";
|
|
18
|
+
source?: string | undefined;
|
|
19
|
+
total_mib?: number | undefined;
|
|
20
|
+
reserved_mib?: number | undefined;
|
|
21
|
+
free_mib?: number | undefined;
|
|
22
|
+
}>;
|
|
23
|
+
export type LedgerDeviceState = z.infer<typeof LedgerDeviceStateSchema>;
|
|
24
|
+
export declare const ResidentStateSchema: z.ZodEnum<["hot", "warming", "paused", "draining"]>;
|
|
25
|
+
export type ResidentState = z.infer<typeof ResidentStateSchema>;
|
|
26
|
+
export declare const LedgerResidentSchema: z.ZodObject<{
|
|
27
|
+
model: z.ZodString;
|
|
28
|
+
config: z.ZodString;
|
|
29
|
+
device: z.ZodString;
|
|
30
|
+
engine: z.ZodOptional<z.ZodString>;
|
|
31
|
+
endpoint: z.ZodOptional<z.ZodString>;
|
|
32
|
+
unit: z.ZodOptional<z.ZodString>;
|
|
33
|
+
state: z.ZodEnum<["hot", "warming", "paused", "draining"]>;
|
|
34
|
+
gpu_mib: z.ZodOptional<z.ZodNumber>;
|
|
35
|
+
host_rss_mib: z.ZodOptional<z.ZodNumber>;
|
|
36
|
+
holders: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
37
|
+
since: z.ZodString;
|
|
38
|
+
}, "strip", z.ZodTypeAny, {
|
|
39
|
+
model: string;
|
|
40
|
+
config: string;
|
|
41
|
+
since: string;
|
|
42
|
+
device: string;
|
|
43
|
+
state: "hot" | "warming" | "paused" | "draining";
|
|
44
|
+
holders: string[];
|
|
45
|
+
endpoint?: string | undefined;
|
|
46
|
+
engine?: string | undefined;
|
|
47
|
+
unit?: string | undefined;
|
|
48
|
+
host_rss_mib?: number | undefined;
|
|
49
|
+
gpu_mib?: number | undefined;
|
|
50
|
+
}, {
|
|
51
|
+
model: string;
|
|
52
|
+
config: string;
|
|
53
|
+
since: string;
|
|
54
|
+
device: string;
|
|
55
|
+
state: "hot" | "warming" | "paused" | "draining";
|
|
56
|
+
endpoint?: string | undefined;
|
|
57
|
+
engine?: string | undefined;
|
|
58
|
+
unit?: string | undefined;
|
|
59
|
+
host_rss_mib?: number | undefined;
|
|
60
|
+
gpu_mib?: number | undefined;
|
|
61
|
+
holders?: string[] | undefined;
|
|
62
|
+
}>;
|
|
63
|
+
export type LedgerResident = z.infer<typeof LedgerResidentSchema>;
|
|
64
|
+
export declare const LedgerPendingSchema: z.ZodObject<{
|
|
65
|
+
id: z.ZodString;
|
|
66
|
+
requested_model: z.ZodString;
|
|
67
|
+
client: z.ZodOptional<z.ZodString>;
|
|
68
|
+
session: z.ZodOptional<z.ZodString>;
|
|
69
|
+
pid: z.ZodOptional<z.ZodNumber>;
|
|
70
|
+
created_at: z.ZodString;
|
|
71
|
+
expires_at: z.ZodString;
|
|
72
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
73
|
+
/** The request's context need in cells (tokens + generation headroom),
|
|
74
|
+
* as estimated by the proxy when it parked the request. Option math is
|
|
75
|
+
* per-request (spec §4.4): cost(config, cells) — without this, the
|
|
76
|
+
* operator's `apply` and the pending list would recompute options
|
|
77
|
+
* against the 32k default instead of what the parked client needs.
|
|
78
|
+
* Absent on entries parked by older proxies → the default applies. */
|
|
79
|
+
cells: z.ZodOptional<z.ZodNumber>;
|
|
80
|
+
}, "strip", z.ZodTypeAny, {
|
|
81
|
+
id: string;
|
|
82
|
+
created_at: string;
|
|
83
|
+
requested_model: string;
|
|
84
|
+
expires_at: string;
|
|
85
|
+
reason?: string | undefined;
|
|
86
|
+
client?: string | undefined;
|
|
87
|
+
session?: string | undefined;
|
|
88
|
+
pid?: number | undefined;
|
|
89
|
+
cells?: number | undefined;
|
|
90
|
+
}, {
|
|
91
|
+
id: string;
|
|
92
|
+
created_at: string;
|
|
93
|
+
requested_model: string;
|
|
94
|
+
expires_at: string;
|
|
95
|
+
reason?: string | undefined;
|
|
96
|
+
client?: string | undefined;
|
|
97
|
+
session?: string | undefined;
|
|
98
|
+
pid?: number | undefined;
|
|
99
|
+
cells?: number | undefined;
|
|
100
|
+
}>;
|
|
101
|
+
export type LedgerPending = z.infer<typeof LedgerPendingSchema>;
|
|
102
|
+
/** Canonical placement TARGET identity for a resident: `device:endpoint`
|
|
103
|
+
* (spec §5.1 — the placement identity, never the model name). One composer,
|
|
104
|
+
* one spelling: the dashboard's admit answer, the proxy's per-target
|
|
105
|
+
* semaphore budget, and (phase 3) coordination leases must key off the
|
|
106
|
+
* identical string, so it is exported rather than re-derived per caller. */
|
|
107
|
+
export declare function residentTargetId(resident: LedgerResident): string;
|
|
108
|
+
export declare const PlacementLedgerSchema: z.ZodObject<{
|
|
109
|
+
version: z.ZodLiteral<1>;
|
|
110
|
+
updated_at: z.ZodOptional<z.ZodString>;
|
|
111
|
+
devices: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
112
|
+
kind: z.ZodEnum<["gpu", "cpu"]>;
|
|
113
|
+
total_mib: z.ZodOptional<z.ZodNumber>;
|
|
114
|
+
free_mib: z.ZodOptional<z.ZodNumber>;
|
|
115
|
+
reserved_mib: z.ZodOptional<z.ZodNumber>;
|
|
116
|
+
source: z.ZodOptional<z.ZodString>;
|
|
117
|
+
}, "strip", z.ZodTypeAny, {
|
|
118
|
+
kind: "cpu" | "gpu";
|
|
119
|
+
source?: string | undefined;
|
|
120
|
+
total_mib?: number | undefined;
|
|
121
|
+
reserved_mib?: number | undefined;
|
|
122
|
+
free_mib?: number | undefined;
|
|
123
|
+
}, {
|
|
124
|
+
kind: "cpu" | "gpu";
|
|
125
|
+
source?: string | undefined;
|
|
126
|
+
total_mib?: number | undefined;
|
|
127
|
+
reserved_mib?: number | undefined;
|
|
128
|
+
free_mib?: number | undefined;
|
|
129
|
+
}>>;
|
|
130
|
+
residents: z.ZodArray<z.ZodObject<{
|
|
131
|
+
model: z.ZodString;
|
|
132
|
+
config: z.ZodString;
|
|
133
|
+
device: z.ZodString;
|
|
134
|
+
engine: z.ZodOptional<z.ZodString>;
|
|
135
|
+
endpoint: z.ZodOptional<z.ZodString>;
|
|
136
|
+
unit: z.ZodOptional<z.ZodString>;
|
|
137
|
+
state: z.ZodEnum<["hot", "warming", "paused", "draining"]>;
|
|
138
|
+
gpu_mib: z.ZodOptional<z.ZodNumber>;
|
|
139
|
+
host_rss_mib: z.ZodOptional<z.ZodNumber>;
|
|
140
|
+
holders: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
141
|
+
since: z.ZodString;
|
|
142
|
+
}, "strip", z.ZodTypeAny, {
|
|
143
|
+
model: string;
|
|
144
|
+
config: string;
|
|
145
|
+
since: string;
|
|
146
|
+
device: string;
|
|
147
|
+
state: "hot" | "warming" | "paused" | "draining";
|
|
148
|
+
holders: string[];
|
|
149
|
+
endpoint?: string | undefined;
|
|
150
|
+
engine?: string | undefined;
|
|
151
|
+
unit?: string | undefined;
|
|
152
|
+
host_rss_mib?: number | undefined;
|
|
153
|
+
gpu_mib?: number | undefined;
|
|
154
|
+
}, {
|
|
155
|
+
model: string;
|
|
156
|
+
config: string;
|
|
157
|
+
since: string;
|
|
158
|
+
device: string;
|
|
159
|
+
state: "hot" | "warming" | "paused" | "draining";
|
|
160
|
+
endpoint?: string | undefined;
|
|
161
|
+
engine?: string | undefined;
|
|
162
|
+
unit?: string | undefined;
|
|
163
|
+
host_rss_mib?: number | undefined;
|
|
164
|
+
gpu_mib?: number | undefined;
|
|
165
|
+
holders?: string[] | undefined;
|
|
166
|
+
}>, "many">;
|
|
167
|
+
pending: z.ZodArray<z.ZodObject<{
|
|
168
|
+
id: z.ZodString;
|
|
169
|
+
requested_model: z.ZodString;
|
|
170
|
+
client: z.ZodOptional<z.ZodString>;
|
|
171
|
+
session: z.ZodOptional<z.ZodString>;
|
|
172
|
+
pid: z.ZodOptional<z.ZodNumber>;
|
|
173
|
+
created_at: z.ZodString;
|
|
174
|
+
expires_at: z.ZodString;
|
|
175
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
176
|
+
/** The request's context need in cells (tokens + generation headroom),
|
|
177
|
+
* as estimated by the proxy when it parked the request. Option math is
|
|
178
|
+
* per-request (spec §4.4): cost(config, cells) — without this, the
|
|
179
|
+
* operator's `apply` and the pending list would recompute options
|
|
180
|
+
* against the 32k default instead of what the parked client needs.
|
|
181
|
+
* Absent on entries parked by older proxies → the default applies. */
|
|
182
|
+
cells: z.ZodOptional<z.ZodNumber>;
|
|
183
|
+
}, "strip", z.ZodTypeAny, {
|
|
184
|
+
id: string;
|
|
185
|
+
created_at: string;
|
|
186
|
+
requested_model: string;
|
|
187
|
+
expires_at: string;
|
|
188
|
+
reason?: string | undefined;
|
|
189
|
+
client?: string | undefined;
|
|
190
|
+
session?: string | undefined;
|
|
191
|
+
pid?: number | undefined;
|
|
192
|
+
cells?: number | undefined;
|
|
193
|
+
}, {
|
|
194
|
+
id: string;
|
|
195
|
+
created_at: string;
|
|
196
|
+
requested_model: string;
|
|
197
|
+
expires_at: string;
|
|
198
|
+
reason?: string | undefined;
|
|
199
|
+
client?: string | undefined;
|
|
200
|
+
session?: string | undefined;
|
|
201
|
+
pid?: number | undefined;
|
|
202
|
+
cells?: number | undefined;
|
|
203
|
+
}>, "many">;
|
|
204
|
+
/** Last-served clock for the idle sweep (§4.4.2): model key → epoch ms
|
|
205
|
+
* of the most recent gated request forwarded to a live resident of
|
|
206
|
+
* that model. Survives syncLedger's wholesale resident rebuild by
|
|
207
|
+
* living at the top level; entries for models with no live resident
|
|
208
|
+
* are harmless residue. Absent on pre-§4.4.2 ledgers → the default. */
|
|
209
|
+
usage: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodNumber>>;
|
|
210
|
+
}, "strip", z.ZodTypeAny, {
|
|
211
|
+
version: 1;
|
|
212
|
+
pending: {
|
|
213
|
+
id: string;
|
|
214
|
+
created_at: string;
|
|
215
|
+
requested_model: string;
|
|
216
|
+
expires_at: string;
|
|
217
|
+
reason?: string | undefined;
|
|
218
|
+
client?: string | undefined;
|
|
219
|
+
session?: string | undefined;
|
|
220
|
+
pid?: number | undefined;
|
|
221
|
+
cells?: number | undefined;
|
|
222
|
+
}[];
|
|
223
|
+
usage: Record<string, number>;
|
|
224
|
+
devices: Record<string, {
|
|
225
|
+
kind: "cpu" | "gpu";
|
|
226
|
+
source?: string | undefined;
|
|
227
|
+
total_mib?: number | undefined;
|
|
228
|
+
reserved_mib?: number | undefined;
|
|
229
|
+
free_mib?: number | undefined;
|
|
230
|
+
}>;
|
|
231
|
+
residents: {
|
|
232
|
+
model: string;
|
|
233
|
+
config: string;
|
|
234
|
+
since: string;
|
|
235
|
+
device: string;
|
|
236
|
+
state: "hot" | "warming" | "paused" | "draining";
|
|
237
|
+
holders: string[];
|
|
238
|
+
endpoint?: string | undefined;
|
|
239
|
+
engine?: string | undefined;
|
|
240
|
+
unit?: string | undefined;
|
|
241
|
+
host_rss_mib?: number | undefined;
|
|
242
|
+
gpu_mib?: number | undefined;
|
|
243
|
+
}[];
|
|
244
|
+
updated_at?: string | undefined;
|
|
245
|
+
}, {
|
|
246
|
+
version: 1;
|
|
247
|
+
pending: {
|
|
248
|
+
id: string;
|
|
249
|
+
created_at: string;
|
|
250
|
+
requested_model: string;
|
|
251
|
+
expires_at: string;
|
|
252
|
+
reason?: string | undefined;
|
|
253
|
+
client?: string | undefined;
|
|
254
|
+
session?: string | undefined;
|
|
255
|
+
pid?: number | undefined;
|
|
256
|
+
cells?: number | undefined;
|
|
257
|
+
}[];
|
|
258
|
+
devices: Record<string, {
|
|
259
|
+
kind: "cpu" | "gpu";
|
|
260
|
+
source?: string | undefined;
|
|
261
|
+
total_mib?: number | undefined;
|
|
262
|
+
reserved_mib?: number | undefined;
|
|
263
|
+
free_mib?: number | undefined;
|
|
264
|
+
}>;
|
|
265
|
+
residents: {
|
|
266
|
+
model: string;
|
|
267
|
+
config: string;
|
|
268
|
+
since: string;
|
|
269
|
+
device: string;
|
|
270
|
+
state: "hot" | "warming" | "paused" | "draining";
|
|
271
|
+
endpoint?: string | undefined;
|
|
272
|
+
engine?: string | undefined;
|
|
273
|
+
unit?: string | undefined;
|
|
274
|
+
host_rss_mib?: number | undefined;
|
|
275
|
+
gpu_mib?: number | undefined;
|
|
276
|
+
holders?: string[] | undefined;
|
|
277
|
+
}[];
|
|
278
|
+
usage?: Record<string, number> | undefined;
|
|
279
|
+
updated_at?: string | undefined;
|
|
280
|
+
}>;
|
|
281
|
+
export type PlacementLedger = z.infer<typeof PlacementLedgerSchema>;
|
|
282
|
+
export declare function placementLedgerPath(): string;
|
|
283
|
+
export declare function emptyLedger(): PlacementLedger;
|
|
284
|
+
export declare function loadLedger(path?: string): PlacementLedger;
|
|
285
|
+
export declare function acquireLedgerLock(path: string, timeoutMs?: number): boolean;
|
|
286
|
+
export declare function releaseLedgerLock(path: string): void;
|
|
287
|
+
/** Mutate the ledger under the advisory lock: re-read, apply, stamp
|
|
288
|
+
* updated_at, write atomically. Throws (fails closed) when the lock cannot
|
|
289
|
+
* be taken — two writers must never interleave. `lockTimeoutMs` bounds the
|
|
290
|
+
* wait (default 5000ms); hot-path callers that only ADVANCE a clock use a
|
|
291
|
+
* short fuse so contention costs them a lost update, never a stalled
|
|
292
|
+
* request (see the admit forward touch, placement-routes.ts). */
|
|
293
|
+
export declare function withLedger(path: string | undefined, mutate: (ledger: PlacementLedger) => void, opts?: {
|
|
294
|
+
lockTimeoutMs?: number;
|
|
295
|
+
}): PlacementLedger;
|
|
296
|
+
/** Live free memory for every registry device, from the probes. gpuN keys map
|
|
297
|
+
* to nvidia-smi index N; cpu keys read MemAvailable. Unprobeable devices
|
|
298
|
+
* carry no free_mib — unknown, never guessed. */
|
|
299
|
+
export declare function probeDeviceStates(registry: ModelRegistry, opts?: {
|
|
300
|
+
gpu?: Map<number, {
|
|
301
|
+
total_mib?: number;
|
|
302
|
+
free_mib?: number;
|
|
303
|
+
}>;
|
|
304
|
+
hostAvailable?: number | null;
|
|
305
|
+
}): Record<string, LedgerDeviceState>;
|
|
306
|
+
/** Derive residents from live unit activity. A registry config whose unit
|
|
307
|
+
* is active IS a resident (fact), regardless of measurement — the ledger
|
|
308
|
+
* records what runs; measurement gates what may be LOADED. Measured
|
|
309
|
+
* residents carry their declared cost so the admission budget sum has real
|
|
310
|
+
* numbers; unmeasured ones carry none and admission fails closed on them.
|
|
311
|
+
* The `active` predicate is injectable for tests. */
|
|
312
|
+
export declare function deriveLiveResidents(registry: ModelRegistry, isActive?: (unit: string) => boolean): LedgerResident[];
|
|
313
|
+
/** Refresh device facts + reconcile residents in one locked write. Returns
|
|
314
|
+
* the resulting ledger. Measured gpu/host figures on reconciled residents
|
|
315
|
+
* come from the registry measurement when present (display), from the live
|
|
316
|
+
* process when possible — Phase 1 keeps registry values and leaves live
|
|
317
|
+
* attribution to `measure`. */
|
|
318
|
+
export declare function syncLedger(registry: ModelRegistry, path?: string, isActive?: (unit: string) => boolean, opts?: {
|
|
319
|
+
gpuFree?: Map<number, number>;
|
|
320
|
+
hostAvailable?: number | null;
|
|
321
|
+
deviceProbe?: typeof probeDeviceStates;
|
|
322
|
+
}): PlacementLedger;
|
|
323
|
+
//# sourceMappingURL=ledger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ledger.d.ts","sourceRoot":"","sources":["../../src/placement/ledger.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AAErE,eAAO,MAAM,cAAc,IAAI,CAAC;AAMhC,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;EAMlC,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE,eAAO,MAAM,mBAAmB,qDAAmD,CAAC;AACpF,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAY/B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,eAAO,MAAM,mBAAmB;;;;;;;;;IAS9B;;;;;0EAKsE;;;;;;;;;;;;;;;;;;;;;;EAEtE,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE;;;;4EAI4E;AAC5E,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAEjE;AAED,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAnBhC;;;;;8EAKsE;;;;;;;;;;;;;;;;;;;;;;;IAoBtE;;;;2EAIuE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAEvE,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAMpE,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED,wBAAgB,WAAW,IAAI,eAAe,CAE7C;AAED,wBAAgB,UAAU,CAAC,IAAI,GAAE,MAA8B,GAAG,eAAe,CAQhF;AAiBD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,SAAO,GAAG,OAAO,CAsBzE;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEpD;AAED;;;;;iEAKiE;AACjE,wBAAgB,UAAU,CACxB,IAAI,EAAE,MAAM,YAAwB,EACpC,MAAM,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,EACzC,IAAI,GAAE;IAAE,aAAa,CAAC,EAAE,MAAM,CAAA;CAAO,GACpC,eAAe,CAajB;AAMD;;iDAEiD;AACjD,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,aAAa,EACvB,IAAI,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GACrG,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CA0BnC;AAMD;;;;;qDAKqD;AACrD,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,aAAa,EACvB,QAAQ,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAqB,GAChD,cAAc,EAAE,CAsBlB;AAED;;;;+BAI+B;AAC/B,wBAAgB,UAAU,CACxB,QAAQ,EAAE,aAAa,EACvB,IAAI,GAAE,MAA8B,EACpC,QAAQ,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAqB,EACjD,IAAI,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,WAAW,CAAC,EAAE,OAAO,iBAAiB,CAAA;CAAE,GAC9G,eAAe,CAyBjB"}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Placement ledger — `~/.uap/placement.json` (spec §4.2).
|
|
3
|
+
*
|
|
4
|
+
* Live state: devices (probe fact), residents (what is loaded, with state),
|
|
5
|
+
* pending placement requests (what is parked awaiting the operator).
|
|
6
|
+
*
|
|
7
|
+
* The ledger records intent; the probe records fact. Reads refresh device
|
|
8
|
+
* free memory from the live probes — never from the last writer's snapshot —
|
|
9
|
+
* and disagreement between the two is surfaced, not averaged.
|
|
10
|
+
*
|
|
11
|
+
* Writers hold an advisory lock (the mkdirSync-atomic pattern from
|
|
12
|
+
* src/cli/proxy-lifecycle.ts) and re-read before committing, so a stale view
|
|
13
|
+
* cannot clobber. Lock acquisition fails CLOSED (throws): a lost ledger write
|
|
14
|
+
* must be loud.
|
|
15
|
+
*/
|
|
16
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from 'fs';
|
|
17
|
+
import { homedir } from 'os';
|
|
18
|
+
import { dirname, join } from 'path';
|
|
19
|
+
import { z } from 'zod';
|
|
20
|
+
import { gpuStatsByIndex, hostAvailableMiB } from './probes.js';
|
|
21
|
+
import { isMeasuredConfig } from './registry.js';
|
|
22
|
+
export const LEDGER_VERSION = 1;
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Schema
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
export const LedgerDeviceStateSchema = z.object({
|
|
27
|
+
kind: z.enum(['gpu', 'cpu']),
|
|
28
|
+
total_mib: z.number().int().positive().optional(),
|
|
29
|
+
free_mib: z.number().int().nonnegative().optional(),
|
|
30
|
+
reserved_mib: z.number().int().nonnegative().optional(),
|
|
31
|
+
source: z.string().optional(),
|
|
32
|
+
});
|
|
33
|
+
export const ResidentStateSchema = z.enum(['hot', 'warming', 'paused', 'draining']);
|
|
34
|
+
export const LedgerResidentSchema = z.object({
|
|
35
|
+
model: z.string(),
|
|
36
|
+
config: z.string(),
|
|
37
|
+
device: z.string(),
|
|
38
|
+
engine: z.string().optional(),
|
|
39
|
+
endpoint: z.string().optional(),
|
|
40
|
+
unit: z.string().optional(),
|
|
41
|
+
state: ResidentStateSchema,
|
|
42
|
+
gpu_mib: z.number().int().positive().optional(),
|
|
43
|
+
host_rss_mib: z.number().int().positive().optional(),
|
|
44
|
+
holders: z.array(z.string()).default([]),
|
|
45
|
+
since: z.string(),
|
|
46
|
+
});
|
|
47
|
+
export const LedgerPendingSchema = z.object({
|
|
48
|
+
id: z.string(),
|
|
49
|
+
requested_model: z.string(),
|
|
50
|
+
client: z.string().optional(),
|
|
51
|
+
session: z.string().optional(),
|
|
52
|
+
pid: z.number().int().positive().optional(),
|
|
53
|
+
created_at: z.string(),
|
|
54
|
+
expires_at: z.string(),
|
|
55
|
+
reason: z.string().optional(),
|
|
56
|
+
/** The request's context need in cells (tokens + generation headroom),
|
|
57
|
+
* as estimated by the proxy when it parked the request. Option math is
|
|
58
|
+
* per-request (spec §4.4): cost(config, cells) — without this, the
|
|
59
|
+
* operator's `apply` and the pending list would recompute options
|
|
60
|
+
* against the 32k default instead of what the parked client needs.
|
|
61
|
+
* Absent on entries parked by older proxies → the default applies. */
|
|
62
|
+
cells: z.number().int().positive().optional(),
|
|
63
|
+
});
|
|
64
|
+
/** Canonical placement TARGET identity for a resident: `device:endpoint`
|
|
65
|
+
* (spec §5.1 — the placement identity, never the model name). One composer,
|
|
66
|
+
* one spelling: the dashboard's admit answer, the proxy's per-target
|
|
67
|
+
* semaphore budget, and (phase 3) coordination leases must key off the
|
|
68
|
+
* identical string, so it is exported rather than re-derived per caller. */
|
|
69
|
+
export function residentTargetId(resident) {
|
|
70
|
+
return `${resident.device}:${resident.endpoint ?? 'default'}`;
|
|
71
|
+
}
|
|
72
|
+
export const PlacementLedgerSchema = z.object({
|
|
73
|
+
version: z.literal(LEDGER_VERSION),
|
|
74
|
+
updated_at: z.string().optional(),
|
|
75
|
+
devices: z.record(z.string(), LedgerDeviceStateSchema),
|
|
76
|
+
residents: z.array(LedgerResidentSchema),
|
|
77
|
+
pending: z.array(LedgerPendingSchema),
|
|
78
|
+
/** Last-served clock for the idle sweep (§4.4.2): model key → epoch ms
|
|
79
|
+
* of the most recent gated request forwarded to a live resident of
|
|
80
|
+
* that model. Survives syncLedger's wholesale resident rebuild by
|
|
81
|
+
* living at the top level; entries for models with no live resident
|
|
82
|
+
* are harmless residue. Absent on pre-§4.4.2 ledgers → the default. */
|
|
83
|
+
usage: z.record(z.string(), z.number()).default({}),
|
|
84
|
+
});
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
// Paths, load, save
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
export function placementLedgerPath() {
|
|
89
|
+
return process.env.UAP_PLACEMENT_LEDGER ?? join(homedir(), '.uap', 'placement.json');
|
|
90
|
+
}
|
|
91
|
+
export function emptyLedger() {
|
|
92
|
+
return { version: LEDGER_VERSION, devices: {}, residents: [], pending: [], usage: {} };
|
|
93
|
+
}
|
|
94
|
+
export function loadLedger(path = placementLedgerPath()) {
|
|
95
|
+
if (!existsSync(path))
|
|
96
|
+
return emptyLedger();
|
|
97
|
+
try {
|
|
98
|
+
const parsed = PlacementLedgerSchema.safeParse(JSON.parse(readFileSync(path, 'utf-8')));
|
|
99
|
+
return parsed.success ? parsed.data : emptyLedger(); // corrupt → empty, fail closed
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return emptyLedger(); // unparseable → empty, never a partial read
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/** Atomic write: tmp file + rename, so a reader never sees a torn document. */
|
|
106
|
+
function saveLedgerAtomically(path, ledger) {
|
|
107
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
108
|
+
const tmp = `${path}.tmp-${process.pid}`;
|
|
109
|
+
writeFileSync(tmp, `${JSON.stringify(ledger, null, 2)}\n`);
|
|
110
|
+
renameSync(tmp, path);
|
|
111
|
+
}
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
// Advisory lock (mkdirSync-atomic, stale-steal — proxy-lifecycle pattern,
|
|
114
|
+
// but fail-CLOSED: a ledger writer that cannot take the lock must not write)
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
const LOCK_STALE_MS = 30_000;
|
|
117
|
+
export function acquireLedgerLock(path, timeoutMs = 5000) {
|
|
118
|
+
const lock = `${path}.lock`;
|
|
119
|
+
mkdirSync(dirname(lock), { recursive: true });
|
|
120
|
+
const deadline = Date.now() + timeoutMs;
|
|
121
|
+
for (;;) {
|
|
122
|
+
try {
|
|
123
|
+
mkdirSync(lock); // atomic: succeeds only if it did not exist
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
try {
|
|
128
|
+
const st = statSync(lock);
|
|
129
|
+
if (Date.now() - st.mtimeMs > LOCK_STALE_MS) {
|
|
130
|
+
rmSync(lock, { recursive: true, force: true }); // steal a crashed holder
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
continue; // vanished between attempts — retry
|
|
136
|
+
}
|
|
137
|
+
if (Date.now() >= deadline)
|
|
138
|
+
return false;
|
|
139
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 100); // sleep 100ms, sync context
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
export function releaseLedgerLock(path) {
|
|
144
|
+
rmSync(`${path}.lock`, { recursive: true, force: true });
|
|
145
|
+
}
|
|
146
|
+
/** Mutate the ledger under the advisory lock: re-read, apply, stamp
|
|
147
|
+
* updated_at, write atomically. Throws (fails closed) when the lock cannot
|
|
148
|
+
* be taken — two writers must never interleave. `lockTimeoutMs` bounds the
|
|
149
|
+
* wait (default 5000ms); hot-path callers that only ADVANCE a clock use a
|
|
150
|
+
* short fuse so contention costs them a lost update, never a stalled
|
|
151
|
+
* request (see the admit forward touch, placement-routes.ts). */
|
|
152
|
+
export function withLedger(path = placementLedgerPath(), mutate, opts = {}) {
|
|
153
|
+
if (!acquireLedgerLock(path, opts.lockTimeoutMs)) {
|
|
154
|
+
throw new Error(`placement ledger lock held longer than expected: ${path}.lock`);
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
const ledger = loadLedger(path);
|
|
158
|
+
mutate(ledger);
|
|
159
|
+
ledger.updated_at = new Date().toISOString();
|
|
160
|
+
saveLedgerAtomically(path, ledger);
|
|
161
|
+
return ledger;
|
|
162
|
+
}
|
|
163
|
+
finally {
|
|
164
|
+
releaseLedgerLock(path);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// ---------------------------------------------------------------------------
|
|
168
|
+
// Device fact: probe live, overlay onto registry shape
|
|
169
|
+
// ---------------------------------------------------------------------------
|
|
170
|
+
/** Live free memory for every registry device, from the probes. gpuN keys map
|
|
171
|
+
* to nvidia-smi index N; cpu keys read MemAvailable. Unprobeable devices
|
|
172
|
+
* carry no free_mib — unknown, never guessed. */
|
|
173
|
+
export function probeDeviceStates(registry, opts) {
|
|
174
|
+
const gpu = opts?.gpu ?? gpuStatsByIndex();
|
|
175
|
+
const hostAvailable = opts?.hostAvailable !== undefined ? opts.hostAvailable : hostAvailableMiB();
|
|
176
|
+
const out = {};
|
|
177
|
+
for (const [key, dev] of Object.entries(registry.devices)) {
|
|
178
|
+
const state = { kind: dev.kind, total_mib: dev.total_mib, reserved_mib: dev.reserved_mib };
|
|
179
|
+
if (dev.kind === 'gpu') {
|
|
180
|
+
const idx = /^gpu(\d+)$/.exec(key);
|
|
181
|
+
const stats = idx ? gpu.get(Number(idx[1])) : undefined;
|
|
182
|
+
if (stats?.free_mib !== undefined) {
|
|
183
|
+
state.free_mib = stats.free_mib;
|
|
184
|
+
state.source = 'nvidia-smi';
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
state.source = 'unprobed';
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
if (typeof hostAvailable === 'number') {
|
|
192
|
+
state.free_mib = hostAvailable;
|
|
193
|
+
state.source = 'MemAvailable';
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
state.source = 'unprobed';
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
out[key] = state;
|
|
200
|
+
}
|
|
201
|
+
return out;
|
|
202
|
+
}
|
|
203
|
+
// ---------------------------------------------------------------------------
|
|
204
|
+
// Reconcile: align ledger residents with what is actually running
|
|
205
|
+
// ---------------------------------------------------------------------------
|
|
206
|
+
/** Derive residents from live unit activity. A registry config whose unit
|
|
207
|
+
* is active IS a resident (fact), regardless of measurement — the ledger
|
|
208
|
+
* records what runs; measurement gates what may be LOADED. Measured
|
|
209
|
+
* residents carry their declared cost so the admission budget sum has real
|
|
210
|
+
* numbers; unmeasured ones carry none and admission fails closed on them.
|
|
211
|
+
* The `active` predicate is injectable for tests. */
|
|
212
|
+
export function deriveLiveResidents(registry, isActive = () => false) {
|
|
213
|
+
const residents = [];
|
|
214
|
+
for (const [mKey, model] of Object.entries(registry.models)) {
|
|
215
|
+
for (const [cKey, cfg] of Object.entries(model.configs)) {
|
|
216
|
+
const unit = cfg.unit ?? model.unit;
|
|
217
|
+
if (!unit || !isActive(unit))
|
|
218
|
+
continue;
|
|
219
|
+
residents.push({
|
|
220
|
+
model: mKey,
|
|
221
|
+
config: cKey,
|
|
222
|
+
device: model.affinity?.device[0] ?? 'gpu0',
|
|
223
|
+
engine: cfg.engine ?? model.engine,
|
|
224
|
+
endpoint: model.endpoint,
|
|
225
|
+
unit,
|
|
226
|
+
state: 'hot',
|
|
227
|
+
gpu_mib: isMeasuredConfig(cfg) ? cfg.resident_gpu_mib : undefined,
|
|
228
|
+
host_rss_mib: isMeasuredConfig(cfg) ? cfg.host_rss_mib : undefined,
|
|
229
|
+
holders: [],
|
|
230
|
+
since: new Date().toISOString(),
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return residents;
|
|
235
|
+
}
|
|
236
|
+
/** Refresh device facts + reconcile residents in one locked write. Returns
|
|
237
|
+
* the resulting ledger. Measured gpu/host figures on reconciled residents
|
|
238
|
+
* come from the registry measurement when present (display), from the live
|
|
239
|
+
* process when possible — Phase 1 keeps registry values and leaves live
|
|
240
|
+
* attribution to `measure`. */
|
|
241
|
+
export function syncLedger(registry, path = placementLedgerPath(), isActive = () => false, opts) {
|
|
242
|
+
const devices = opts?.deviceProbe
|
|
243
|
+
? opts.deviceProbe(registry, opts)
|
|
244
|
+
: probeDeviceStates(registry, opts);
|
|
245
|
+
const residents = deriveLiveResidents(registry, isActive);
|
|
246
|
+
return withLedger(path, (ledger) => {
|
|
247
|
+
ledger.devices = devices;
|
|
248
|
+
// `draining` markers survive the wholesale resident rebuild: an
|
|
249
|
+
// enforcement's drain window can outlast a concurrent `models status`
|
|
250
|
+
// or dashboard auto-refresh, and resetting victims to hot would
|
|
251
|
+
// re-open admission to a unit being stopped (spec §4.6 step 1's
|
|
252
|
+
// "refuse new requests" must hold for the whole sequence).
|
|
253
|
+
const draining = new Set(ledger.residents
|
|
254
|
+
.filter((r) => r.state === 'draining')
|
|
255
|
+
.map((r) => `${r.model}/${r.config}`));
|
|
256
|
+
ledger.residents = residents.map((r) => draining.has(`${r.model}/${r.config}`) ? { ...r, state: 'draining' } : r);
|
|
257
|
+
// Pending entries survive the sync: they are requests awaiting the
|
|
258
|
+
// operator, not backend state. Expired ones are pruned.
|
|
259
|
+
const now = new Date().toISOString();
|
|
260
|
+
ledger.pending = ledger.pending.filter((p) => p.expires_at > now);
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
//# sourceMappingURL=ledger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ledger.js","sourceRoot":"","sources":["../../src/placement/ledger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Impact preview (spec §4.5): the victim list the operator sees BEFORE
|
|
3
|
+
* committing, and the re-derivation check enforcement uses AFTER the yes.
|
|
4
|
+
*
|
|
5
|
+
* Nothing in this view is advisory-only: enforcement re-derives the same
|
|
6
|
+
* victim set and refuses to proceed if it differs from what the operator
|
|
7
|
+
* approved. A preview that lies is worse than no preview.
|
|
8
|
+
*
|
|
9
|
+
* In-flight status is `unknown` in phase 1: the proxy exposes no live
|
|
10
|
+
* request state yet (the phase-2 in-flight-per-target endpoint supplies it).
|
|
11
|
+
* The preview must say unknown rather than guess "idle".
|
|
12
|
+
*/
|
|
13
|
+
import { type ModelRegistry } from './registry.js';
|
|
14
|
+
import type { LedgerResident } from './ledger.js';
|
|
15
|
+
export type InFlight = 'unknown' | boolean;
|
|
16
|
+
export interface VictimPreview {
|
|
17
|
+
model: string;
|
|
18
|
+
config: string;
|
|
19
|
+
device: string;
|
|
20
|
+
state: LedgerResident['state'];
|
|
21
|
+
holders: string[];
|
|
22
|
+
inflight: InFlight;
|
|
23
|
+
/** MiB the eviction returns to the device; unknown → undefined. */
|
|
24
|
+
frees_gpu_mib: number;
|
|
25
|
+
/** MiB the eviction returns to host RAM; unknown → undefined. */
|
|
26
|
+
frees_host_mib?: number;
|
|
27
|
+
/** Measured cost of loading this config back, if the registry knows it. */
|
|
28
|
+
reload_cost_mib?: number;
|
|
29
|
+
/** Human consequence from the spec §4.5 table. */
|
|
30
|
+
consequence: string;
|
|
31
|
+
}
|
|
32
|
+
/** Build the victim preview for a displace option. */
|
|
33
|
+
export declare function buildVictimPreviews(registry: ModelRegistry, victims: LedgerResident[], inflightOf?: (victim: LedgerResident) => InFlight): VictimPreview[];
|
|
34
|
+
/** Stable signature of a victim set — what the operator approved and what
|
|
35
|
+
* enforcement re-derives must produce the same string, or enforcement
|
|
36
|
+
* aborts. Order-insensitive; content-sensitive. */
|
|
37
|
+
export declare function victimSignature(victims: Array<{
|
|
38
|
+
model: string;
|
|
39
|
+
config: string;
|
|
40
|
+
}>): string;
|
|
41
|
+
/** The enforcement-time check: re-derive the victims from live state and
|
|
42
|
+
* refuse when the set the operator approved no longer matches. A mismatch
|
|
43
|
+
* means the world moved between preview and commit — abort, re-ask. */
|
|
44
|
+
export declare function previewStillValid(approved: Array<{
|
|
45
|
+
model: string;
|
|
46
|
+
config: string;
|
|
47
|
+
}>, rederived: Array<{
|
|
48
|
+
model: string;
|
|
49
|
+
config: string;
|
|
50
|
+
}>): boolean;
|
|
51
|
+
//# sourceMappingURL=preview.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../../src/placement/preview.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC;AAE3C,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC;IAC/B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,QAAQ,CAAC;IACnB,mEAAmE;IACnE,aAAa,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,2EAA2E;IAC3E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kDAAkD;IAClD,WAAW,EAAE,MAAM,CAAC;CACrB;AAaD,sDAAsD;AACtD,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,aAAa,EACvB,OAAO,EAAE,cAAc,EAAE,EACzB,UAAU,GAAE,CAAC,MAAM,EAAE,cAAc,KAAK,QAA0B,GACjE,aAAa,EAAE,CAgBjB;AAED;;mDAEmD;AACnD,wBAAgB,eAAe,CAAC,OAAO,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,MAAM,CAKzF;AAED;;uEAEuE;AACvE,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,EAClD,SAAS,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,GAClD,OAAO,CAET"}
|