Spaces:
Paused
Paused
Mirrowel commited on
Commit ·
06b3f7d
1
Parent(s): 1af1879
feat(quota): restructure quota aggregation with cumulative counts and tier registry
Browse filesRemove averaged percentage calculations in favor of summing actual request counts across all credentials in a group. Introduce per-tier credential registries that distinguish active from exhausted allocations. Simplify viewer presentation to show absolute usage totals alongside compact tier status markers, deprecating the previous exhaustion counting approach.
- src/proxy_app/quota_viewer.py +42 -16
- src/rotator_library/client.py +60 -9
src/proxy_app/quota_viewer.py
CHANGED
|
@@ -417,25 +417,51 @@ class QuotaViewer:
|
|
| 417 |
if quota_groups:
|
| 418 |
quota_lines = []
|
| 419 |
for group_name, group_stats in quota_groups.items():
|
| 420 |
-
|
| 421 |
-
|
| 422 |
-
|
| 423 |
-
|
| 424 |
-
|
| 425 |
-
|
| 426 |
-
|
| 427 |
-
|
| 428 |
-
|
| 429 |
-
|
| 430 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 431 |
else:
|
| 432 |
-
color = "
|
| 433 |
-
status = ""
|
| 434 |
|
| 435 |
-
bar = create_progress_bar(
|
| 436 |
-
display_name = group_name[:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 437 |
quota_lines.append(
|
| 438 |
-
f"[{color}]{display_name}: {
|
| 439 |
)
|
| 440 |
|
| 441 |
# First line goes in the main row
|
|
|
|
| 417 |
if quota_groups:
|
| 418 |
quota_lines = []
|
| 419 |
for group_name, group_stats in quota_groups.items():
|
| 420 |
+
# Use total requests for global view
|
| 421 |
+
total_used = group_stats.get("total_requests_used", 0)
|
| 422 |
+
total_max = group_stats.get("total_requests_max", 0)
|
| 423 |
+
total_pct = group_stats.get("total_remaining_pct")
|
| 424 |
+
tiers = group_stats.get("tiers", {})
|
| 425 |
+
|
| 426 |
+
# Format tier info: "5(15)f/2s" = 5 active out of 15 free, 2 standard all active
|
| 427 |
+
tier_parts = []
|
| 428 |
+
for tier_name, tier_info in sorted(tiers.items()):
|
| 429 |
+
if tier_name == "unknown":
|
| 430 |
+
continue # Skip unknown tiers in display
|
| 431 |
+
total_t = tier_info.get("total", 0)
|
| 432 |
+
active_t = tier_info.get("active", 0)
|
| 433 |
+
# Use first letter: standard-tier -> s, free-tier -> f
|
| 434 |
+
short = tier_name.replace("-tier", "")[0]
|
| 435 |
+
|
| 436 |
+
if active_t < total_t:
|
| 437 |
+
# Some exhausted - show active(total)
|
| 438 |
+
tier_parts.append(f"{active_t}({total_t}){short}")
|
| 439 |
+
else:
|
| 440 |
+
# All active - just show total
|
| 441 |
+
tier_parts.append(f"{total_t}{short}")
|
| 442 |
+
tier_str = "/".join(tier_parts) if tier_parts else ""
|
| 443 |
+
|
| 444 |
+
# Determine color based purely on remaining percentage
|
| 445 |
+
if total_pct is not None:
|
| 446 |
+
if total_pct <= 10:
|
| 447 |
+
color = "red"
|
| 448 |
+
elif total_pct < 30:
|
| 449 |
+
color = "yellow"
|
| 450 |
+
else:
|
| 451 |
+
color = "green"
|
| 452 |
else:
|
| 453 |
+
color = "dim"
|
|
|
|
| 454 |
|
| 455 |
+
bar = create_progress_bar(total_pct)
|
| 456 |
+
display_name = group_name[:11]
|
| 457 |
+
pct_str = f"{total_pct}%" if total_pct is not None else "?"
|
| 458 |
+
|
| 459 |
+
# Build status suffix (just tiers now, no outer parens)
|
| 460 |
+
status = tier_str
|
| 461 |
+
|
| 462 |
+
# Compact format: "claude: 1228/1625 24% ████░░░░░░ (5(15)f/2s)"
|
| 463 |
quota_lines.append(
|
| 464 |
+
f"[{color}]{display_name}: {total_used}/{total_max} {pct_str} {bar}[/{color}] {status}"
|
| 465 |
)
|
| 466 |
|
| 467 |
# First line goes in the main row
|
src/rotator_library/client.py
CHANGED
|
@@ -2657,6 +2657,11 @@ class RotatingClient:
|
|
| 2657 |
"credentials_exhausted": 0,
|
| 2658 |
"avg_remaining_pct": 0,
|
| 2659 |
"total_remaining_pcts": [],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2660 |
}
|
| 2661 |
|
| 2662 |
# Calculate per-credential quota for this group
|
|
@@ -2664,17 +2669,44 @@ class RotatingClient:
|
|
| 2664 |
models_data = cred.get("models", {})
|
| 2665 |
group_stats["credentials_total"] += 1
|
| 2666 |
|
| 2667 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2668 |
model_stats = None
|
| 2669 |
for model in group_models:
|
| 2670 |
-
|
| 2671 |
models_data, model, provider, provider_instance
|
| 2672 |
)
|
| 2673 |
-
if
|
| 2674 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2675 |
|
| 2676 |
if model_stats:
|
| 2677 |
baseline = model_stats.get("baseline_remaining_fraction")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2678 |
if baseline is not None:
|
| 2679 |
remaining_pct = int(baseline * 100)
|
| 2680 |
group_stats["total_remaining_pcts"].append(
|
|
@@ -2682,8 +2714,11 @@ class RotatingClient:
|
|
| 2682 |
)
|
| 2683 |
if baseline <= 0:
|
| 2684 |
group_stats["credentials_exhausted"] += 1
|
|
|
|
|
|
|
|
|
|
| 2685 |
|
| 2686 |
-
# Calculate average remaining percentage
|
| 2687 |
if group_stats["total_remaining_pcts"]:
|
| 2688 |
group_stats["avg_remaining_pct"] = int(
|
| 2689 |
sum(group_stats["total_remaining_pcts"])
|
|
@@ -2691,6 +2726,16 @@ class RotatingClient:
|
|
| 2691 |
)
|
| 2692 |
del group_stats["total_remaining_pcts"]
|
| 2693 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2694 |
prov_stats["quota_groups"][group_name] = group_stats
|
| 2695 |
|
| 2696 |
# Also enrich each credential with formatted quota group info
|
|
@@ -2699,14 +2744,20 @@ class RotatingClient:
|
|
| 2699 |
models_data = cred.get("models", {})
|
| 2700 |
|
| 2701 |
for group_name, group_models in quota_groups.items():
|
| 2702 |
-
# Find
|
| 2703 |
model_stats = None
|
| 2704 |
for model in group_models:
|
| 2705 |
-
|
| 2706 |
models_data, model, provider, provider_instance
|
| 2707 |
)
|
| 2708 |
-
if
|
| 2709 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2710 |
|
| 2711 |
if model_stats:
|
| 2712 |
baseline = model_stats.get("baseline_remaining_fraction")
|
|
|
|
| 2657 |
"credentials_exhausted": 0,
|
| 2658 |
"avg_remaining_pct": 0,
|
| 2659 |
"total_remaining_pcts": [],
|
| 2660 |
+
# Total requests tracking across all credentials
|
| 2661 |
+
"total_requests_used": 0,
|
| 2662 |
+
"total_requests_max": 0,
|
| 2663 |
+
# Tier breakdown: tier_name -> {"total": N, "active": M}
|
| 2664 |
+
"tiers": {},
|
| 2665 |
}
|
| 2666 |
|
| 2667 |
# Calculate per-credential quota for this group
|
|
|
|
| 2669 |
models_data = cred.get("models", {})
|
| 2670 |
group_stats["credentials_total"] += 1
|
| 2671 |
|
| 2672 |
+
# Track tier - get directly from provider cache since cred["tier"] not set yet
|
| 2673 |
+
tier = cred.get("tier")
|
| 2674 |
+
if not tier and hasattr(
|
| 2675 |
+
provider_instance, "project_tier_cache"
|
| 2676 |
+
):
|
| 2677 |
+
cred_path = cred.get("full_path", "")
|
| 2678 |
+
tier = provider_instance.project_tier_cache.get(cred_path)
|
| 2679 |
+
tier = tier or "unknown"
|
| 2680 |
+
|
| 2681 |
+
# Initialize tier entry if needed
|
| 2682 |
+
if tier not in group_stats["tiers"]:
|
| 2683 |
+
group_stats["tiers"][tier] = {"total": 0, "active": 0}
|
| 2684 |
+
group_stats["tiers"][tier]["total"] += 1
|
| 2685 |
+
|
| 2686 |
+
# Find model with VALID baseline (not just any model with stats)
|
| 2687 |
model_stats = None
|
| 2688 |
for model in group_models:
|
| 2689 |
+
candidate = self._find_model_stats_in_data(
|
| 2690 |
models_data, model, provider, provider_instance
|
| 2691 |
)
|
| 2692 |
+
if candidate:
|
| 2693 |
+
baseline = candidate.get("baseline_remaining_fraction")
|
| 2694 |
+
if baseline is not None:
|
| 2695 |
+
model_stats = candidate
|
| 2696 |
+
break
|
| 2697 |
+
# Keep first found as fallback (for request counts)
|
| 2698 |
+
if model_stats is None:
|
| 2699 |
+
model_stats = candidate
|
| 2700 |
|
| 2701 |
if model_stats:
|
| 2702 |
baseline = model_stats.get("baseline_remaining_fraction")
|
| 2703 |
+
req_count = model_stats.get("request_count", 0)
|
| 2704 |
+
max_req = model_stats.get("quota_max_requests") or 0
|
| 2705 |
+
|
| 2706 |
+
# Accumulate totals (one model per group per credential)
|
| 2707 |
+
group_stats["total_requests_used"] += req_count
|
| 2708 |
+
group_stats["total_requests_max"] += max_req
|
| 2709 |
+
|
| 2710 |
if baseline is not None:
|
| 2711 |
remaining_pct = int(baseline * 100)
|
| 2712 |
group_stats["total_remaining_pcts"].append(
|
|
|
|
| 2714 |
)
|
| 2715 |
if baseline <= 0:
|
| 2716 |
group_stats["credentials_exhausted"] += 1
|
| 2717 |
+
else:
|
| 2718 |
+
# Credential is active (has quota remaining)
|
| 2719 |
+
group_stats["tiers"][tier]["active"] += 1
|
| 2720 |
|
| 2721 |
+
# Calculate average remaining percentage (per-credential average)
|
| 2722 |
if group_stats["total_remaining_pcts"]:
|
| 2723 |
group_stats["avg_remaining_pct"] = int(
|
| 2724 |
sum(group_stats["total_remaining_pcts"])
|
|
|
|
| 2726 |
)
|
| 2727 |
del group_stats["total_remaining_pcts"]
|
| 2728 |
|
| 2729 |
+
# Calculate total remaining percentage (global)
|
| 2730 |
+
if group_stats["total_requests_max"] > 0:
|
| 2731 |
+
used = group_stats["total_requests_used"]
|
| 2732 |
+
max_r = group_stats["total_requests_max"]
|
| 2733 |
+
group_stats["total_remaining_pct"] = max(
|
| 2734 |
+
0, int((1 - used / max_r) * 100)
|
| 2735 |
+
)
|
| 2736 |
+
else:
|
| 2737 |
+
group_stats["total_remaining_pct"] = None
|
| 2738 |
+
|
| 2739 |
prov_stats["quota_groups"][group_name] = group_stats
|
| 2740 |
|
| 2741 |
# Also enrich each credential with formatted quota group info
|
|
|
|
| 2744 |
models_data = cred.get("models", {})
|
| 2745 |
|
| 2746 |
for group_name, group_models in quota_groups.items():
|
| 2747 |
+
# Find model with VALID baseline (prefer over any model with stats)
|
| 2748 |
model_stats = None
|
| 2749 |
for model in group_models:
|
| 2750 |
+
candidate = self._find_model_stats_in_data(
|
| 2751 |
models_data, model, provider, provider_instance
|
| 2752 |
)
|
| 2753 |
+
if candidate:
|
| 2754 |
+
baseline = candidate.get("baseline_remaining_fraction")
|
| 2755 |
+
if baseline is not None:
|
| 2756 |
+
model_stats = candidate
|
| 2757 |
+
break
|
| 2758 |
+
# Keep first found as fallback
|
| 2759 |
+
if model_stats is None:
|
| 2760 |
+
model_stats = candidate
|
| 2761 |
|
| 2762 |
if model_stats:
|
| 2763 |
baseline = model_stats.get("baseline_remaining_fraction")
|