diff --git a/src/features/authFiles/components/AuthFileDetailsSheet.tsx b/src/features/authFiles/components/AuthFileDetailsSheet.tsx
index 5ca106ca..b162d3ca 100644
--- a/src/features/authFiles/components/AuthFileDetailsSheet.tsx
+++ b/src/features/authFiles/components/AuthFileDetailsSheet.tsx
@@ -208,6 +208,16 @@ export function AuthFileDetailsSheet(props: AuthFileDetailsSheetProps) {
disabled={disableControls || editor.saving || !editor.json}
onChange={(e) => onChange('weight', e.target.value)}
/>
+
+
+
onChange('disableCooling', value)}
+ disabled={disableControls || editor.saving || !editor.json}
+ ariaLabel={t('auth_files.disable_cooling_label')}
+ />
+ {t('auth_files.disable_cooling_hint')}
+
{supportsAuthFileWebsockets(editor.providerKey) && (
diff --git a/src/features/authFiles/constants.ts b/src/features/authFiles/constants.ts
index be1db86c..8d72494a 100644
--- a/src/features/authFiles/constants.ts
+++ b/src/features/authFiles/constants.ts
@@ -194,6 +194,12 @@ export const parseDisableCoolingValue = (value: unknown): boolean | undefined =>
return undefined;
};
+export const readAuthFileDisableCooling = (value: Record): boolean => {
+ const canonical = parseDisableCoolingValue(value.disable_cooling);
+ if (canonical !== undefined) return canonical;
+ return parseDisableCoolingValue(value['disable-cooling']) ?? false;
+};
+
export const supportsAuthFileWebsockets = (providerKey: string): boolean =>
AUTH_FILE_WEBSOCKET_PROVIDERS.has(normalizeProviderKey(providerKey));
diff --git a/src/features/authFiles/hooks/useAuthFilesPrefixProxyEditor.ts b/src/features/authFiles/hooks/useAuthFilesPrefixProxyEditor.ts
index 8a25244b..d6f286f2 100644
--- a/src/features/authFiles/hooks/useAuthFilesPrefixProxyEditor.ts
+++ b/src/features/authFiles/hooks/useAuthFilesPrefixProxyEditor.ts
@@ -8,6 +8,7 @@ import {
applyAuthFileUsingApi,
normalizeProviderKey,
parsePriorityValue,
+ readAuthFileDisableCooling,
readAuthFileWebsockets,
readAuthFileUsingApi,
supportsAuthFileWebsockets,
@@ -35,6 +36,7 @@ export type PrefixProxyEditorField =
| 'proxyUrl'
| 'priority'
| 'weight'
+ | 'disableCooling'
| 'websockets'
| 'usingApi'
| 'note'
@@ -59,6 +61,8 @@ export type PrefixProxyEditorState = {
priority: string;
weight: string;
weightError: string | null;
+ disableCooling: boolean;
+ disableCoolingTouched: boolean;
websockets: boolean;
websocketsTouched: boolean;
usingApi: boolean;
@@ -313,6 +317,18 @@ export const buildAuthFileFieldsPatch = (
patch.weight = nextWeight;
}
+ if (editor.disableCoolingTouched) {
+ const originalDisableCooling = readAuthFileDisableCooling(original);
+ const nextDisableCooling = Boolean(editor.disableCooling);
+ if (nextDisableCooling !== originalDisableCooling) {
+ const field =
+ original.disable_cooling === undefined && original['disable-cooling'] !== undefined
+ ? 'disable-cooling'
+ : 'disable_cooling';
+ patch[field] = nextDisableCooling;
+ }
+ }
+
if (editor.noteTouched) {
const originalNote = normalizeTextField(original.note);
const nextNote = editor.note.trim();
@@ -400,6 +416,13 @@ const buildPrefixProxyUpdatedText = (
}
}
+ if (patch.disable_cooling !== undefined) {
+ next.disable_cooling = patch.disable_cooling;
+ }
+ if (patch['disable-cooling'] !== undefined) {
+ next['disable-cooling'] = patch['disable-cooling'];
+ }
+
if (patch.note !== undefined) {
if (patch.note) {
next.note = patch.note;
@@ -483,6 +506,8 @@ export function useAuthFilesPrefixProxyEditor(
priority: '',
weight: '',
weightError: null,
+ disableCooling: false,
+ disableCoolingTouched: false,
websockets: false,
websocketsTouched: false,
usingApi: false,
@@ -534,6 +559,7 @@ export function useAuthFilesPrefixProxyEditor(
const proxyUrl = typeof json.proxy_url === 'string' ? json.proxy_url : '';
const priority = parsePriorityValue(json.priority);
const weight = readCredentialWeight(json.weight);
+ const disableCooling = readAuthFileDisableCooling(json);
const websockets = supportsAuthFileWebsockets(providerKey)
? readAuthFileWebsockets(json)
: false;
@@ -564,6 +590,8 @@ export function useAuthFilesPrefixProxyEditor(
priority: priority !== undefined ? String(priority) : '',
weight: weight !== undefined ? String(weight) : '',
weightError: null,
+ disableCooling,
+ disableCoolingTouched: false,
websockets,
websocketsTouched: false,
usingApi,
@@ -606,6 +634,13 @@ export function useAuthFilesPrefixProxyEditor(
weightError: error ? t(credentialWeightErrorKey(error)) : null,
};
}
+ if (field === 'disableCooling') {
+ return {
+ ...prev,
+ disableCooling: Boolean(value),
+ disableCoolingTouched: true,
+ };
+ }
if (field === 'websockets') {
return { ...prev, websockets: Boolean(value), websocketsTouched: true };
}
diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json
index 03755f8e..a0cf1d78 100644
--- a/src/i18n/locales/en.json
+++ b/src/i18n/locales/en.json
@@ -343,6 +343,8 @@
"weight_hint": "Defaults to 1. Values at or below 0 exclude this credential from weighted scheduling; maximum 1,000,000.",
"weight_invalid_integer": "Weight must be an integer.",
"weight_invalid_max": "Weight cannot exceed 1,000,000.",
+ "disable_cooling_label": "Disable exponential cooldown (disable_cooling)",
+ "disable_cooling_hint": "Skip failure cooldown and exponential backoff only for this credential; other credentials are unaffected.",
"websockets_label": "WebSockets (websockets)",
"websockets_hint": "Enable Responses API websocket transport for this credential.",
"using_api_label": "Use official API (using_api)",
diff --git a/src/i18n/locales/ru.json b/src/i18n/locales/ru.json
index f3c9fe71..5389368e 100644
--- a/src/i18n/locales/ru.json
+++ b/src/i18n/locales/ru.json
@@ -342,6 +342,8 @@
"weight_hint": "По умолчанию 1. Значения ≤ 0 исключают эти учётные данные из взвешенного планирования; максимум 1 000 000.",
"weight_invalid_integer": "Вес должен быть целым числом.",
"weight_invalid_max": "Вес не может превышать 1 000 000.",
+ "disable_cooling_label": "Отключить экспоненциальный cooldown (disable_cooling)",
+ "disable_cooling_hint": "Отключает cooldown после ошибок и экспоненциальную задержку только для этих учётных данных; остальные не затрагиваются.",
"websockets_label": "WebSockets (websockets)",
"websockets_hint": "Включает websocket-транспорт Responses API для этих учётных данных.",
"using_api_label": "Использовать официальный API (using_api)",
diff --git a/src/i18n/locales/zh-CN.json b/src/i18n/locales/zh-CN.json
index f68998ae..859ec319 100644
--- a/src/i18n/locales/zh-CN.json
+++ b/src/i18n/locales/zh-CN.json
@@ -343,6 +343,8 @@
"weight_hint": "默认值为 1;小于或等于 0 时不参与加权调度;最大值为 1,000,000。",
"weight_invalid_integer": "权重必须是整数。",
"weight_invalid_max": "权重不能超过 1,000,000。",
+ "disable_cooling_label": "禁用指数冷却(disable_cooling)",
+ "disable_cooling_hint": "开启后,仅当前凭证跳过失败后的冷却与指数退避;其他凭证不受影响。",
"websockets_label": "WebSockets(websockets)",
"websockets_hint": "为该凭证开启 Responses API 的 websocket 传输。",
"using_api_label": "使用官方 API(using_api)",
diff --git a/src/i18n/locales/zh-TW.json b/src/i18n/locales/zh-TW.json
index f17318c2..d7c90561 100644
--- a/src/i18n/locales/zh-TW.json
+++ b/src/i18n/locales/zh-TW.json
@@ -343,6 +343,8 @@
"weight_hint": "預設值為 1;小於或等於 0 時不參與加權調度;最大值為 1,000,000。",
"weight_invalid_integer": "權重必須是整數。",
"weight_invalid_max": "權重不能超過 1,000,000。",
+ "disable_cooling_label": "停用指數冷卻(disable_cooling)",
+ "disable_cooling_hint": "開啟後,僅目前憑證會略過失敗後的冷卻與指數退避;其他憑證不受影響。",
"websockets_label": "WebSockets(websockets)",
"websockets_hint": "為該憑證開啟 Responses API 的 websocket 傳輸。",
"using_api_label": "使用官方 API(using_api)",
diff --git a/src/services/api/authFiles.ts b/src/services/api/authFiles.ts
index 8a0121a8..74f67c2c 100644
--- a/src/services/api/authFiles.ts
+++ b/src/services/api/authFiles.ts
@@ -22,6 +22,8 @@ export type AuthFileFieldsPatch = {
headers?: Record;
priority?: number;
weight?: number | null;
+ disable_cooling?: boolean;
+ 'disable-cooling'?: boolean;
websockets?: boolean;
using_api?: boolean;
note?: string;
diff --git a/tests/authFileWeight.test.ts b/tests/authFileWeight.test.ts
index 79f2cdc5..3ef49064 100644
--- a/tests/authFileWeight.test.ts
+++ b/tests/authFileWeight.test.ts
@@ -3,6 +3,7 @@ import {
buildAuthFileFieldsPatch,
type PrefixProxyEditorState,
} from '../src/features/authFiles/hooks/useAuthFilesPrefixProxyEditor';
+import { readAuthFileDisableCooling } from '../src/features/authFiles/constants';
const makeEditor = (json: Record, weight: string): PrefixProxyEditorState => ({
fileName: 'credential.json',
@@ -20,6 +21,8 @@ const makeEditor = (json: Record, weight: string): PrefixProxyE
priority: '',
weight,
weightError: null,
+ disableCooling: false,
+ disableCoolingTouched: false,
websockets: false,
websocketsTouched: false,
usingApi: false,
@@ -60,6 +63,55 @@ describe('auth-file credential weight patch', () => {
});
});
+describe('auth-file disable cooling patch', () => {
+ test('reads canonical and legacy boolean-compatible metadata', () => {
+ expect(readAuthFileDisableCooling({ disable_cooling: 'true' })).toBe(true);
+ expect(readAuthFileDisableCooling({ 'disable-cooling': 1 })).toBe(true);
+ expect(
+ readAuthFileDisableCooling({ disable_cooling: 'invalid', 'disable-cooling': true })
+ ).toBe(true);
+ expect(readAuthFileDisableCooling({ disable_cooling: false, 'disable-cooling': true })).toBe(
+ false
+ );
+ });
+
+ test('writes the canonical field when enabling the per-credential override', () => {
+ const editor = {
+ ...makeEditor({}, ''),
+ disableCooling: true,
+ disableCoolingTouched: true,
+ };
+
+ expect(buildAuthFileFieldsPatch(editor, resolveError)).toEqual({ disable_cooling: true });
+ });
+
+ test('preserves the legacy field name and writes false when disabling it', () => {
+ const editor = {
+ ...makeEditor({ 'disable-cooling': true }, ''),
+ disableCooling: false,
+ disableCoolingTouched: true,
+ };
+
+ expect(buildAuthFileFieldsPatch(editor, resolveError)).toEqual({ 'disable-cooling': false });
+ });
+
+ test('does not patch an untouched or unchanged override', () => {
+ expect(buildAuthFileFieldsPatch(makeEditor({ disable_cooling: true }, ''), resolveError)).toEqual(
+ {}
+ );
+ expect(
+ buildAuthFileFieldsPatch(
+ {
+ ...makeEditor({ disable_cooling: 'true' }, ''),
+ disableCooling: true,
+ disableCoolingTouched: true,
+ },
+ resolveError
+ )
+ ).toEqual({});
+ });
+});
+
describe('auth-file excluded models patch', () => {
test('writes normalized model patterns to the canonical field', () => {
const editor = {