fix(quota): return exhausted models with resetTime from API

- treat models with resetTime but null/missing remainingFraction as 0%
- matches CLIProxy Management Center behavior
- enables accurate Claude reset time display when quota exhausted
- mark primary models at 0% as exhausted for red styling
This commit is contained in:
kaitranntt
2026-01-25 17:34:30 -05:00
parent a84cc036df
commit e3920e0776
2 changed files with 21 additions and 12 deletions
+13 -6
View File
@@ -494,15 +494,22 @@ async function fetchAvailableModels(accessToken: string, _projectId: string): Pr
const remaining =
quotaInfo.remainingFraction ?? quotaInfo.remaining_fraction ?? quotaInfo.remaining;
// Skip invalid values (NaN, Infinity, non-numbers)
if (typeof remaining !== 'number' || !isFinite(remaining)) continue;
// Convert to percentage (0-100) and clamp to valid range
const percentage = Math.max(0, Math.min(100, Math.round(remaining * 100)));
// Extract reset time
const resetTime = quotaInfo.resetTime || quotaInfo.reset_time || null;
// If remaining is not a valid number but resetTime exists, treat as exhausted (0%)
// This happens when Claude models hit quota limit - API returns resetTime but no fraction
let percentage: number;
if (typeof remaining === 'number' && isFinite(remaining)) {
percentage = Math.max(0, Math.min(100, Math.round(remaining * 100)));
} else if (resetTime) {
// Model is exhausted but has reset time - show as 0%
percentage = 0;
} else {
// No valid data, skip this model
continue;
}
models.push({
name: modelId,
displayName: modelData.displayName,
+8 -6
View File
@@ -174,8 +174,8 @@ export function getMinClaudeQuota<
}
/**
* Get reset time for Claude/GPT models (matches getMinClaudeQuota logic).
* Falls back to earliest of all models if Claude/GPT not present (still need reset info).
* Get reset time for Claude/GPT models (primary models).
* Returns null only if no primary models present in response.
*/
export function getClaudeResetTime<
T extends { name: string; displayName?: string; resetTime: string | null },
@@ -183,10 +183,9 @@ export function getClaudeResetTime<
if (models.length === 0) return null;
const primaryModels = filterPrimaryModels(models);
// Fall back to all models for reset time (we need some reset info even if exhausted)
const targetModels = primaryModels.length > 0 ? primaryModels : models;
if (primaryModels.length === 0) return null;
return targetModels.reduce(
return primaryModels.reduce(
(earliest, m) => {
if (!m.resetTime) return earliest;
if (!earliest) return m.resetTime;
@@ -267,11 +266,14 @@ export function getModelsWithTiers<
// Add all models with tier info
for (const m of models) {
const displayName = m.displayName || m.name;
const tier = getModelTier(displayName);
result.push({
name: m.name,
displayName,
percentage: m.percentage,
tier: getModelTier(displayName),
tier,
// Mark primary models at 0% as exhausted for red styling
exhausted: tier === 'primary' && m.percentage === 0,
});
}