/** * Local-model fit ranking for Ollama-backed open-weight models (T11982). * * ## Purpose * * Given the current machine's hardware (RAM, VRAM), rank 2–3 curated * open-weight models that are actually likely to run well. This is the * `cleo llm fit` wizard building block — it powers T11983's one-keypress * pull+connect flow. * * ## Vendor-vs-inspire decision (llmfit) * * Evaluated `github.com/AlexsJones/llmfit` (MIT, Go, ~400 LOC). The library * is a standalone Go binary — not a TS/ESM package. Its key contribution is * the IDEA: use RAM thresholds to gate model selection, prefer VRAM when * available, and produce a ranked list with human-readable reasons. We * **vendor the IDEA** (TS-native re-implementation) rather than shelling out * to a Go binary or adding a cross-language dependency. The model thresholds * below are independently calibrated from Ollama's documentation and the * Hugging Face model cards, not copy-pasted from llmfit. * * ## Gate-13 compliance * * This module: * - MUST NOT construct any transport or SDK client. * - MUST NOT read `process.env.*_API_KEY` directly. * - MUST NOT define a new `resolveLLMFor*` function. * - MUST NOT hardcode model-id literals in logic — all model IDs come from * the {@link LOCAL_MODEL_CANDIDATES} data table. * * The hardware detection (`os.totalmem`, `os.freemem`, `/proc/meminfo`, * `nvidia-smi`) is plain system inspection — not covered by Gate-13 (which * governs LLM resolution / transport construction). * * Ollama liveness re-uses {@link probeOllamaAlive} from * `cross-provider-selector.ts` (no duplication). The `/api/tags` fetch is * a vanilla HTTP call to localhost, not a transport construction. * * @module llm/local-model-fit * @task T11982 * @epic T11671 */ /** * A single entry in the curated open-weight model catalog. * * These values are calibrated from Ollama model pages and Hugging Face cards. * Quantisation notes describe the default GGUF quantisation level pulled by * `ollama pull `. */ export interface LocalModelCandidate { /** Ollama model tag (used verbatim in `ollama pull `). */ readonly modelTag: string; /** Human-readable display name. */ readonly displayName: string; /** Model family (for grouping in the wizard). */ readonly family: 'gemma4' | 'qwen3' | 'llama3.2' | 'qwen2.5-coder' | 'phi4'; /** Minimum RAM required to run (worst-case, CPU inference), in GiB. */ readonly minRamGb: number; /** Recommended RAM for comfortable CPU inference, in GiB. */ readonly recommendedRamGb: number; /** Minimum VRAM required for GPU acceleration, in GiB (0 = CPU-only model). */ readonly minVramGb: number; /** Recommended VRAM for full GPU offload, in GiB (0 = CPU-only model). */ readonly recommendedVramGb: number; /** Approximate model size on disk (for download size estimation), in GiB. */ readonly diskSizeGb: number; /** Default quantisation pulled by `ollama pull` (informational). */ readonly quantNote: string; /** * Whether this model is intended for code tasks (true) or general use (false). * Shown as a tag in the wizard. */ readonly codeSpecialist: boolean; /** * Context window in tokens. * Used to surface context-length as a selection criterion. */ readonly contextLengthK: number; } /** * Curated candidate table. These are the ONLY models `rankLocalModelFit` * can recommend — expand this table to add new models. * * Exclusions: * - `qwen2:0.5b` — proof-of-life only; deliberately absent from this list. * * @task T11982 */ export declare const LOCAL_MODEL_CANDIDATES: ReadonlyArray; /** * VRAM detection result. * * VRAM detection is best-effort and graceful — the field is `null` when no * GPU is detected (nvidia-smi absent, no Apple Silicon heuristic, etc.). */ export interface VramInfo { /** Total VRAM in bytes, or null if detection failed. */ totalBytes: number | null; /** Free VRAM in bytes, or null if detection failed. */ freeBytes: number | null; /** Detection method used. */ method: 'nvidia-smi' | 'rocm-smi' | 'apple-unified' | 'none'; } /** * Full hardware snapshot used by the ranking algorithm. */ export interface HardwareSnapshot { /** Total system RAM in bytes (from `os.totalmem()`). */ totalRamBytes: number; /** * Available RAM in bytes. * Linux: from `/proc/meminfo` `MemAvailable` (more accurate than `os.freemem`). * Other platforms: `os.freemem()`. */ availableRamBytes: number; /** VRAM information (best-effort, never throws). */ vram: VramInfo; /** Detected platform. */ platform: string; } /** * Capture a full hardware snapshot. * * Never throws — VRAM detection failures degrade gracefully to `null`. * * @param overrides - Override fields for testing (avoids OS calls in unit tests). * @returns A hardware snapshot describing this machine's capabilities. * * @task T11982 */ export declare function captureHardwareSnapshot(overrides?: { totalRamBytes?: number; availableRamBytes?: number; vram?: VramInfo; platform?: string; }): Promise; /** * A model already pulled and available locally in Ollama. */ export interface OllamaPulledModel { /** Tag as returned by `/api/tags`. */ name: string; /** Parameter size string (e.g. `"3.1B"`). */ parameterSize: string; /** Quantisation level (e.g. `"Q4_K_M"`). */ quantizationLevel: string; /** Model family (e.g. `"qwen2"`). */ family: string; } /** * Fetch the list of locally-pulled models from Ollama's `/api/tags` endpoint. * * Uses the HTTP API (not shelling out) — Gate-13 does not govern plain `fetch` * calls to localhost. * * @param baseUrl - Ollama base URL (default `http://localhost:11434`). * @param fetchFn - Injectable `fetch` for testing (defaults to global `fetch`). * @returns Array of pulled models, or empty array if Ollama is not reachable. * * @task T11982 */ export declare function listOllamaPulledModels(baseUrl?: string, fetchFn?: typeof fetch): Promise; /** * Hard RAM floor below which no local model recommendation is made. * Machines under this threshold are steered toward cloud providers instead. */ export declare const LOCAL_FIT_FLOOR_GB = 4; /** * A ranked local model fit result. */ export interface LocalModelFitResult { /** The candidate model. */ candidate: LocalModelCandidate; /** * Overall fit score (higher is better). * Used to rank candidates — not surfaced directly in the wizard UI. */ score: number; /** * Whether this model is already pulled in the local Ollama installation. * Models that are already pulled get a score boost (prefer not re-downloading). */ alreadyPulled: boolean; /** * Human-readable fit reasons (bullet list for wizard display). * Examples: "fits in 10GB VRAM", "already pulled", "recommended for 8GB RAM". */ reasons: string[]; /** * Fit tier: * - `'excellent'` — hardware is well above requirements; full GPU offload expected. * - `'good'` — hardware meets recommended threshold; runs well. * - `'marginal'` — hardware meets minimum; expect slower inference. */ fitTier: 'excellent' | 'good' | 'marginal'; /** * The `ollama pull ` string for this model. */ pullCommand: string; } /** * Output envelope returned by {@link rankLocalModelFit}. */ export interface LocalModelFitEnvelope { /** Hardware snapshot used for ranking. */ hardware: { totalRamGb: number; availableRamGb: number; vramTotalGb: number | null; vramFreeGb: number | null; vramMethod: VramInfo['method']; }; /** Whether Ollama was detected as running. */ ollamaRunning: boolean; /** Models already available in the local Ollama installation. */ pulledModels: OllamaPulledModel[]; /** * Ranked 2–3 best-fit candidates. * Empty when hardware is below the 4 GB floor. */ recommendations: LocalModelFitResult[]; /** * Human-readable explanation when no recommendations are available. * null when recommendations is non-empty. */ noRecommendationReason: string | null; } /** * Rank the 2–3 best-fit local open-weight models for this machine. * * ## Floor * * Machines with total RAM below {@link LOCAL_FIT_FLOOR_GB} (4 GB) receive no * recommendations. This deliberately excludes `qwen2:0.5b` (proof-of-life * only) — it is absent from {@link LOCAL_MODEL_CANDIDATES}. * * ## Algorithm * * 1. Capture hardware snapshot. * 2. Probe Ollama liveness and fetch the pulled-models list. * 3. Filter candidates to those whose `minRamGb` ≤ total RAM. * 4. Score remaining candidates (VRAM fit + RAM comfort + already-pulled bonus). * 5. Return the top 3 (or fewer), ordered by score descending. * * @param opts - Override hardware snapshot for testing. * @returns Fit envelope with ranked candidates. * * @task T11982 */ export declare function rankLocalModelFit(opts?: { /** Override the hardware snapshot (for testing). */ hardwareOverride?: Parameters[0]; /** Override Ollama base URL. */ ollamaBaseUrl?: string; /** Injectable fetch for testing. */ fetchFn?: typeof fetch; /** Override pulled models list (for testing). */ pulledModelsOverride?: OllamaPulledModel[]; }): Promise; //# sourceMappingURL=local-model-fit.d.ts.map