feat: improve model rate limit tracking

This commit is contained in:
2026-05-12 03:22:29 +08:00
parent 05632172d0
commit ba850a06c6
25 changed files with 1223 additions and 96 deletions
+34 -8
View File
@@ -18,6 +18,7 @@ import type {
GatewayWalletTransaction,
IntegrationPlatform,
ModelCatalogResponse,
ModelRateLimitStatus,
PlatformModel,
PricingRule,
PricingRuleSet,
@@ -54,6 +55,7 @@ import {
listBaseModels,
listCatalogProviders,
listModelCatalog,
listModelRateLimitStatuses,
listModels,
listPlayableApiKeys,
listPlayableModels,
@@ -140,6 +142,7 @@ type DataKey =
| 'runnerPolicy'
| 'runtimePolicySets'
| 'rateLimitWindows'
| 'modelRateLimits'
| 'tenants'
| 'users'
| 'userGroups'
@@ -183,6 +186,7 @@ export function App() {
const [accessRules, setAccessRules] = useState<GatewayAccessRule[]>([]);
const [auditLogs, setAuditLogs] = useState<GatewayAuditLog[]>([]);
const [rateLimitWindows, setRateLimitWindows] = useState<RateLimitWindow[]>([]);
const [modelRateLimits, setModelRateLimits] = useState<ModelRateLimitStatus[]>([]);
const [tenants, setTenants] = useState<GatewayTenant[]>([]);
const [users, setUsers] = useState<GatewayUser[]>([]);
const [userGroups, setUserGroups] = useState<UserGroup[]>([]);
@@ -239,6 +243,23 @@ export function App() {
useEffect(() => {
void ensureRouteData(token);
}, [activePage, adminSection, taskListRequestKey, transactionListRequestKey, workspaceSection, token]);
useEffect(() => {
if (!token || activePage !== 'admin' || adminSection !== 'platforms') return undefined;
const timer = window.setInterval(() => {
void Promise.all([listModelRateLimitStatuses(token), listPlatforms(token)])
.then(([rateLimitResponse, platformResponse]) => {
setModelRateLimits(rateLimitResponse.items);
setPlatforms(platformResponse.items);
loadedDataKeysRef.current.add('modelRateLimits');
loadedDataKeysRef.current.add('platforms');
})
.catch(() => {
loadedDataKeysRef.current.delete('modelRateLimits');
loadedDataKeysRef.current.delete('platforms');
});
}, 3000);
return () => window.clearInterval(timer);
}, [activePage, adminSection, token]);
useEffect(() => {
function handlePopState() {
applyRoute(parseAppRoute());
@@ -278,9 +299,10 @@ export function App() {
platforms,
pricingRules,
pricingRuleSets,
providers,
rateLimitWindows,
runtimePolicySets,
providers,
rateLimitWindows,
modelRateLimits,
runtimePolicySets,
taskResult,
tasks,
tenants,
@@ -288,7 +310,7 @@ export function App() {
users,
walletAccounts,
walletTransactions,
}), [accessRules, apiKeys, auditLogs, baseModels, modelCatalog, models, networkProxyConfig, platforms, pricingRuleSets, pricingRules, providers, rateLimitWindows, runnerPolicy, runtimePolicySets, taskResult, tasks, tenants, userGroups, 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]);
async function refresh(nextToken = token) {
await ensureRouteData(nextToken, true);
@@ -392,6 +414,9 @@ export function App() {
case 'rateLimitWindows':
setRateLimitWindows((await listRateLimitWindows(nextToken)).items);
return;
case 'modelRateLimits':
setModelRateLimits((await listModelRateLimitStatuses(nextToken)).items);
return;
case 'tenants':
setTenants((await listTenants(nextToken)).items);
return;
@@ -514,7 +539,7 @@ export function App() {
const modelsResponse = await replacePlatformModels(token, platform.id, modelBindings);
setPlatforms((current) => [platformForState, ...current.filter((item) => item.id !== platform.id)]);
setModels((current) => [...current.filter((model) => model.platformId !== platform.id), ...modelsResponse.items]);
invalidateDataKeys('modelCatalog');
invalidateDataKeys('modelCatalog', 'modelRateLimits');
setCoreState('ready');
setCoreMessage(input.platformId
? `平台已更新,当前绑定 ${input.models.length} 个模型。`
@@ -533,7 +558,7 @@ export function App() {
await deletePlatform(token, platformId);
setPlatforms((current) => current.filter((item) => item.id !== platformId));
setModels((current) => current.filter((item) => item.platformId !== platformId));
invalidateDataKeys('modelCatalog');
invalidateDataKeys('modelCatalog', 'modelRateLimits');
setCoreState('ready');
setCoreMessage('平台已删除。');
} catch (err) {
@@ -788,6 +813,7 @@ export function App() {
setAccessRules([]);
setAuditLogs([]);
setRateLimitWindows([]);
setModelRateLimits([]);
setTenants([]);
setUsers([]);
setUserGroups([]);
@@ -1132,7 +1158,7 @@ function dataKeysForRoute(
if (activePage !== 'admin') return [];
switch (adminSection) {
case 'overview':
return ['platforms', 'models', 'providers', 'pricingRules', 'runtimePolicySets', 'rateLimitWindows', 'tenants', 'users', 'userGroups', 'accessRules'];
return ['platforms', 'models', 'providers', 'pricingRules', 'runtimePolicySets', 'rateLimitWindows', 'modelRateLimits', 'tenants', 'users', 'userGroups', 'accessRules'];
case 'globalModels':
return ['providers'];
case 'pricing':
@@ -1142,7 +1168,7 @@ function dataKeysForRoute(
case 'baseModels':
return ['baseModels', 'providers', 'pricingRuleSets', 'runtimePolicySets'];
case 'platforms':
return ['platforms', 'models', 'providers', 'baseModels', 'pricingRuleSets', 'networkProxyConfig'];
return ['platforms', 'models', 'modelRateLimits', 'providers', 'baseModels', 'pricingRuleSets', 'networkProxyConfig'];
case 'tenants':
return ['tenants', 'userGroups'];
case 'users':