Skip to content

Commit 6981907

Browse files
committed
UI polish, do not send disabled models
1 parent c9b078a commit 6981907

4 files changed

Lines changed: 41 additions & 67 deletions

File tree

internal/api/chat/list_supported_models_v2.go

Lines changed: 14 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -220,62 +220,37 @@ func (s *ChatServerV2) ListSupportedModels(
220220
}
221221

222222
var models []*chatv2.SupportedModel
223-
for _, config := range allModels {
224-
// Check if user has set API key for this particular model
225-
hasOwnAPIKey := false
226-
for _, model := range settings.CustomModels {
227-
if model.Slug == config.slugOpenRouter {
228-
// User has API key for this model, use slugOpenAI instead of slugOpenRouter if applicable
229-
// slug := config.slugOpenRouter
230-
// if strings.TrimSpace(config.slugOpenAI) != "" {
231-
// slug = config.slugOpenAI
232-
// }
233-
234-
models = append(models, &chatv2.SupportedModel{
235-
Name: model.Name,
236-
Slug: model.Slug,
237-
TotalContext: int64(model.ContextWindow),
238-
MaxOutput: int64(model.MaxOutput),
239-
InputPrice: int64(model.InputPrice),
240-
OutputPrice: int64(model.OutputPrice),
241-
IsCustom: true,
242-
})
243-
hasOwnAPIKey = true
244-
continue
245-
}
246-
}
247-
248-
if hasOwnAPIKey {
249-
continue
250-
}
251223

252-
// Choose the appropriate slug based on whether user has their own API key.
253-
//
254-
// Some models are only available via OpenRouter; for those, slugOpenAI may be empty.
255-
// In that case, keep using the OpenRouter slug to avoid returning an empty model slug.
256-
// slug := config.slugOpenRouter
257-
// if hasOwnAPIKey && strings.TrimSpace(config.slugOpenAI) != "" {
258-
// slug = config.slugOpenAI
259-
// }
224+
for _, model := range settings.CustomModels {
225+
models = append(models, &chatv2.SupportedModel{
226+
Name: model.Name,
227+
Slug: model.Slug,
228+
TotalContext: int64(model.ContextWindow),
229+
MaxOutput: int64(model.MaxOutput),
230+
InputPrice: int64(model.InputPrice),
231+
OutputPrice: int64(model.OutputPrice),
232+
IsCustom: true,
233+
})
234+
}
260235

236+
for _, config := range allModels {
261237
model := &chatv2.SupportedModel{
262238
Name: config.name,
263239
Slug: config.slugOpenRouter,
264240
TotalContext: config.totalContext,
265241
MaxOutput: config.maxOutput,
266242
InputPrice: config.inputPrice,
267243
OutputPrice: config.outputPrice,
268-
IsCustom: false,
269244
}
270245

271246
// If model requires own key but user hasn't provided one, mark as disabled
272247
if config.requireOwnKey {
273-
model.Disabled = true
274-
model.DisabledReason = stringPtr("Requires your own OpenAI API key. Configure it in Settings.")
248+
continue
275249
}
276250

277251
models = append(models, model)
278252
}
253+
279254
return &chatv2.ListSupportedModelsResponse{
280255
Models: models,
281256
}, nil

internal/services/toolkit/client/completion_v2.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,8 @@ func (a *AIClientV2) ChatCompletionStreamV2(ctx context.Context, callbackStream
6565
streamHandler.SendFinalization()
6666
}()
6767

68-
if llmProvider.IsCustomModel {
69-
// e.g., Strip "google/" from "google/gemini-2.5-flash"
70-
modelSlug = modelSlug[strings.Index(modelSlug, "/")+1:]
71-
}
72-
7368
oaiClient := a.GetOpenAIClient(llmProvider)
74-
params := getDefaultParamsV2(modelSlug, a.toolCallHandler.Registry)
69+
params := getDefaultParamsV2(modelSlug, a.toolCallHandler.Registry, llmProvider.IsCustomModel)
7570

7671
for {
7772
params.Messages = openaiChatHistory

internal/services/toolkit/client/utils_v2.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ func appendAssistantTextResponseV2(openaiChatHistory *OpenAIChatHistory, inappCh
5353
})
5454
}
5555

56-
func getDefaultParamsV2(modelSlug string, toolRegistry *registry.ToolRegistryV2) openaiv3.ChatCompletionNewParams {
56+
func getDefaultParamsV2(modelSlug string, toolRegistry *registry.ToolRegistryV2, isCustomModel bool) openaiv3.ChatCompletionNewParams {
5757
var reasoningModels = []string{
5858
"gpt-5",
5959
"gpt-5-mini",
@@ -67,8 +67,8 @@ func getDefaultParamsV2(modelSlug string, toolRegistry *registry.ToolRegistryV2)
6767
"codex-mini-latest",
6868
}
6969

70-
// Gemini does not support Store param
71-
if strings.HasPrefix(strings.ToLower(modelSlug), "gemini") {
70+
// Other model providers generally do not support the Store param
71+
if isCustomModel {
7272
return openaiv3.ChatCompletionNewParams{
7373
Model: modelSlug,
7474
Temperature: openaiv3.Float(0.7),

webapp/_webapp/src/views/settings/sections/api-key-settings.tsx

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -117,25 +117,29 @@ const CustomModelSection = ({ isNew, onChange, model: customModel }: CustomModel
117117
const [outputPrice, setOutputPrice] = useState<number>(customModel?.outputPrice || 0);
118118
const [modelName, setModelName] = useState(customModel?.name || "");
119119

120-
const baseInputClassName = "bg-transparent p-1 focus:outline-none mt-[4px]";
121-
const modelNameInputClassName = `${baseInputClassName} text-sm text-default-900 font-medium flex-1 truncate`;
122-
const labelClassName = `${baseInputClassName} text-xs text-default-900 w-auto`;
123-
const detailInputClassName = `${baseInputClassName} flex-1 noselect focus:outline-none
124-
rnd-cancel px-2 py-1 border !border-gray-200 dark:!border-default-200 rounded-md
125-
text-xs text-default-700 dark:text-default-300 placeholder:text-default-400 disabled:opacity-70 disabled:cursor-not-allowed`;
120+
const baseClassName = "bg-transparent p-1 focus:outline-none disabled:opacity-70 disabled:cursor-not-allowed";
121+
const modelNameInputClassName = `${baseClassName} text-sm text-default-900 font-medium flex-1 truncate rnd-cancel px-2 py-1 border !border-gray-200 dark:!border-default-200 rounded-md mr-1`;
122+
const labelClassName = `${baseClassName} text-xs text-default-900 w-auto`;
123+
const detailInputClassName = `${baseClassName} flex-1 noselect focus:outline-none rnd-cancel px-2 py-1 border !border-gray-200 dark:!border-default-200 rounded-md
124+
text-xs text-default-700 placeholder:text-default-400`;
126125

127126
const handleOnChange = (isDelete: boolean) => {
128-
if (modelName.length < 1 || slug.length < 1 || baseUrl.length < 1 || apiKey.length < 1) {
127+
if (
128+
modelName.trim().length < 1 ||
129+
slug.trim().length < 1 ||
130+
baseUrl.trim().length < 1 ||
131+
apiKey.trim().length < 1
132+
) {
129133
return;
130134
}
131135

132136
onChange(
133137
{
134138
id: id,
135-
name: modelName,
136-
baseUrl: baseUrl,
137-
slug: slug,
138-
apiKey: apiKey,
139+
name: modelName.trim(),
140+
baseUrl: baseUrl.trim(),
141+
slug: slug.trim(),
142+
apiKey: apiKey.trim(),
139143
contextWindow: contextWindow,
140144
maxOutput: maxOutput,
141145
inputPrice: inputPrice,
@@ -165,7 +169,7 @@ const CustomModelSection = ({ isNew, onChange, model: customModel }: CustomModel
165169
placeholder="My Model"
166170
type="text"
167171
disabled={!isEditing}
168-
onChange={(e) => setModelName(e.target.value.trim())}
172+
onChange={(e) => setModelName(e.target.value)}
169173
></input>
170174

171175
{isNew ? (
@@ -198,39 +202,39 @@ const CustomModelSection = ({ isNew, onChange, model: customModel }: CustomModel
198202
)}
199203
</div>
200204

201-
<div className="flex flex-row">
205+
<div className="flex flex-row mt-[4px]">
202206
<label className={labelClassName}>Slug</label>
203207
<input
204208
className={detailInputClassName}
205209
value={slug}
206-
placeholder="e.g., google/gemini-2.5-flash"
210+
placeholder="e.g., gemini-2.5-flash"
207211
type="text"
208212
disabled={!isEditing}
209-
onChange={(e) => setSlug(e.target.value.trim())}
213+
onChange={(e) => setSlug(e.target.value)}
210214
/>
211215
</div>
212216

213-
<div className="flex flex-row">
217+
<div className="flex flex-row mt-[4px]">
214218
<label className={labelClassName}>Base URL</label>
215219
<input
216220
className={detailInputClassName}
217221
value={baseUrl}
218222
placeholder="An OpenAI-compatible endpoint"
219223
type="text"
220224
disabled={!isEditing}
221-
onChange={(e) => setBaseUrl(e.target.value.trim())}
225+
onChange={(e) => setBaseUrl(e.target.value)}
222226
/>
223227
</div>
224228

225-
<div className="flex flex-row">
229+
<div className="flex flex-row mt-[4px]">
226230
<label className={labelClassName}>API Key</label>
227231
<input
228232
className={detailInputClassName}
229233
value={apiKey}
230234
placeholder="Your API Key"
231235
type={!isEditing && !isNew ? "password" : "text"}
232236
disabled={!isEditing}
233-
onChange={(e) => setApiKey(e.target.value.trim())}
237+
onChange={(e) => setApiKey(e.target.value)}
234238
/>
235239
</div>
236240

0 commit comments

Comments
 (0)