From d69aaed44476193d0ebad8597da26d0ac363c112 Mon Sep 17 00:00:00 2001 From: wangbo Date: Tue, 12 May 2026 03:43:00 +0800 Subject: [PATCH] feat(web): refresh rate limits timestamp and tpm settlement display --- apps/web/src/App.tsx | 19 ++++++--- apps/web/src/app-state.ts | 1 + apps/web/src/pages/AdminPage.tsx | 7 ++-- .../pages/admin/PlatformManagementPanel.tsx | 41 +++++++++++++------ 4 files changed, 47 insertions(+), 21 deletions(-) diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index 82a8849..6d7d557 100644 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -187,6 +187,7 @@ export function App() { const [auditLogs, setAuditLogs] = useState([]); const [rateLimitWindows, setRateLimitWindows] = useState([]); const [modelRateLimits, setModelRateLimits] = useState([]); + const [modelRateLimitsUpdatedAt, setModelRateLimitsUpdatedAt] = useState(null); const [tenants, setTenants] = useState([]); const [users, setUsers] = useState([]); const [userGroups, setUserGroups] = useState([]); @@ -249,6 +250,7 @@ export function App() { void Promise.all([listModelRateLimitStatuses(token), listPlatforms(token)]) .then(([rateLimitResponse, platformResponse]) => { setModelRateLimits(rateLimitResponse.items); + setModelRateLimitsUpdatedAt(Date.now()); setPlatforms(platformResponse.items); loadedDataKeysRef.current.add('modelRateLimits'); loadedDataKeysRef.current.add('platforms'); @@ -299,10 +301,11 @@ export function App() { platforms, pricingRules, pricingRuleSets, - providers, - rateLimitWindows, - modelRateLimits, - runtimePolicySets, + providers, + rateLimitWindows, + modelRateLimits, + modelRateLimitsUpdatedAt, + runtimePolicySets, taskResult, tasks, tenants, @@ -310,7 +313,7 @@ export function App() { users, walletAccounts, walletTransactions, - }), [accessRules, apiKeys, auditLogs, baseModels, modelCatalog, modelRateLimits, models, networkProxyConfig, platforms, pricingRuleSets, pricingRules, providers, rateLimitWindows, runnerPolicy, runtimePolicySets, taskResult, tasks, tenants, userGroups, users, walletAccounts, walletTransactions]); + }), [accessRules, apiKeys, auditLogs, baseModels, modelCatalog, modelRateLimits, modelRateLimitsUpdatedAt, models, networkProxyConfig, platforms, pricingRuleSets, pricingRules, providers, rateLimitWindows, runnerPolicy, runtimePolicySets, taskResult, tasks, tenants, userGroups, users, walletAccounts, walletTransactions]); async function refresh(nextToken = token) { await ensureRouteData(nextToken, true); @@ -415,7 +418,11 @@ export function App() { setRateLimitWindows((await listRateLimitWindows(nextToken)).items); return; case 'modelRateLimits': - setModelRateLimits((await listModelRateLimitStatuses(nextToken)).items); + { + const response = await listModelRateLimitStatuses(nextToken); + setModelRateLimits(response.items); + setModelRateLimitsUpdatedAt(Date.now()); + } return; case 'tenants': setTenants((await listTenants(nextToken)).items); diff --git a/apps/web/src/app-state.ts b/apps/web/src/app-state.ts index 31ec637..83a567f 100644 --- a/apps/web/src/app-state.ts +++ b/apps/web/src/app-state.ts @@ -37,6 +37,7 @@ export interface ConsoleData { providers: CatalogProvider[]; rateLimitWindows: RateLimitWindow[]; modelRateLimits: ModelRateLimitStatus[]; + modelRateLimitsUpdatedAt: number | null; runtimePolicySets: RuntimePolicySet[]; taskResult: GatewayTask | null; tasks: GatewayTask[]; diff --git a/apps/web/src/pages/AdminPage.tsx b/apps/web/src/pages/AdminPage.tsx index b1a55ab..036ee7c 100644 --- a/apps/web/src/pages/AdminPage.tsx +++ b/apps/web/src/pages/AdminPage.tsx @@ -137,9 +137,10 @@ export function AdminPage(props: { - ) : ( - - )} + onPlatformChange={setSelectedPlatformId} + /> + ) : ( + + )} {props.message &&

{props.message}

} @@ -572,7 +578,7 @@ function PlatformModelTable(props: { ); } -function RateLimitStatusTable(props: { statuses: ModelRateLimitStatus[]; platformMap: Map; now: number }) { +function RateLimitStatusTable(props: { statuses: ModelRateLimitStatus[]; platformMap: Map; now: number; updatedAt: number | null }) { if (!props.statuses.length) { return ; } @@ -580,7 +586,7 @@ function RateLimitStatusTable(props: { statuses: ModelRateLimitStatus[]; platfor
按综合满载率从高到低排序 - 每 3 秒刷新一次;TPM 显示已结算 + 预占。 + 每 3 秒刷新一次;TPM 显示已结算 + 预占;最新更新时间 {formatTimeOfDay(props.updatedAt)}。
@@ -1195,15 +1201,26 @@ function rateLimitMetricText(metric: string) { } function metricCell(metric: ModelRateLimitStatus['rpm'], includeReserved = false) { - if (!metric.limited) return {formatLimit(metric.currentValue)} / 不限未配置上限; + if (!metric.limited) return {formatLimit(metric.currentValue)} / 不限{includeReserved ? reservedMetricText(metric) : '未配置上限'}; return ( {formatLimit(metric.currentValue)} / {formatLimit(metric.limitValue)} - {includeReserved && metric.reservedValue > 0 ? `已用 ${formatLimit(metric.usedValue)} · 预占 ${formatLimit(metric.reservedValue)}` : `窗口 ${formatPercent(metric.ratio)}`} + {includeReserved ? reservedMetricText(metric) : `窗口 ${formatPercent(metric.ratio)}`} ); } +function reservedMetricText(metric: ModelRateLimitStatus['rpm']) { + return `已结算 ${formatLimit(metric.usedValue)} + 预占 ${formatLimit(metric.reservedValue)}`; +} + +function formatTimeOfDay(timestamp: number | null) { + if (!timestamp) return '暂无'; + const date = new Date(timestamp); + const pad = (value: number) => String(value).padStart(2, '0'); + return `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`; +} + function modelRuntimeStatusCell(status: ModelRateLimitStatus, now: number) { const modelCooldownMs = cooldownRemainingMs(status.modelCooldownUntil, now); const platformCooldownMs = cooldownRemainingMs(status.platformCooldownUntil, now);