增加单一源保护全局策略

This commit is contained in:
2026-06-15 00:14:40 +08:00
parent bffd4ecb98
commit b860ef37e8
12 changed files with 210 additions and 14 deletions
@@ -6,7 +6,7 @@ import { Badge, Button, Card, CardContent, CardHeader, CardTitle, ConfirmDialog,
import type { LoadState } from '../../types';
type RuntimePanelTab = 'model' | 'runner';
type RunnerPolicyStrategy = 'failover' | 'hardStop' | 'priorityDemote';
type RunnerPolicyStrategy = 'failover' | 'hardStop' | 'priorityDemote' | 'singleSource';
type RuntimePolicyForm = {
policyKey: string;
@@ -54,6 +54,7 @@ type RunnerPolicyForm = {
priorityDemoteCodes: string[];
priorityDemoteStatusCodes: string[];
priorityDemoteKeywords: string[];
singleSourceProtectionEnabled: boolean;
metadataJson: string;
status: string;
};
@@ -353,6 +354,13 @@ function RunnerPolicyEditor(props: {
enabled: props.form.priorityDemoteEnabled,
icon: <Gauge size={15} />,
},
{
value: 'singleSource' as const,
title: '单一源保护',
description: '只有一个可用源时跳过自动禁用和冷却',
enabled: props.form.singleSourceProtectionEnabled,
icon: <ShieldCheck size={15} />,
},
];
const activeDefinition = strategyDefinitions.find((item) => item.value === activeStrategy) ?? strategyDefinitions[0];
@@ -455,6 +463,17 @@ function RunnerPolicyEditor(props: {
<KeywordField label="降级关键词" value={props.form.priorityDemoteKeywords} onChange={(value) => patch({ priorityDemoteKeywords: value })} />
</div>
)}
{activeStrategy === 'singleSource' && (
<div className="runtimePolicyRows runnerPolicyDetailRows">
<Toggle
checked={props.form.singleSourceProtectionEnabled}
label="启用单一源保护"
onChange={(checked) => patch({ singleSourceProtectionEnabled: checked })}
/>
<span className="runtimeFieldHint spanTwo"></span>
</div>
)}
</div>
</div>
@@ -554,6 +573,7 @@ function runnerPolicyToForm(policy: GatewayRunnerPolicy | null): RunnerPolicyFor
const failover = readObject(policy?.failoverPolicy);
const hardStop = readObject(policy?.hardStopPolicy);
const priorityDemote = readObject(policy?.priorityDemotePolicy);
const singleSource = readObject(policy?.singleSourcePolicy);
return {
name: policy?.name ?? '默认全局调度策略',
description: policy?.description ?? '控制多个候选平台之间的故障切换;模型运行策略只可覆盖 failoverPolicy,不能覆盖 hardStopPolicy。',
@@ -579,6 +599,7 @@ function runnerPolicyToForm(policy: GatewayRunnerPolicy | null): RunnerPolicyFor
priorityDemoteCodes: tagsFromValue(priorityDemote.codes ?? ['network', 'timeout', 'stream_read_error', 'rate_limit', 'server_error', 'overloaded']),
priorityDemoteStatusCodes: tagsFromValue(priorityDemote.statusCodes ?? [408, 429, 500, 502, 503, 504]),
priorityDemoteKeywords: tagsFromValue(priorityDemote.keywords ?? ['timeout', 'network', 'rate_limit', 'overloaded', 'temporarily_unavailable', 'server_error', '429', '5xx']),
singleSourceProtectionEnabled: readBool(singleSource.enabled, true),
metadataJson: JSON.stringify(policy?.metadata ?? {}, null, 2),
status: policy?.status ?? 'active',
};
@@ -617,6 +638,9 @@ function runnerFormToPayload(form: RunnerPolicyForm): GatewayRunnerPolicyUpsertR
statusCodes: parseNumberTags(form.priorityDemoteStatusCodes),
keywords: cleanTags(form.priorityDemoteKeywords),
},
singleSourcePolicy: {
enabled: form.singleSourceProtectionEnabled,
},
metadata: parseJson(form.metadataJson),
status: form.status.trim() || 'active',
};