diff --git a/bin/lib/inference-config.js b/bin/lib/inference-config.js index 6384d8a60..0f10a4dd7 100644 --- a/bin/lib/inference-config.js +++ b/bin/lib/inference-config.js @@ -107,6 +107,17 @@ function getProviderSelectionConfig(provider, model) { provider, providerLabel: "Local Ollama", }; + case "bedrock": + return { + endpointType: "custom", + endpointUrl: INFERENCE_ROUTE_URL, + ncpPartner: null, + model: model || "nvidia.nemotron-super-3-120b", + profile: DEFAULT_ROUTE_PROFILE, + credentialEnv: "BEDROCK_API_KEY", + provider, + providerLabel: "Amazon Bedrock (OpenAI-compatible)", + }; default: return null; } diff --git a/bin/lib/onboard.js b/bin/lib/onboard.js index e74d49921..82cfefcbf 100644 --- a/bin/lib/onboard.js +++ b/bin/lib/onboard.js @@ -47,6 +47,7 @@ const BUILD_ENDPOINT_URL = "https://integrate.api.nvidia.com/v1"; const OPENAI_ENDPOINT_URL = "https://api.openai.com/v1"; const ANTHROPIC_ENDPOINT_URL = "https://api.anthropic.com"; const GEMINI_ENDPOINT_URL = "https://generativelanguage.googleapis.com/v1beta/openai/"; +const BEDROCK_ENDPOINT_URL = `https://bedrock-mantle.${process.env.BEDROCK_REGION || process.env.AWS_REGION || ""}.api.aws/v1`; const REMOTE_PROVIDER_CONFIG = { build: { @@ -112,6 +113,17 @@ const REMOTE_PROVIDER_CONFIG = { defaultModel: "", skipVerify: true, }, + bedrock: { + label: "Amazon Bedrock (OpenAI-compatible endpoint)", + providerName: "bedrock", + providerType: "openai", + credentialEnv: "BEDROCK_API_KEY", + endpointUrl: BEDROCK_ENDPOINT_URL, + helpUrl: "https://docs.aws.amazon.com/bedrock/latest/userguide/api-keys-generate.html", + modelMode: "curated", + defaultModel: "nvidia.nemotron-super-3-120b", + skipVerify: true, + }, }; const REMOTE_MODEL_OPTIONS = { @@ -134,6 +146,15 @@ const REMOTE_MODEL_OPTIONS = { "gemini-2.5-flash", "gemini-2.5-flash-lite", ], + bedrock: [ + "nvidia.nemotron-nano-3-30b", + "nvidia.nemotron-super-3-120b", + "deepseek.v3.2", + "openai.gpt-oss-120b", + "mistral.devstral-2-123b", + "moonshotai.kimi-k2.5", + "minimax.minimax-m2.5", + ], }; // Non-interactive mode: set by --non-interactive flag or env var. @@ -478,6 +499,7 @@ function getSandboxInferenceConfig(model, provider = null, preferredInferenceApi supportsStore: false, }; break; + case "bedrock": case "compatible-endpoint": providerKey = "inference"; primaryModelRef = `inference/${model}`; @@ -1193,10 +1215,10 @@ function getNonInteractiveProvider() { anthropiccompatible: "anthropicCompatible", }; const normalized = aliases[providerKey] || providerKey; - const validProviders = new Set(["build", "openai", "anthropic", "anthropicCompatible", "gemini", "ollama", "custom", "nim-local", "vllm"]); + const validProviders = new Set(["build", "openai", "anthropic", "anthropicCompatible", "gemini", "ollama", "custom", "nim-local", "vllm", "bedrock"]); if (!validProviders.has(normalized)) { console.error(` Unsupported NEMOCLAW_PROVIDER: ${providerKey}`); - console.error(" Valid values: build, openai, anthropic, anthropicCompatible, gemini, ollama, custom, nim-local, vllm"); + console.error(" Valid values: build, openai, anthropic, anthropicCompatible, gemini, ollama, custom, nim-local, vllm, bedrock"); process.exit(1); } @@ -1626,6 +1648,7 @@ async function setupNim(gpu) { (ollamaRunning ? " (suggested)" : ""), }); } + options.push({ key: "bedrock", label: "Amazon Bedrock (OpenAI-compatible endpoint)" }); if (EXPERIMENTAL && gpu && gpu.nimCapable) { options.push({ key: "nim-local", label: "Local NVIDIA NIM [experimental]" }); } @@ -1684,6 +1707,18 @@ async function setupNim(gpu) { endpointUrl = remoteConfig.endpointUrl; preferredInferenceApi = null; + if (selected.key === "bedrock") { + const bedrockRegion = process.env.BEDROCK_REGION || process.env.AWS_REGION; + if (!bedrockRegion) { + console.error(" BEDROCK_REGION or AWS_REGION must be set for Amazon Bedrock."); + if (isNonInteractive()) { + process.exit(1); + } + continue selectionLoop; + } + endpointUrl = `https://bedrock-mantle.${bedrockRegion}.api.aws/v1`; + } + if (selected.key === "custom") { endpointUrl = isNonInteractive() ? (process.env.NEMOCLAW_ENDPOINT_URL || "").trim() @@ -1723,7 +1758,7 @@ async function setupNim(gpu) { } const defaultModel = requestedModel || remoteConfig.defaultModel; let modelValidator = null; - if (selected.key === "openai" || selected.key === "gemini") { + if (selected.key === "openai" || selected.key === "gemini" || selected.key === "bedrock") { modelValidator = (candidate) => validateOpenAiLikeModel(remoteConfig.label, endpointUrl, candidate, getCredential(credentialEnv)); } else if (selected.key === "anthropic") { @@ -1753,6 +1788,20 @@ async function setupNim(gpu) { if (validation.retry === "selection") { continue selectionLoop; } + } else if (selected.key === "bedrock") { + // Bedrock Mantle exposes an OpenAI-compatible API + const retryMessage = "Please choose a provider/model again."; + preferredInferenceApi = await validateOpenAiLikeSelection( + remoteConfig.label, + endpointUrl, + model, + credentialEnv, + retryMessage + ); + if (preferredInferenceApi) { + break; + } + continue selectionLoop; } else if (selected.key === "anthropicCompatible") { const validation = await validateCustomAnthropicSelection( remoteConfig.label, @@ -1994,7 +2043,7 @@ async function setupInference(sandboxName, model, provider, endpointUrl = null, step(4, 7, "Setting up inference provider"); runOpenshell(["gateway", "select", GATEWAY_NAME], { ignoreError: true }); - if (provider === "nvidia-prod" || provider === "nvidia-nim" || provider === "openai-api" || provider === "anthropic-prod" || provider === "compatible-anthropic-endpoint" || provider === "gemini-api" || provider === "compatible-endpoint") { + if (provider === "nvidia-prod" || provider === "nvidia-nim" || provider === "openai-api" || provider === "anthropic-prod" || provider === "compatible-anthropic-endpoint" || provider === "gemini-api" || provider === "compatible-endpoint" || provider === "bedrock") { const config = provider === "nvidia-nim" ? REMOTE_PROVIDER_CONFIG.build : Object.values(REMOTE_PROVIDER_CONFIG).find((entry) => entry.providerName === provider); @@ -2282,6 +2331,7 @@ function printDashboard(sandboxName, model, provider, nimContainer = null) { else if (provider === "gemini-api") providerLabel = "Google Gemini"; else if (provider === "compatible-endpoint") providerLabel = "Other OpenAI-compatible endpoint"; else if (provider === "vllm-local") providerLabel = "Local vLLM"; + else if (provider === "bedrock") providerLabel = "Amazon Bedrock"; else if (provider === "ollama-local") providerLabel = "Local Ollama"; const token = fetchGatewayAuthTokenFromSandbox(sandboxName);