@@ -11,48 +11,34 @@ import { ExternalLinkIcon } from "@radix-ui/react-icons"
1111import {
1212 ApiConfiguration ,
1313 ModelInfo ,
14- anthropicDefaultModelId ,
15- anthropicModels ,
1614 azureOpenAiDefaultApiVersion ,
17- bedrockDefaultModelId ,
18- bedrockModels ,
19- deepSeekDefaultModelId ,
20- deepSeekModels ,
21- geminiDefaultModelId ,
22- geminiModels ,
2315 glamaDefaultModelId ,
2416 glamaDefaultModelInfo ,
2517 mistralDefaultModelId ,
26- mistralModels ,
2718 openAiModelInfoSaneDefaults ,
28- openAiNativeDefaultModelId ,
29- openAiNativeModels ,
3019 openRouterDefaultModelId ,
3120 openRouterDefaultModelInfo ,
32- vertexDefaultModelId ,
33- vertexModels ,
3421 unboundDefaultModelId ,
3522 unboundDefaultModelInfo ,
3623 requestyDefaultModelId ,
3724 requestyDefaultModelInfo ,
38- xaiDefaultModelId ,
39- xaiModels ,
4025 ApiProvider ,
41- vscodeLlmModels ,
42- vscodeLlmDefaultModelId ,
4326} from "@roo/shared/api"
4427import { ExtensionMessage } from "@roo/shared/ExtensionMessage"
28+ import { AWS_REGIONS } from "@roo/shared/aws_regions"
4529
46- import { vscode } from "@/utils/vscode"
47- import { validateApiConfiguration , validateModelId , validateBedrockArn } from "@/utils/validate"
30+ import { vscode } from "@src/utils/vscode"
31+ import { validateApiConfiguration , validateModelId , validateBedrockArn } from "@src/utils/validate"
32+ import { normalizeApiConfiguration } from "@src/utils/normalizeApiConfiguration"
4833import {
4934 useOpenRouterModelProviders ,
5035 OPENROUTER_DEFAULT_PROVIDER_NAME ,
51- } from "@/components/ui/hooks/useOpenRouterModelProviders"
52- import { Select , SelectContent , SelectItem , SelectTrigger , SelectValue , SelectSeparator , Button } from "@/components/ui"
53- import { MODELS_BY_PROVIDER , PROVIDERS , VERTEX_REGIONS , REASONING_MODELS } from "./constants"
54- import { AWS_REGIONS } from "@roo/shared/aws_regions"
36+ } from "@src/components/ui/hooks/useOpenRouterModelProviders"
37+ import { Select , SelectContent , SelectItem , SelectTrigger , SelectValue , Button } from "@src/components/ui"
38+
5539import { VSCodeButtonLink } from "../common/VSCodeButtonLink"
40+
41+ import { MODELS_BY_PROVIDER , PROVIDERS , VERTEX_REGIONS , REASONING_MODELS } from "./constants"
5642import { ModelInfoView } from "./ModelInfoView"
5743import { ModelPicker } from "./ModelPicker"
5844import { TemperatureControl } from "./TemperatureControl"
@@ -281,6 +267,7 @@ const ApiOptions = ({
281267 // Helper function to get the documentation URL and name for the currently selected provider
282268 const getSelectedProviderDocUrl = ( ) : { url : string ; name : string } | undefined => {
283269 const displayName = getProviderDisplayName ( selectedProvider )
270+
284271 if ( ! displayName ) {
285272 return undefined
286273 }
@@ -294,6 +281,49 @@ const ApiOptions = ({
294281 }
295282 }
296283
284+ const onApiProviderChange = useCallback (
285+ ( value : ApiProvider ) => {
286+ // It would be much easier to have a single attribute that stores
287+ // the modelId, but we have a separate attribute for each of
288+ // OpenRouter, Glama, Unbound, and Requesty.
289+ // If you switch to one of these providers and the corresponding
290+ // modelId is not set then you immediately end up in an error state.
291+ // To address that we set the modelId to the default value for th
292+ // provider if it's not already set.
293+ switch ( value ) {
294+ case "openrouter" :
295+ if ( ! apiConfiguration . openRouterModelId ) {
296+ setApiConfigurationField ( "openRouterModelId" , openRouterDefaultModelId )
297+ }
298+ break
299+ case "glama" :
300+ if ( ! apiConfiguration . glamaModelId ) {
301+ setApiConfigurationField ( "glamaModelId" , glamaDefaultModelId )
302+ }
303+ break
304+ case "unbound" :
305+ if ( ! apiConfiguration . unboundModelId ) {
306+ setApiConfigurationField ( "unboundModelId" , unboundDefaultModelId )
307+ }
308+ break
309+ case "requesty" :
310+ if ( ! apiConfiguration . requestyModelId ) {
311+ setApiConfigurationField ( "requestyModelId" , requestyDefaultModelId )
312+ }
313+ break
314+ }
315+
316+ setApiConfigurationField ( "apiProvider" , value )
317+ } ,
318+ [
319+ setApiConfigurationField ,
320+ apiConfiguration . openRouterModelId ,
321+ apiConfiguration . glamaModelId ,
322+ apiConfiguration . unboundModelId ,
323+ apiConfiguration . requestyModelId ,
324+ ] ,
325+ )
326+
297327 return (
298328 < div className = "flex flex-col gap-3" >
299329 < div className = "flex flex-col gap-1 relative" >
@@ -312,16 +342,12 @@ const ApiOptions = ({
312342 </ div >
313343 ) }
314344 </ div >
315- < Select
316- value = { selectedProvider }
317- onValueChange = { ( value ) => setApiConfigurationField ( "apiProvider" , value as ApiProvider ) } >
345+ < Select value = { selectedProvider } onValueChange = { ( value ) => onApiProviderChange ( value as ApiProvider ) } >
318346 < SelectTrigger className = "w-full" >
319347 < SelectValue placeholder = { t ( "settings:common.select" ) } />
320348 </ SelectTrigger >
321349 < SelectContent >
322- < SelectItem value = "openrouter" > OpenRouter</ SelectItem >
323- < SelectSeparator />
324- { PROVIDERS . filter ( ( p ) => p . value !== "openrouter" ) . map ( ( { value, label } ) => (
350+ { PROVIDERS . map ( ( { value, label } ) => (
325351 < SelectItem key = { value } value = { value } >
326352 { label }
327353 </ SelectItem >
@@ -1738,113 +1764,4 @@ const ApiOptions = ({
17381764 )
17391765}
17401766
1741- export function normalizeApiConfiguration ( apiConfiguration ?: ApiConfiguration ) {
1742- const provider = apiConfiguration ?. apiProvider || "anthropic"
1743- const modelId = apiConfiguration ?. apiModelId
1744- const getProviderData = ( models : Record < string , ModelInfo > , defaultId : string ) => {
1745- let selectedModelId : string
1746- let selectedModelInfo : ModelInfo
1747-
1748- if ( modelId && modelId in models ) {
1749- selectedModelId = modelId
1750- selectedModelInfo = models [ modelId ]
1751- } else {
1752- selectedModelId = defaultId
1753- selectedModelInfo = models [ defaultId ]
1754- }
1755-
1756- return { selectedProvider : provider , selectedModelId, selectedModelInfo }
1757- }
1758-
1759- switch ( provider ) {
1760- case "anthropic" :
1761- return getProviderData ( anthropicModels , anthropicDefaultModelId )
1762- case "xai" :
1763- return getProviderData ( xaiModels , xaiDefaultModelId )
1764- case "bedrock" :
1765- // Special case for custom ARN
1766- if ( modelId === "custom-arn" ) {
1767- return {
1768- selectedProvider : provider ,
1769- selectedModelId : "custom-arn" ,
1770- selectedModelInfo : {
1771- maxTokens : 5000 ,
1772- contextWindow : 128_000 ,
1773- supportsPromptCache : false ,
1774- supportsImages : true ,
1775- } ,
1776- }
1777- }
1778- return getProviderData ( bedrockModels , bedrockDefaultModelId )
1779- case "vertex" :
1780- return getProviderData ( vertexModels , vertexDefaultModelId )
1781- case "gemini" :
1782- return getProviderData ( geminiModels , geminiDefaultModelId )
1783- case "deepseek" :
1784- return getProviderData ( deepSeekModels , deepSeekDefaultModelId )
1785- case "openai-native" :
1786- return getProviderData ( openAiNativeModels , openAiNativeDefaultModelId )
1787- case "mistral" :
1788- return getProviderData ( mistralModels , mistralDefaultModelId )
1789- case "openrouter" :
1790- return {
1791- selectedProvider : provider ,
1792- selectedModelId : apiConfiguration ?. openRouterModelId || openRouterDefaultModelId ,
1793- selectedModelInfo : apiConfiguration ?. openRouterModelInfo || openRouterDefaultModelInfo ,
1794- }
1795- case "glama" :
1796- return {
1797- selectedProvider : provider ,
1798- selectedModelId : apiConfiguration ?. glamaModelId || glamaDefaultModelId ,
1799- selectedModelInfo : apiConfiguration ?. glamaModelInfo || glamaDefaultModelInfo ,
1800- }
1801- case "unbound" :
1802- return {
1803- selectedProvider : provider ,
1804- selectedModelId : apiConfiguration ?. unboundModelId || unboundDefaultModelId ,
1805- selectedModelInfo : apiConfiguration ?. unboundModelInfo || unboundDefaultModelInfo ,
1806- }
1807- case "requesty" :
1808- return {
1809- selectedProvider : provider ,
1810- selectedModelId : apiConfiguration ?. requestyModelId || requestyDefaultModelId ,
1811- selectedModelInfo : apiConfiguration ?. requestyModelInfo || requestyDefaultModelInfo ,
1812- }
1813- case "openai" :
1814- return {
1815- selectedProvider : provider ,
1816- selectedModelId : apiConfiguration ?. openAiModelId || "" ,
1817- selectedModelInfo : apiConfiguration ?. openAiCustomModelInfo || openAiModelInfoSaneDefaults ,
1818- }
1819- case "ollama" :
1820- return {
1821- selectedProvider : provider ,
1822- selectedModelId : apiConfiguration ?. ollamaModelId || "" ,
1823- selectedModelInfo : openAiModelInfoSaneDefaults ,
1824- }
1825- case "lmstudio" :
1826- return {
1827- selectedProvider : provider ,
1828- selectedModelId : apiConfiguration ?. lmStudioModelId || "" ,
1829- selectedModelInfo : openAiModelInfoSaneDefaults ,
1830- }
1831- case "vscode-lm" :
1832- const modelFamily = apiConfiguration ?. vsCodeLmModelSelector ?. family ?? vscodeLlmDefaultModelId
1833- const modelInfo = {
1834- ...openAiModelInfoSaneDefaults ,
1835- ...vscodeLlmModels [ modelFamily as keyof typeof vscodeLlmModels ] ,
1836- supportsImages : false , // VSCode LM API currently doesn't support images.
1837- }
1838- return {
1839- selectedProvider : provider ,
1840- selectedModelId : apiConfiguration ?. vsCodeLmModelSelector
1841- ? `${ apiConfiguration . vsCodeLmModelSelector . vendor } /${ apiConfiguration . vsCodeLmModelSelector . family } `
1842- : "" ,
1843- selectedModelInfo : modelInfo ,
1844- }
1845- default :
1846- return getProviderData ( anthropicModels , anthropicDefaultModelId )
1847- }
1848- }
1849-
18501767export default memo ( ApiOptions )
0 commit comments