286 lines · 11.1 KB · TypeScript
RawDownload
/** * The compute layer's contracts. This file has no runtime imports and no I/O — it is * the only module every other compute file may depend on. * * Three rules carry the design: *   1. Engines are pure. `plan()` is a total function of its request; no clock, no env, *      no filesystem. That is what makes the whole launch path golden-testable. *   2. The instance record is the lease. A device is held iff a live record claims it, *      so there is no registry to fall out of sync with reality. *   3. Status is derived, never stored. `stateOf` reads liveness, then health, then the *      deadline — there is no status field to go stale. */ /** Superset of the shared `EngineBackend`, which does not yet carry exllamav3. The two *  converge when exllamav3 can actually launch; until then `toEngineBackend` narrows. */export type EngineId = "vllm" | "sglang" | "llamacpp" | "mlx" | "exllamav3"; export const ENGINE_IDS: readonly EngineId[] = [  "vllm",  "sglang",  "llamacpp",  "mlx",  "exllamav3",] as const; export type Accelerator = "cuda" | "rocm" | "metal" | "xpu" | "cpu";/** Where an engine runs. Distinct from the installer-facing `RuntimeKind` in *  controller/contracts/system.ts ("venv" | "docker" | "binary" | "system"), *  which describes how a runtime was installed, not how a job is launched. */export type EngineRuntimeKind = "process" | "docker";export type HostPlatform = "linux" | "darwin" | "win32";export type HostArch = "x64" | "arm64"; /** Node id. `"self"` is always this host; peers use their registered id. */export type NodeId = string; /** Stable across reboots: an NVIDIA/AMD GPU UUID, or a synthetic id for accelerators *  that have none (`"metal:0"`, `"cpu:0"`). Never a bare index — indices renumber. */export type DeviceId = string; export interface HostProfile {  readonly nodeId: NodeId;  readonly platform: HostPlatform;  readonly arch: HostArch;  readonly accelerator: Accelerator;  /** Apple Silicon and DGX Spark (GB10): VRAM *is* RAM. Never budget them separately. */  readonly unifiedMemory: boolean;  readonly wsl: boolean;  readonly docker: boolean;  /** Docker is present *and* GPU passthrough works. False on macOS, always. */  readonly dockerGpu: boolean;  readonly deviceCount: number;} /* ── engines ─────────────────────────────────────────────────────────────── */ export type EngineSupport =  | { readonly ok: true; readonly runtimes: readonly EngineRuntimeKind[] }  | { readonly ok: false; readonly reason: string }; export interface HealthCheck {  readonly path: string;  /** Cold start budget. vLLM compiles graphs; llama.cpp just mmaps. */  readonly readyDeadlineMs: number;  readonly intervalMs: number;} /** Canonical metric name -> the Prometheus names that may carry it, in priority order. *  Engines disagree on spelling (`vllm:` vs `llamacpp:`), so the reader takes the first *  present rather than branching per engine. */export type MetricMap = Readonly<Record<CanonicalMetric, readonly string[]>>; export type CanonicalMetric =  | "requestsRunning"  | "requestsWaiting"  | "kvCacheUtilization"  | "promptTokensTotal"  | "generationTokensTotal"; export interface PortBinding {  readonly container: number;  readonly host: number;} export interface Mount {  readonly from: string;  readonly to: string;  readonly readOnly: boolean;} /** * The universal currency between an engine and a launcher. An engine says *what* to run; * a launcher decides *how*. Note `devices` is abstract — the engine never writes * CUDA_VISIBLE_DEVICES or `--gpus`; `applyDevices` does, once, for every engine. */export interface LaunchPlan {  readonly kind: EngineRuntimeKind;  /** process: [binary, ...args]. docker: the container's entrypoint args. */  readonly argv: readonly string[];  readonly image?: string;  readonly env: Readonly<Record<string, string>>;  readonly ports: readonly PortBinding[];  readonly mounts: readonly Mount[];  readonly devices: readonly DeviceId[];  readonly workdir?: string;  readonly health: HealthCheck;} /** * The tuning knobs a recipe exposes, named once and canonically. Each engine declares how * it spells them (`Spelling`); an engine with no equivalent for a knob drops it rather * than guessing. Adding a knob is a one-line change per engine instead of an edit to two * near-identical 40-line argument builders. */export interface ServingOptions {  readonly tensorParallel: number;  readonly pipelineParallel: number;  readonly maxContextLength: number;  readonly memoryFraction: number;  readonly maxConcurrentRequests: number;  /** "auto" is treated as unset — every engine defaults it the same way. */  readonly kvCacheDtype: string | null;  readonly dtype: string | null;  readonly quantization: string | null;  readonly trustRemoteCode: boolean;  readonly toolCallParser: string | null;  readonly reasoningParser: string | null;} export interface LaunchRequest {  readonly engine: EngineId;  readonly host: HostProfile;  readonly runtime: EngineRuntimeKind;  readonly devices: readonly DeviceId[];  readonly port: number;  /** Absolute path to the model directory, or the .gguf file for llama.cpp. */  readonly modelPath: string;  readonly servedModelName: string;  readonly options: ServingOptions;  /** Verbatim recipe flags, appended last; any base flag they repeat is dropped. */  readonly extraArgs: readonly string[];  readonly env: Readonly<Record<string, string>>;  readonly dockerImage: string | null;  /** Resolved executable for process launches; ignored for docker. */  readonly binary: string;} /** An engine as the compute layer sees it: what it supports, how to plan a *  launch, how to check it. Named apart from the legacy engines module's *  EngineSpec (install/probe/runtime-info), which it will eventually replace. */export interface ComputeEngineSpec {  readonly id: EngineId;  readonly supports: (host: HostProfile) => EngineSupport;  readonly plan: (request: LaunchRequest) => LaunchPlan;  readonly health: HealthCheck;  readonly metrics: MetricMap;  readonly image?: (host: HostProfile) => string | null;  /** Default executable name when the recipe does not pin one. */  readonly defaultBinary: string;  readonly defaultPort: number;} /* ── instances ───────────────────────────────────────────────────────────── */ export type HandleReference =  | {      readonly kind: "process";      readonly pid: number;      /** Linux /proc/<pid>/stat field 22. Closes pid reuse across reboots. */      readonly startToken: string | null;    }  | { readonly kind: "docker"; readonly container: string }  | { readonly kind: "remote"; readonly nodeId: NodeId; readonly name: string }  /** A device hold with no supervised process — e.g. the speech worker claims its GPU   *  through the lease shim. Always "alive"; freed only by explicit release. */  | { readonly kind: "pinned"; readonly holder: string }; /** * The single source of truth for a running deployment, persisted as one JSON file per * instance (write-then-rename). `devices` **is** the GPU lease: capacity is derived by * unioning the devices of every record whose handle is still alive. */export interface InstanceRecord {  readonly name: string;  readonly nodeId: NodeId;  readonly engine: EngineId;  readonly recipeId: string;  readonly runtime: EngineRuntimeKind;  /** null while the record is a reservation and nothing has spawned yet. */  readonly ref: HandleReference | null;  readonly port: number;  readonly devices: readonly DeviceId[];  /** Proves we started it; a launcher refuses to signal a handle whose nonce differs. */  readonly nonce: string;  readonly startedAt: string;  readonly readyDeadlineAt: string;} export type InstanceState = "reserving" | "starting" | "ready" | "unhealthy" | "exited"; /* ── devices ─────────────────────────────────────────────────────────────── */ export type TelemetryField =  | "memory"  | "utilization"  | "temperature"  | "power"  | "storage"  | "hostMemory"; export type DeviceVendor = "nvidia" | "amd" | "apple" | "intel" | "unknown"; export interface AcceleratorInfo {  readonly id: DeviceId;  readonly index: number;  readonly vendor: DeviceVendor;  readonly name: string;  readonly accelerator: Accelerator;  readonly memoryTotalBytes: number;  readonly memoryUsedBytes: number;  readonly unifiedMemory: boolean;  /** null means "this platform cannot report it", not "zero". */  readonly utilizationPct: number | null;  readonly temperatureC: number | null;  readonly powerWatts: number | null;  readonly powerLimitWatts: number | null;  readonly driver: string | null;} export interface HostInfo {  readonly cpuModel: string;  readonly cpuCount: number;  readonly memoryTotalBytes: number;  readonly memoryAvailableBytes: number;  readonly swapTotalBytes: number | null;  readonly platform: HostPlatform;  readonly arch: HostArch;  readonly release: string;  readonly uptimeSeconds: number;} export interface VolumeInfo {  readonly mount: string;  readonly totalBytes: number;  readonly freeBytes: number;  readonly filesystem: string | null;  readonly model: string | null;  readonly rotational: boolean | null;} export interface ThermalInfo {  readonly label: string;  readonly celsius: number;  readonly source: "gpu" | "cpu" | "chassis";} export interface DeviceSnapshot {  readonly sampledAt: string;  readonly accelerators: readonly AcceleratorInfo[];  readonly host: HostInfo;  readonly storage: readonly VolumeInfo[];  readonly thermals: readonly ThermalInfo[];  /** What the probes on this host can actually answer. A field absent here renders as   *  "unsupported" rather than a plausible zero. */  readonly capabilities: readonly TelemetryField[];} /* ── failure ─────────────────────────────────────────────────────────────── */ /** One union replaces twelve ad-hoc failure channels. Only `lifecycle` produces these, *  and only `toHttp`/`toEvent` render them — so an HTTP status can never again be *  chosen by substring-matching an error message. */export type LaunchFailure =  | { readonly kind: "unsupported"; readonly engine: EngineId; readonly reason: string }  | { readonly kind: "already-running"; readonly name: string }  | { readonly kind: "no-capacity"; readonly need: number; readonly free: number }  | { readonly kind: "install-failed"; readonly engine: EngineId; readonly detail: string }  | { readonly kind: "spawn-failed"; readonly detail: string }  | {      readonly kind: "exited-early";      readonly exitCode: number | null;      readonly signal: string | null;      readonly logTail: string;    }  | { readonly kind: "unhealthy-timeout"; readonly waitedMs: number; readonly logTail: string }  | { readonly kind: "cancelled" };