Repository navigation
Conversation
Register llama.cpp in the shared engine table and give it a facade on the unified proxy at 8080, with the PAIR-managed engine on 8081. Engine Manager installs the official llama app into an owned directory from a checksum-pinned installer or archive recipe (Windows ARM64 tries the current upstream build first and falls back to pinned CUDA archives; confirmed non-NVIDIA ARM and Intel Macs use the pinned CPU recipes), starts and stops it, keeps a private model cache, and exposes download, import, load, unload, delete and download cancellation. Uninstall removes only the runtime slots and keeps models. Routing eligibility for llama.cpp is loaded models only, because the engine runs with --no-models-autoload. The proxy keeps per-facade cancellation isolated and delivers exactly one terminal workload event even when a committed upstream stream is truncated; the classification of that case follows the foundation's existing mid-stream test. The broker advertises the engine, probes manual peers for it, and applies the same engine-settings port path it uses for Ollama and LM Studio, made profile-generic so a third engine does not fall through two-engine branches. llama.cpp declares reviewed launch controls (--port, --host) with serve --no-models-autoload fixed; its owned cache environment is injected on every launch rather than edited, and the settings preview refuses attempts to set it. This is the focused integration replayed onto the public develop foundation from the internal review branch. Runtime updating is not offered for llama.cpp (the desktop bridge refuses update rather than substituting uninstall and reinstall), and the GPU/OS telemetry work is kept as a separate patch candidate. Co-authored-by: Terve <ntervalon@nvidia.com> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: pgoode41 <pgoode41@gmail.com>
validateLlamaPath on Unix resolved the whole path with EvalSymlinks and returned nil as soon as the leaf did not exist, so a symlinked ancestor of a not-yet-created runtime slot, model file or archive entry was never inspected, and a dangling link at the leaf passed. The Windows guard already walks every component up to the volume root and refuses any reparse point regardless of whether the leaf exists. Make the Unix guard do the same with Lstat per component: skip missing components, refuse a symlink anywhere in the chain, return other errors. Existing real paths keep the strictness they had. The fixture covers an ordinary tree, a plain missing leaf, a models slot redirected outside the managed root, missing children beneath it, and a dangling leaf, and checks the outside marker is untouched. It carries the !windows build tag; on this Windows host it was compile-checked with GOOS=linux and GOOS=darwin vet and test builds, not executed. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: pgoode41 <pgoode41@gmail.com>
Register the engine type, display name and links; map the llamacpp-proxy relay source; carry the engine manager's install-support, install-reason and managed facts through the bridge so an external llama.cpp runtime is shown read-only and the install decision follows the manager rather than the displayed OS; keep cached models distinct from loaded ones; and route model import, load, delete and download cancellation through the owning engine manager, locally and on peers. The settings editor develop introduced is reused for llama.cpp and hidden for an external runtime PAIR does not own. The bridge refuses the update command for llama.cpp instead of letting it fall into the generic uninstall-then-install pair; remote update is refused for every engine as before. Workload identity carries the engine and proxy run so two engines' equal request ids stay distinct. The service contract inventory is regenerated for the new methods. Co-authored-by: Terve <ntervalon@nvidia.com> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: pgoode41 <pgoode41@gmail.com>
Describe the managed llama.cpp engine in the product docs: install, lifecycle, model actions, loaded-only routing, launch settings, data retention on uninstall, and the terminal interface keys. The inference dispatcher recognises the engine, the app-data wipe scripts preserve an unmigrated llama model library instead of deleting it, and shell scripts are checked out with LF endings so Windows-built packages ship executable Unix scripts. Co-authored-by: Terve <ntervalon@nvidia.com> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: pgoode41 <pgoode41@gmail.com>
Defer optional desktop/TUI inference-cancel controls and expanded workload browsing while retaining full workload identity and engine/model controls. Restore the public-base live workload table. Consume llama proxy relay events, nested Engine Manager residency events, and LM Studio model keys. Cover the real consumer boundaries with socket-free regressions for all three engines. Signed-off-by: pgoode41 <pgoode41@gmail.com>
Signed-off-by: pgoode41 <pgoode41@gmail.com>
The desktop's service-binary cache keys on a content hash of the Go sources and module files, but nvpair-engine-manager compiles manifests/*.json into the binary (`//go:embed manifests/*.json`). A manifest-only change, such as a pin bump or a launch default, therefore produced a different binary while the cache still reported cli-bin as current, so a developer or packaging run could ship stale engine behaviour without noticing. Hash the embedded manifests with the sources. Verified by building twice (rebuild, then skip), appending one byte to llamacpp.json (rebuild), and restoring it (rebuild back to the earlier fingerprint). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: pgoode41 <pgoode41@gmail.com> (cherry picked from commit 2dae370af14c1662d0f05c6f3ba63b4e030396bd)
Each fix was verified against source and carries a regression test.
- The paired-node settings relay dropped every llama.cpp snapshot, so a
peer's llama editor never received live updates; relay every engine in the
shared table.
- The llama.cpp advertiser polled without the node configuration lock its
siblings hold, so a tick could re-register lc between a settings apply's
withdraw and restart.
- The copied engine:set-port reservation guard was unreachable because the
settings path intercepts the method before the relay; removed with its
parser.
- The registry pinned one vendor build's literals (archive root name, archive
count, platform keys); validate the recipe shape and leave the exact pins to
the tests so a pin bump is a manifest change.
- Install and uninstall discarded the Detect result after promoting or
removing the runtime; both now fail like the generic paths.
- The fake engine gained the vendor's `download` shape so pull_model, preset
rejection and cancel_pull are exercised; public Uninstall for llama.cpp is
covered for runtime-only removal, model retention, saved Off and refusal
while another process serves the port.
- The broker engine-table tests pin llama.cpp's ownership, probe path,
occupied-facade outcome and the third restore gate.
- The node list offered Start/Stop for an observe-only external runtime and
Install where the node reported no recipe; one shared predicate now gates
the engine row, model manager and node list, and an unreachable Cancel
button is gone.
- Documentation drift: manual-probe port, engine default port, removed
workload cancellation, executable count, facade relay list, profile and
advertiser comments. spec.md and MANIFEST.md now name the bundled driver,
builtins, identity probe, archive recipe fields, {model_dir} and dotted
result fields as the fail-closed exception to manifest-only onboarding, and
EngineStatus's install and managed fields.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: pgoode41 <pgoode41@gmail.com>
(cherry picked from commit 9845b82963d827e3abddb3668edf673ab5c770ce)
Managed llama.cpp launched with --no-models-autoload, so a downloaded model was not served until someone loaded it explicitly, and the stack grew a second routing rule to match: the proxy consulted a loaded-only inventory for this one engine, the residency watcher published "unknown" whenever llama missed an observation, the manual-node probe filtered the router's list down to loaded ids, and the shared node record carried a loaded-set accessor used by the proxy alone. Run the vendor default instead: `serve --models-autoload` is now a fixed argument (stated explicitly so a LLAMA_ARG_MODELS_AUTOLOAD in the user's environment cannot flip it). A cached model loads on the first request that names it, which is how Ollama and LM Studio already behave through PAIR, so the per-engine eligibility switch, the loaded-set accessor and both filters go away and every facade routes on the advertised catalog. The loaded-model watcher and `loadedByEngine` stay for the UI's residency dot and its Load/Eject controls. Documentation follows: the explicit-load guidance becomes the on-demand behaviour (the first response waits for the load; the router keeps up to four models resident and evicts idle ones), the stale claim that a llama request can be cancelled from the workload card is dropped because that cancellation was deferred in dd958ba, and the managed engine port is 8081, not 8082. A persisted settings switch for turning on-demand loading off is deliberately not part of this change; it follows separately. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: pgoode41 <pgoode41@gmail.com>
Two sentences still described llama.cpp routing on the loaded set after 931647a made every facade route on the advertised catalog. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: pgoode41 <pgoode41@gmail.com>
Ignore per-engine hydration results when a newer state notification arrived while the request was in flight. This prevents an already-running engine from displaying Start and then stopping when toggled. Cover both running and stopped updates for llama.cpp, Ollama, and LM Studio, plus a later uncontested refresh. Native UI reproduction retained privately; fixed-build UI retest remains pending. Signed-off-by: pgoode41 <pgoode41@gmail.com>
The official Windows installer selects its CUDA build only when the CUDA Toolkit is installed, so NVIDIA PCs with just the driver received the Vulkan build. When every NVIDIA GPU reports compute capability 7.5 or newer, Install now stages the checksum-pinned b10826 CUDA 13.3 app and CUDA runtime archives and requires a CUDA device before promotion. Any other outcome runs the pinned vendor installer as before and records the reason in the install receipt. Signed-off-by: pgoode41 <pgoode41@gmail.com>
Signed-off-by: pgoode41 <pgoode41@gmail.com>
Two hunks conflicted with develop and are resolved here: - services/nvpair-engine-manager/lifecycle.go: the HTTP probe now defers httpcon.DrainAndClose (develop's connection-reuse fix from NVIDIA#37) after the llama.cpp identity check has read the body, instead of closing it unread. - services/nvpair-proxy/engines.go: develop's per-engine base route sets (NVIDIA#27) gain llamaCppBaseRoutes, and the llama.cpp facade profile is built with slices.Concat like Ollama and LM Studio, so it also classifies the Anthropic /v1/messages route. The llama.cpp router at b10826 serves /v1/messages itself, so the facade routes it like the other engines. The route-role test gains llama.cpp cases for the shared inference routes and for the router's own /models passthrough. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: pgoode41 <pgoode41@gmail.com>
Every managed llama.cpp install now records what it actually produced: `pair-install.json` carries `acceleration_policy` and the `llama cli --list-devices` rows as `devices`, and `engine:status` exposes them as `acceleration` and `devices` for a managed runtime, so a Vulkan or CPU landing is visible in the receipt, the desktop engine row and the TUI instead of silent. On the lab fleet the same vendor installer put one GB10 Spark on CUDA and two identical ones on Vulkan, and Jetson Thor on Vulkan, with nothing in the receipt to tell them apart. Linux gets the fail-closed CUDA selection Windows x64 already had. When every NVIDIA GPU reports compute capability 7.5 or newer, Install runs the pinned vendor installer restricted to its CUDA payload (SKIP_VULKAN, SKIP_ROCM), requires a CUDA device before promotion and retries the transfer once; only when CUDA cannot be had does the unrestricted installer run, and the receipt records the reason as `cuda_not_used`. Non-NVIDIA hosts never enter the gate, so Vulkan and CPU keep working as before. Transfers are bounded by lack of progress rather than a fixed 30-minute budget, which failed real installs and pulls on slow lab links (a 680 MB CUDA runtime at 0.3 MB/s needs 38 minutes). The vendor installer's staging tree and the vendor downloader's stderr are the progress signals; a stall of ten minutes, or six hours in total, fails with that reason. Windows ARM64 with NVIDIA hardware installs the pinned CUDA archives directly. Without a CUDA Toolkit the upstream-latest installer could only produce a CPU build, fail the CUDA check and fall back to those same archives after a wasted attempt; that path stays available behind `install.upstream_first`. A stop that the engine, or a model child it spawned, ignores for fifteen seconds is finished with a force-kill of the whole process group. Two llama processes outlived a stop on an Intel Mac during validation. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: pgoode41 <pgoode41@gmail.com>
…d paths
Live validation of the previous commit on an Apple M1 recorded
acceleration_policy "cpu": ggml names its Metal device "MTL0" and its HIP
device "ROCm0", so the alphabetic-prefix classifier never matched them. The
classifier now maps ggml device prefixes to policy names (cuda, rocm, metal,
vulkan, sycl, opencl, cann, musa) and the desktop type comment follows.
Findings from the independent review of that commit:
- The gated install paths returned ctx.Err() after a cancellation, which
turned a stall ("no download progress for 10m0s") into "context canceled".
They return context.Cause(ctx) now; a user Stop still reads as canceled.
- pull_model kept only the first 16 KiB of the vendor's stderr and read it by
lines, but the vendor redraws its progress in place. Bytes read are now the
progress signal and the diagnostic keeps head and tail, so a late "No space
left on device" still classifies.
- Device enumeration on the non-CUDA validation path ran inside the shared
30-second budget and could fail an install when merely slow; it now runs on
its own budget after validation and is recorded, not gated.
- The stage-tree watcher is joined before promotion so no "installing"
heartbeat can follow the terminal progress event.
- A successful retry on the Linux gate records why the first attempt was
rejected (retried_after); on a DGX Spark the first restricted attempt landed
on the vendor's CPU build because install.sh has no CPU skip, and only the
device check caught it. README wording says so.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: pgoode41 <pgoode41@gmail.com>
Live runs of the progress-bounded transfers on a 30 KB/s laptop link found two cases where bytes were flowing but nothing we watched moved, so the watchdog cancelled a live transfer as a stall after ten minutes: - The vendor's `llama download` writes nothing to stderr while it transfers (its progress bar goes to stdout only on a TTY), so a model pull longer than the idle budget always failed. The model cache directory now counts as progress while it grows, alongside stderr bytes. - On Windows the vendor installer downloads through PowerShell, which buffers each payload in memory and writes the file at the end, so the staging tree stayed flat for the whole transfer. The child's own I/O counters (GetProcessIoCounters) now count as progress; Unix tools stream to disk, so the tree watcher already covers them there. Both signals also emit the heartbeat a waiting UI needs. A downloader that fails without any stderr (seen once on an RTX 5070 host) now has its stdout tail classified instead, and a bounded excerpt is logged locally; the user-facing message stays classified. Vendor-installer and pinned-archive receipts record `source` like the CUDA receipts do. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: pgoode41 <pgoode41@gmail.com>
The engine row and node list read "External llama.cpp runtime" while PAIR itself was installing llama.cpp. isExternalRuntime treated every status other than not-installed with managed !== true as an observed external runtime. The engine manager always reports managed as a boolean, so an absent flag is a status PAIR synthesized before any fact arrived (a placeholder, a discovery-only peer, a pending op on a node without facts), and the installing status carries the pre-install managed: false fact. Neither is a detected runtime. Only a settled running or stopped runtime the manager reports as unmanaged is external now. The predicate test covers every process status for undefined, true and false; the state test reproduces the live install, a local installing status with managed false and a peer pending op with no facts, and asserts neither renders as external. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: pgoode41 <pgoode41@gmail.com>
The engine manager wrote every download to the system temp directory. On
2026-09-26 a Windows workstation with 287 MB free on its system drive
failed the pinned 516 MiB CUDA archive set there ("write %TEMP% zip: There
is not enough space on the disk") and fell back to the vendor installer's
Vulkan build. The receipt recorded cuda_not_used correctly, but the
outcome depended on a drive the install never touches while the app-data
drive holding the install stage had room.
The llama.cpp paths (the pinned CUDA and Windows ARM64 archives, the
vendor installer script on every platform, the Linux CUDA-restricted
installer and the opt-in upstream-latest installer) now fetch into a
`.downloads` directory inside their install stage under the managed
install directory, so a transfer only ever depends on the drive that
holds the install. The stage's existing lifecycle applies: it is removed
after a successful promotion and kept for diagnosis after a failure.
Generic engine downloads keep using the system temp directory;
downloadInto takes the target directory and download passes none.
watchTreeGrowth skips that directory. The stage watcher exists for the
vendor installer's silent curl; PAIR's own transfers already report
"downloading N%" and keep the stall context alive, and an "installing"
heartbeat every fifteen seconds would otherwise displace the percent line
for most of a slow transfer. The stall-bounded and 30-minute budgets are
unchanged.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: pgoode41 <pgoode41@gmail.com>
The 2026-09-26 desktop log showed engine:models-changed frames tagged
"llamacpp" that carried Ollama model names, and an Ollama bucket holding
"local_llamacpp" and an LM Studio embedding key. Neither is cross-wiring:
the push is {trigger engine, full multi-engine snapshot} by design, the
broker relays engine:* verbatim, and the desktop reads only the per-engine
loadedByEngine bucket. The odd names came from the engine itself: the
process answering as Ollama on 11434 was a HiveMind gateway whose
/api/tags federates LM Studio models and lists local_llamacpp, and whose
/api/ps repeats rows.
Add regression coverage so a future change cannot turn that appearance
into a real defect:
- engine-manager: sweep two fake engines named ollama and lmstudio that
share one model name while ollama also reports a llama.cpp-looking name.
Only the engine whose loaded set moved is the trigger, each ByEngine and
LoadedByEngine bucket repeats exactly its own engine's inventory, the flat
union carries the shared name once, and the marshalled push keeps the
keys the desktop and TUI parse without leaking names across buckets.
- desktop: replay the log payload through the supervisor's broker dispatch
and assert the Ollama, LM Studio and llama.cpp rows of the self node are
built from their own buckets, that duplicated residency rows collapse,
and that a name two engines serve is marked loaded only under the engine
that reported it resident.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: pgoode41 <pgoode41@gmail.com>
A local client sent about 100 inference requests a second for qwen3.8:27b, a model that only this node's own Ollama advertised. Ollama's queue filled and it answered 503 within milliseconds, but the proxy admitted every request as a queued workload and, per the spec §5.1 retry policy, re-dispatched it into the same full queue five times over fifteen seconds. The desktop log shows 236k queued-state events, 27.6k jobs failed with "upstream returned HTTP 503", and the Jobs view held a wall of queued cards for minutes. The no-owner path was not involved: a model nobody advertises is already refused with a local 502 before it becomes a workload, which the extended test now pins down. A node that answers 429 or 503 now enters a one-second busy hold, renewed on every such answer and cleared the moment it commits a response. A new inference request whose every eligible owner is held is refused at admission with a local 503 and Retry-After, contacts no engine, and emits no workload event. Admitted requests keep their full retry budget, so a single owner that is loading a model is still retried; one free owner disables the gate; control routes are never gated. The transition into a hold is logged once, the per-request refusals at debug, so shedding a flood does not itself flood the log. Tests cover the refusal, the hold lapsing, clear-on-commit, a free owner among busy ones, control routes, and the no-owner 502 emitting no workload. README and spec describe the hold alongside the existing failover and no-owner rules. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: pgoode41 <pgoode41@gmail.com>
…iles
On a Windows lab node with Defender real-time protection, two consecutive
llama.cpp installs failed at the very end: the staged runtime had been
downloaded, validated and receipted, and promoteLlamaRuntime's rename of the
candidate onto `runtime` was denied ("Access is denied"). The third install
succeeded. Reproduced on that node: a directory holding freshly written
executables that were just executed for the validation probes cannot be
renamed for about a second, then can. Nothing else held the files.
The renames in the promotion (current -> previous, candidate -> current, and
the rollback) now retry while the error is one Windows reports for a file
briefly held by another process (access denied, sharing violation, lock
violation), every 250 ms for up to ten seconds. Any other error, and Unix,
return at once. Tests inject a held rename through a package hook and cover
the recovery, a permanent error returned immediately, and the bounded window.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: pgoode41 <pgoode41@gmail.com>
…early A client that closes its socket the instant it has read the terminal frame (`data: [DONE]`, `event: message_stop`, or an NDJSON `"done":true` frame) cancels the request context before the ReverseProxy copy has consumed the upstream EOF, which the llama.cpp router sends a few milliseconds later. That cancel also propagates to the upstream request, so the copy ends with a context error rather than EOF. Both reporters keyed purely on the cancelled context: the disconnect watcher emitted "cancelled" at once and the deferred finalize classified the same condition as cancelled. In a recorded eight-node run, 21 of 120 fully delivered 200 streams were labelled cancelled; a client that read to EOF got 24 of 24 completed. The committed response capture now records when the body is complete: the route's terminal frame has been written (line-anchored for SSE, so model text quoting the marker does not count), Content-Length bytes have been written, or the upstream body reached EOF and its bytes are out. The disconnect watcher does not emit when the body is complete, and finalize treats a cancelled context as "cancelled" only when it is not. A close before the last frame, a failed write to a dead client, and a truncated upstream are classified as before. Tests run a real server and raw TCP clients for the OpenAI, Anthropic and NDJSON dialects in both the late-close and mid-stream-close cases, and the completion record's line anchoring, content length and EOF paths. spec.md section 5.4 gains the outcome row and the reasoning. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: pgoode41 <pgoode41@gmail.com>
7 tasks done
Collaborator
|
POC, so we're going to close the PR. Going to be bringing in llama.cpp with another targeted commit. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Changelog title
Managed llama.cpp engine
Changelog body
Bumps
Summary
Add managed llama.cpp using the existing Engine Manager, unified proxy and
engine-settings UI. No new service process, no parallel settings implementation,
and no llama.cpp-specific routing rule.
download/import/load/unload/delete models; cancel model downloads.
every detected NVIDIA GPU has compute capability 7.5 or newer, and require a
working CUDA device probe before promotion. Otherwise retain vendor selection
and record why CUDA was not used; no local CUDA Toolkit installation is required.
run the pinned installer restricted to its CUDA payload (
SKIP_VULKAN=1 SKIP_ROCM=1), aCUDA0:device is required before promotion and the transferis retried once; otherwise the unrestricted installer runs and the receipt
records
cuda_not_used. Non-NVIDIA hosts are unaffected. Windows ARM64installs the pinned CUDA archives directly; the upstream-latest attempt is
opt-in (
install.upstream_first).pair-install.json)carries
source,acceleration_policyanddevicesfromllama cli --list-devices;engine:statusexposes them asacceleration/devices; thedesktop engine row and the TUI show the backend (CUDA, ROCM, METAL, VULKAN, CPU).
6 hours total) rather than a 30-minute wall clock, so a slow but live link
finishes and a stalled one fails with that reason. Progress is anything
observable: the staging tree or model cache growing, downloader stderr, and
on Windows the child's own I/O counters (PowerShell buffers each download in
memory). llama.cpp downloads land inside the install stage, never on the
system temp drive. Fifteen seconds after an ignored graceful stop, the whole
process group is force-killed. The runtime promotion retries a rename that
Windows denies while Defender still holds the freshly written binaries.
one-second busy hold and a new request whose every eligible owner is held is
refused with a local 503 and
Retry-Afterinstead of being queued into thesaturated engine; a streamed response the client received in full is
reported completed even if the client closes right after the final chunk.
read-only rather than taking ownership of another installation.
serve --models-autoloadas afixed argument, so a request for a downloaded model loads it first (the first
response waits for the load; the router keeps up to four models resident and
evicts idle ones). Routing eligibility follows the advertised catalog, exactly
as it does for Ollama and LM Studio; the loaded-model watcher remains for the
UI's residency indicator and Load/Eject controls.
exactly-once workload terminal delivery, with correct workload attribution.
for live proxy events and observed model residency.
Excluded: a per-engine switch to turn on-demand loading off (with
loaded-only routing), engine updating, GPU/OS telemetry or chart redesign,
expanded workload browsing, explicit inference-workload cancellation
controls/API, and a per-node pin for llama.cpp requests. Those are independent
follow-ups, not prerequisites for this integration.
Non-goals and known limits
Vulkan installation to the CUDA selection path, uninstall and reinstall the
managed runtime; model files are retained.
aarch64/linux/cuda/110payload (Jetson Thor); thegate records the missing build as
cuda_not_usedand the runtime lands onVulkan. Receipts written before this change carry no
acceleration_policyand the status shows nothing for them until reinstall.
llama.cpp capabilities (staged official-app installer, on-disk model cache
operations, router identity probe); they are engine-specific Go accepted only
for the bundled manifest and are recorded in
spec.mdas a current gap.Studio's with two policy differences; sharing it is left for the next
managed engine.
that request while the router finishes loading; retry or load explicitly.
answering once; the proxy's response-header timeout and failover covered it.
That is a vendor-build behaviour, recorded here so it is not mistaken for a
routing fault.
Review First
Start with
services/nvpair-engine-manager/manifests/llamacpp.json(fixedserve --models-autoload, declared--host/--portcontrols), thenservices/nvpair-proxy/proxy.gosubscribedToNode(one eligibility rule forevery facade),
services/nvpair-manual-nodes/manager.goprobeLlamaCpp, andthe settings regressions in
settings_llama_test.goandservices/nvpair-ui-broker/enginesettings_test.go. For the accelerator work:llamaacceleration.go(classifier),llamalinuxcuda.go(Linux gate),stall.go(progress-bounded contexts) andTestLlamaLinuxCUDAGate. For theproxy follow-ups: the busy hold in
proxy.gowithbusy_test.go, and thecompleted-versus-cancelled classification around
emitTerminal.Validation
81624e8and again ona7391b0: desktopchecks, SPDX, release intent, build-script checks on three operating
systems, the full Linux Go component, cross-process and tooling suites, and
six platform installer builds.
26271a0: all 15 Go modules build and vet. Full Engine Manager,broker, TUI, manual-node, cluster-manager, scheduler, workload-manager, errors,
node-settings, shared and EAP suites pass; focused scanner/node-info and the
cross-process external-listener refusal check pass. Network-test limits below.
26271a0: 260 tests pass, 2 existing skips; typecheck, lint,dead-code, service contracts, build-script and SPDX checks pass. Go binaries
and Electron production bundles build.
26271a0: fresh managed CUDA installation passed.Four consecutive full GPU journeys passed after one earlier streaming-reload
timeout. The passes cover cold/warm and streamed inference, unload/reload,
unknown-model rejection, restart persistence, workload attribution and cleanup.
The initial timeout remains recorded; its cause is not established.
f305c3b: actual Load/Unload,Start/Stop, proxy inference, completed-workload display and clean exit passed.
26271a0: fresh managed install, model pull and firstproxied inference passed, followed by warm inference, a completed long stream,
streamed reload after unload, unknown-model rejection, restart persistence and
five locally attributed completed workloads. Owned compute ran on the selected
GPU; memory returned to baseline, both brokers exited normally, test ports and
owned processes closed, and temporary state was removed. Unrelated GPU process
identities were preserved. The earlier interrupted harness run is retained.
81624e8binaries on 13 lab hosts (2026-09-25), one fullinstall/pull/serve/unload/restart/cleanup journey each: Windows ARM64 RTX
Spark N1X (pinned CUDA), Snapdragon (CPU), Windows x64 RTX 3500 Ada, RTX 5090
and RTX 5070 (CUDA), RTX 2070 on driver 566 (CUDA probe failed, recorded
Vulkan fallback), Intel Arc A770+B580, Arc 130V and AMD 840M (Vulkan), Intel
Mac (CPU), three GB10 DGX Sparks (CUDA), Jetson Thor (Vulkan). It also showed
that Linux fell to Vulkan silently under download contention and that receipts
could not tell CUDA from Vulkan; both are addressed by the follow-up commits.
fefad2cthrough the head of this branch): Engine Manager,proxy and broker suites green, cross-vet for linux/arm64, linux/amd64,
darwin/arm64, darwin/amd64 and windows/arm64, desktop 266 tests plus
typecheck/lint/dead-code/service-contract checks, TUI tests, SPDX. Live
re-validation with those binaries: Intel Arc Windows x64 and Arc 130V (Vulkan
receipt and status), Apple M1 and M4 Max (Metal receipt after the classifier
fix), Snapdragon Windows ARM64 (CPU build through the new ARM64 dispatch), RTX
5090 and RTX 5070 over Wi-Fi (pinned CUDA archives finished after 35- and
73-minute downloads that the previous 30-minute budget had cut off), a
two-GPU RTX PRO 6000 workstation (both CUDA devices recorded and used), GB10
DGX Sparks over slow links (restricted CUDA install with one observed retry
after the vendor script landed on its CPU build). A 30 KB/s laptop exposed two
false-stall cases fixed by
a7391b0.this branch installed llama.cpp with the pinned CUDA archives on a two-GPU RTX
PRO 6000 workstation through its own Install control, showed the CUDA pill with
both devices, pulled a model, and paired seven lab nodes into a cluster from the
GUI (RTX 5090, RTX 5070, RTX 3500 Ada, RTX 2070 on the Vulkan fallback, Arc
A770/B580, Arc 130V, Snapdragon). Bursts of 24, 24, 24 and 48 concurrent
768-token streaming requests: 144 of 144 completed at the client, every node
served, four to six nodes running at once with their performance panels open,
each completed job attributed to the node that ran it. The run surfaced the
busy-hold, completed-versus-cancelled and rename-retry follow-ups above.
x64/arm64 and macOS x64/arm64 on
81624e8; these are build results, not nativehardware qualification for every platform.
snapshots from replacing newer running/stopped events; regression checks pass.
81624e8corrects an assertion that assumed Windowsenvironment-name case folding on Unix. Canonical cache overrides remain
rejected on every platform; lowercase aliases are additionally rejected on
Windows.
Remaining Qualification
isolated Linux CI runner. The local Windows gate used the focused network
selection described above; this does not claim full Windows network-suite coverage.
fefad2cis covered by unittests; the ARM64 RTX Spark lab host passed on
81624e8through the fallbackpath and was reserved during the follow-up runs.
host's endpoint agent, which kills the unsigned test binary during download
regardless of commit; the Intel Mac passed on
81624e8earlier the same day.was qualified on the two-GPU Windows workstation instead.
later full runs or a targeted reload probe, but is not explained or erased.
Terve adapted the earlier integration onto the unified proxy. The earlier
llama.cpp work includes the proxy adaptation from #41; #18 and #36 provided
additional design/review context. This does not represent those PRs as merged
unchanged or claim authorship of their underlying shared PAIR infrastructure.
Checklist