From 6d10c57399174e13022b46b8485563c0a2817a5f Mon Sep 17 00:00:00 2001 From: Prekshi Vyas Date: Tue, 25 Aug 2026 15:35:26 -0700 Subject: [PATCH 1/4] fix(onboard): clarify existing vllm GPU conflicts Signed-off-by: Prekshi Vyas --- docs/inference/set-up-vllm.mdx | 45 ++++++++++++++++++- .../setup-nim-flow-vllm-gpu-device.test.ts | 42 +++++++++++++++-- src/lib/onboard/setup-nim-flow.test.ts | 1 + src/lib/onboard/setup-nim-flow.ts | 23 +++++++--- src/lib/onboard/vllm-menu.test.ts | 19 ++++++++ src/lib/onboard/vllm-menu.ts | 12 +++-- 6 files changed, 128 insertions(+), 14 deletions(-) diff --git a/docs/inference/set-up-vllm.mdx b/docs/inference/set-up-vllm.mdx index 4abb1ccbce7..080d3ccb930 100644 --- a/docs/inference/set-up-vllm.mdx +++ b/docs/inference/set-up-vllm.mdx @@ -53,7 +53,7 @@ When you request a vLLM serving profile, the detected server must report the pro NemoClaw stops onboarding when neither identifier matches, rather than recording a route that the profile does not declare. Stop the server and rerun the original command, or unset `NEMOCLAW_SERVING_PRESET` and `NEMOCLAW_PROVIDER` before you start fresh onboarding to keep the detected model. -On hosts other than N1x, when an install request encounters an already-running server, NemoClaw selects that server instead of starting another container. +On hosts other than N1x, when an install request without a managed GPU selection encounters an already-running server, NemoClaw selects that server instead of starting another container. On N1x, explicit managed-preview intent never reuses an already-running server. Stop the server, then rerun managed onboarding. @@ -146,6 +146,47 @@ Only managed single-host vLLM installation accepts this selector. $$nemoclaw onboard --profile --vllm-gpu-device ``` +#### Handle a Running vLLM Server + +The single-host managed vLLM runtime is host-global. +Destroying a sandbox does not stop it, and the container remains available for other sandboxes. +During later onboarding, NemoClaw normally reuses a server that is already listening on the configured vLLM port instead of replacing it automatically. +When a managed GPU selection accompanies explicit managed-install intent, NemoClaw preserves that intent and reports the running-server conflict. + +Choose one action: + +- To reuse the running server, omit `--vllm-gpu-device` and select **Local vLLM**. +- To change the GPU or port, inspect the sandbox registry for every gateway-port environment on the host. + Run `NEMOCLAW_GATEWAY_PORT= $$nemoclaw list` for port `8080` and for every non-default port represented by a numeric directory under `~/.nemoclaw/gateways/`. + Treat every sandbox that reports provider `vllm-local` as a possible consumer because the list output does not distinguish local vLLM from Local NIM. + Continue only when no environment reports that provider; otherwise, keep the server running or move each possible consumer to another inference provider. + Stop an operator-managed server through the system service, container, or process workflow that started it. + For a NemoClaw-managed container, inspect its ID and labels before stopping it: + + ```bash + docker container inspect \ + --format '{{.Id}} {{json .Config.Labels}}' \ + nemoclaw-vllm + ``` + + Continue only when the output includes `"com.nvidia.nemoclaw.managed-vllm":"true"` and does not include `com.nvidia.nemoclaw.vllm-role`, `com.nvidia.nemoclaw.vllm-endpoint`, or `com.nvidia.nemoclaw.vllm-cluster`. + Do not stop the container when any distributed label is present; follow the matching multi-node vLLM guide instead. + Stop that exact container ID: + + ```bash + docker container stop + ``` + + Set `NEMOCLAW_VLLM_PORT` before onboarding if you are changing the port. + Rerun managed onboarding: + + ```bash + $$nemoclaw onboard --profile --vllm-gpu-device + ``` + +Accept the result when onboarding completes and `$$nemoclaw list` reports the new sandbox with provider `vllm-local` and the expected model. +NemoClaw does not stop or recreate a running vLLM server automatically because another sandbox can use it. + NemoClaw requires a resumed session to reuse the recorded GPU selector. A legacy in-progress session cannot add one, and a resumed session cannot change one. Run `$$nemoclaw onboard --fresh` when you need a different GPU selection. @@ -166,7 +207,7 @@ The container carries NemoClaw ownership and immutable catalog labels so later o NemoClaw also records an owner-only, credential-free runtime receipt that binds those labels to the exact container ID and API-key fingerprint. If an existing `nemoclaw-vllm` container does not carry that label, NemoClaw preserves it and asks you to remove or rename it before installing managed vLLM. -On hosts other than N1x, if another vLLM server already occupies the configured vLLM port, NemoClaw selects the existing-server path and applies the model-identity checks described above. +On hosts other than N1x, if another vLLM server already occupies the configured vLLM port and no managed GPU selection accompanies explicit managed-install intent, NemoClaw selects the existing-server path and applies the model-identity checks described above. On N1x, NemoClaw stops managed onboarding and requires you to stop that server before trying the Deferred preview again. If another process holds the configured single-host port, NemoClaw stops the managed install before it persists a bearer key or records the selected model. NemoClaw also stops before storage prompts, cache creation, image pull, or container start. diff --git a/src/lib/onboard/setup-nim-flow-vllm-gpu-device.test.ts b/src/lib/onboard/setup-nim-flow-vllm-gpu-device.test.ts index 6dc04dcd852..ae7d5ccbec7 100644 --- a/src/lib/onboard/setup-nim-flow-vllm-gpu-device.test.ts +++ b/src/lib/onboard/setup-nim-flow-vllm-gpu-device.test.ts @@ -3,6 +3,7 @@ import { afterEach, describe, expect, it, vi } from "vitest"; +import type { VllmProfile } from "../inference/vllm"; import { NEMOCLAW_VLLM_GPU_DEVICE_ENV } from "../inference/vllm-models"; import { makeDeps, makeHostState } from "./__test-helpers__/setup-nim-flow"; import type { SetupNimFlowDeps } from "./setup-nim-flow"; @@ -44,6 +45,7 @@ describe("managed vLLM GPU provider selection", () => { makeDeps({ isNonInteractive: () => true, getNonInteractiveProvider: () => "vllm", + vllmPort: 18000, detectInferenceProviderHostState: () => makeHostState({ vllmRunning: true, @@ -54,15 +56,49 @@ describe("managed vLLM GPU provider selection", () => { }), ); - await expect(setupNim(null)).rejects.toThrow( - "applies only when NemoClaw installs managed vLLM", + await expect(setupNim(null)).rejects.toThrow("vLLM is already running on localhost:18000"); + expect(abortNonInteractive).toHaveBeenCalledWith( + expect.stringContaining("Omit --vllm-gpu-device to reuse that server"), ); expect(abortNonInteractive).toHaveBeenCalledWith( - expect.stringContaining("selected provider is 'vllm'"), + expect.stringContaining("stop the existing server"), ); expect(handleVllmSelection).not.toHaveBeenCalled(); }); + it("preserves managed intent and explains how to resolve a running-server conflict", async () => { + vi.stubEnv(NEMOCLAW_VLLM_GPU_DEVICE_ENV, "2"); + const abortNonInteractive = vi.fn((message: string): never => { + throw new Error(message); + }); + const installVllm = vi.fn(); + const profile = { name: "DGX Station" } as VllmProfile; + const setupNim = createSetupNim( + makeDeps({ + isNonInteractive: () => true, + getNonInteractiveProvider: () => "install-vllm", + vllmPort: 18000, + detectInferenceProviderHostState: () => + makeHostState({ + vllmRunning: true, + vllmProfile: profile, + vllmEntries: [{ key: "install-vllm", label: "Start vLLM (DGX Station)" }], + }), + abortNonInteractive, + installVllm, + }), + ); + + await expect(setupNim(null)).rejects.toThrow("vLLM is already running on localhost:18000"); + expect(abortNonInteractive).toHaveBeenCalledWith( + expect.stringContaining("Omit --vllm-gpu-device and select Local vLLM"), + ); + expect(abortNonInteractive).toHaveBeenCalledWith( + expect.stringContaining("stop the existing server"), + ); + expect(installVllm).not.toHaveBeenCalled(); + }); + it("allows the persisted device when resuming its managed vLLM provider", async () => { vi.stubEnv(NEMOCLAW_VLLM_GPU_DEVICE_ENV, "2"); const handleVllmSelection = vi.fn(async (state) => { diff --git a/src/lib/onboard/setup-nim-flow.test.ts b/src/lib/onboard/setup-nim-flow.test.ts index fdf37d16204..27b6cd44c9e 100644 --- a/src/lib/onboard/setup-nim-flow.test.ts +++ b/src/lib/onboard/setup-nim-flow.test.ts @@ -1404,6 +1404,7 @@ describe("createSetupNim", () => { expect(prompt).toHaveBeenCalledTimes(2); expect(selectFromNumberedMenu).toHaveBeenCalledTimes(2); expect(error).toHaveBeenCalledWith(expect.stringContaining("stop the existing server")); + expect(error).toHaveBeenCalledWith(expect.stringContaining("change its GPU or port")); expect(installVllm).not.toHaveBeenCalled(); expect(handleVllmSelection).not.toHaveBeenCalled(); expect(handleRemoteProviderSelection).toHaveBeenCalledOnce(); diff --git a/src/lib/onboard/setup-nim-flow.ts b/src/lib/onboard/setup-nim-flow.ts index 2fadd1ef6de..ccf604ec41f 100644 --- a/src/lib/onboard/setup-nim-flow.ts +++ b/src/lib/onboard/setup-nim-flow.ts @@ -286,7 +286,7 @@ function assertVllmGpuProviderSelection( recoveredFromSandbox: boolean, deps: Pick< SetupNimFlowDeps, - "abortNonInteractive" | "error" | "exitProcess" | "isNonInteractive" + "abortNonInteractive" | "error" | "exitProcess" | "isNonInteractive" | "vllmPort" >, ): void { const requestedDevice = String(process.env.NEMOCLAW_VLLM_GPU_DEVICE ?? "").trim(); @@ -294,8 +294,11 @@ function assertVllmGpuProviderSelection( if (!requestedDevice || selected.key === "install-vllm" || resumedManagedVllm) return; const message = - `--vllm-gpu-device applies only when NemoClaw installs managed vLLM; ` + - `the selected provider is '${selected.key}'.`; + selected.key === "vllm" + ? `vLLM is already running on localhost:${deps.vllmPort}, so --vllm-gpu-device cannot change its GPU. ` + + `Omit --vllm-gpu-device to reuse that server. To select a different GPU, stop the existing server, then rerun managed onboarding.` + : `--vllm-gpu-device applies only when NemoClaw installs managed vLLM; ` + + `the selected provider is '${selected.key}'.`; deps.error(` ${message}`); if (deps.isNonInteractive()) deps.abortNonInteractive(message); deps.exitProcess(1); @@ -583,11 +586,15 @@ async function handleEndpointProviderSelection(input: { function vllmPortConflictMessage( platform: InferenceProviderHostGpu["platform"], port: number, + hasGpuSelection: boolean, ): string { if (platform === "n1x") { return `The N1x Deferred preview requires managed vLLM, but vLLM is already running on localhost:${port}. Stop the existing server, then rerun with NEMOCLAW_PROVIDER=install-vllm.`; } - return "vLLM is already running on this host. Select Local vLLM, or stop the existing server before selecting the managed install path."; + const reuseAction = hasGpuSelection + ? "Omit --vllm-gpu-device and select Local vLLM to reuse it." + : "Select Local vLLM to reuse it."; + return `vLLM is already running on localhost:${port}. ${reuseAction} To change its GPU or port, stop the existing server before rerunning managed onboarding.`; } /** @@ -1165,7 +1172,13 @@ export function createSetupNim( continue selectionLoop; } if (vllmRunning) { - const message = vllmPortConflictMessage(gpu?.platform, deps.vllmPort); + const hasGpuSelection = + String(process.env.NEMOCLAW_VLLM_GPU_DEVICE ?? "").trim() !== ""; + const message = vllmPortConflictMessage( + gpu?.platform, + deps.vllmPort, + hasGpuSelection, + ); deps.error(` ${message}`); if (deps.isNonInteractive()) { deps.abortNonInteractive(message); diff --git a/src/lib/onboard/vllm-menu.test.ts b/src/lib/onboard/vllm-menu.test.ts index 26f35bbc88e..0e93bf41c12 100644 --- a/src/lib/onboard/vllm-menu.test.ts +++ b/src/lib/onboard/vllm-menu.test.ts @@ -209,6 +209,25 @@ describe("buildVllmMenuEntries", () => { assert.match(logs[0], /selecting the running instance/); }); + it("preserves managed install intent when a running server conflicts with GPU selection", () => { + const logs: string[] = []; + const entries = buildVllmMenuEntries({ + vllmRunning: true, + vllmProfile: { name: "DGX Station" }, + experimental: false, + platform: "station", + hasVllmImage: true, + env: { + NEMOCLAW_PROVIDER: "install-vllm", + NEMOCLAW_VLLM_GPU_DEVICE: "2", + }, + log: (message) => logs.push(message), + }); + + assert.equal(entries[0].key, "install-vllm"); + assert.deepEqual(logs, []); + }); + it("does not log the override note when the user did not request install-vllm", () => { const logs: string[] = []; buildVllmMenuEntries({ diff --git a/src/lib/onboard/vllm-menu.ts b/src/lib/onboard/vllm-menu.ts index 316637eb5a6..eceddc601fc 100644 --- a/src/lib/onboard/vllm-menu.ts +++ b/src/lib/onboard/vllm-menu.ts @@ -21,8 +21,9 @@ * available for this host." message. It also lets the caller surface managed * vLLM by default for known DGX platforms while generic Linux stays gated, and * logs a note when running-vLLM takes precedence over the env-var opt-in. N1x - * keeps the managed selection because its readiness exception is limited to - * the managed-vLLM preview. + * and explicit managed GPU selection keep the managed selection so the + * provider flow can report the running-server conflict without changing the + * user's intent. */ import { VLLM_PORT } from "../core/ports"; @@ -66,8 +67,11 @@ export function buildVllmMenuEntries(opts: BuildVllmMenuOptions): VllmMenuEntry[ const env = opts.env ?? process.env; const userChoseManagedVllm = (env.NEMOCLAW_PROVIDER || "").trim().toLowerCase() === MANAGED_VLLM_PROVIDER_KEY; - const keepN1xManagedPreview = userChoseManagedVllm && opts.platform === "n1x"; - if (opts.vllmRunning && !keepN1xManagedPreview) { + const hasManagedVllmGpuSelection = + String(env.NEMOCLAW_VLLM_GPU_DEVICE ?? "").trim() !== ""; + const preserveManagedVllmIntent = + userChoseManagedVllm && (opts.platform === "n1x" || hasManagedVllmGpuSelection); + if (opts.vllmRunning && !preserveManagedVllmIntent) { if (userChoseManagedVllm) { log( ` Note: NEMOCLAW_PROVIDER=install-vllm requested, but vLLM is already running on localhost:${VLLM_PORT} — selecting the running instance.`, From 9d31c7065d718a51f405aa12bb7712e961b604ae Mon Sep 17 00:00:00 2001 From: Prekshi Vyas Date: Tue, 25 Aug 2026 15:57:55 -0700 Subject: [PATCH 2/4] docs(vllm): clarify N1x runtime admission Signed-off-by: Prekshi Vyas --- docs/inference/set-up-vllm.mdx | 8 ++++---- src/lib/onboard/vllm-menu.test.ts | 5 ++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/inference/set-up-vllm.mdx b/docs/inference/set-up-vllm.mdx index 080d3ccb930..d8fc8f2b7d9 100644 --- a/docs/inference/set-up-vllm.mdx +++ b/docs/inference/set-up-vllm.mdx @@ -46,7 +46,7 @@ $$nemoclaw onboard Select **Local vLLM**. On generic hosts, the entry includes an experimental label. -N1x does not offer the existing-server entry; it offers only the managed **Install vLLM** or **Start vLLM** entry with the **Deferred preview** label. +On N1x, the readiness gate admits only explicit managed-preview intent, so the user-facing onboarding flow does not offer the existing-server entry. NemoClaw validates the detected endpoint and records the model reported by `/v1/models`. When you request a vLLM serving profile, the detected server must report the profile's served model alias or a safe `root` that matches the profile's declared model. @@ -54,7 +54,7 @@ NemoClaw stops onboarding when neither identifier matches, rather than recording Stop the server and rerun the original command, or unset `NEMOCLAW_SERVING_PRESET` and `NEMOCLAW_PROVIDER` before you start fresh onboarding to keep the detected model. On hosts other than N1x, when an install request without a managed GPU selection encounters an already-running server, NemoClaw selects that server instead of starting another container. -On N1x, explicit managed-preview intent never reuses an already-running server. +On N1x, explicit managed-preview intent keeps the managed entry and stops onboarding when another vLLM server occupies the configured port. Stop the server, then rerun managed onboarding. ```bash @@ -150,12 +150,12 @@ $$nemoclaw onboard --profile --vllm-gpu-device The single-host managed vLLM runtime is host-global. Destroying a sandbox does not stop it, and the container remains available for other sandboxes. -During later onboarding, NemoClaw normally reuses a server that is already listening on the configured vLLM port instead of replacing it automatically. +On hosts other than N1x, later onboarding normally reuses a server that is already listening on the configured vLLM port instead of replacing it automatically. When a managed GPU selection accompanies explicit managed-install intent, NemoClaw preserves that intent and reports the running-server conflict. Choose one action: -- To reuse the running server, omit `--vllm-gpu-device` and select **Local vLLM**. +- On hosts other than N1x, to reuse the running server, omit `--vllm-gpu-device` and select **Local vLLM**. - To change the GPU or port, inspect the sandbox registry for every gateway-port environment on the host. Run `NEMOCLAW_GATEWAY_PORT= $$nemoclaw list` for port `8080` and for every non-default port represented by a numeric directory under `~/.nemoclaw/gateways/`. Treat every sandbox that reports provider `vllm-local` as a possible consumer because the list output does not distinguish local vLLM from Local NIM. diff --git a/src/lib/onboard/vllm-menu.test.ts b/src/lib/onboard/vllm-menu.test.ts index 0e93bf41c12..97d69ffc669 100644 --- a/src/lib/onboard/vllm-menu.test.ts +++ b/src/lib/onboard/vllm-menu.test.ts @@ -99,7 +99,7 @@ describe("buildVllmMenuEntries", () => { assert.equal(entries[0].label, "Start vLLM (DGX Station)"); }); - it("labels only the N1x managed install entry as a Deferred preview (#8574)", () => { + it("keeps the Deferred label scoped to the pre-admission N1x managed entry (#8574)", () => { const install = buildVllmMenuEntries({ vllmRunning: false, vllmProfile: { name: "N1x" }, @@ -119,6 +119,9 @@ describe("buildVllmMenuEntries", () => { log: () => {}, }); + // N1x readiness rejects the running-server state without explicit managed + // intent. If this lower-level helper sees that state in isolation, it must + // not mislabel an operator-managed server as the Deferred managed preview. assert.equal(install[0].label, "Install vLLM (N1x) [Deferred preview]"); assert.equal(running[0].label, "Local vLLM (localhost:8000) — running"); assert.doesNotMatch(running[0].label, /Deferred preview/); From e777f77d1c53f43c6272a853ccb1fb542fda9064 Mon Sep 17 00:00:00 2001 From: Carlos Villela Date: Tue, 1 Sep 2026 02:11:37 -0700 Subject: [PATCH 3/4] fix(onboard): clarify shared vllm conflict recovery Signed-off-by: Carlos Villela --- .../setup-nim-flow-vllm-gpu-device.test.ts | 18 ++++++++++++++---- src/lib/onboard/setup-nim-flow.test.ts | 11 ++++++++--- src/lib/onboard/setup-nim-flow.ts | 12 +++++++----- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/src/lib/onboard/setup-nim-flow-vllm-gpu-device.test.ts b/src/lib/onboard/setup-nim-flow-vllm-gpu-device.test.ts index ae7d5ccbec7..a502e13796f 100644 --- a/src/lib/onboard/setup-nim-flow-vllm-gpu-device.test.ts +++ b/src/lib/onboard/setup-nim-flow-vllm-gpu-device.test.ts @@ -58,10 +58,15 @@ describe("managed vLLM GPU provider selection", () => { await expect(setupNim(null)).rejects.toThrow("vLLM is already running on localhost:18000"); expect(abortNonInteractive).toHaveBeenCalledWith( - expect.stringContaining("Omit --vllm-gpu-device to reuse that server"), + expect.stringContaining( + "Omit --vllm-gpu-device and rerun with NEMOCLAW_PROVIDER=vllm", + ), ); expect(abortNonInteractive).toHaveBeenCalledWith( - expect.stringContaining("stop the existing server"), + expect.stringContaining("only if no other gateway or distributed deployment uses it"), + ); + expect(abortNonInteractive).toHaveBeenCalledWith( + expect.stringContaining("NEMOCLAW_VLLM_PORT"), ); expect(handleVllmSelection).not.toHaveBeenCalled(); }); @@ -91,10 +96,15 @@ describe("managed vLLM GPU provider selection", () => { await expect(setupNim(null)).rejects.toThrow("vLLM is already running on localhost:18000"); expect(abortNonInteractive).toHaveBeenCalledWith( - expect.stringContaining("Omit --vllm-gpu-device and select Local vLLM"), + expect.stringContaining( + "Omit --vllm-gpu-device and rerun with NEMOCLAW_PROVIDER=vllm", + ), + ); + expect(abortNonInteractive).toHaveBeenCalledWith( + expect.stringContaining("only if no other gateway or distributed deployment uses it"), ); expect(abortNonInteractive).toHaveBeenCalledWith( - expect.stringContaining("stop the existing server"), + expect.stringContaining("NEMOCLAW_VLLM_PORT"), ); expect(installVllm).not.toHaveBeenCalled(); }); diff --git a/src/lib/onboard/setup-nim-flow.test.ts b/src/lib/onboard/setup-nim-flow.test.ts index 5afeca71dd0..6d560ee6dee 100644 --- a/src/lib/onboard/setup-nim-flow.test.ts +++ b/src/lib/onboard/setup-nim-flow.test.ts @@ -1111,7 +1111,10 @@ describe("createSetupNim", () => { expect(error).toHaveBeenCalledWith(expect.stringContaining("requires managed vLLM")); expect(error).toHaveBeenCalledWith(expect.stringContaining("localhost:8000")); - expect(error).toHaveBeenCalledWith(expect.stringContaining("Stop the existing server")); + expect(error).toHaveBeenCalledWith( + expect.stringContaining("only if no other gateway or distributed deployment uses it"), + ); + expect(error).toHaveBeenCalledWith(expect.stringContaining("NEMOCLAW_VLLM_PORT")); expect(error).toHaveBeenCalledWith( expect.stringContaining("NEMOCLAW_PROVIDER=install-vllm"), ); @@ -1435,8 +1438,10 @@ describe("createSetupNim", () => { expect(prompt).toHaveBeenCalledTimes(2); expect(selectFromNumberedMenu).toHaveBeenCalledTimes(2); - expect(error).toHaveBeenCalledWith(expect.stringContaining("stop the existing server")); - expect(error).toHaveBeenCalledWith(expect.stringContaining("change its GPU or port")); + expect(error).toHaveBeenCalledWith( + expect.stringContaining("only if no other gateway or distributed deployment uses it"), + ); + expect(error).toHaveBeenCalledWith(expect.stringContaining("NEMOCLAW_VLLM_PORT")); expect(installVllm).not.toHaveBeenCalled(); expect(handleVllmSelection).not.toHaveBeenCalled(); expect(handleRemoteProviderSelection).toHaveBeenCalledOnce(); diff --git a/src/lib/onboard/setup-nim-flow.ts b/src/lib/onboard/setup-nim-flow.ts index bb915d67293..b3f7540f0c0 100644 --- a/src/lib/onboard/setup-nim-flow.ts +++ b/src/lib/onboard/setup-nim-flow.ts @@ -309,7 +309,9 @@ function assertVllmGpuProviderSelection( const message = selected.key === "vllm" ? `vLLM is already running on localhost:${deps.vllmPort}, so --vllm-gpu-device cannot change its GPU. ` + - `Omit --vllm-gpu-device to reuse that server. To select a different GPU, stop the existing server, then rerun managed onboarding.` + `Omit --vllm-gpu-device and rerun with NEMOCLAW_PROVIDER=vllm to reuse that server. ` + + `To select a different GPU, stop the server only if no other gateway or distributed deployment uses it. ` + + `Otherwise, keep it running and set NEMOCLAW_VLLM_PORT to an unused port before rerunning managed onboarding.` : `--vllm-gpu-device applies only when NemoClaw installs managed vLLM; ` + `the selected provider is '${selected.key}'.`; deps.error(` ${message}`); @@ -619,12 +621,12 @@ function vllmPortConflictMessage( hasGpuSelection: boolean, ): string { if (platform === "n1x") { - return `The N1x Deferred preview requires managed vLLM, but vLLM is already running on localhost:${port}. Stop the existing server, then rerun with NEMOCLAW_PROVIDER=install-vllm.`; + return `The N1x Deferred preview requires managed vLLM, but vLLM is already running on localhost:${port}. Stop the server only if no other gateway or distributed deployment uses it. Otherwise, keep it running and set NEMOCLAW_VLLM_PORT to an unused port. Then rerun with NEMOCLAW_PROVIDER=install-vllm.`; } const reuseAction = hasGpuSelection - ? "Omit --vllm-gpu-device and select Local vLLM to reuse it." - : "Select Local vLLM to reuse it."; - return `vLLM is already running on localhost:${port}. ${reuseAction} To change its GPU or port, stop the existing server before rerunning managed onboarding.`; + ? "Omit --vllm-gpu-device and rerun with NEMOCLAW_PROVIDER=vllm to reuse it." + : "Rerun with NEMOCLAW_PROVIDER=vllm to reuse it."; + return `vLLM is already running on localhost:${port}. ${reuseAction} To change its GPU or port, stop the server only if no other gateway or distributed deployment uses it. Otherwise, keep it running and set NEMOCLAW_VLLM_PORT to an unused port before rerunning managed onboarding.`; } /** From 77e8bf91270918066ff9cc9ec1fcdc03cfa0483b Mon Sep 17 00:00:00 2001 From: Carlos Villela Date: Tue, 1 Sep 2026 02:55:17 -0700 Subject: [PATCH 4/4] test(onboard): stabilize readiness deadline clock Signed-off-by: Carlos Villela --- src/lib/onboard/sandbox-readiness-tracing.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/onboard/sandbox-readiness-tracing.test.ts b/src/lib/onboard/sandbox-readiness-tracing.test.ts index 005345b4e40..cead6a01d25 100644 --- a/src/lib/onboard/sandbox-readiness-tracing.test.ts +++ b/src/lib/onboard/sandbox-readiness-tracing.test.ts @@ -80,6 +80,7 @@ describe("createSandboxReadyWaiter", () => { target: TARGET, isLinuxDockerDriverGatewayEnabled: () => true, sleep, + now: () => 0, }); await expect(waitForSandboxReady(NAME, 2, 3)).resolves.toEqual({