Skip to content

Commit 47c2782

Browse files
committed
feat(config): expose openai provider disabled state
1 parent 4bf6bc7 commit 47c2782

11 files changed

Lines changed: 42 additions & 1 deletion

File tree

src/components/providers/OpenAISection/OpenAISection.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ export function OpenAISection({
9393
onEdit={onEdit}
9494
onDelete={onDelete}
9595
actionsDisabled={actionsDisabled}
96+
getRowDisabled={(item) => Boolean(item.disabled)}
9697
renderContent={(item) => {
9798
const stats = getOpenAIProviderStats(item.apiKeyEntries, keyStats, item.prefix);
9899
const headerEntries = Object.entries(item.headers || {});
@@ -101,7 +102,14 @@ export function OpenAISection({
101102

102103
return (
103104
<Fragment>
104-
<div className="item-title">{item.name}</div>
105+
<div className="item-title">
106+
{item.name}
107+
{item.disabled && (
108+
<span className="status-badge muted">
109+
{t('ai_providers.openai_disabled_badge')}
110+
</span>
111+
)}
112+
</div>
105113
{item.priority !== undefined && (
106114
<div className={styles.fieldRow}>
107115
<span className={styles.fieldLabel}>{t('common.priority')}:</span>

src/components/providers/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export interface ModelEntry {
1010
export interface OpenAIFormState {
1111
name: string;
1212
priority?: number;
13+
disabled: boolean;
1314
prefix: string;
1415
baseUrl: string;
1516
headers: HeaderEntry[];

src/i18n/locales/en.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,9 @@
408408
"openai_add_modal_name_placeholder": "e.g.: openrouter",
409409
"openai_add_modal_url_label": "Base URL:",
410410
"openai_add_modal_url_placeholder": "e.g.: https://openrouter.ai/api/v1",
411+
"openai_enabled_label": "Enable provider",
412+
"openai_enabled_hint": "When disabled, the provider stays in config but is skipped for routing, model registration, and API-key clients.",
413+
"openai_disabled_badge": "Disabled",
411414
"openai_add_modal_keys_label": "API Keys",
412415
"openai_edit_modal_keys_label": "API Keys",
413416
"openai_keys_hint": "Add each key separately with an optional proxy URL to keep things organized.",

src/i18n/locales/ru.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,9 @@
408408
"openai_add_modal_name_placeholder": "например: openrouter",
409409
"openai_add_modal_url_label": "Базовый URL:",
410410
"openai_add_modal_url_placeholder": "например: https://openrouter.ai/api/v1",
411+
"openai_enabled_label": "Включить провайдера",
412+
"openai_enabled_hint": "Если отключено, провайдер останется в конфигурации, но не будет использоваться для маршрутизации, регистрации моделей и API-key клиентов.",
413+
"openai_disabled_badge": "Отключен",
411414
"openai_add_modal_keys_label": "API-ключи",
412415
"openai_edit_modal_keys_label": "API-ключи",
413416
"openai_keys_hint": "Добавляйте каждый ключ отдельно с необязательным URL прокси для удобства.",

src/i18n/locales/zh-CN.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,9 @@
408408
"openai_add_modal_name_placeholder": "例如: openrouter",
409409
"openai_add_modal_url_label": "Base URL:",
410410
"openai_add_modal_url_placeholder": "例如: https://openrouter.ai/api/v1",
411+
"openai_enabled_label": "启用提供商",
412+
"openai_enabled_hint": "关闭后会保留配置,但后端不会把该提供商用于路由、模型注册或 API Key 客户端。",
413+
"openai_disabled_badge": "已禁用",
411414
"openai_add_modal_keys_label": "API密钥",
412415
"openai_edit_modal_keys_label": "API密钥",
413416
"openai_keys_hint": "每个密钥可搭配一个可选代理地址,更便于管理。",

src/pages/AiProvidersOpenAIEditLayout.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export type OpenAIEditOutletContext = {
4444
const buildEmptyForm = (): OpenAIFormState => ({
4545
name: '',
4646
priority: undefined,
47+
disabled: false,
4748
prefix: '',
4849
baseUrl: '',
4950
headers: [],
@@ -108,6 +109,7 @@ const buildOpenAIBaseline = (form: OpenAIFormState, testModel: string): OpenAIEd
108109
name: String(form.name ?? '').trim(),
109110
priority:
110111
form.priority !== undefined && Number.isFinite(form.priority) ? Math.trunc(form.priority) : null,
112+
disabled: Boolean(form.disabled),
111113
prefix: String(form.prefix ?? '').trim(),
112114
baseUrl: String(form.baseUrl ?? '').trim(),
113115
headers: normalizeHeaderEntries(form.headers),
@@ -287,6 +289,7 @@ export function AiProvidersOpenAIEditLayout() {
287289
const seededForm: OpenAIFormState = {
288290
name: initialData.name,
289291
priority: initialData.priority,
292+
disabled: Boolean(initialData.disabled),
290293
prefix: initialData.prefix ?? '',
291294
baseUrl: initialData.baseUrl,
292295
headers: headersToEntries(initialData.headers),
@@ -411,6 +414,7 @@ export function AiProvidersOpenAIEditLayout() {
411414
baseline !== null &&
412415
(baseline.name !== form.name.trim() ||
413416
baseline.priority !== normalizedPriority ||
417+
baseline.disabled !== Boolean(form.disabled) ||
414418
baseline.prefix !== form.prefix.trim() ||
415419
baseline.baseUrl !== form.baseUrl.trim() ||
416420
baseline.testModel !== normalizedTestModel ||
@@ -456,6 +460,7 @@ export function AiProvidersOpenAIEditLayout() {
456460
const payload: OpenAIProviderConfig = {
457461
name,
458462
prefix: form.prefix?.trim() || undefined,
463+
disabled: Boolean(form.disabled),
459464
baseUrl,
460465
headers: buildHeaderObject(form.headers),
461466
apiKeyEntries: form.apiKeyEntries.map((entry: ApiKeyEntry) => ({

src/pages/AiProvidersOpenAIEditPage.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { HeaderInputList } from '@/components/ui/HeaderInputList';
77
import { Input } from '@/components/ui/Input';
88
import { ModelInputList } from '@/components/ui/ModelInputList';
99
import { Select } from '@/components/ui/Select';
10+
import { ToggleSwitch } from '@/components/ui/ToggleSwitch';
1011
import { SecondaryScreenShell } from '@/components/common/SecondaryScreenShell';
1112
import { useEdgeSwipeBack } from '@/hooks/useEdgeSwipeBack';
1213
import { useNotificationStore } from '@/stores';
@@ -569,6 +570,17 @@ export function AiProvidersOpenAIEditPage() {
569570
disabled={saving || disableControls || isTestingKeys}
570571
/>
571572

573+
<div className="form-group">
574+
<ToggleSwitch
575+
label={t('ai_providers.openai_enabled_label')}
576+
checked={!form.disabled}
577+
onChange={(enabled) => setForm((prev) => ({ ...prev, disabled: !enabled }))}
578+
disabled={saving || disableControls || isTestingKeys}
579+
ariaLabel={t('ai_providers.openai_enabled_label')}
580+
/>
581+
<div className={styles.sectionHint}>{t('ai_providers.openai_enabled_hint')}</div>
582+
</div>
583+
572584
<HeaderInputList
573585
entries={form.headers}
574586
onChange={(entries) => setForm((prev) => ({ ...prev, headers: entries }))}

src/services/api/providers.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ const serializeOpenAIProvider = (provider: OpenAIProviderConfig) => {
144144
? provider.apiKeyEntries.map((entry) => serializeApiKeyEntry(entry))
145145
: []
146146
};
147+
payload.disabled = Boolean(provider.disabled);
147148
if (provider.prefix?.trim()) payload.prefix = provider.prefix.trim();
148149
const headers = serializeHeaders(provider.headers);
149150
if (headers) payload.headers = headers;

src/services/api/transformers.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ const normalizeOpenAIProvider = (provider: unknown): OpenAIProviderConfig | null
231231
const models = normalizeModelAliases(provider.models);
232232
const priority = provider.priority ?? provider['priority'];
233233
const testModel = provider['test-model'] ?? provider.testModel;
234+
const disabled = normalizeBoolean(provider.disabled ?? provider['disabled']);
234235

235236
const result: OpenAIProviderConfig = {
236237
name: String(name),
@@ -244,6 +245,7 @@ const normalizeOpenAIProvider = (provider: unknown): OpenAIProviderConfig | null
244245
if (models.length) result.models = models;
245246
if (priority !== undefined) result.priority = Number(priority);
246247
if (testModel) result.testModel = String(testModel);
248+
if (disabled !== undefined) result.disabled = disabled;
247249
return result;
248250
};
249251

src/stores/useOpenAIEditDraftStore.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export type KeyTestStatus = {
2323
export type OpenAIEditBaseline = {
2424
name: string;
2525
priority: number | null;
26+
disabled: boolean;
2627
prefix: string;
2728
baseUrl: string;
2829
headers: Array<{ key: string; value: string }>;
@@ -68,6 +69,7 @@ const resolveAction = <T,>(action: SetStateAction<T>, prev: T): T =>
6869
const buildEmptyForm = (): OpenAIFormState => ({
6970
name: '',
7071
prefix: '',
72+
disabled: false,
7173
baseUrl: '',
7274
headers: [],
7375
apiKeyEntries: [buildApiKeyEntry()],

0 commit comments

Comments
 (0)