From c538b5ad11122c6f0cad213d421286b94be869da Mon Sep 17 00:00:00 2001 From: Tin TA Date: Tue, 17 Mar 2026 22:54:52 +0700 Subject: [PATCH 1/5] feat: implement AWS Bedrock integration and update authentication handling --- .env.example | 27 +++++++ package-lock.json | 2 - server/claude-sdk.js | 77 +++++++++++++++++-- server/routes/cli-auth.js | 45 +++++++++-- .../settings/constants/constants.ts | 1 + .../settings/hooks/useSettingsController.ts | 3 + src/components/settings/types/types.ts | 1 + .../sections/content/AccountContent.tsx | 2 +- 8 files changed, 142 insertions(+), 16 deletions(-) diff --git a/.env.example b/.env.example index d18b97e435..a555d60433 100755 --- a/.env.example +++ b/.env.example @@ -43,3 +43,30 @@ VITE_CONTEXT_WINDOW=160000 CONTEXT_WINDOW=160000 +# ============================================================================= +# AWS BEDROCK CONFIGURATION FOR CLAUDE +# ============================================================================= +# +# Prerequisites: +# - AWS CLI must be installed and configured +# - Run 'aws configure' to set up credentials and default region +# - Or provide AWS credentials through environment variables or IAM roles +# +# To use AWS Bedrock instead of the Anthropic API, uncomment the lines below. +# CLAUDE_CODE_USE_BEDROCK=1 +# +# AWS credentials (or use AWS_PROFILE from ~/.aws/config and ~/.aws/credentials) +# AWS_REGION=eu-central-1 +# AWS_PROFILE=default +# AWS_ACCESS_KEY_ID=your-access-key-id +# AWS_SECRET_ACCESS_KEY=your-secret-access-key +# +# Default models used when selecting aliases (sonnet/opus/haiku) in the UI +# ANTHROPIC_DEFAULT_SONNET_MODEL=eu.anthropic.claude-sonnet-4-5-20250929-v1:0 +# ANTHROPIC_DEFAULT_OPUS_MODEL=eu.anthropic.claude-opus-4-5-20251101-v1:0 +# ANTHROPIC_DEFAULT_HAIKU_MODEL=eu.anthropic.claude-haiku-4-5-20251001-v1:0 +# +# Optional explicit model overrides used by Claude SDK +# ANTHROPIC_MODEL=eu.anthropic.claude-sonnet-4-5-20250929-v1:0 +# ANTHROPIC_SMALL_FAST_MODEL=eu.anthropic.claude-haiku-4-5-20251001-v1:0 + diff --git a/package-lock.json b/package-lock.json index 979d333b44..6299f836a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1910,7 +1910,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1927,7 +1926,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ diff --git a/server/claude-sdk.js b/server/claude-sdk.js index 2bc80b0f15..81bb7c3848 100644 --- a/server/claude-sdk.js +++ b/server/claude-sdk.js @@ -141,7 +141,7 @@ function matchesToolPermission(entry, toolName, input) { * @param {Object} options - CLI options * @returns {Object} SDK-compatible options */ -function mapCliOptionsToSDK(options = {}) { +async function mapCliOptionsToSDK(options = {}) { const { sessionId, cwd, toolsSettings, permissionMode, images } = options; const sdkOptions = {}; @@ -190,9 +190,10 @@ function mapCliOptionsToSDK(options = {}) { sdkOptions.disallowedTools = settings.disallowedTools || []; - // Map model (default to sonnet) - // Valid models: sonnet, opus, haiku, opusplan, sonnet[1m] - sdkOptions.model = options.model || CLAUDE_MODELS.DEFAULT; + // Map model (default to sonnet). In Bedrock mode this resolves model aliases + // (sonnet/opus/haiku) to configured inference profile IDs when available. + const settingsEnv = await loadClaudeSettingsEnv(); + sdkOptions.model = resolveClaudeModel(options.model, settingsEnv); console.log(`Using model: ${sdkOptions.model}`); // Map system prompt configuration @@ -459,6 +460,72 @@ async function loadMcpConfig(cwd) { } } +function isTruthyValue(value) { + if (typeof value !== 'string') { + return false; + } + + const normalized = value.trim().toLowerCase(); + return normalized === '1' || normalized === 'true' || normalized === 'yes' || normalized === 'on'; +} + +async function loadClaudeSettingsEnv() { + try { + const settingsPath = path.join(os.homedir(), '.claude', 'settings.json'); + const content = await fs.readFile(settingsPath, 'utf8'); + const parsed = JSON.parse(content); + if (parsed?.env && typeof parsed.env === 'object') { + return parsed.env; + } + } catch { + // Ignore missing/malformed settings and fall back to process.env. + } + + return {}; +} + +function resolveClaudeEnvValue(key, settingsEnv) { + const processValue = process.env[key]; + if (typeof processValue === 'string' && processValue.trim()) { + return processValue.trim(); + } + + const settingsValue = settingsEnv[key]; + if (typeof settingsValue === 'string' && settingsValue.trim()) { + return settingsValue.trim(); + } + + return ''; +} + +function resolveClaudeModel(modelAlias, settingsEnv) { + const requestedModel = modelAlias || CLAUDE_MODELS.DEFAULT; + const isBedrockEnabled = isTruthyValue(resolveClaudeEnvValue('CLAUDE_CODE_USE_BEDROCK', settingsEnv)); + if (!isBedrockEnabled) { + return requestedModel; + } + + const explicitModel = resolveClaudeEnvValue('ANTHROPIC_MODEL', settingsEnv); + const explicitFastModel = resolveClaudeEnvValue('ANTHROPIC_SMALL_FAST_MODEL', settingsEnv); + const sonnetDefault = resolveClaudeEnvValue('ANTHROPIC_DEFAULT_SONNET_MODEL', settingsEnv); + const opusDefault = resolveClaudeEnvValue('ANTHROPIC_DEFAULT_OPUS_MODEL', settingsEnv); + const haikuDefault = resolveClaudeEnvValue('ANTHROPIC_DEFAULT_HAIKU_MODEL', settingsEnv); + + if (requestedModel === 'haiku') { + return haikuDefault || explicitFastModel || explicitModel || requestedModel; + } + + if (requestedModel === 'opus' || requestedModel === 'opusplan') { + return opusDefault || explicitModel || requestedModel; + } + + if (requestedModel === 'sonnet' || requestedModel === 'sonnet[1m]') { + return sonnetDefault || explicitModel || requestedModel; + } + + return explicitModel || requestedModel; +} + /** * Executes a Claude query using the SDK * @param {string} command - User prompt/command @@ -483,7 +550,7 @@ async function queryClaudeSDK(command, options = {}, ws) { try { // Map CLI options to SDK format - const sdkOptions = mapCliOptionsToSDK(options); + const sdkOptions = await mapCliOptionsToSDK(options); // Load MCP configuration const mcpServers = await loadMcpConfig(options.cwd); diff --git a/server/routes/cli-auth.js b/server/routes/cli-auth.js index 78ffa30b4f..9ca08ca9ff 100644 --- a/server/routes/cli-auth.js +++ b/server/routes/cli-auth.js @@ -6,6 +6,15 @@ import os from 'os'; const router = express.Router(); +function isTruthyValue(value) { + if (typeof value !== 'string') { + return false; + } + + const normalized = value.trim().toLowerCase(); + return normalized === '1' || normalized === 'true' || normalized === 'yes' || normalized === 'on'; +} + router.get('/claude/status', async (req, res) => { try { const credentialsResult = await checkClaudeCredentials(); @@ -14,7 +23,8 @@ router.get('/claude/status', async (req, res) => { return res.json({ authenticated: true, email: credentialsResult.email || 'Authenticated', - method: credentialsResult.method // 'api_key' or 'credentials_file' + method: credentialsResult.method, // 'api_key', 'credentials_file', or 'bedrock' + isBedrock: Boolean(credentialsResult.isBedrock) }); } @@ -22,6 +32,7 @@ router.get('/claude/status', async (req, res) => { authenticated: false, email: null, method: null, + isBedrock: false, error: credentialsResult.error || 'Not authenticated' }); @@ -31,6 +42,7 @@ router.get('/claude/status', async (req, res) => { authenticated: false, email: null, method: null, + isBedrock: false, error: error.message }); } @@ -134,6 +146,20 @@ async function loadClaudeSettingsEnv() { * - method: 'api_key' for env var, 'credentials_file' for OAuth tokens */ async function checkClaudeCredentials() { + const settingsEnv = await loadClaudeSettingsEnv(); + + // Priority 0: Bedrock mode bypasses Claude OAuth checks. + const bedrockEnabled = isTruthyValue(process.env.CLAUDE_CODE_USE_BEDROCK) + || isTruthyValue(settingsEnv.CLAUDE_CODE_USE_BEDROCK); + if (bedrockEnabled) { + return { + authenticated: true, + email: 'AWS Bedrock', + method: 'bedrock', + isBedrock: true + }; + } + // Priority 1: Check for ANTHROPIC_API_KEY environment variable // The SDK checks this first and uses it if present, even if OAuth tokens exist. // When set, API calls are charged via pay-as-you-go rates instead of subscription. @@ -148,13 +174,12 @@ async function checkClaudeCredentials() { // Priority 1b: Check ~/.claude/settings.json env values. // Claude Code can read proxy/auth values from settings.json even when the // CloudCLI server process itself was not started with those env vars exported. - const settingsEnv = await loadClaudeSettingsEnv(); - if (typeof settingsEnv.ANTHROPIC_API_KEY === 'string' && settingsEnv.ANTHROPIC_API_KEY.trim()) { return { authenticated: true, email: 'API Key Auth', - method: 'api_key' + method: 'api_key', + isBedrock: false }; } @@ -162,7 +187,8 @@ async function checkClaudeCredentials() { return { authenticated: true, email: 'Configured via settings.json', - method: 'api_key' + method: 'api_key', + isBedrock: false }; } @@ -182,7 +208,8 @@ async function checkClaudeCredentials() { return { authenticated: true, email: creds.email || creds.user || null, - method: 'credentials_file' + method: 'credentials_file', + isBedrock: false }; } } @@ -190,13 +217,15 @@ async function checkClaudeCredentials() { return { authenticated: false, email: null, - method: null + method: null, + isBedrock: false }; } catch (error) { return { authenticated: false, email: null, - method: null + method: null, + isBedrock: false }; } } diff --git a/src/components/settings/constants/constants.ts b/src/components/settings/constants/constants.ts index 36f45392d1..c133461ec7 100644 --- a/src/components/settings/constants/constants.ts +++ b/src/components/settings/constants/constants.ts @@ -39,6 +39,7 @@ export const DEFAULT_AUTH_STATUS: AuthStatus = { email: null, loading: true, error: null, + isBedrock: false, }; export const DEFAULT_MCP_TEST_RESULT: McpTestResult = { diff --git a/src/components/settings/hooks/useSettingsController.ts b/src/components/settings/hooks/useSettingsController.ts index 293cbceb57..aa48d0911e 100644 --- a/src/components/settings/hooks/useSettingsController.ts +++ b/src/components/settings/hooks/useSettingsController.ts @@ -43,6 +43,7 @@ type StatusApiResponse = { email?: string | null; error?: string | null; method?: string; + isBedrock?: boolean; }; type JsonResult = { @@ -289,6 +290,7 @@ export function useSettingsController({ isOpen, initialTab, projects, onClose }: loading: false, error: data.error || null, method: data.method, + isBedrock: Boolean(data.isBedrock), }); } catch (error) { console.error(`Error checking ${provider} auth status:`, error); @@ -297,6 +299,7 @@ export function useSettingsController({ isOpen, initialTab, projects, onClose }: email: null, loading: false, error: getErrorMessage(error), + isBedrock: false, }); } }, [setAuthStatusByProvider]); diff --git a/src/components/settings/types/types.ts b/src/components/settings/types/types.ts index 096059ced0..c41edc81fb 100644 --- a/src/components/settings/types/types.ts +++ b/src/components/settings/types/types.ts @@ -24,6 +24,7 @@ export type AuthStatus = { loading: boolean; error: string | null; method?: string; + isBedrock?: boolean; }; export type KeyValueMap = Record; diff --git a/src/components/settings/view/tabs/agents-settings/sections/content/AccountContent.tsx b/src/components/settings/view/tabs/agents-settings/sections/content/AccountContent.tsx index dcf4baf14e..390a660974 100644 --- a/src/components/settings/view/tabs/agents-settings/sections/content/AccountContent.tsx +++ b/src/components/settings/view/tabs/agents-settings/sections/content/AccountContent.tsx @@ -106,7 +106,7 @@ export default function AccountContent({ agent, authStatus, onLogin }: AccountCo - {authStatus.method !== 'api_key' && ( + {authStatus.method !== 'api_key' && !authStatus.isBedrock && (
From 0715c8cbfdd43869990caa5615d838c86ca084a1 Mon Sep 17 00:00:00 2001 From: Tin TA Date: Sun, 15 Mar 2026 16:38:43 +0700 Subject: [PATCH 2/5] refactor: move env helper functions to a new module --- server/claude-sdk.js | 25 +----------- server/routes/cli-auth.js | 29 ++------------ server/utils/env-helpers.js | 39 +++++++++++++++++++ .../settings/hooks/useSettingsController.ts | 1 + 4 files changed, 44 insertions(+), 50 deletions(-) create mode 100644 server/utils/env-helpers.js diff --git a/server/claude-sdk.js b/server/claude-sdk.js index 81bb7c3848..3b74ad7c88 100644 --- a/server/claude-sdk.js +++ b/server/claude-sdk.js @@ -18,6 +18,7 @@ import { promises as fs } from 'fs'; import path from 'path'; import os from 'os'; import { CLAUDE_MODELS } from '../shared/modelConstants.js'; +import { isTruthyValue, loadClaudeSettingsEnv } from './utils/env-helpers.js'; import { createNotificationEvent, notifyRunFailed, @@ -460,30 +461,6 @@ async function loadMcpConfig(cwd) { } } -function isTruthyValue(value) { - if (typeof value !== 'string') { - return false; - } - - const normalized = value.trim().toLowerCase(); - return normalized === '1' || normalized === 'true' || normalized === 'yes' || normalized === 'on'; -} - -async function loadClaudeSettingsEnv() { - try { - const settingsPath = path.join(os.homedir(), '.claude', 'settings.json'); - const content = await fs.readFile(settingsPath, 'utf8'); - const parsed = JSON.parse(content); - if (parsed?.env && typeof parsed.env === 'object') { - return parsed.env; - } - } catch { - // Ignore missing/malformed settings and fall back to process.env. - } - - return {}; -} - function resolveClaudeEnvValue(key, settingsEnv) { const processValue = process.env[key]; if (typeof processValue === 'string' && processValue.trim()) { diff --git a/server/routes/cli-auth.js b/server/routes/cli-auth.js index 9ca08ca9ff..f8e014fcc2 100644 --- a/server/routes/cli-auth.js +++ b/server/routes/cli-auth.js @@ -3,18 +3,10 @@ import { spawn } from 'child_process'; import fs from 'fs/promises'; import path from 'path'; import os from 'os'; +import { isTruthyValue, loadClaudeSettingsEnv } from '../utils/env-helpers.js'; const router = express.Router(); -function isTruthyValue(value) { - if (typeof value !== 'string') { - return false; - } - - const normalized = value.trim().toLowerCase(); - return normalized === '1' || normalized === 'true' || normalized === 'yes' || normalized === 'on'; -} - router.get('/claude/status', async (req, res) => { try { const credentialsResult = await checkClaudeCredentials(); @@ -108,22 +100,6 @@ router.get('/gemini/status', async (req, res) => { } }); -async function loadClaudeSettingsEnv() { - try { - const settingsPath = path.join(os.homedir(), '.claude', 'settings.json'); - const content = await fs.readFile(settingsPath, 'utf8'); - const settings = JSON.parse(content); - - if (settings?.env && typeof settings.env === 'object') { - return settings.env; - } - } catch (error) { - // Ignore missing or malformed settings and fall back to other auth sources. - } - - return {}; -} - /** * Checks Claude authentication credentials using two methods with priority order: * @@ -167,7 +143,8 @@ async function checkClaudeCredentials() { return { authenticated: true, email: 'API Key Auth', - method: 'api_key' + method: 'api_key', + isBedrock: false }; } diff --git a/server/utils/env-helpers.js b/server/utils/env-helpers.js new file mode 100644 index 0000000000..fa32e0dde8 --- /dev/null +++ b/server/utils/env-helpers.js @@ -0,0 +1,39 @@ +import { promises as fs } from 'fs'; +import path from 'path'; +import os from 'os'; + +/** + * Returns true if the value is a string considered "truthy" for env flags + * (e.g. CLAUDE_CODE_USE_BEDROCK). Accepts '1', 'true', 'yes', 'on' (case-insensitive). + * @param {unknown} value + * @returns {boolean} + */ +export function isTruthyValue(value) { + if (typeof value !== 'string') { + return false; + } + + const normalized = value.trim().toLowerCase(); + return normalized === '1' || normalized === 'true' || normalized === 'yes' || normalized === 'on'; +} + +/** + * Loads env key/value pairs from ~/.claude/settings.json (settings.env). + * Used for auth and model config that may be set in Claude Code settings. + * @returns {Promise>} Env object or {} on missing/malformed file. + */ +export async function loadClaudeSettingsEnv() { + try { + const settingsPath = path.join(os.homedir(), '.claude', 'settings.json'); + const content = await fs.readFile(settingsPath, 'utf8'); + const settings = JSON.parse(content); + + if (settings?.env && typeof settings.env === 'object') { + return settings.env; + } + } catch { + // Ignore missing or malformed settings and fall back to empty. + } + + return {}; +} diff --git a/src/components/settings/hooks/useSettingsController.ts b/src/components/settings/hooks/useSettingsController.ts index aa48d0911e..e4487626ce 100644 --- a/src/components/settings/hooks/useSettingsController.ts +++ b/src/components/settings/hooks/useSettingsController.ts @@ -279,6 +279,7 @@ export function useSettingsController({ isOpen, initialTab, projects, onClose }: email: null, loading: false, error: 'Failed to check authentication status', + isBedrock: false, }); return; } From d5cd3d4a1580ed3f9a0587bbdaaf98b1579b96a9 Mon Sep 17 00:00:00 2001 From: Tin TA Date: Tue, 17 Mar 2026 10:40:19 +0700 Subject: [PATCH 3/5] docs: update .env.example for AWS Bedrock authentication options --- .env.example | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.env.example b/.env.example index a555d60433..65dbdda7ba 100755 --- a/.env.example +++ b/.env.example @@ -55,11 +55,12 @@ CONTEXT_WINDOW=160000 # To use AWS Bedrock instead of the Anthropic API, uncomment the lines below. # CLAUDE_CODE_USE_BEDROCK=1 # -# AWS credentials (or use AWS_PROFILE from ~/.aws/config and ~/.aws/credentials) +# AWS authentication for Bedrock: +# - Prefer IAM role or AWS_PROFILE from ~/.aws/config and ~/.aws/credentials +# - Optionally use AWS_BEARER_TOKEN_BEDROCK for Bedrock API key auth # AWS_REGION=eu-central-1 # AWS_PROFILE=default -# AWS_ACCESS_KEY_ID=your-access-key-id -# AWS_SECRET_ACCESS_KEY=your-secret-access-key +# AWS_BEARER_TOKEN_BEDROCK=your-bedrock-bearer-token # # Default models used when selecting aliases (sonnet/opus/haiku) in the UI # ANTHROPIC_DEFAULT_SONNET_MODEL=eu.anthropic.claude-sonnet-4-5-20250929-v1:0 From 4c1fa8b153fd6289d6468c538881e10f75fdbd71 Mon Sep 17 00:00:00 2001 From: Tin TA Date: Tue, 17 Mar 2026 10:40:19 +0700 Subject: [PATCH 4/5] refactor: cetranlize Bedrock model IDs in modelConstants and simplify resolution --- .env.example | 13 +++++-------- server/claude-sdk.js | 28 ++++++++++++---------------- shared/modelConstants.js | 18 ++++++++++++++++++ 3 files changed, 35 insertions(+), 24 deletions(-) diff --git a/.env.example b/.env.example index cf411ebd0f..a41763f42e 100755 --- a/.env.example +++ b/.env.example @@ -62,12 +62,9 @@ CONTEXT_WINDOW=160000 # AWS_PROFILE=default # AWS_BEARER_TOKEN_BEDROCK=your-bedrock-bearer-token # -# Default models used when selecting aliases (sonnet/opus/haiku) in the UI -# ANTHROPIC_DEFAULT_SONNET_MODEL=eu.anthropic.claude-sonnet-4-5-20250929-v1:0 -# ANTHROPIC_DEFAULT_OPUS_MODEL=eu.anthropic.claude-opus-4-5-20251101-v1:0 -# ANTHROPIC_DEFAULT_HAIKU_MODEL=eu.anthropic.claude-haiku-4-5-20251001-v1:0 -# -# Optional explicit model overrides used by Claude SDK -# ANTHROPIC_MODEL=eu.anthropic.claude-sonnet-4-5-20250929-v1:0 -# ANTHROPIC_SMALL_FAST_MODEL=eu.anthropic.claude-haiku-4-5-20251001-v1:0 +# Model defaults for Bedrock are defined in shared/modelConstants.js. +# The UI aliases (sonnet/opus/haiku) automatically resolve to the correct +# Bedrock model IDs (anthropic.claude-*). Override only if you need a +# custom inference profile or a specific regional endpoint: +# ANTHROPIC_MODEL=us.anthropic.claude-sonnet-4-6 diff --git a/server/claude-sdk.js b/server/claude-sdk.js index 3b74ad7c88..df2ae7b6fc 100644 --- a/server/claude-sdk.js +++ b/server/claude-sdk.js @@ -475,6 +475,13 @@ function resolveClaudeEnvValue(key, settingsEnv) { return ''; } +/** + * Resolves a UI model alias (e.g. "sonnet") to the actual model ID. + * + * When Bedrock is enabled, looks up the alias in CLAUDE_MODELS.BEDROCK + * for sensible defaults. Users can still override via ANTHROPIC_MODEL + * (in env or ~/.claude/settings.json) for custom inference profiles. + */ function resolveClaudeModel(modelAlias, settingsEnv) { const requestedModel = modelAlias || CLAUDE_MODELS.DEFAULT; const isBedrockEnabled = isTruthyValue(resolveClaudeEnvValue('CLAUDE_CODE_USE_BEDROCK', settingsEnv)); @@ -482,25 +489,14 @@ function resolveClaudeModel(modelAlias, settingsEnv) { return requestedModel; } + // Allow explicit env override for custom inference profiles / regions const explicitModel = resolveClaudeEnvValue('ANTHROPIC_MODEL', settingsEnv); - const explicitFastModel = resolveClaudeEnvValue('ANTHROPIC_SMALL_FAST_MODEL', settingsEnv); - const sonnetDefault = resolveClaudeEnvValue('ANTHROPIC_DEFAULT_SONNET_MODEL', settingsEnv); - const opusDefault = resolveClaudeEnvValue('ANTHROPIC_DEFAULT_OPUS_MODEL', settingsEnv); - const haikuDefault = resolveClaudeEnvValue('ANTHROPIC_DEFAULT_HAIKU_MODEL', settingsEnv); - - if (requestedModel === 'haiku') { - return haikuDefault || explicitFastModel || explicitModel || requestedModel; - } - - if (requestedModel === 'opus' || requestedModel === 'opusplan') { - return opusDefault || explicitModel || requestedModel; - } - - if (requestedModel === 'sonnet' || requestedModel === 'sonnet[1m]') { - return sonnetDefault || explicitModel || requestedModel; + if (explicitModel) { + return explicitModel; } - return explicitModel || requestedModel; + // Look up in centralized Bedrock constants + return CLAUDE_MODELS.BEDROCK[requestedModel] || requestedModel; } /** diff --git a/shared/modelConstants.js b/shared/modelConstants.js index 514a177250..b422f01a72 100644 --- a/shared/modelConstants.js +++ b/shared/modelConstants.js @@ -21,6 +21,24 @@ export const CLAUDE_MODELS = { ], DEFAULT: "sonnet", + + /** + * AWS Bedrock model IDs keyed by SDK alias. + * + * The Claude Agent SDK supports both `global.anthropic.*` (dynamic + * routing, no pricing premium) and `anthropic.*` (base) model IDs. + * Regional prefixes (`us.`, `eu.`, etc.) are also valid but carry a + * 10% premium and are only needed for data-residency requirements. + * + * @see https://platform.claude.com/docs/en/build-with-claude/claude-on-amazon-bedrock + */ + BEDROCK: { + sonnet: "anthropic.claude-sonnet-4-6", + "sonnet[1m]": "anthropic.claude-sonnet-4-6", + opus: "anthropic.claude-opus-4-6-v1", + opusplan: "anthropic.claude-opus-4-6-v1", + haiku: "anthropic.claude-haiku-4-5-20251001-v1:0", + }, }; /** From 050f7dcf068677a1d99b6f96802e88d04aeddabf Mon Sep 17 00:00:00 2001 From: Tin TA Date: Thu, 19 Mar 2026 15:41:34 +0700 Subject: [PATCH 5/5] fix: preserve explicit model ID precedence over env override in Bedrock mode --- server/claude-sdk.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/server/claude-sdk.js b/server/claude-sdk.js index df2ae7b6fc..0d80995d39 100644 --- a/server/claude-sdk.js +++ b/server/claude-sdk.js @@ -489,6 +489,12 @@ function resolveClaudeModel(modelAlias, settingsEnv) { return requestedModel; } + // If the caller passed a specific model ID (not a UI alias), honour it directly + const UI_ALIASES = new Set(Object.keys(CLAUDE_MODELS.BEDROCK)); + if (modelAlias && !UI_ALIASES.has(requestedModel)) { + return requestedModel; + } + // Allow explicit env override for custom inference profiles / regions const explicitModel = resolveClaudeEnvValue('ANTHROPIC_MODEL', settingsEnv); if (explicitModel) {