import { useEffect, useState, type FormEvent } from 'react'; import { Select as AntSelect } from 'antd'; import { Gauge, Pencil, Plus, RotateCcw, Route, Save, ShieldCheck, Trash2 } from 'lucide-react'; import type { GatewayRunnerPolicy, GatewayRunnerPolicyUpsertRequest, RuntimePolicySet, RuntimePolicySetUpsertRequest } from '@easyai-ai-gateway/contracts'; import { Badge, Button, Card, CardContent, CardHeader, CardTitle, ConfirmDialog, FormDialog, Input, Label, Select, Tabs, Textarea } from '../../components/ui'; import type { LoadState } from '../../types'; type RuntimePanelTab = 'model' | 'runner'; type RunnerPolicyStrategy = 'failover' | 'hardStop' | 'priorityDemote'; type RuntimePolicyForm = { policyKey: string; name: string; description: string; rpm: string; tpm: string; concurrency: string; retryEnabled: boolean; retryMaxAttempts: string; retryAllowKeywords: string[]; retryDenyKeywords: string[]; autoDisableEnabled: boolean; autoDisableThreshold: string; autoDisableKeywords: string[]; degradeEnabled: boolean; degradeCooldownSeconds: string; degradeKeywords: string[]; metadataJson: string; status: string; }; type RunnerPolicyForm = { name: string; description: string; failoverEnabled: boolean; maxPlatforms: string; maxDurationSeconds: string; allowCategories: string[]; denyCategories: string[]; allowCodes: string[]; denyCodes: string[]; allowKeywords: string[]; denyKeywords: string[]; allowStatusCodes: string[]; denyStatusCodes: string[]; failoverActions: Record; hardStopEnabled: boolean; hardStopCategories: string[]; hardStopCodes: string[]; hardStopStatusCodes: string[]; hardStopKeywords: string[]; priorityDemoteEnabled: boolean; priorityDemoteCategories: string[]; priorityDemoteCodes: string[]; priorityDemoteStatusCodes: string[]; priorityDemoteKeywords: string[]; metadataJson: string; status: string; }; const failoverActionDefinitions = [ { value: 'next', title: '仅轮转', description: '这些错误分类只尝试下一个平台,不修改当前平台状态。', }, { value: 'disable_and_next', title: '禁用后轮转', description: '这些错误分类会先禁用当前平台,再尝试下一个平台。', }, { value: 'cooldown_and_next', title: '冷却后轮转', description: '这些错误分类会先让当前平台模型进入冷却,再尝试下一个平台。', }, ] as const; const failoverCategoryOptions = [ 'network', 'timeout', 'stream_error', 'rate_limit', 'provider_5xx', 'provider_overloaded', 'auth_error', 'request_error', 'unsupported_model', 'user_permission', 'insufficient_balance', 'client_error', ].map((item) => ({ label: item, value: item })); export function RuntimePoliciesPanel(props: { message: string; runnerPolicy: GatewayRunnerPolicy | null; runtimePolicySets: RuntimePolicySet[]; state: LoadState; onDeleteRuntimePolicySet: (policySetId: string) => Promise; onSaveRunnerPolicy: (input: GatewayRunnerPolicyUpsertRequest) => Promise; onSaveRuntimePolicySet: (input: RuntimePolicySetUpsertRequest, policySetId?: string) => Promise; }) { const [activeTab, setActiveTab] = useState('model'); const [dialogOpen, setDialogOpen] = useState(false); const [editingId, setEditingId] = useState(''); const [form, setForm] = useState(() => createDefaultForm()); const [runnerForm, setRunnerForm] = useState(() => runnerPolicyToForm(null)); const [localError, setLocalError] = useState(''); const [pendingDeletePolicy, setPendingDeletePolicy] = useState(null); useEffect(() => { setRunnerForm(runnerPolicyToForm(props.runnerPolicy)); }, [props.runnerPolicy?.id, props.runnerPolicy?.updatedAt]); function openCreateDialog() { setEditingId(''); setLocalError(''); setForm(createDefaultForm(`runtime-${Date.now().toString(36)}`)); setDialogOpen(true); } function editPolicy(policy: RuntimePolicySet) { setEditingId(policy.id); setLocalError(''); setForm(policyToForm(policy)); setDialogOpen(true); } function closeDialog() { setDialogOpen(false); setEditingId(''); setLocalError(''); setForm(createDefaultForm()); } async function submit(event: FormEvent) { event.preventDefault(); setLocalError(''); try { await props.onSaveRuntimePolicySet(formToPayload(form), editingId || undefined); closeDialog(); } catch (err) { setLocalError(err instanceof Error ? err.message : '运行策略保存失败'); } } async function deletePolicy(policy: RuntimePolicySet) { if (isDefaultPolicy(policy)) return; try { await props.onDeleteRuntimePolicySet(policy.id); setPendingDeletePolicy(null); } catch (err) { setLocalError(err instanceof Error ? err.message : '运行策略删除失败'); } } async function submitRunnerPolicy(event: FormEvent) { event.preventDefault(); setLocalError(''); try { await props.onSaveRunnerPolicy(runnerFormToPayload(runnerForm)); } catch (err) { setLocalError(err instanceof Error ? err.message : '全局调度策略保存失败'); } } return (
运行策略

模型运行策略维护限流、平台内重试、自动禁用和降级;全局调度策略控制平台间切换、硬拒绝和失败平台优先级降级。

{activeTab === 'model' && }
{(props.message || localError) &&

{localError || props.message}

} }, { value: 'runner', label: '全局调度策略', icon: }, ]} onValueChange={setActiveTab} />
{activeTab === 'runner' && ( )} {activeTab === 'model' && (
{props.runtimePolicySets.map((policy) => (
{policy.name} {policy.policyKey}
{policy.status}
{policy.description &&

{policy.description}

}
{rateLimitSummary(policy)} {retrySummary(policy)} {autoDisableSummary(policy)} {degradeSummary(policy)}
))}
)} )} open={dialogOpen} title={editingId ? '编辑运行策略' : '新增运行策略'} onClose={closeDialog} onSubmit={submit} >
限流策略TPM / RPM / 并发
平台内重试策略允许/拒绝关键词控制同一平台是否再次调用
setForm({ ...form, retryEnabled: checked })} /> setForm({ ...form, retryAllowKeywords: value })} /> setForm({ ...form, retryDenyKeywords: value })} />
禁用与降级自动禁用错误关键词、优先级降级关键词
setForm({ ...form, autoDisableEnabled: checked })} /> setForm({ ...form, autoDisableKeywords: value })} /> setForm({ ...form, degradeEnabled: checked })} /> setForm({ ...form, degradeKeywords: value })} />