import { useEffect, useMemo, useState, type FormEvent, type ReactNode } from 'react'; import { Boxes, CheckCircle2, Gauge, Globe2, KeyRound, Pencil, Plus, RotateCcw, Search, ServerCog, ShieldCheck, SlidersHorizontal, Trash2, X } from 'lucide-react'; import type { BaseModelCatalogItem, CatalogProvider, IntegrationPlatform, ModelRateLimitStatus, PlatformModel, PricingRuleSet } from '@easyai-ai-gateway/contracts'; import { Badge, Button, Card, CardContent, CardHeader, CardTitle, ConfirmDialog, EmptyState, FormDialog, Input, Label, ScreenMessage, Select, Table, TableCell, TableHead, TableRow } from '../../components/ui'; import type { LoadState, PlatformWithModelsInput } from '../../types'; import { authTypes, applyProviderDefaults, baseModelTypes, baseModelTypeText, createEmptyPlatformForm, modelsForProvider, platformModelPayloads, platformPayload, providerLabel, selectedModelsForForm, stableModelAlias, validatePlatformForm, type PlatformWizardForm, } from './platform-form'; import { ModelCatalogCard } from './ModelCatalogCard'; export function PlatformManagementPanel(props: { baseModels: BaseModelCatalogItem[]; message: string; modelRateLimits: ModelRateLimitStatus[]; networkProxyConfig: { globalHttpProxy?: string; globalHttpProxySet: boolean; globalHttpProxySource?: string } | null; platforms: IntegrationPlatform[]; platformModels: PlatformModel[]; pricingRuleSets: PricingRuleSet[]; providers: CatalogProvider[]; state: LoadState; onDeletePlatform: (platformId: string) => Promise; onSavePlatform: (input: PlatformWithModelsInput) => Promise; }) { const defaultProvider = props.providers[0]?.providerKey ?? props.baseModels[0]?.providerKey ?? ''; const [now, setNow] = useState(() => Date.now()); const [dialogOpen, setDialogOpen] = useState(false); const [viewMode, setViewMode] = useState<'platforms' | 'models' | 'limits'>('platforms'); const [modelQuery, setModelQuery] = useState(''); const [selectedPlatformId, setSelectedPlatformId] = useState(''); const [validationMessage, setValidationMessage] = useState(''); const [globalProxyNoticeOpen, setGlobalProxyNoticeOpen] = useState(false); const [editingPlatform, setEditingPlatform] = useState(null); const [pendingDeletePlatform, setPendingDeletePlatform] = useState(null); const providerMap = useMemo(() => new Map(props.providers.map((item) => [item.providerKey, item])), [props.providers]); const platformMap = useMemo(() => new Map(props.platforms.map((item) => [item.id, item])), [props.platforms]); const [form, setForm] = useState(() => createEmptyPlatformForm(defaultProvider, providerDefaults(providerMap.get(defaultProvider)))); const providerOptions = useMemo( () => Array.from(new Set([...props.providers.map((item) => item.providerKey), ...props.baseModels.map((item) => item.providerKey)])).filter(Boolean), [props.baseModels, props.providers], ); const availableModels = useMemo(() => props.baseModels.filter((item) => item.status !== 'hidden'), [props.baseModels]); const selectedModels = useMemo(() => selectedModelsForForm(props.baseModels, form), [form, props.baseModels]); const platformModelCount = useMemo(() => countModelsByPlatform(props.platformModels), [props.platformModels]); const selectedModelPlatformId = selectedPlatformId || props.platforms[0]?.id || ''; const filteredPlatformModels = useMemo(() => { const keyword = modelQuery.trim().toLowerCase(); return props.platformModels.filter((model) => { const matchesPlatform = !selectedModelPlatformId || model.platformId === selectedModelPlatformId; const platform = platformMap.get(model.platformId); const text = [ model.displayName, model.modelName, model.providerModelName, model.modelAlias, ...model.modelType, model.provider, platform?.name, platform?.internalName, platform?.platformKey, ].filter(Boolean).join(' ').toLowerCase(); return matchesPlatform && (!keyword || text.includes(keyword)); }); }, [modelQuery, platformMap, props.platformModels, selectedModelPlatformId]); useEffect(() => { const timer = window.setInterval(() => setNow(Date.now()), 1000); return () => window.clearInterval(timer); }, []); function openCreateDialog() { const provider = providerOptions[0] ?? ''; const providerName = providerDisplayName(provider, providerMap); setValidationMessage(''); setEditingPlatform(null); setForm({ ...createEmptyPlatformForm(provider, providerDefaults(providerMap.get(provider))), name: providerName, internalName: providerName, selectedModelIds: defaultSelectedModelIds(props.baseModels, provider), }); setDialogOpen(true); } function openEditDialog(platform: IntegrationPlatform) { setValidationMessage(''); setEditingPlatform(platform); setForm(platformToForm(platform, props.baseModels, props.platformModels, providerDefaults(providerMap.get(platform.provider)))); setDialogOpen(true); } function closeDialog() { setDialogOpen(false); setEditingPlatform(null); } function updateProvider(provider: string) { const previousProviderName = providerDisplayName(form.provider, providerMap); const providerName = providerDisplayName(provider, providerMap); const nextForm = applyProviderDefaults(form, provider, providerDefaults(providerMap.get(provider))); setForm({ ...nextForm, name: shouldUseProviderDefaultName(form.name, previousProviderName) ? providerName : form.name, internalName: shouldUseProviderDefaultName(form.internalName, previousProviderName) ? providerName : form.internalName, selectionMode: 'partial', selectedModelIds: defaultSelectedModelIds(props.baseModels, provider), }); } function updateProxyMode(proxyMode: PlatformWizardForm['proxyMode']) { if (proxyMode === 'global') { setGlobalProxyNoticeOpen(true); } setForm({ ...form, proxyMode, httpProxy: proxyMode === 'custom' ? form.httpProxy : '', }); } async function submit(event: FormEvent) { event.preventDefault(); const validationMessage = validatePlatformForm(form, selectedModels.length, { allowEmptyCredentials: Boolean(editingPlatform) }); if (validationMessage) { setValidationMessage(validationMessage); return; } try { await props.onSavePlatform({ platformId: editingPlatform?.id, platform: platformPayload(form, { preserveEmptyCredentials: Boolean(editingPlatform) }), models: platformModelPayloads(props.baseModels, form), selectionMode: form.selectionMode, }); closeDialog(); } catch (err) { setValidationMessage(err instanceof Error ? err.message : editingPlatform ? '更新平台失败' : '创建平台失败'); } } async function deletePlatform(platform: IntegrationPlatform) { try { await props.onDeletePlatform(platform.id); setPendingDeletePlatform(null); } catch (err) { setValidationMessage(err instanceof Error ? err.message : '删除平台失败'); } } return (
setValidationMessage('')} />
平台管理

配置平台授权、Base URL、平台内重试与限流策略,并选择接入全部或部分基准模型。

{viewMode === 'platforms' ? ( ) : viewMode === 'models' ? ( ) : ( )} {props.message &&

{props.message}

}
)} open={dialogOpen} title={editingPlatform ? '编辑平台' : '新增平台'} onClose={closeDialog} onSubmit={submit} > } title="基础信息"> } title="授权信息"> setForm({ ...form, testMode: checked })} /> } title="网络代理"> {form.proxyMode === 'custom' && ( )} } title="路由与计费"> setForm({ ...form, retryEnabled: checked })} /> } title="限流策略">
setForm({ ...form, supportBase64Input: checked })} /> setForm({ ...form, supportUrlInput: checked })} />
} title={`模型绑定 · ${selectedModels.length}/${availableModels.length}`}>
setGlobalProxyNoticeOpen(false)} onConfirm={() => setGlobalProxyNoticeOpen(false)} >

当前设置代理服务器:{globalProxyStatusText(props.networkProxyConfig)}

setPendingDeletePlatform(null)} onConfirm={() => pendingDeletePlatform ? deletePlatform(pendingDeletePlatform) : undefined} />
); } function ModelBindingPolicy(props: { form: PlatformWizardForm; onChange: (value: PlatformWizardForm) => void }) { const { form, onChange } = props; return (
onChange({ ...form, modelOverrideRetry: checked })} /> {form.modelOverrideRetry && ( <> onChange({ ...form, modelRetryEnabled: checked })} /> )} onChange({ ...form, modelOverrideRateLimit: checked })} /> {form.modelOverrideRateLimit && ( <> )}
); } function PlatformTable(props: { now: number; platformModelCount: Map; platforms: IntegrationPlatform[]; providerMap: Map; pricingRuleSets: PricingRuleSet[]; onCreate: () => void; onDelete: (platform: IntegrationPlatform) => void; onEdit: (platform: IntegrationPlatform) => void; }) { if (!props.platforms.length) { return (
还没有接入平台 点击“添加平台”配置平台信息、运行策略和模型绑定后,平台模型即可参与网关路由。
); } return ( 平台 Provider 计价规则 折扣 限流 模型数 运行 操作 {props.platforms.map((platform) => { const pricing = platformPricingSummary(platform, props.pricingRuleSets); const rateLimit = platformRateLimitSummary(platform.rateLimitPolicy); const runtime = platformRuntimeSummary(platform); const platformCooldownMs = cooldownRemainingMs(platform.cooldownUntil, props.now); return ( {platformDisplayName(platform)} {platform.internalName ? `对外:${platform.name} · ${platform.platformKey}` : platform.platformKey} {props.providerMap.get(platform.provider)?.displayName ?? platform.provider} {pricing.title} {pricing.subtitle} {formatDiscountFactor(platform.defaultDiscountFactor)} 平台默认折扣 {rateLimit.title} {rateLimit.subtitle} {props.platformModelCount.get(platform.id) ?? 0} {platformCooldownMs > 0 ? ( 冷却中 ) : ( {platform.status} )} {platformCooldownMs > 0 ? `剩余 ${formatCooldownRemaining(platformCooldownMs)}` : runtime}
); })}
); } function PlatformModelTable(props: { baseModels: BaseModelCatalogItem[]; modelQuery: string; models: PlatformModel[]; platformId: string; platformMap: Map; platforms: IntegrationPlatform[]; providerMap: Map; now: number; onModelQueryChange: (value: string) => void; onPlatformChange: (value: string) => void; }) { return (
{!props.platforms.length ? ( ) : ( props.models.length ? (
{props.models.map((model) => { const platform = props.platformMap.get(model.platformId); const provider = platform ? props.providerMap.get(platform.provider) : undefined; const baseModel = findBaseModelForPlatformModel(platform, props.baseModels, model); const modelIconPath = readPlatformModelIconPath(model, baseModel); const modelCooldownMs = cooldownRemainingMs(model.cooldownUntil, props.now); return ( {model.modelType.join(', ')}, ...(modelCooldownMs > 0 ? [冷却中 · {formatCooldownRemaining(modelCooldownMs)}] : []), {model.enabled ? 'enabled' : 'disabled'}, ]} chips={platformModelChips(model)} iconPath={modelIconPath || provider?.iconPath} iconText={provider?.displayName ?? model.provider ?? 'model'} meta={[ platform ? platformDisplayName(platform) : model.platformName ?? '-', provider?.displayName ?? platform?.provider ?? model.provider ?? '-', model.modelAlias || model.modelName || '-', ]} subtitle={model.providerModelName ? `调用模型名:${model.providerModelName}` : model.modelName} title={model.displayName || model.modelName} /> ); })}
) : ( ) )}
); } function RateLimitStatusTable(props: { statuses: ModelRateLimitStatus[]; platformMap: Map; now: number }) { if (!props.statuses.length) { return ; } return (
按综合满载率从高到低排序 每 3 秒刷新一次;TPM 显示已结算 + 预占。
模型 平台 并发 TPM RPM 状态 满载率 {props.statuses.map((status) => { const platform = props.platformMap.get(status.platformId); return ( {status.displayName || status.modelAlias || status.modelName} {status.providerModelName || status.modelName} {platform ? platformDisplayName(platform) : status.platformName} {status.provider} {metricCell(status.concurrent)} {metricCell(status.tpm, true)} {metricCell(status.rpm)} {modelRuntimeStatusCell(status, props.now)} {formatPercent(status.loadRatio)} ); })}
); } function platformDisplayName(platform: IntegrationPlatform) { return platform.internalName?.trim() || platform.name; } function FormSection(props: { children: ReactNode; icon: ReactNode; title: string }) { return (
{props.icon}{props.title}
{props.children}
); } function AuthFields(props: { form: PlatformWizardForm; onChange: (value: PlatformWizardForm) => void }) { const { form, onChange } = props; const apiKeyPreview = credentialPreviewValue(form.credentialsPreview, 'apiKey', 'api_key', 'key'); const tokenPreview = credentialPreviewValue(form.credentialsPreview, 'token'); const accessKeyPreview = credentialPreviewValue(form.credentialsPreview, 'accessKey', 'access_key'); const secretKeyPreview = credentialPreviewValue(form.credentialsPreview, 'secretKey', 'secret_key'); if (form.authType === 'Token') { return ( onChange({ ...form, token: event.target.value })} /> ); } if (form.authType === 'AccessKey-SecretKey') { return ( <> onChange({ ...form, accessKey: event.target.value })} /> onChange({ ...form, secretKey: event.target.value })} /> ); } if (form.authType === 'none') { return
不写入平台凭证
; } return ( onChange({ ...form, apiKey: event.target.value })} /> ); } function CredentialField(props: { children: ReactNode; label: string }) { return ( ); } function ToggleField(props: { checked: boolean; description?: string; label: string; onChange: (checked: boolean) => void }) { return ( ); } function ModelSelection(props: { currentProvider: string; form: PlatformWizardForm; models: BaseModelCatalogItem[]; providerMap: Map; providerName: string; onChange: (value: PlatformWizardForm) => void; }) { const [pickerOpen, setPickerOpen] = useState(false); const selectedIds = new Set(props.form.selectedModelIds); const selectedModels = selectedModelsForForm(props.models, props.form); function removeModel(modelId: string) { const next = new Set(selectedIds); next.delete(modelId); const modelDiscountFactors = { ...props.form.modelDiscountFactors }; const modelNameMappings = { ...props.form.modelNameMappings }; delete modelDiscountFactors[modelId]; delete modelNameMappings[modelId]; props.onChange({ ...props.form, modelDiscountFactors, modelNameMappings, selectionMode: 'partial', selectedModelIds: Array.from(next), }); } function updateSelectedModelIds(modelIds: string[]) { const visibleIds = new Set(props.models.map((model) => model.id)); const nextIds = Array.from(new Set(modelIds)).filter((modelId) => visibleIds.has(modelId)); props.onChange({ ...props.form, selectionMode: 'partial', selectedModelIds: nextIds }); } function updateModelDiscount(modelId: string, value: string) { props.onChange({ ...props.form, modelDiscountFactors: { ...props.form.modelDiscountFactors, [modelId]: value, }, }); } function updateModelNameMapping(modelId: string, value: string) { props.onChange({ ...props.form, modelNameMappings: { ...props.form.modelNameMappings, [modelId]: value, }, }); } return (
已选模型 默认添加 {props.providerName} 的模型,也可以从整个模型库添加其它厂商模型。
{!selectedModels.length ? (
当前没有已选模型,点击“添加模型”从模型库选择。
) : (
{selectedModels.map((model) => { const modelLabel = stableModelAlias(model) || model.providerModelName; const providerModelName = props.form.modelNameMappings[model.id] ?? model.providerModelName; return (
{modelLabel} {props.providerMap.get(model.providerKey)?.displayName ?? model.providerKey} · {model.providerModelName} · {baseModelTypeText(model)}
); })}
)} setPickerOpen(false)} onConfirm={updateSelectedModelIds} />
); } function ModelPickerDialog(props: { currentProvider: string; models: BaseModelCatalogItem[]; open: boolean; providerMap: Map; selectedModelIds: string[]; onClose: () => void; onConfirm: (modelIds: string[]) => void; }) { const [query, setQuery] = useState(''); const [providerFilter, setProviderFilter] = useState(props.currentProvider || 'all'); const [draftIds, setDraftIds] = useState(props.selectedModelIds); const providerOptions = useMemo(() => Array.from(new Set(props.models.map((model) => model.providerKey))).filter(Boolean), [props.models]); const draftIdSet = new Set(draftIds); const keyword = query.trim().toLowerCase(); const filteredModels = props.models.filter((model) => { if (providerFilter !== 'all' && model.providerKey !== providerFilter) return false; if (!keyword) return true; return [ stableModelAlias(model), model.providerModelName, model.canonicalModelKey, baseModelTypeText(model), model.providerKey, props.providerMap.get(model.providerKey)?.displayName, JSON.stringify(model.capabilities ?? {}), ].filter(Boolean).join(' ').toLowerCase().includes(keyword); }); useEffect(() => { if (!props.open) return; setQuery(''); setProviderFilter(props.currentProvider || 'all'); setDraftIds(props.selectedModelIds); }, [props.currentProvider, props.open, props.selectedModelIds]); useEffect(() => { if (!props.open) return undefined; function onKeyDown(event: KeyboardEvent) { if (event.key !== 'Escape') return; event.preventDefault(); event.stopImmediatePropagation(); props.onClose(); } window.addEventListener('keydown', onKeyDown, true); return () => window.removeEventListener('keydown', onKeyDown, true); }, [props.open, props.onClose]); function toggleModel(modelId: string) { setDraftIds((current) => { const next = new Set(current); if (next.has(modelId)) next.delete(modelId); else next.add(modelId); return Array.from(next); }); } function addFilteredModels() { setDraftIds((current) => Array.from(new Set([...current, ...filteredModels.map((model) => model.id)]))); } if (!props.open) return null; return (
添加模型 已选择 {draftIds.length} 个模型
{filteredModels.length ? filteredModels.map((model) => ( )) :
没有匹配的模型。
}
); } function countModelsByPlatform(models: PlatformModel[]) { const counts = new Map(); models.forEach((model) => counts.set(model.platformId, (counts.get(model.platformId) ?? 0) + 1)); return counts; } function providerDefaults(provider?: CatalogProvider) { return { defaultAuthType: provider?.defaultAuthType || 'APIKey', defaultBaseUrl: provider?.defaultBaseUrl || '', }; } function platformToForm( platform: IntegrationPlatform, baseModels: BaseModelCatalogItem[], platformModels: PlatformModel[], defaults?: ReturnType, ): PlatformWizardForm { const config = platform.config ?? {}; const retryPolicy = platform.retryPolicy ?? {}; const rateLimitPolicy = platform.rateLimitPolicy ?? {}; const networkProxy = readNetworkProxyConfig(config); const currentModels = platformModels.filter((model) => model.platformId === platform.id); return { ...createEmptyPlatformForm(platform.provider, defaults), provider: platform.provider, platformKey: platform.platformKey, name: platform.name, internalName: platform.internalName ?? '', baseUrl: platform.baseUrl ?? defaults?.defaultBaseUrl ?? '', authType: platform.authType || defaults?.defaultAuthType || 'APIKey', credentialsPreview: platform.credentialsPreview ?? {}, apiKey: credentialPreviewValue(platform.credentialsPreview, 'apiKey', 'api_key', 'key'), token: credentialPreviewValue(platform.credentialsPreview, 'token'), accessKey: credentialPreviewValue(platform.credentialsPreview, 'accessKey', 'access_key'), secretKey: credentialPreviewValue(platform.credentialsPreview, 'secretKey', 'secret_key'), pricingRuleSetId: platform.pricingRuleSetId ?? '', defaultDiscountFactor: numberText(platform.defaultDiscountFactor, '1'), priority: numberText(platform.priority, '100'), retryEnabled: readBoolean(retryPolicy, 'enabled', true), retryMaxAttempts: numberText(readNumber(retryPolicy, 'maxAttempts'), '2'), rpmLimit: readLimit(rateLimitPolicy, 'rpm'), rpsLimit: readLimit(rateLimitPolicy, 'rps'), tpmLimit: readLimit(rateLimitPolicy, 'tpm_total'), concurrencyLimit: readLimit(rateLimitPolicy, 'concurrent'), testMode: readBoolean(config, 'testMode', false), proxyMode: networkProxy.proxyMode, httpProxy: networkProxy.httpProxy, supportBase64Input: readBoolean(config, 'supportBase64Input', true), supportUrlInput: readBoolean(config, 'supportUrlInput', true), selectedModelIds: platformModelBaseIds(platform, baseModels, currentModels), modelDiscountFactors: platformModelDiscountFactors(platform, baseModels, currentModels), modelNameMappings: platformModelNameMappings(platform, baseModels, currentModels), selectionMode: 'partial', }; } function defaultSelectedModelIds(models: BaseModelCatalogItem[], provider: string) { return modelsForProvider(models, provider).map((model) => model.id); } function platformModelBaseIds(platform: IntegrationPlatform, baseModels: BaseModelCatalogItem[], platformModels: PlatformModel[]) { const ids = platformModels.map((model) => findBaseModelForPlatformModel(platform, baseModels, model)?.id).filter((id): id is string => Boolean(id)); return Array.from(new Set(ids)); } function platformModelDiscountFactors(platform: IntegrationPlatform, baseModels: BaseModelCatalogItem[], platformModels: PlatformModel[]) { return platformModels.reduce>((acc, model) => { const baseModel = findBaseModelForPlatformModel(platform, baseModels, model); if (baseModel?.id && model.discountFactor) acc[baseModel.id] = String(model.discountFactor); return acc; }, {}); } function platformModelNameMappings(platform: IntegrationPlatform, baseModels: BaseModelCatalogItem[], platformModels: PlatformModel[]) { return platformModels.reduce>((acc, model) => { const baseModel = findBaseModelForPlatformModel(platform, baseModels, model); if (baseModel?.id) acc[baseModel.id] = model.providerModelName || model.modelName; return acc; }, {}); } function findBaseModelForPlatformModel(platform: IntegrationPlatform | undefined, baseModels: BaseModelCatalogItem[], model: PlatformModel) { return baseModels.find((item) => item.id === model.baseModelId) ?? baseModels.find((item) => item.canonicalModelKey === model.modelAlias) ?? baseModels.find((item) => stableModelAlias(item) === model.modelAlias) ?? baseModels.find((item) => item.providerModelName === model.modelName && model.modelType.some((type) => baseModelTypes(item).includes(type))) ?? baseModels.find((item) => item.providerKey === platform?.provider && item.providerModelName === model.modelName && model.modelType.some((type) => baseModelTypes(item).includes(type))); } function readPlatformModelIconPath(model: PlatformModel, baseModel?: BaseModelCatalogItem) { return readString(baseModel?.metadata?.iconPath) || readString(readRecord(baseModel?.metadata?.rawModel)?.icon_path) || readString(readRecord(model.capabilities)?.iconPath) || readString(readRecord(model.capabilities)?.icon_path) || readString(readRecord(model.billingConfig)?.iconPath) || readString(readRecord(model.billingConfig)?.icon_path); } function readString(value: unknown) { return typeof value === 'string' ? value : ''; } function credentialPreviewValue(preview: Record | undefined, ...keys: string[]) { if (!preview) return ''; for (const key of keys) { const value = preview[key]; if (typeof value === 'string' && value.trim()) return value; } return ''; } function credentialInputPlaceholder(preview: string) { return preview ? '填写新凭证以覆盖当前值' : ''; } function readRecord(value: unknown) { return value && typeof value === 'object' && !Array.isArray(value) ? value as Record : {}; } function readBoolean(source: Record, key: string, fallback: boolean) { const value = source[key]; return typeof value === 'boolean' ? value : fallback; } function readNetworkProxyConfig(config: Record): Pick { const networkProxy = readRecord(config.networkProxy); const mode = readString(networkProxy.mode || config.proxyMode); const httpProxy = readString(networkProxy.httpProxy || config.httpProxy); if (mode === 'global' || mode === 'custom') { return { proxyMode: mode, httpProxy }; } return { proxyMode: 'none', httpProxy: '' }; } function readNumber(source: Record, key: string) { const value = source[key]; return typeof value === 'number' && Number.isFinite(value) ? value : undefined; } function readLimit(policy: Record, metric: string) { const rules = Array.isArray(policy.rules) ? policy.rules : []; const rule = rules.find((item): item is { metric?: unknown; limit?: unknown } => typeof item === 'object' && item !== null && 'metric' in item && item.metric === metric); return typeof rule?.limit === 'number' ? String(rule.limit) : ''; } function numberText(value: unknown, fallback: string) { return typeof value === 'number' && Number.isFinite(value) && value > 0 ? String(value) : fallback; } function providerDisplayName(providerKey: string, providerMap: Map) { return providerMap.get(providerKey)?.displayName || providerKey; } function shouldUseProviderDefaultName(value: string, previousProviderName: string) { const trimmed = value.trim(); return !trimmed || trimmed === previousProviderName; } function platformModelChips(model: PlatformModel) { const chips: string[] = [model.pricingMode]; if (model.discountFactor) chips.push(`折扣 ${model.discountFactor}`); if (model.pricingRuleSetId) chips.push('计价规则'); if (model.runtimePolicySetId) chips.push('运行策略'); return chips.filter(Boolean); } function platformPricingSummary(platform: IntegrationPlatform, ruleSets: PricingRuleSet[]) { const matchedRuleSet = platform.pricingRuleSetId ? ruleSets.find((item) => item.id === platform.pricingRuleSetId) : undefined; const defaultRuleSet = ruleSets.find((item) => item.category === 'default' && item.status === 'active') ?? ruleSets.find((item) => item.category === 'default'); const title = matchedRuleSet?.name || defaultRuleSet?.name || '默认计价规则'; return { title, subtitle: pricingModeText(platform.defaultPricingMode), }; } function pricingModeText(mode: IntegrationPlatform['defaultPricingMode']) { if (mode === 'custom') return '自定义计价'; if (mode === 'inherit') return '直接继承'; return '继承并应用折扣'; } function formatDiscountFactor(value: number | undefined) { const factor = typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : 1; return `${trimNumber(factor * 100)}%`; } function platformRateLimitSummary(policy: IntegrationPlatform['rateLimitPolicy']) { const rules = Array.isArray(policy?.rules) ? policy.rules : []; if (!rules.length) { return { title: '未设置', subtitle: '跟随全局或模型策略' }; } const labels = rules .filter((rule) => typeof rule.limit === 'number' && Number.isFinite(rule.limit)) .map((rule) => `${rateLimitMetricText(rule.metric)} ${formatLimit(rule.limit)}`); if (!labels.length) { return { title: '未设置', subtitle: '跟随全局或模型策略' }; } return { title: labels.slice(0, 2).join(' · '), subtitle: labels.length > 2 ? labels.slice(2).join(' · ') : '平台级限流', }; } function rateLimitMetricText(metric: string) { const labels: Record = { tpm_total: 'TPM', tpm_input: '输入TPM', tpm_output: '输出TPM', rpm: 'RPM', rps: 'RPS', concurrent: '并发', queue_size: '队列', }; return labels[metric] ?? metric; } function metricCell(metric: ModelRateLimitStatus['rpm'], includeReserved = false) { if (!metric.limited) return {formatLimit(metric.currentValue)} / 不限未配置上限; return ( {formatLimit(metric.currentValue)} / {formatLimit(metric.limitValue)} {includeReserved && metric.reservedValue > 0 ? `已用 ${formatLimit(metric.usedValue)} · 预占 ${formatLimit(metric.reservedValue)}` : `窗口 ${formatPercent(metric.ratio)}`} ); } function modelRuntimeStatusCell(status: ModelRateLimitStatus, now: number) { const modelCooldownMs = cooldownRemainingMs(status.modelCooldownUntil, now); const platformCooldownMs = cooldownRemainingMs(status.platformCooldownUntil, now); if (modelCooldownMs > 0) { return ( 模型冷却中 剩余 {formatCooldownRemaining(modelCooldownMs)} ); } if (platformCooldownMs > 0) { return ( 平台冷却中 剩余 {formatCooldownRemaining(platformCooldownMs)} ); } return ( {status.enabled ? '可用' : '已停用'} {status.enabled ? '参与路由' : '不参与路由'} ); } function cooldownRemainingMs(cooldownUntil: string | undefined, now: number) { if (!cooldownUntil) return 0; const until = Date.parse(cooldownUntil); if (!Number.isFinite(until)) return 0; return Math.max(until - now, 0); } function formatCooldownRemaining(milliseconds: number) { const minutes = milliseconds / 60000; if (minutes >= 1) return `${trimNumber(Math.ceil(minutes * 10) / 10)} 分钟`; const seconds = Math.ceil(milliseconds / 1000); return `${Math.max(seconds, 1)} 秒`; } function formatPercent(value: number) { if (!Number.isFinite(value) || value <= 0) return '0%'; return `${trimNumber(value * 100)}%`; } function platformRuntimeSummary(platform: IntegrationPlatform) { const retryPolicy = platform.retryPolicy ?? {}; const retryEnabled = readBoolean(retryPolicy, 'enabled', true); const maxAttempts = readNumber(retryPolicy, 'maxAttempts') ?? 2; return `优先级 ${platform.priority} · ${retryEnabled ? `同平台最多尝试 ${maxAttempts} 次` : '同平台不重试'} · ${proxyModeText(readNetworkProxyConfig(platform.config ?? {}).proxyMode)}`; } function proxyModeText(mode: PlatformWizardForm['proxyMode']) { if (mode === 'global') return '全局代理'; if (mode === 'custom') return '自定义代理'; return '直连'; } function globalProxyStatusText(config: { globalHttpProxy?: string; globalHttpProxySet: boolean } | null) { if (!config) return '读取中'; const proxy = config.globalHttpProxy?.trim() ?? ''; return config.globalHttpProxySet && proxy ? proxy : '未设置'; } function formatLimit(value: number) { if (Math.abs(value) >= 10000) return `${trimNumber(value / 10000)}万`; if (Math.abs(value) >= 1000) return `${trimNumber(value / 1000)}k`; return trimNumber(value); } function trimNumber(value: number) { return Number.isInteger(value) ? String(value) : value.toFixed(2).replace(/\.?0+$/, ''); }