mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-06-16 21:03:58 +08:00
feat(provider): add support for disable cooling feature and enhance model configuration options
This commit is contained in:
@@ -56,6 +56,11 @@ const emptyApiKeyEntry = (): ApiKeyEntryInput => ({
|
|||||||
const stripDisableAllRule = (list?: string[]): string[] =>
|
const stripDisableAllRule = (list?: string[]): string[] =>
|
||||||
(list ?? []).filter((s) => s.trim() !== '*');
|
(list ?? []).filter((s) => s.trim() !== '*');
|
||||||
|
|
||||||
|
const formatJsonObject = (value?: Record<string, unknown>): string => {
|
||||||
|
if (!value || Object.keys(value).length === 0) return '';
|
||||||
|
return JSON.stringify(value, null, 2);
|
||||||
|
};
|
||||||
|
|
||||||
function buildInitialForm(
|
function buildInitialForm(
|
||||||
brand: Exclude<ProviderBrand, 'ampcode'>,
|
brand: Exclude<ProviderBrand, 'ampcode'>,
|
||||||
resource: ProviderResource | null,
|
resource: ProviderResource | null,
|
||||||
@@ -69,13 +74,17 @@ function buildInitialForm(
|
|||||||
proxyUrl: '',
|
proxyUrl: '',
|
||||||
prefix: '',
|
prefix: '',
|
||||||
disabled: false,
|
disabled: false,
|
||||||
|
disableCooling: false,
|
||||||
priority: undefined,
|
priority: undefined,
|
||||||
models: [emptyModel()],
|
models: [emptyModel()],
|
||||||
headers: [emptyHeader()],
|
headers: [emptyHeader()],
|
||||||
excludedModelsText: '',
|
excludedModelsText: '',
|
||||||
websockets: brand === 'codex' ? false : undefined,
|
websockets: brand === 'codex' ? false : undefined,
|
||||||
cloak:
|
cloak:
|
||||||
brand === 'claude' ? { mode: '', strictMode: false, sensitiveWordsText: '' } : undefined,
|
brand === 'claude'
|
||||||
|
? { mode: '', strictMode: false, sensitiveWordsText: '', cacheUserId: false }
|
||||||
|
: undefined,
|
||||||
|
experimentalCchSigning: brand === 'claude' ? false : undefined,
|
||||||
testModel:
|
testModel:
|
||||||
brand === 'openaiCompatibility' || brand === 'claude' || brand === 'gemini'
|
brand === 'openaiCompatibility' || brand === 'claude' || brand === 'gemini'
|
||||||
? ''
|
? ''
|
||||||
@@ -94,6 +103,7 @@ function buildInitialForm(
|
|||||||
proxyUrl: '',
|
proxyUrl: '',
|
||||||
prefix: cfg.prefix ?? '',
|
prefix: cfg.prefix ?? '',
|
||||||
disabled: cfg.disabled === true,
|
disabled: cfg.disabled === true,
|
||||||
|
disableCooling: cfg.disableCooling === true,
|
||||||
priority: cfg.priority,
|
priority: cfg.priority,
|
||||||
models: cfg.models?.length
|
models: cfg.models?.length
|
||||||
? cfg.models.map((m) => ({
|
? cfg.models.map((m) => ({
|
||||||
@@ -101,6 +111,8 @@ function buildInitialForm(
|
|||||||
alias: m.alias ?? '',
|
alias: m.alias ?? '',
|
||||||
priority: m.priority,
|
priority: m.priority,
|
||||||
testModel: m.testModel,
|
testModel: m.testModel,
|
||||||
|
image: m.image === true,
|
||||||
|
thinkingJson: formatJsonObject(m.thinking),
|
||||||
}))
|
}))
|
||||||
: [emptyModel()],
|
: [emptyModel()],
|
||||||
headers: cfg.headers
|
headers: cfg.headers
|
||||||
@@ -133,6 +145,7 @@ function buildInitialForm(
|
|||||||
proxyUrl: cfg.proxyUrl ?? '',
|
proxyUrl: cfg.proxyUrl ?? '',
|
||||||
prefix: cfg.prefix ?? '',
|
prefix: cfg.prefix ?? '',
|
||||||
disabled,
|
disabled,
|
||||||
|
disableCooling: cfg.disableCooling === true,
|
||||||
priority: cfg.priority,
|
priority: cfg.priority,
|
||||||
models: cfg.models?.length
|
models: cfg.models?.length
|
||||||
? cfg.models.map((m) => ({
|
? cfg.models.map((m) => ({
|
||||||
@@ -153,8 +166,13 @@ function buildInitialForm(
|
|||||||
mode: (cfg as ProviderKeyConfig).cloak?.mode ?? '',
|
mode: (cfg as ProviderKeyConfig).cloak?.mode ?? '',
|
||||||
strictMode: (cfg as ProviderKeyConfig).cloak?.strictMode === true,
|
strictMode: (cfg as ProviderKeyConfig).cloak?.strictMode === true,
|
||||||
sensitiveWordsText: (cfg as ProviderKeyConfig).cloak?.sensitiveWords?.join('\n') ?? '',
|
sensitiveWordsText: (cfg as ProviderKeyConfig).cloak?.sensitiveWords?.join('\n') ?? '',
|
||||||
|
cacheUserId: (cfg as ProviderKeyConfig).cloak?.cacheUserId === true,
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
|
experimentalCchSigning:
|
||||||
|
brand === 'claude'
|
||||||
|
? (cfg as ProviderKeyConfig).experimentalCchSigning === true
|
||||||
|
: undefined,
|
||||||
testModel: brand === 'claude' || brand === 'gemini' ? '' : undefined,
|
testModel: brand === 'claude' || brand === 'gemini' ? '' : undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -368,7 +386,12 @@ export function BaseProviderForm({
|
|||||||
setForm((prev) => ({
|
setForm((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
cloak: {
|
cloak: {
|
||||||
...(prev.cloak ?? { mode: '', strictMode: false, sensitiveWordsText: '' }),
|
...(prev.cloak ?? {
|
||||||
|
mode: '',
|
||||||
|
strictMode: false,
|
||||||
|
sensitiveWordsText: '',
|
||||||
|
cacheUserId: false,
|
||||||
|
}),
|
||||||
[key]: value,
|
[key]: value,
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
@@ -418,6 +441,12 @@ export function BaseProviderForm({
|
|||||||
[form.apiKeyEntries]
|
[form.apiKeyEntries]
|
||||||
);
|
);
|
||||||
const actualApiKeyEntries = form.apiKeyEntries ?? [];
|
const actualApiKeyEntries = form.apiKeyEntries ?? [];
|
||||||
|
const supportsDisableCooling =
|
||||||
|
brand === 'gemini' ||
|
||||||
|
brand === 'codex' ||
|
||||||
|
brand === 'claude' ||
|
||||||
|
brand === 'openaiCompatibility';
|
||||||
|
const supportsOpenAIModelOptions = brand === 'openaiCompatibility';
|
||||||
const singleConnectivity =
|
const singleConnectivity =
|
||||||
brand === 'gemini'
|
brand === 'gemini'
|
||||||
? { status: connectivity.geminiStatus, run: connectivity.runGemini }
|
? { status: connectivity.geminiStatus, run: connectivity.runGemini }
|
||||||
@@ -444,6 +473,20 @@ export function BaseProviderForm({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const updateModelEntry = (idx: number, patch: Partial<ModelEntryInput>) => {
|
||||||
|
updateField(
|
||||||
|
'models',
|
||||||
|
modelsList.map((it, i) => (i === idx ? { ...it, ...patch } : it))
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeModelEntry = (idx: number) => {
|
||||||
|
updateField(
|
||||||
|
'models',
|
||||||
|
modelsList.filter((_, i) => i !== idx)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form id={formId} className={styles.form} onSubmit={handleSubmit} noValidate>
|
<form id={formId} className={styles.form} onSubmit={handleSubmit} noValidate>
|
||||||
{/* 基础字段 */}
|
{/* 基础字段 */}
|
||||||
@@ -661,6 +704,22 @@ export function BaseProviderForm({
|
|||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
|
{supportsDisableCooling ? (
|
||||||
|
<label className={styles.checkboxRow}>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className={styles.checkboxBox}
|
||||||
|
checked={form.disableCooling ?? false}
|
||||||
|
disabled={mutating}
|
||||||
|
onChange={(e) => updateField('disableCooling', e.target.checked)}
|
||||||
|
/>
|
||||||
|
<span className={styles.checkboxText}>
|
||||||
|
<span>{t('providersPage.form.disableCooling')}</span>
|
||||||
|
<small>{t('providersPage.form.disableCoolingHint')}</small>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 高级折叠区 */}
|
{/* 高级折叠区 */}
|
||||||
@@ -906,50 +965,97 @@ export function BaseProviderForm({
|
|||||||
onClose={closeDiscovery}
|
onClose={closeDiscovery}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
{modelsList.map((entry, idx) => (
|
{modelsList.map((entry, idx) =>
|
||||||
<div
|
supportsOpenAIModelOptions ? (
|
||||||
key={idx}
|
<div key={idx} className={styles.entryCard}>
|
||||||
style={{ display: 'grid', gridTemplateColumns: '1fr 1fr auto', gap: 8 }}
|
<div className={styles.entryCardHeader}>
|
||||||
>
|
<span>{t('providersPage.form.modelEntry', { index: idx + 1 })}</span>
|
||||||
<input
|
<button
|
||||||
className={styles.input}
|
type="button"
|
||||||
placeholder="model-name"
|
className={styles.removeBtn}
|
||||||
value={entry.name}
|
disabled={mutating || modelsList.length <= 1}
|
||||||
onChange={(e) =>
|
onClick={() => removeModelEntry(idx)}
|
||||||
updateField(
|
>
|
||||||
'models',
|
<IconX size={12} />
|
||||||
modelsList.map((it, i) => (i === idx ? { ...it, name: e.target.value } : it))
|
</button>
|
||||||
)
|
</div>
|
||||||
}
|
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
|
||||||
disabled={mutating}
|
<input
|
||||||
/>
|
className={styles.input}
|
||||||
<input
|
placeholder="model-name"
|
||||||
className={styles.input}
|
value={entry.name}
|
||||||
placeholder="alias (optional)"
|
onChange={(e) => updateModelEntry(idx, { name: e.target.value })}
|
||||||
value={entry.alias ?? ''}
|
disabled={mutating}
|
||||||
onChange={(e) =>
|
/>
|
||||||
updateField(
|
<input
|
||||||
'models',
|
className={styles.input}
|
||||||
modelsList.map((it, i) => (i === idx ? { ...it, alias: e.target.value } : it))
|
placeholder="alias (optional)"
|
||||||
)
|
value={entry.alias ?? ''}
|
||||||
}
|
onChange={(e) => updateModelEntry(idx, { alias: e.target.value })}
|
||||||
disabled={mutating}
|
disabled={mutating}
|
||||||
/>
|
/>
|
||||||
<button
|
</div>
|
||||||
type="button"
|
<label className={styles.checkboxRow}>
|
||||||
className={styles.removeBtn}
|
<input
|
||||||
disabled={mutating || modelsList.length <= 1}
|
type="checkbox"
|
||||||
onClick={() =>
|
className={styles.checkboxBox}
|
||||||
updateField(
|
checked={entry.image === true}
|
||||||
'models',
|
disabled={mutating}
|
||||||
modelsList.filter((_, i) => i !== idx)
|
onChange={(e) => updateModelEntry(idx, { image: e.target.checked })}
|
||||||
)
|
/>
|
||||||
}
|
<span className={styles.checkboxText}>
|
||||||
|
<span>{t('providersPage.form.modelImage')}</span>
|
||||||
|
<small>{t('providersPage.form.modelImageHint')}</small>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<div className={styles.field}>
|
||||||
|
<label className={styles.label}>
|
||||||
|
{t('providersPage.form.thinkingConfig')}
|
||||||
|
<span className={styles.labelHint}>
|
||||||
|
{' '}
|
||||||
|
· {t('providersPage.form.thinkingConfigHint')}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
className={styles.textarea}
|
||||||
|
rows={4}
|
||||||
|
value={entry.thinkingJson ?? ''}
|
||||||
|
onChange={(e) => updateModelEntry(idx, { thinkingJson: e.target.value })}
|
||||||
|
disabled={mutating}
|
||||||
|
placeholder={'{"levels":["low","medium","high"]}'}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
key={idx}
|
||||||
|
style={{ display: 'grid', gridTemplateColumns: '1fr 1fr auto', gap: 8 }}
|
||||||
>
|
>
|
||||||
<IconX size={12} />
|
<input
|
||||||
</button>
|
className={styles.input}
|
||||||
</div>
|
placeholder="model-name"
|
||||||
))}
|
value={entry.name}
|
||||||
|
onChange={(e) => updateModelEntry(idx, { name: e.target.value })}
|
||||||
|
disabled={mutating}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
className={styles.input}
|
||||||
|
placeholder="alias (optional)"
|
||||||
|
value={entry.alias ?? ''}
|
||||||
|
onChange={(e) => updateModelEntry(idx, { alias: e.target.value })}
|
||||||
|
disabled={mutating}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={styles.removeBtn}
|
||||||
|
disabled={mutating || modelsList.length <= 1}
|
||||||
|
onClick={() => removeModelEntry(idx)}
|
||||||
|
>
|
||||||
|
<IconX size={12} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
)}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={styles.addBtn}
|
className={styles.addBtn}
|
||||||
@@ -1004,6 +1110,32 @@ export function BaseProviderForm({
|
|||||||
<span>{t('providersPage.form.cloakStrict')}</span>
|
<span>{t('providersPage.form.cloakStrict')}</span>
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
|
<label className={styles.checkboxRow}>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className={styles.checkboxBox}
|
||||||
|
checked={form.cloak.cacheUserId}
|
||||||
|
disabled={mutating}
|
||||||
|
onChange={(e) => updateCloak('cacheUserId', e.target.checked)}
|
||||||
|
/>
|
||||||
|
<span className={styles.checkboxText}>
|
||||||
|
<span>{t('providersPage.form.cloakCacheUserId')}</span>
|
||||||
|
<small>{t('providersPage.form.cloakCacheUserIdHint')}</small>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label className={styles.checkboxRow}>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className={styles.checkboxBox}
|
||||||
|
checked={form.experimentalCchSigning ?? false}
|
||||||
|
disabled={mutating}
|
||||||
|
onChange={(e) => updateField('experimentalCchSigning', e.target.checked)}
|
||||||
|
/>
|
||||||
|
<span className={styles.checkboxText}>
|
||||||
|
<span>{t('providersPage.form.experimentalCchSigning')}</span>
|
||||||
|
<small>{t('providersPage.form.experimentalCchSigningHint')}</small>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
<div className={styles.field}>
|
<div className={styles.field}>
|
||||||
<label className={styles.label}>{t('providersPage.form.cloakSensitiveWords')}</label>
|
<label className={styles.label}>{t('providersPage.form.cloakSensitiveWords')}</label>
|
||||||
<textarea
|
<textarea
|
||||||
|
|||||||
@@ -87,6 +87,8 @@ export interface ModelEntryInput {
|
|||||||
alias?: string;
|
alias?: string;
|
||||||
priority?: number;
|
priority?: number;
|
||||||
testModel?: string;
|
testModel?: string;
|
||||||
|
image?: boolean;
|
||||||
|
thinkingJson?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiKeyEntryInput {
|
export interface ApiKeyEntryInput {
|
||||||
@@ -100,6 +102,7 @@ export interface CloakInput {
|
|||||||
mode: string;
|
mode: string;
|
||||||
strictMode: boolean;
|
strictMode: boolean;
|
||||||
sensitiveWordsText: string;
|
sensitiveWordsText: string;
|
||||||
|
cacheUserId: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ProviderEntryFormInput {
|
export interface ProviderEntryFormInput {
|
||||||
@@ -111,6 +114,7 @@ export interface ProviderEntryFormInput {
|
|||||||
proxyUrl: string;
|
proxyUrl: string;
|
||||||
prefix: string;
|
prefix: string;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
|
disableCooling?: boolean;
|
||||||
priority?: number;
|
priority?: number;
|
||||||
|
|
||||||
/** 高级折叠区 */
|
/** 高级折叠区 */
|
||||||
@@ -122,6 +126,7 @@ export interface ProviderEntryFormInput {
|
|||||||
websockets?: boolean;
|
websockets?: boolean;
|
||||||
/** Claude 专属 */
|
/** Claude 专属 */
|
||||||
cloak?: CloakInput;
|
cloak?: CloakInput;
|
||||||
|
experimentalCchSigning?: boolean;
|
||||||
/** OpenAI persists this; Gemini/Claude use it for one-off connectivity tests. */
|
/** OpenAI persists this; Gemini/Claude use it for one-off connectivity tests. */
|
||||||
testModel?: string;
|
testModel?: string;
|
||||||
apiKeyEntries?: ApiKeyEntryInput[];
|
apiKeyEntries?: ApiKeyEntryInput[];
|
||||||
|
|||||||
@@ -69,6 +69,16 @@ const headersFromEntries = (
|
|||||||
return out;
|
return out;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const parseThinkingJson = (value: string | undefined): Record<string, unknown> | undefined => {
|
||||||
|
const trimmed = (value ?? '').trim();
|
||||||
|
if (!trimmed) return undefined;
|
||||||
|
const parsed = JSON.parse(trimmed) as unknown;
|
||||||
|
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
||||||
|
throw new Error('Thinking config must be a JSON object');
|
||||||
|
}
|
||||||
|
return parsed as Record<string, unknown>;
|
||||||
|
};
|
||||||
|
|
||||||
const buildExcludedModels = (
|
const buildExcludedModels = (
|
||||||
textValue: string,
|
textValue: string,
|
||||||
disabled: boolean,
|
disabled: boolean,
|
||||||
@@ -110,6 +120,7 @@ const buildProviderKeyConfig = (
|
|||||||
models: models.length ? models : undefined,
|
models: models.length ? models : undefined,
|
||||||
headers: Object.keys(headers).length ? headers : undefined,
|
headers: Object.keys(headers).length ? headers : undefined,
|
||||||
excludedModels: excluded,
|
excludedModels: excluded,
|
||||||
|
disableCooling: input.disableCooling === true,
|
||||||
authIndex: existing?.authIndex,
|
authIndex: existing?.authIndex,
|
||||||
};
|
};
|
||||||
if (brand === 'codex' && input.websockets !== undefined) {
|
if (brand === 'codex' && input.websockets !== undefined) {
|
||||||
@@ -120,8 +131,12 @@ const buildProviderKeyConfig = (
|
|||||||
mode: input.cloak.mode.trim() || undefined,
|
mode: input.cloak.mode.trim() || undefined,
|
||||||
strictMode: input.cloak.strictMode,
|
strictMode: input.cloak.strictMode,
|
||||||
sensitiveWords: parseTextList(input.cloak.sensitiveWordsText),
|
sensitiveWords: parseTextList(input.cloak.sensitiveWordsText),
|
||||||
|
cacheUserId: input.cloak.cacheUserId === true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if (brand === 'claude') {
|
||||||
|
next.experimentalCchSigning = input.experimentalCchSigning === true;
|
||||||
|
}
|
||||||
return next;
|
return next;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -136,6 +151,8 @@ const buildOpenAIConfig = (
|
|||||||
alias: m.alias?.trim() || undefined,
|
alias: m.alias?.trim() || undefined,
|
||||||
priority: m.priority,
|
priority: m.priority,
|
||||||
testModel: m.testModel,
|
testModel: m.testModel,
|
||||||
|
image: m.image === true,
|
||||||
|
thinking: parseThinkingJson(m.thinkingJson),
|
||||||
}))
|
}))
|
||||||
.filter((m) => m.name);
|
.filter((m) => m.name);
|
||||||
const apiKeyEntries =
|
const apiKeyEntries =
|
||||||
@@ -158,6 +175,7 @@ const buildOpenAIConfig = (
|
|||||||
prefix: input.prefix.trim() || undefined,
|
prefix: input.prefix.trim() || undefined,
|
||||||
apiKeyEntries,
|
apiKeyEntries,
|
||||||
disabled: input.disabled,
|
disabled: input.disabled,
|
||||||
|
disableCooling: input.disableCooling === true,
|
||||||
headers: Object.keys(headers).length ? headers : undefined,
|
headers: Object.keys(headers).length ? headers : undefined,
|
||||||
models: models.length ? models : undefined,
|
models: models.length ? models : undefined,
|
||||||
priority: input.priority,
|
priority: input.priority,
|
||||||
|
|||||||
@@ -1470,6 +1470,17 @@
|
|||||||
"addApiKeyEntry": "Add key entry",
|
"addApiKeyEntry": "Add key entry",
|
||||||
"showApiKey": "Show API key",
|
"showApiKey": "Show API key",
|
||||||
"hideApiKey": "Hide API key",
|
"hideApiKey": "Hide API key",
|
||||||
|
"disableCooling": "Disable cooling",
|
||||||
|
"disableCoolingHint": "Disable failure cooldown windows only for this credential or provider",
|
||||||
|
"modelEntry": "Model #{{index}}",
|
||||||
|
"modelImage": "Allow image endpoints",
|
||||||
|
"modelImageHint": "Allow this model on /v1/images/generations and /v1/images/edits",
|
||||||
|
"thinkingConfig": "Thinking config (JSON)",
|
||||||
|
"thinkingConfigHint": "Supports levels, min, max, zero_allowed, dynamic_allowed",
|
||||||
|
"cloakCacheUserId": "Cache user_id",
|
||||||
|
"cloakCacheUserIdHint": "Reuse the Claude cloak user_id per API key",
|
||||||
|
"experimentalCchSigning": "Experimental CCH signing",
|
||||||
|
"experimentalCchSigningHint": "Sign the final cloaked Claude /v1/messages body with CCH",
|
||||||
"validation": {
|
"validation": {
|
||||||
"nameRequired": "Name is required",
|
"nameRequired": "Name is required",
|
||||||
"apiKeyRequired": "At least one API key is required",
|
"apiKeyRequired": "At least one API key is required",
|
||||||
|
|||||||
@@ -1445,6 +1445,17 @@
|
|||||||
"addApiKeyEntry": "Добавить ключ",
|
"addApiKeyEntry": "Добавить ключ",
|
||||||
"showApiKey": "Показать ключ API",
|
"showApiKey": "Показать ключ API",
|
||||||
"hideApiKey": "Скрыть ключ API",
|
"hideApiKey": "Скрыть ключ API",
|
||||||
|
"disableCooling": "Отключить cooldown",
|
||||||
|
"disableCoolingHint": "Отключает окна охлаждения после ошибок только для этой записи",
|
||||||
|
"modelEntry": "Модель #{{index}}",
|
||||||
|
"modelImage": "Разрешить image endpoints",
|
||||||
|
"modelImageHint": "Разрешить модель для /v1/images/generations и /v1/images/edits",
|
||||||
|
"thinkingConfig": "Thinking config (JSON)",
|
||||||
|
"thinkingConfigHint": "Поддерживает levels, min, max, zero_allowed, dynamic_allowed",
|
||||||
|
"cloakCacheUserId": "Кэшировать user_id",
|
||||||
|
"cloakCacheUserIdHint": "Переиспользовать Claude cloak user_id для каждого API-ключа",
|
||||||
|
"experimentalCchSigning": "Экспериментальная CCH-подпись",
|
||||||
|
"experimentalCchSigningHint": "Подписывать финальное тело cloaked Claude /v1/messages через CCH",
|
||||||
"validation": {
|
"validation": {
|
||||||
"nameRequired": "Название обязательно",
|
"nameRequired": "Название обязательно",
|
||||||
"apiKeyRequired": "Нужен хотя бы один API-ключ",
|
"apiKeyRequired": "Нужен хотя бы один API-ключ",
|
||||||
|
|||||||
@@ -1470,6 +1470,17 @@
|
|||||||
"addApiKeyEntry": "添加密钥条目",
|
"addApiKeyEntry": "添加密钥条目",
|
||||||
"showApiKey": "显示密钥",
|
"showApiKey": "显示密钥",
|
||||||
"hideApiKey": "隐藏密钥",
|
"hideApiKey": "隐藏密钥",
|
||||||
|
"disableCooling": "禁用冷却调度",
|
||||||
|
"disableCoolingHint": "仅对当前凭据或提供商禁用失败后的冷却窗口",
|
||||||
|
"modelEntry": "模型 #{{index}}",
|
||||||
|
"modelImage": "允许图片端点",
|
||||||
|
"modelImageHint": "允许该模型用于 /v1/images/generations 和 /v1/images/edits",
|
||||||
|
"thinkingConfig": "Thinking 配置(JSON)",
|
||||||
|
"thinkingConfigHint": "可配置 levels、min、max、zero_allowed、dynamic_allowed",
|
||||||
|
"cloakCacheUserId": "缓存 user_id",
|
||||||
|
"cloakCacheUserIdHint": "按 API key 复用 Claude cloak 生成的 user_id",
|
||||||
|
"experimentalCchSigning": "实验性 CCH 签名",
|
||||||
|
"experimentalCchSigningHint": "对 cloaked Claude /v1/messages 最终请求体启用 CCH 签名",
|
||||||
"validation": {
|
"validation": {
|
||||||
"nameRequired": "名称必填",
|
"nameRequired": "名称必填",
|
||||||
"apiKeyRequired": "至少填写一个 API 密钥",
|
"apiKeyRequired": "至少填写一个 API 密钥",
|
||||||
|
|||||||
@@ -1496,6 +1496,17 @@
|
|||||||
"addApiKeyEntry": "新增金鑰條目",
|
"addApiKeyEntry": "新增金鑰條目",
|
||||||
"showApiKey": "顯示金鑰",
|
"showApiKey": "顯示金鑰",
|
||||||
"hideApiKey": "隱藏金鑰",
|
"hideApiKey": "隱藏金鑰",
|
||||||
|
"disableCooling": "停用冷卻調度",
|
||||||
|
"disableCoolingHint": "僅對目前憑證或提供商停用失敗後的冷卻視窗",
|
||||||
|
"modelEntry": "模型 #{{index}}",
|
||||||
|
"modelImage": "允許圖片端點",
|
||||||
|
"modelImageHint": "允許該模型用於 /v1/images/generations 和 /v1/images/edits",
|
||||||
|
"thinkingConfig": "Thinking 設定(JSON)",
|
||||||
|
"thinkingConfigHint": "可設定 levels、min、max、zero_allowed、dynamic_allowed",
|
||||||
|
"cloakCacheUserId": "快取 user_id",
|
||||||
|
"cloakCacheUserIdHint": "按 API key 複用 Claude cloak 產生的 user_id",
|
||||||
|
"experimentalCchSigning": "實驗性 CCH 簽名",
|
||||||
|
"experimentalCchSigningHint": "對 cloaked Claude /v1/messages 最終請求體啟用 CCH 簽名",
|
||||||
"validation": {
|
"validation": {
|
||||||
"nameRequired": "名稱必填",
|
"nameRequired": "名稱必填",
|
||||||
"apiKeyRequired": "至少填寫一個 API 金鑰",
|
"apiKeyRequired": "至少填寫一個 API 金鑰",
|
||||||
|
|||||||
@@ -22,23 +22,35 @@ const serializeHeaders = (headers?: Record<string, string>) =>
|
|||||||
|
|
||||||
const RESPONSE_ONLY_FIELDS = ['auth-index'] as const;
|
const RESPONSE_ONLY_FIELDS = ['auth-index'] as const;
|
||||||
|
|
||||||
const PROVIDER_KEY_FIELDS = [
|
const PROVIDER_COMMON_KEY_FIELDS = [
|
||||||
'api-key',
|
'api-key',
|
||||||
'priority',
|
'priority',
|
||||||
'prefix',
|
'prefix',
|
||||||
'base-url',
|
'base-url',
|
||||||
'websockets',
|
|
||||||
'proxy-url',
|
'proxy-url',
|
||||||
'headers',
|
'headers',
|
||||||
'models',
|
'models',
|
||||||
'excluded-models',
|
'excluded-models',
|
||||||
'cloak',
|
'disable-cooling',
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
const GEMINI_KEY_FIELDS = PROVIDER_KEY_FIELDS.filter(
|
const GEMINI_KEY_FIELDS = PROVIDER_COMMON_KEY_FIELDS;
|
||||||
(field) => field !== 'websockets' && field !== 'cloak'
|
const CODEX_KEY_FIELDS = [...PROVIDER_COMMON_KEY_FIELDS, 'websockets'] as const;
|
||||||
);
|
const CLAUDE_KEY_FIELDS = [
|
||||||
const VERTEX_KEY_FIELDS = GEMINI_KEY_FIELDS;
|
...PROVIDER_COMMON_KEY_FIELDS,
|
||||||
|
'cloak',
|
||||||
|
'experimental-cch-signing',
|
||||||
|
] as const;
|
||||||
|
const VERTEX_KEY_FIELDS = [
|
||||||
|
'api-key',
|
||||||
|
'priority',
|
||||||
|
'prefix',
|
||||||
|
'base-url',
|
||||||
|
'proxy-url',
|
||||||
|
'headers',
|
||||||
|
'models',
|
||||||
|
'excluded-models',
|
||||||
|
] as const;
|
||||||
|
|
||||||
const OPENAI_PROVIDER_FIELDS = [
|
const OPENAI_PROVIDER_FIELDS = [
|
||||||
'name',
|
'name',
|
||||||
@@ -50,13 +62,15 @@ const OPENAI_PROVIDER_FIELDS = [
|
|||||||
'headers',
|
'headers',
|
||||||
'models',
|
'models',
|
||||||
'test-model',
|
'test-model',
|
||||||
|
'disable-cooling',
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
const MODEL_ALIAS_FIELDS = ['name', 'alias', 'priority', 'test-model'] as const;
|
const MODEL_ALIAS_FIELDS = ['name', 'alias', 'priority', 'test-model'] as const;
|
||||||
|
const OPENAI_MODEL_ALIAS_FIELDS = [...MODEL_ALIAS_FIELDS, 'image', 'thinking'] as const;
|
||||||
|
|
||||||
const API_KEY_ENTRY_FIELDS = ['api-key', 'proxy-url'] as const;
|
const API_KEY_ENTRY_FIELDS = ['api-key', 'proxy-url'] as const;
|
||||||
|
|
||||||
const CLOAK_FIELDS = ['mode', 'strict-mode', 'sensitive-words'] as const;
|
const CLOAK_FIELDS = ['mode', 'strict-mode', 'sensitive-words', 'cache-user-id'] as const;
|
||||||
|
|
||||||
const getStringField = (record: Record<string, unknown>, keys: readonly string[]) => {
|
const getStringField = (record: Record<string, unknown>, keys: readonly string[]) => {
|
||||||
for (const key of keys) {
|
for (const key of keys) {
|
||||||
@@ -170,12 +184,16 @@ const getRawSectionList = (rawConfig: unknown, section: string): unknown[] => {
|
|||||||
return Array.isArray(value) ? value : [];
|
return Array.isArray(value) ? value : [];
|
||||||
};
|
};
|
||||||
|
|
||||||
const mergeModelPayloads = (raw: unknown, models: unknown) =>
|
const mergeModelPayloads = (
|
||||||
|
raw: unknown,
|
||||||
|
models: unknown,
|
||||||
|
knownFields: readonly string[] = MODEL_ALIAS_FIELDS
|
||||||
|
) =>
|
||||||
Array.isArray(models)
|
Array.isArray(models)
|
||||||
? mergeKnownRecordList(
|
? mergeKnownRecordList(
|
||||||
isRecord(raw) ? raw.models : undefined,
|
isRecord(raw) ? raw.models : undefined,
|
||||||
models.filter(isRecord),
|
models.filter(isRecord),
|
||||||
MODEL_ALIAS_FIELDS,
|
knownFields,
|
||||||
modelIdentity,
|
modelIdentity,
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
@@ -211,7 +229,7 @@ const mergeOpenAIProviderPayload = (raw: unknown, payload: Record<string, unknow
|
|||||||
apiKeyEntryIdentity
|
apiKeyEntryIdentity
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const models = mergeModelPayloads(raw, payload.models);
|
const models = mergeModelPayloads(raw, payload.models, OPENAI_MODEL_ALIAS_FIELDS);
|
||||||
if (models) next.models = models;
|
if (models) next.models = models;
|
||||||
return next;
|
return next;
|
||||||
};
|
};
|
||||||
@@ -252,7 +270,7 @@ const buildProviderDeleteQuery = (apiKey: string, baseUrl?: string) => {
|
|||||||
return `?${params.toString()}`;
|
return `?${params.toString()}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const serializeModelAliases = (models?: ModelAlias[]) =>
|
const serializeModelAliases = (models?: ModelAlias[], includeOpenAIFields = false) =>
|
||||||
Array.isArray(models)
|
Array.isArray(models)
|
||||||
? models
|
? models
|
||||||
.map((model) => {
|
.map((model) => {
|
||||||
@@ -267,6 +285,14 @@ const serializeModelAliases = (models?: ModelAlias[]) =>
|
|||||||
if (model.testModel) {
|
if (model.testModel) {
|
||||||
payload['test-model'] = model.testModel;
|
payload['test-model'] = model.testModel;
|
||||||
}
|
}
|
||||||
|
if (includeOpenAIFields) {
|
||||||
|
if (model.image) {
|
||||||
|
payload.image = true;
|
||||||
|
}
|
||||||
|
if (model.thinking) {
|
||||||
|
payload.thinking = model.thinking;
|
||||||
|
}
|
||||||
|
}
|
||||||
return payload;
|
return payload;
|
||||||
})
|
})
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
@@ -285,6 +311,7 @@ const serializeProviderKey = (config: ProviderKeyConfig) => {
|
|||||||
if (config.baseUrl) payload['base-url'] = config.baseUrl;
|
if (config.baseUrl) payload['base-url'] = config.baseUrl;
|
||||||
if (config.websockets !== undefined) payload.websockets = config.websockets;
|
if (config.websockets !== undefined) payload.websockets = config.websockets;
|
||||||
if (config.proxyUrl) payload['proxy-url'] = config.proxyUrl;
|
if (config.proxyUrl) payload['proxy-url'] = config.proxyUrl;
|
||||||
|
if (config.disableCooling) payload['disable-cooling'] = true;
|
||||||
const headers = serializeHeaders(config.headers);
|
const headers = serializeHeaders(config.headers);
|
||||||
if (headers) payload.headers = headers;
|
if (headers) payload.headers = headers;
|
||||||
const models = serializeModelAliases(config.models);
|
const models = serializeModelAliases(config.models);
|
||||||
@@ -301,10 +328,16 @@ const serializeProviderKey = (config: ProviderKeyConfig) => {
|
|||||||
if (config.cloak.sensitiveWords && config.cloak.sensitiveWords.length) {
|
if (config.cloak.sensitiveWords && config.cloak.sensitiveWords.length) {
|
||||||
cloakPayload['sensitive-words'] = config.cloak.sensitiveWords;
|
cloakPayload['sensitive-words'] = config.cloak.sensitiveWords;
|
||||||
}
|
}
|
||||||
|
if (config.cloak.cacheUserId) {
|
||||||
|
cloakPayload['cache-user-id'] = true;
|
||||||
|
}
|
||||||
if (Object.keys(cloakPayload).length) {
|
if (Object.keys(cloakPayload).length) {
|
||||||
payload.cloak = cloakPayload;
|
payload.cloak = cloakPayload;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (config.experimentalCchSigning) {
|
||||||
|
payload['experimental-cch-signing'] = true;
|
||||||
|
}
|
||||||
return payload;
|
return payload;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -342,6 +375,7 @@ const serializeGeminiKey = (config: GeminiKeyConfig) => {
|
|||||||
if (config.prefix?.trim()) payload.prefix = config.prefix.trim();
|
if (config.prefix?.trim()) payload.prefix = config.prefix.trim();
|
||||||
if (config.baseUrl) payload['base-url'] = config.baseUrl;
|
if (config.baseUrl) payload['base-url'] = config.baseUrl;
|
||||||
if (config.proxyUrl) payload['proxy-url'] = config.proxyUrl;
|
if (config.proxyUrl) payload['proxy-url'] = config.proxyUrl;
|
||||||
|
if (config.disableCooling) payload['disable-cooling'] = true;
|
||||||
const headers = serializeHeaders(config.headers);
|
const headers = serializeHeaders(config.headers);
|
||||||
if (headers) payload.headers = headers;
|
if (headers) payload.headers = headers;
|
||||||
const models = serializeModelAliases(config.models);
|
const models = serializeModelAliases(config.models);
|
||||||
@@ -364,10 +398,11 @@ const serializeOpenAIProvider = (provider: OpenAIProviderConfig) => {
|
|||||||
if (provider.disabled !== undefined) payload.disabled = provider.disabled;
|
if (provider.disabled !== undefined) payload.disabled = provider.disabled;
|
||||||
const headers = serializeHeaders(provider.headers);
|
const headers = serializeHeaders(provider.headers);
|
||||||
if (headers) payload.headers = headers;
|
if (headers) payload.headers = headers;
|
||||||
const models = serializeModelAliases(provider.models);
|
const models = serializeModelAliases(provider.models, true);
|
||||||
if (models && models.length) payload.models = models;
|
if (models && models.length) payload.models = models;
|
||||||
if (provider.priority !== undefined) payload.priority = provider.priority;
|
if (provider.priority !== undefined) payload.priority = provider.priority;
|
||||||
if (provider.testModel) payload['test-model'] = provider.testModel;
|
if (provider.testModel) payload['test-model'] = provider.testModel;
|
||||||
|
if (provider.disableCooling) payload['disable-cooling'] = true;
|
||||||
return payload;
|
return payload;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -408,7 +443,7 @@ export const providersApi = {
|
|||||||
'codex-api-key',
|
'codex-api-key',
|
||||||
configs,
|
configs,
|
||||||
serializeProviderKey,
|
serializeProviderKey,
|
||||||
(raw, payload) => mergeProviderKeyPayload(raw, payload, PROVIDER_KEY_FIELDS),
|
(raw, payload) => mergeProviderKeyPayload(raw, payload, CODEX_KEY_FIELDS),
|
||||||
providerKeyIdentity
|
providerKeyIdentity
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
@@ -431,7 +466,7 @@ export const providersApi = {
|
|||||||
'claude-api-key',
|
'claude-api-key',
|
||||||
configs,
|
configs,
|
||||||
serializeProviderKey,
|
serializeProviderKey,
|
||||||
(raw, payload) => mergeProviderKeyPayload(raw, payload, PROVIDER_KEY_FIELDS),
|
(raw, payload) => mergeProviderKeyPayload(raw, payload, CLAUDE_KEY_FIELDS),
|
||||||
providerKeyIdentity
|
providerKeyIdentity
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ import { isRecord } from '@/utils/helpers';
|
|||||||
const normalizeBoolean = (value: unknown): boolean | undefined =>
|
const normalizeBoolean = (value: unknown): boolean | undefined =>
|
||||||
typeof value === 'boolean' ? value : undefined;
|
typeof value === 'boolean' ? value : undefined;
|
||||||
|
|
||||||
|
const normalizeRecord = (value: unknown): Record<string, unknown> | undefined =>
|
||||||
|
isRecord(value) ? value : undefined;
|
||||||
|
|
||||||
const normalizeModelAliases = (models: unknown): ModelAlias[] => {
|
const normalizeModelAliases = (models: unknown): ModelAlias[] => {
|
||||||
if (!Array.isArray(models)) return [];
|
if (!Array.isArray(models)) return [];
|
||||||
return models
|
return models
|
||||||
@@ -32,6 +35,8 @@ const normalizeModelAliases = (models: unknown): ModelAlias[] => {
|
|||||||
const alias = item.alias;
|
const alias = item.alias;
|
||||||
const priority = item.priority;
|
const priority = item.priority;
|
||||||
const testModel = item['test-model'];
|
const testModel = item['test-model'];
|
||||||
|
const image = normalizeBoolean(item.image);
|
||||||
|
const thinking = normalizeRecord(item.thinking);
|
||||||
const entry: ModelAlias = { name: String(name) };
|
const entry: ModelAlias = { name: String(name) };
|
||||||
if (alias && alias !== name) {
|
if (alias && alias !== name) {
|
||||||
entry.alias = String(alias);
|
entry.alias = String(alias);
|
||||||
@@ -45,6 +50,12 @@ const normalizeModelAliases = (models: unknown): ModelAlias[] => {
|
|||||||
if (testModel) {
|
if (testModel) {
|
||||||
entry.testModel = String(testModel);
|
entry.testModel = String(testModel);
|
||||||
}
|
}
|
||||||
|
if (image !== undefined) {
|
||||||
|
entry.image = image;
|
||||||
|
}
|
||||||
|
if (thinking) {
|
||||||
|
entry.thinking = thinking;
|
||||||
|
}
|
||||||
return entry;
|
return entry;
|
||||||
})
|
})
|
||||||
.filter(Boolean) as ModelAlias[];
|
.filter(Boolean) as ModelAlias[];
|
||||||
@@ -130,6 +141,8 @@ const normalizeProviderKeyConfig = (item: unknown): ProviderKeyConfig | null =>
|
|||||||
const websockets = normalizeBoolean(record?.websockets);
|
const websockets = normalizeBoolean(record?.websockets);
|
||||||
if (websockets !== undefined) config.websockets = websockets;
|
if (websockets !== undefined) config.websockets = websockets;
|
||||||
if (proxyUrl) config.proxyUrl = String(proxyUrl);
|
if (proxyUrl) config.proxyUrl = String(proxyUrl);
|
||||||
|
const disableCooling = normalizeBoolean(record?.['disable-cooling']);
|
||||||
|
if (disableCooling !== undefined) config.disableCooling = disableCooling;
|
||||||
const headers = normalizeHeaders(record?.headers);
|
const headers = normalizeHeaders(record?.headers);
|
||||||
if (headers) config.headers = headers;
|
if (headers) config.headers = headers;
|
||||||
const models = normalizeModelAliases(record?.models);
|
const models = normalizeModelAliases(record?.models);
|
||||||
@@ -154,10 +167,18 @@ const normalizeProviderKeyConfig = (item: unknown): ProviderKeyConfig | null =>
|
|||||||
if (sensitiveWords.length) {
|
if (sensitiveWords.length) {
|
||||||
cloak.sensitiveWords = sensitiveWords;
|
cloak.sensitiveWords = sensitiveWords;
|
||||||
}
|
}
|
||||||
|
const cacheUserId = normalizeBoolean(cloakRaw['cache-user-id']);
|
||||||
|
if (cacheUserId !== undefined) {
|
||||||
|
cloak.cacheUserId = cacheUserId;
|
||||||
|
}
|
||||||
if (Object.keys(cloak).length) {
|
if (Object.keys(cloak).length) {
|
||||||
config.cloak = cloak;
|
config.cloak = cloak;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const experimentalCchSigning = normalizeBoolean(record?.['experimental-cch-signing']);
|
||||||
|
if (experimentalCchSigning !== undefined) {
|
||||||
|
config.experimentalCchSigning = experimentalCchSigning;
|
||||||
|
}
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
};
|
};
|
||||||
@@ -186,6 +207,8 @@ const normalizeGeminiKeyConfig = (item: unknown): GeminiKeyConfig | null => {
|
|||||||
if (baseUrl) config.baseUrl = String(baseUrl);
|
if (baseUrl) config.baseUrl = String(baseUrl);
|
||||||
const proxyUrl = record?.['proxy-url'];
|
const proxyUrl = record?.['proxy-url'];
|
||||||
if (proxyUrl) config.proxyUrl = String(proxyUrl);
|
if (proxyUrl) config.proxyUrl = String(proxyUrl);
|
||||||
|
const disableCooling = normalizeBoolean(record?.['disable-cooling']);
|
||||||
|
if (disableCooling !== undefined) config.disableCooling = disableCooling;
|
||||||
const models = normalizeModelAliases(record?.models);
|
const models = normalizeModelAliases(record?.models);
|
||||||
if (models.length) config.models = models;
|
if (models.length) config.models = models;
|
||||||
const headers = normalizeHeaders(record?.headers);
|
const headers = normalizeHeaders(record?.headers);
|
||||||
@@ -222,6 +245,8 @@ const normalizeOpenAIProvider = (provider: unknown): OpenAIProviderConfig | null
|
|||||||
|
|
||||||
const disabled = normalizeBoolean(provider.disabled);
|
const disabled = normalizeBoolean(provider.disabled);
|
||||||
if (disabled !== undefined) result.disabled = disabled;
|
if (disabled !== undefined) result.disabled = disabled;
|
||||||
|
const disableCooling = normalizeBoolean(provider['disable-cooling']);
|
||||||
|
if (disableCooling !== undefined) result.disableCooling = disableCooling;
|
||||||
const prefix = normalizePrefix(provider.prefix);
|
const prefix = normalizePrefix(provider.prefix);
|
||||||
if (prefix) result.prefix = prefix;
|
if (prefix) result.prefix = prefix;
|
||||||
if (headers) result.headers = headers;
|
if (headers) result.headers = headers;
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ export interface ModelAlias {
|
|||||||
alias?: string;
|
alias?: string;
|
||||||
priority?: number;
|
priority?: number;
|
||||||
testModel?: string;
|
testModel?: string;
|
||||||
|
image?: boolean;
|
||||||
|
thinking?: Record<string, unknown>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiKeyEntry {
|
export interface ApiKeyEntry {
|
||||||
@@ -20,6 +22,7 @@ export interface CloakConfig {
|
|||||||
mode?: string;
|
mode?: string;
|
||||||
strictMode?: boolean;
|
strictMode?: boolean;
|
||||||
sensitiveWords?: string[];
|
sensitiveWords?: string[];
|
||||||
|
cacheUserId?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GeminiKeyConfig {
|
export interface GeminiKeyConfig {
|
||||||
@@ -31,6 +34,7 @@ export interface GeminiKeyConfig {
|
|||||||
models?: ModelAlias[];
|
models?: ModelAlias[];
|
||||||
headers?: Record<string, string>;
|
headers?: Record<string, string>;
|
||||||
excludedModels?: string[];
|
excludedModels?: string[];
|
||||||
|
disableCooling?: boolean;
|
||||||
authIndex?: string;
|
authIndex?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,7 +48,9 @@ export interface ProviderKeyConfig {
|
|||||||
headers?: Record<string, string>;
|
headers?: Record<string, string>;
|
||||||
models?: ModelAlias[];
|
models?: ModelAlias[];
|
||||||
excludedModels?: string[];
|
excludedModels?: string[];
|
||||||
|
disableCooling?: boolean;
|
||||||
cloak?: CloakConfig;
|
cloak?: CloakConfig;
|
||||||
|
experimentalCchSigning?: boolean;
|
||||||
authIndex?: string;
|
authIndex?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,6 +64,7 @@ export interface OpenAIProviderConfig {
|
|||||||
models?: ModelAlias[];
|
models?: ModelAlias[];
|
||||||
priority?: number;
|
priority?: number;
|
||||||
testModel?: string;
|
testModel?: string;
|
||||||
|
disableCooling?: boolean;
|
||||||
authIndex?: string;
|
authIndex?: string;
|
||||||
[key: string]: unknown;
|
[key: string]: unknown;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user