mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-16 04:18:05 +00:00
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:
@@ -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
@@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user