From 470501adb8aadfe9ae61aeb5e910cc8d3056d60b Mon Sep 17 00:00:00 2001 From: Roo Code Date: Sat, 20 Dec 2025 14:19:39 +0000 Subject: [PATCH] fix: allow custom model names in Model Combo Box for static providers Replace the generic Select component with ModelPicker for providers with static model lists (Anthropic, Mistral, Gemini, etc.). The ModelPicker component already supports entering custom model IDs that are not in the predefined list. This enables users to use newly released models from providers without waiting for Roo Code updates. Fixes #10229 --- .../src/components/settings/ApiOptions.tsx | 122 +++++++++--------- 1 file changed, 58 insertions(+), 64 deletions(-) diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index 72518c783f..467b840a09 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -104,7 +104,7 @@ import { import { MODELS_BY_PROVIDER, PROVIDERS } from "./constants" import { inputEventTransform, noTransform } from "./transforms" -import { ModelInfoView } from "./ModelInfoView" +import { ModelPicker } from "./ModelPicker" import { ApiErrorMessage } from "./ApiErrorMessage" import { ThinkingBudget } from "./ThinkingBudget" import { Verbosity } from "./Verbosity" @@ -173,7 +173,6 @@ const ApiOptions = ({ [customHeaders, apiConfiguration?.openAiHeaders, setApiConfigurationField], ) - const [isDescriptionExpanded, setIsDescriptionExpanded] = useState(false) const [isAdvancedSettingsOpen, setIsAdvancedSettingsOpen] = useState(false) const handleInputChange = useCallback( @@ -272,31 +271,47 @@ const ApiOptions = ({ setErrorMessage(apiValidationResult) }, [apiConfiguration, routerModels, organizationAllowList, setErrorMessage]) - const selectedProviderModels = useMemo(() => { + // Get models for static providers (those with models defined in MODELS_BY_PROVIDER) + const staticProviderModels = useMemo(() => { const models = MODELS_BY_PROVIDER[selectedProvider] + if (!models) return null + return filterModels(models, selectedProvider, organizationAllowList) + }, [selectedProvider, organizationAllowList]) - if (!models) return [] + // Get the default model ID for the current static provider + const staticProviderDefaultModelId = useMemo(() => { + const defaults: Partial> = { + anthropic: anthropicDefaultModelId, + "openai-native": openAiNativeDefaultModelId, + gemini: geminiDefaultModelId, + deepseek: deepSeekDefaultModelId, + doubao: doubaoDefaultModelId, + moonshot: moonshotDefaultModelId, + mistral: mistralDefaultModelId, + xai: xaiDefaultModelId, + groq: groqDefaultModelId, + cerebras: cerebrasDefaultModelId, + baseten: basetenDefaultModelId, + bedrock: bedrockDefaultModelId, + vertex: vertexDefaultModelId, + sambanova: sambaNovaDefaultModelId, + zai: + apiConfiguration.zaiApiLine === "china_coding" + ? mainlandZAiDefaultModelId + : internationalZAiDefaultModelId, + fireworks: fireworksDefaultModelId, + featherless: featherlessDefaultModelId, + minimax: minimaxDefaultModelId, + "qwen-code": qwenCodeDefaultModelId, + } + return defaults[selectedProvider] || "" + }, [selectedProvider, apiConfiguration.zaiApiLine]) - const filteredModels = filterModels(models, selectedProvider, organizationAllowList) - - // Include the currently selected model even if deprecated (so users can see what they have selected) - // But filter out other deprecated models from being newly selectable - const availableModels = filteredModels - ? Object.entries(filteredModels) - .filter(([modelId, modelInfo]) => { - // Always include the currently selected model - if (modelId === selectedModelId) return true - // Filter out deprecated models that aren't currently selected - return !modelInfo.deprecated - }) - .map(([modelId]) => ({ - value: modelId, - label: modelId, - })) - : [] - - return availableModels - }, [selectedProvider, organizationAllowList, selectedModelId]) + // Get the provider label for display + const staticProviderLabel = useMemo(() => { + const provider = PROVIDERS.find(({ value }) => value === selectedProvider) + return provider?.label || selectedProvider + }, [selectedProvider]) const onProviderChange = useCallback( (value: ProviderName) => { @@ -781,16 +796,16 @@ const ApiOptions = ({ )} {/* Skip generic model picker for claude-code since it has its own in ClaudeCode.tsx */} - {selectedProviderModels.length > 0 && selectedProvider !== "claude-code" && ( + {staticProviderModels !== null && selectedProvider !== "claude-code" && ( <> -
- - -
- - {/* Show error if a deprecated model is selected */} - {selectedModelInfo?.deprecated && ( - - )} + } + }} + defaultModelId={staticProviderDefaultModelId} + models={staticProviderModels} + modelIdKey="apiModelId" + serviceName={staticProviderLabel} + serviceUrl={docs?.url || ""} + organizationAllowList={organizationAllowList} + simplifySettings={fromWelcomeView} + hidePricing + /> {selectedProvider === "bedrock" && selectedModelId === "custom-arn" && ( )} - - {/* Only show model info if not deprecated */} - {!selectedModelInfo?.deprecated && ( - - )} )}