Skip to content

Commit 54ab6d5

Browse files
committed
refactor: show invalid models for better UX
1 parent 37747b9 commit 54ab6d5

File tree

3 files changed

+37
-37
lines changed

3 files changed

+37
-37
lines changed

webview-ui/src/components/settings/ApiOptions.tsx

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -200,13 +200,24 @@ const ApiOptions = ({
200200

201201
const filteredModels = filterModels(models, selectedProvider, organizationAllowList)
202202

203-
return filteredModels
203+
const modelOptions = filteredModels
204204
? Object.keys(filteredModels).map((modelId) => ({
205205
value: modelId,
206206
label: modelId,
207207
}))
208208
: []
209-
}, [selectedProvider, organizationAllowList])
209+
210+
// Add the current selected model if it's not in the valid models list
211+
// This allows users to see their invalid selection
212+
if (selectedModelId && !modelOptions.some(option => option.value === selectedModelId)) {
213+
modelOptions.unshift({
214+
value: selectedModelId,
215+
label: `${selectedModelId} (invalid)`,
216+
})
217+
}
218+
219+
return modelOptions
220+
}, [selectedProvider, organizationAllowList, selectedModelId])
210221

211222
const onProviderChange = useCallback(
212223
(value: ProviderName) => {
@@ -227,13 +238,9 @@ const ApiOptions = ({
227238
// in case we haven't set a default value for a provider
228239
if (!defaultValue) return
229240

230-
let shouldSetDefault = !modelId
231-
if (modelId) {
232-
// if a model has been set, check if it's valid with the new provider
233-
const tempConfig = { ...apiConfiguration, apiProvider: value, apiModelId: modelId }
234-
const hasValidationError = getModelValidationError(tempConfig, routerModels, organizationAllowList)
235-
shouldSetDefault = !!hasValidationError
236-
}
241+
// Only set default if no model is set at all
242+
// Don't reset invalid models - let users see their invalid selection
243+
const shouldSetDefault = !modelId
237244

238245
if (shouldSetDefault) {
239246
setApiConfigurationField(field, defaultValue)

webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.test.ts

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -284,18 +284,8 @@ describe("useSelectedModel", () => {
284284
const wrapper = createWrapper()
285285
const { result } = renderHook(() => useSelectedModel(apiConfiguration), { wrapper })
286286

287-
expect(result.current.id).toBe("anthropic/claude-sonnet-4")
288-
expect(result.current.info).toEqual({
289-
maxTokens: 8192,
290-
contextWindow: 200_000,
291-
supportsImages: true,
292-
supportsComputerUse: true,
293-
supportsPromptCache: true,
294-
inputPrice: 3.0,
295-
outputPrice: 15.0,
296-
cacheWritesPrice: 3.75,
297-
cacheReadsPrice: 0.3,
298-
})
287+
expect(result.current.id).toBe("non-existent-model")
288+
expect(result.current.info).toBeUndefined()
299289
})
300290
})
301291

webview-ui/src/components/ui/hooks/useSelectedModel.ts

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,10 @@ function getSelectedModel({
7575
apiConfiguration: ProviderSettings
7676
routerModels: RouterModels
7777
openRouterModelProviders: Record<string, ModelInfo>
78-
}): { id: string; info: ModelInfo } {
78+
}): { id: string; info: ModelInfo | undefined } {
79+
// the `undefined` case are used to show the invalid selection to prevent
80+
// users from seeing the default model if their selection is invalid
81+
// this gives a better UX than showing the default model
7982
switch (provider) {
8083
case "openrouter": {
8184
const id = apiConfiguration.openRouterModelId ?? openRouterDefaultModelId
@@ -93,48 +96,48 @@ function getSelectedModel({
9396

9497
return info
9598
? { id, info }
96-
: { id: openRouterDefaultModelId, info: routerModels.openrouter[openRouterDefaultModelId] }
99+
: { id, info: undefined }
97100
}
98101
case "requesty": {
99102
const id = apiConfiguration.requestyModelId ?? requestyDefaultModelId
100103
const info = routerModels.requesty[id]
101104
return info
102105
? { id, info }
103-
: { id: requestyDefaultModelId, info: routerModels.requesty[requestyDefaultModelId] }
106+
: { id, info: undefined }
104107
}
105108
case "glama": {
106109
const id = apiConfiguration.glamaModelId ?? glamaDefaultModelId
107110
const info = routerModels.glama[id]
108-
return info ? { id, info } : { id: glamaDefaultModelId, info: routerModels.glama[glamaDefaultModelId] }
111+
return info ? { id, info } : { id, info: undefined }
109112
}
110113
case "unbound": {
111114
const id = apiConfiguration.unboundModelId ?? unboundDefaultModelId
112115
const info = routerModels.unbound[id]
113116
return info
114117
? { id, info }
115-
: { id: unboundDefaultModelId, info: routerModels.unbound[unboundDefaultModelId] }
118+
: { id, info: undefined }
116119
}
117120
case "litellm": {
118121
const id = apiConfiguration.litellmModelId ?? litellmDefaultModelId
119122
const info = routerModels.litellm[id]
120123
return info
121124
? { id, info }
122-
: { id: litellmDefaultModelId, info: routerModels.litellm[litellmDefaultModelId] }
125+
: { id, info: undefined }
123126
}
124127
case "xai": {
125128
const id = apiConfiguration.apiModelId ?? xaiDefaultModelId
126129
const info = xaiModels[id as keyof typeof xaiModels]
127-
return info ? { id, info } : { id: xaiDefaultModelId, info: xaiModels[xaiDefaultModelId] }
130+
return info ? { id, info } : { id, info: undefined }
128131
}
129132
case "groq": {
130133
const id = apiConfiguration.apiModelId ?? groqDefaultModelId
131134
const info = groqModels[id as keyof typeof groqModels]
132-
return info ? { id, info } : { id: groqDefaultModelId, info: groqModels[groqDefaultModelId] }
135+
return info ? { id, info } : { id, info: undefined }
133136
}
134137
case "chutes": {
135138
const id = apiConfiguration.apiModelId ?? chutesDefaultModelId
136139
const info = chutesModels[id as keyof typeof chutesModels]
137-
return info ? { id, info } : { id: chutesDefaultModelId, info: chutesModels[chutesDefaultModelId] }
140+
return info ? { id, info } : { id, info: undefined }
138141
}
139142
case "bedrock": {
140143
const id = apiConfiguration.apiModelId ?? bedrockDefaultModelId
@@ -148,34 +151,34 @@ function getSelectedModel({
148151
}
149152
}
150153

151-
return info ? { id, info } : { id: bedrockDefaultModelId, info: bedrockModels[bedrockDefaultModelId] }
154+
return info ? { id, info } : { id, info: undefined }
152155
}
153156
case "vertex": {
154157
const id = apiConfiguration.apiModelId ?? vertexDefaultModelId
155158
const info = vertexModels[id as keyof typeof vertexModels]
156-
return info ? { id, info } : { id: vertexDefaultModelId, info: vertexModels[vertexDefaultModelId] }
159+
return info ? { id, info } : { id, info: undefined }
157160
}
158161
case "gemini": {
159162
const id = apiConfiguration.apiModelId ?? geminiDefaultModelId
160163
const info = geminiModels[id as keyof typeof geminiModels]
161-
return info ? { id, info } : { id: geminiDefaultModelId, info: geminiModels[geminiDefaultModelId] }
164+
return info ? { id, info } : { id, info: undefined }
162165
}
163166
case "deepseek": {
164167
const id = apiConfiguration.apiModelId ?? deepSeekDefaultModelId
165168
const info = deepSeekModels[id as keyof typeof deepSeekModels]
166-
return info ? { id, info } : { id: deepSeekDefaultModelId, info: deepSeekModels[deepSeekDefaultModelId] }
169+
return info ? { id, info } : { id, info: undefined }
167170
}
168171
case "openai-native": {
169172
const id = apiConfiguration.apiModelId ?? openAiNativeDefaultModelId
170173
const info = openAiNativeModels[id as keyof typeof openAiNativeModels]
171174
return info
172175
? { id, info }
173-
: { id: openAiNativeDefaultModelId, info: openAiNativeModels[openAiNativeDefaultModelId] }
176+
: { id, info: undefined }
174177
}
175178
case "mistral": {
176179
const id = apiConfiguration.apiModelId ?? mistralDefaultModelId
177180
const info = mistralModels[id as keyof typeof mistralModels]
178-
return info ? { id, info } : { id: mistralDefaultModelId, info: mistralModels[mistralDefaultModelId] }
181+
return info ? { id, info } : { id, info: undefined }
179182
}
180183
case "openai": {
181184
const id = apiConfiguration.openAiModelId ?? ""
@@ -206,7 +209,7 @@ function getSelectedModel({
206209
default: {
207210
const id = apiConfiguration.apiModelId ?? anthropicDefaultModelId
208211
const info = anthropicModels[id as keyof typeof anthropicModels]
209-
return info ? { id, info } : { id: anthropicDefaultModelId, info: anthropicModels[anthropicDefaultModelId] }
212+
return info ? { id, info } : { id, info: undefined }
210213
}
211214
}
212215
}

0 commit comments

Comments
 (0)