Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 29 additions & 58 deletions src/lib/onboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,9 +217,11 @@ const {
const {
checkOllamaPortsOrWarn,
resolveOllamaInstallMenuEntry,
resolveRunningOllamaMenuEntry,
assertOllamaUpgradeApplied,
} = require("./onboard/ollama-install-menu");
const {
buildInferenceProviderMenu,
}: typeof import("./onboard/provider-menu") = require("./onboard/provider-menu");
const {
ensureOllamaAuthProxy,
getOllamaProxyToken,
Expand Down Expand Up @@ -3651,7 +3653,7 @@ async function createSandbox(

// ── Step 3: Inference selection ──────────────────────────────────

type ProviderChoice = { key: string; label: string };
type ProviderChoice = import("./onboard/provider-menu").ProviderMenuChoice;

const { readRecordedProvider, readRecordedNimContainer, readRecordedModel } =
providerRecovery.createProviderRecoveryHelpers({
Expand Down Expand Up @@ -3837,77 +3839,46 @@ async function setupNim(
? getNonInteractiveModel(requestedProvider || "build")
: null;
const agentProviderOptions = getAgentInferenceProviderOptions(agent);
const hermesProviderAvailable = agentProviderOptions.includes("hermesProvider");
const options: Array<{ key: string; label: string }> = [];
options.push({ key: "build", label: "NVIDIA Endpoints" });
options.push({ key: "openai", label: "OpenAI" });
options.push({ key: "custom", label: "Other OpenAI-compatible endpoint" });
options.push({ key: "anthropic", label: "Anthropic" });
options.push({ key: "anthropicCompatible", label: "Other Anthropic-compatible endpoint" });
options.push({ key: "gemini", label: "Google Gemini" });
const runningOllamaMenu = resolveRunningOllamaMenuEntry({
const ollamaInstallMenu = resolveOllamaInstallMenuEntry({
hasOllama,
ollamaRunning,
hasWindowsOllama,
ollamaHost,
platform: process.platform,
isWsl: isWsl(),
});

// Model Router: complexity-based routing via blueprint config.
const blueprintRouterCfg = loadBlueprintProfile("routed");
const { options, hermesProviderAvailable } = buildInferenceProviderMenu({
remoteProviderConfig: REMOTE_PROVIDER_CONFIG,
agentProviderOptions,
experimental: EXPERIMENTAL,
gpuNimCapable: Boolean(gpu && gpu.nimCapable),
hasOllama,
ollamaRunning,
ollamaHost,
ollamaPort: OLLAMA_PORT,
isWsl: isWsl(),
hasWindowsOllama,
isWindowsHostOllama,
windowsHostLabelSuffix: windowsHostOllamaDockerRequirement.supported
? ""
: windowsHostOllamaDockerRequirement.labelSuffix,
});
if (runningOllamaMenu) options.push(runningOllamaMenu);
if (EXPERIMENTAL && gpu && gpu.nimCapable) {
options.push({ key: "nim-local", label: "Local NVIDIA NIM [experimental]" });
}
options.push(
...buildVllmMenuEntries({
windowsHostInstallLabel: windowsHostOllamaDockerRequirement.installLabel,
windowsHostStartLabel: windowsHostOllamaDockerRequirement.startLabel,
windowsOllamaReachable,
winOllamaLoopbackOnly,
ollamaInstallEntry: ollamaInstallMenu.entry,
vllmEntries: buildVllmMenuEntries({
vllmRunning,
vllmProfile,
experimental: EXPERIMENTAL,
platform: gpu?.platform,
hasVllmImage,
}),
);
// Skipped when Windows-host already won the cache: the running entry
// above already covers that case.
if (hasWindowsOllama && !isWindowsHostOllama) {
options.push({
key: "start-windows-ollama",
label: windowsHostOllamaDockerRequirement.startLabel({
reachable: windowsOllamaReachable,
loopbackOnly: winOllamaLoopbackOnly,
}),
});
}
// On WSL, always offer to install Ollama on the Windows host when not
// already installed, regardless of WSL Ollama state — users may prefer the
// Windows-host instance (GPU access) even with WSL Ollama running.
if (isWsl() && !hasWindowsOllama) {
options.push({
key: "install-windows-ollama",
label: windowsHostOllamaDockerRequirement.installLabel,
});
}
const ollamaInstallMenu = resolveOllamaInstallMenuEntry({
hasOllama,
ollamaRunning,
hasWindowsOllama,
ollamaHost,
platform: process.platform,
isWsl: isWsl(),
routedEnabled: blueprintRouterCfg?.router?.enabled === true,
});
if (ollamaInstallMenu.entry) options.push(ollamaInstallMenu.entry);

// Model Router: complexity-based routing via blueprint config.
const blueprintRouterCfg = loadBlueprintProfile("routed");
if (blueprintRouterCfg && blueprintRouterCfg.router?.enabled === true) {
options.push({ key: "routed", label: "Model Router (experimental)" });
}
for (const providerKey of agentProviderOptions) {
const remoteConfig = REMOTE_PROVIDER_CONFIG[providerKey];
if (!remoteConfig || options.some((option) => option.key === providerKey)) continue;
options.push({ key: providerKey, label: remoteConfig.label });
}

function rejectWindowsHostOllama(providerKey: string, windowsHostSelected: boolean): boolean {
return rejectUnsupportedWindowsHostOllama(
Expand Down
135 changes: 135 additions & 0 deletions src/lib/onboard/provider-menu.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

import { describe, expect, it } from "vitest";

import { buildInferenceProviderMenu } from "../../../dist/lib/onboard/provider-menu";

const REMOTE_PROVIDER_CONFIG = {
build: { label: "NVIDIA Endpoints" },
openai: { label: "OpenAI" },
custom: { label: "Other OpenAI-compatible endpoint" },
anthropic: { label: "Anthropic" },
anthropicCompatible: { label: "Other Anthropic-compatible endpoint" },
gemini: { label: "Google Gemini" },
hermesProvider: { label: "Hermes Provider" },
};

function buildMenu(overrides: Partial<Parameters<typeof buildInferenceProviderMenu>[0]> = {}) {
return buildInferenceProviderMenu({
remoteProviderConfig: REMOTE_PROVIDER_CONFIG,
agentProviderOptions: [],
experimental: false,
gpuNimCapable: false,
hasOllama: false,
ollamaRunning: false,
ollamaHost: null,
ollamaPort: 11434,
isWsl: false,
hasWindowsOllama: false,
isWindowsHostOllama: false,
windowsHostLabelSuffix: "",
windowsHostInstallLabel: "Install Ollama on Windows host (recommended)",
windowsHostStartLabel: () => "Start Ollama on Windows host (suggested)",
windowsOllamaReachable: false,
winOllamaLoopbackOnly: false,
ollamaInstallEntry: null,
vllmEntries: [],
routedEnabled: false,
...overrides,
});
}

describe("buildInferenceProviderMenu", () => {
it("returns the base remote providers in the existing prompt order", () => {
const result = buildMenu();

expect(result.hermesProviderAvailable).toBe(false);
expect(result.options.map((option) => option.key)).toEqual([
"build",
"openai",
"custom",
"anthropic",
"anthropicCompatible",
"gemini",
]);
});

it("adds local, routed, and agent-scoped providers after the base remote entries", () => {
const result = buildMenu({
agentProviderOptions: ["hermesProvider", "build"],
experimental: true,
gpuNimCapable: true,
hasOllama: true,
ollamaRunning: true,
ollamaHost: "127.0.0.1",
isWsl: false,
ollamaInstallEntry: { key: "install-ollama", label: "Install Ollama (Linux)" },
vllmEntries: [{ key: "install-vllm", label: "Install vLLM (DGX Spark)" }],
routedEnabled: true,
});

expect(result.hermesProviderAvailable).toBe(true);
expect(result.options.map((option) => option.key)).toEqual([
"build",
"openai",
"custom",
"anthropic",
"anthropicCompatible",
"gemini",
"ollama",
"nim-local",
"install-vllm",
"install-ollama",
"routed",
"hermesProvider",
]);
expect(result.options.find((option) => option.key === "build")?.label).toBe("NVIDIA Endpoints");
expect(result.options.find((option) => option.key === "hermesProvider")?.label).toBe(
"Hermes Provider",
);
});

it("offers Windows-host Ollama install when WSL has no Windows Ollama", () => {
const result = buildMenu({
isWsl: true,
hasWindowsOllama: false,
windowsHostInstallLabel: "Install Ollama on Windows host (requires Docker Desktop)",
});

expect(result.options.at(-1)).toEqual({
key: "install-windows-ollama",
label: "Install Ollama on Windows host (requires Docker Desktop)",
});
});

it("offers Windows-host Ollama start when detected but not currently selected", () => {
const result = buildMenu({
isWsl: true,
hasWindowsOllama: true,
isWindowsHostOllama: false,
windowsOllamaReachable: true,
windowsHostStartLabel: ({ reachable }) =>
reachable ? "Use Ollama on Windows host - running" : "Start Ollama on Windows host",
});

expect(result.options.at(-1)).toEqual({
key: "start-windows-ollama",
label: "Use Ollama on Windows host - running",
});
});

it("does not add a separate Windows-host start entry when running Ollama already resolves there", () => {
const result = buildMenu({
isWsl: true,
hasOllama: false,
ollamaRunning: true,
ollamaHost: "host.docker.internal",
hasWindowsOllama: true,
isWindowsHostOllama: true,
});

expect(result.options.map((option) => option.key)).toContain("ollama");
expect(result.options.map((option) => option.key)).not.toContain("start-windows-ollama");
});
});
127 changes: 127 additions & 0 deletions src/lib/onboard/provider-menu.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

import { resolveRunningOllamaMenuEntry } from "./ollama-install-menu";

export interface ProviderMenuChoice {
key: string;
label: string;
}

interface RemoteProviderMenuConfig {
label: string;
}

type WindowsHostOllamaStartLabel = (opts: { reachable: boolean; loopbackOnly: boolean }) => string;

export interface BuildInferenceProviderMenuInput {
remoteProviderConfig: Record<string, RemoteProviderMenuConfig | undefined>;
agentProviderOptions: readonly string[];
experimental: boolean;
gpuNimCapable: boolean;
hasOllama: boolean;
ollamaRunning: boolean;
ollamaHost: string | null;
ollamaPort: number;
isWsl: boolean;
hasWindowsOllama: boolean;
isWindowsHostOllama: boolean;
windowsHostLabelSuffix: string;
windowsHostInstallLabel: string;
windowsHostStartLabel: WindowsHostOllamaStartLabel;
windowsOllamaReachable: boolean;
winOllamaLoopbackOnly: boolean;
ollamaInstallEntry: ProviderMenuChoice | null;
vllmEntries: readonly ProviderMenuChoice[];
routedEnabled: boolean;
}

export interface InferenceProviderMenu {
options: ProviderMenuChoice[];
hermesProviderAvailable: boolean;
}

const BASE_REMOTE_PROVIDER_OPTIONS: readonly ProviderMenuChoice[] = [
{ key: "build", label: "NVIDIA Endpoints" },
{ key: "openai", label: "OpenAI" },
{ key: "custom", label: "Other OpenAI-compatible endpoint" },
{ key: "anthropic", label: "Anthropic" },
{ key: "anthropicCompatible", label: "Other Anthropic-compatible endpoint" },
{ key: "gemini", label: "Google Gemini" },
];

function configuredRemoteOption(
config: Record<string, RemoteProviderMenuConfig | undefined>,
fallback: ProviderMenuChoice,
): ProviderMenuChoice {
return {
key: fallback.key,
label: config[fallback.key]?.label ?? fallback.label,
};
}

function pushUniqueRemoteProviderOption(
options: ProviderMenuChoice[],
config: Record<string, RemoteProviderMenuConfig | undefined>,
providerKey: string,
): void {
const remoteConfig = config[providerKey];
if (!remoteConfig || options.some((option) => option.key === providerKey)) return;
options.push({ key: providerKey, label: remoteConfig.label });
}

export function buildInferenceProviderMenu(
input: BuildInferenceProviderMenuInput,
): InferenceProviderMenu {
const options: ProviderMenuChoice[] = BASE_REMOTE_PROVIDER_OPTIONS.map((option) =>
configuredRemoteOption(input.remoteProviderConfig, option),
);

const runningOllamaMenu = resolveRunningOllamaMenuEntry({
hasOllama: input.hasOllama,
ollamaRunning: input.ollamaRunning,
ollamaHost: input.ollamaHost,
isWsl: input.isWsl,
ollamaPort: input.ollamaPort,
windowsHostLabelSuffix: input.windowsHostLabelSuffix,
});
if (runningOllamaMenu) options.push(runningOllamaMenu);

if (input.experimental && input.gpuNimCapable) {
options.push({ key: "nim-local", label: "Local NVIDIA NIM [experimental]" });
}

options.push(...input.vllmEntries);

if (input.hasWindowsOllama && !input.isWindowsHostOllama) {
options.push({
key: "start-windows-ollama",
label: input.windowsHostStartLabel({
reachable: input.windowsOllamaReachable,
loopbackOnly: input.winOllamaLoopbackOnly,
}),
});
}

if (input.isWsl && !input.hasWindowsOllama) {
options.push({
key: "install-windows-ollama",
label: input.windowsHostInstallLabel,
});
}

if (input.ollamaInstallEntry) options.push(input.ollamaInstallEntry);

if (input.routedEnabled) {
options.push({ key: "routed", label: "Model Router (experimental)" });
}

for (const providerKey of input.agentProviderOptions) {
pushUniqueRemoteProviderOption(options, input.remoteProviderConfig, providerKey);
}

return {
options,
hermesProviderAvailable: input.agentProviderOptions.includes("hermesProvider"),
};
}