feat(providers): pick the connectivity test model from a dropdown

Replace the free-form test-model input on the OpenAI-compatible and
Claude edit sheets with a Select whose options are derived from the
already-added models. An Auto entry (annotated with the first model
name) maps to an empty form.testModel so connectivity tests keep
falling back to the first configured model when the user hasn't made
an explicit pick. If an existing config carried a test model that
isn't in form.models, surface it as a custom option so the value is
preserved instead of silently dropped.
This commit is contained in:
LTbinglingfeng
2026-05-25 02:09:06 +08:00
parent 8c3c9c1ec4
commit 87ddd62c40
5 changed files with 44 additions and 3 deletions
@@ -9,6 +9,7 @@ import {
IconX,
} from '@/components/ui/icons';
import { Collapsible } from '@/components/ui/Collapsible';
import { Select } from '@/components/ui/Select';
import { hasDisableAllModelsRule } from '@/components/providers/utils';
import type {
GeminiKeyConfig,
@@ -271,6 +272,33 @@ export function BaseProviderForm({
return set;
}, [form.models]);
const testModelOptions = useMemo(() => {
const seen = new Set<string>();
const names: string[] = [];
form.models.forEach((m) => {
const name = (m.name ?? '').trim();
if (!name || seen.has(name)) return;
seen.add(name);
names.push(name);
});
const firstName = names[0];
const autoLabel = firstName
? t('providersPage.form.testModelAutoWith', { name: firstName })
: t('providersPage.form.testModelAutoEmpty');
const opts: Array<{ value: string; label: string }> = [
{ value: '', label: autoLabel },
];
names.forEach((n) => opts.push({ value: n, label: n }));
const tm = (form.testModel ?? '').trim();
if (tm && !seen.has(tm)) {
opts.push({
value: tm,
label: t('providersPage.form.testModelCustom', { name: tm }),
});
}
return opts;
}, [form.models, form.testModel, t]);
const openDiscovery = () => {
setDiscoveryOpen(true);
if (!discovery.loading && !discovery.hasFetched) {
@@ -518,12 +546,13 @@ export function BaseProviderForm({
</span>
) : null}
</label>
<input
<Select
id={`${fid}-testModel`}
className={styles.input}
value={form.testModel ?? ''}
onChange={(e) => updateField('testModel', e.target.value)}
options={testModelOptions}
onChange={(value) => updateField('testModel', value)}
disabled={mutating}
ariaLabel={t('providersPage.form.testModel')}
/>
{brand === 'claude' ? (
<div className={styles.connectivityRow}>
+3
View File
@@ -1550,6 +1550,9 @@
"websockets": "Enable WebSockets",
"testModel": "Test model",
"testModelClaudeHint": "Used for connectivity testing only; not saved",
"testModelAutoWith": "Auto · {{name}}",
"testModelAutoEmpty": "Auto (no models added yet)",
"testModelCustom": "{{name}} (custom)",
"modelsSection": "Custom models",
"addModel": "Add model",
"headersSection": "Request headers",
+3
View File
@@ -1547,6 +1547,9 @@
"websockets": "Включить WebSockets",
"testModel": "Тестовая модель",
"testModelClaudeHint": "Используется только для проверки соединения; не сохраняется",
"testModelAutoWith": "Авто · {{name}}",
"testModelAutoEmpty": "Авто (модели ещё не добавлены)",
"testModelCustom": "{{name}} (своя)",
"modelsSection": "Пользовательские модели",
"addModel": "Добавить модель",
"headersSection": "Заголовки запроса",
+3
View File
@@ -1550,6 +1550,9 @@
"websockets": "启用 WebSockets",
"testModel": "测试模型",
"testModelClaudeHint": "仅用于连通性测试,不会被保存",
"testModelAutoWith": "自动 · {{name}}",
"testModelAutoEmpty": "自动(尚未添加模型)",
"testModelCustom": "{{name}}(自定义)",
"modelsSection": "自定义模型",
"addModel": "添加模型",
"headersSection": "请求头",
+3
View File
@@ -1576,6 +1576,9 @@
"websockets": "啟用 WebSockets",
"testModel": "測試模型",
"testModelClaudeHint": "僅用於連通性測試,不會被儲存",
"testModelAutoWith": "自動 · {{name}}",
"testModelAutoEmpty": "自動(尚未新增模型)",
"testModelCustom": "{{name}}(自訂)",
"modelsSection": "自訂模型",
"addModel": "新增模型",
"headersSection": "請求標頭",