Spaces:
Paused
Paused
Mirrowel
commited on
Commit
·
48b6b15
1
Parent(s):
8b4ff52
feat(usage): ✨ use request count for credential selection when failed requests consume quota
Browse filesFor providers like antigravity where failed requests also consume quota,
credential selection now uses `request_count` instead of `success_count`.
This ensures proper credential rotation when quota is consumed by failed
requests.
- Add `_REQUEST_COUNT_PROVIDERS` set to identify affected providers
- Update `_get_grouped_usage_count` to dynamically select usage field based on provider
- Extend `_get_usage_count` signature to support configurable usage fields
src/rotator_library/usage_manager.py
CHANGED
|
@@ -329,6 +329,10 @@ class UsageManager:
|
|
| 329 |
|
| 330 |
return 1
|
| 331 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 332 |
def _get_grouped_usage_count(self, key: str, model: str) -> int:
|
| 333 |
"""
|
| 334 |
Get usage count for credential selection, considering quota groups.
|
|
@@ -339,6 +343,10 @@ class UsageManager:
|
|
| 339 |
Weights are applied per-model to account for models that consume more quota
|
| 340 |
per request (e.g., Opus might count 2x compared to Sonnet).
|
| 341 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 342 |
Args:
|
| 343 |
key: Credential identifier
|
| 344 |
model: Model name (with provider prefix, e.g., "antigravity/claude-sonnet-4-5")
|
|
@@ -346,6 +354,15 @@ class UsageManager:
|
|
| 346 |
Returns:
|
| 347 |
Weighted combined usage if grouped, otherwise individual model usage
|
| 348 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 349 |
# Check if model is in a quota group
|
| 350 |
group = self._get_model_quota_group(key, model)
|
| 351 |
|
|
@@ -356,13 +373,13 @@ class UsageManager:
|
|
| 356 |
# Sum weighted usage across all models in the group
|
| 357 |
total_weighted_usage = 0
|
| 358 |
for grouped_model in grouped_models:
|
| 359 |
-
usage = self._get_usage_count(key, grouped_model)
|
| 360 |
weight = self._get_model_usage_weight(key, grouped_model)
|
| 361 |
total_weighted_usage += usage * weight
|
| 362 |
return total_weighted_usage
|
| 363 |
|
| 364 |
# Not grouped - return individual model usage (no weight applied)
|
| 365 |
-
return self._get_usage_count(key, model)
|
| 366 |
|
| 367 |
def _get_usage_field_name(self, credential: str) -> str:
|
| 368 |
"""
|
|
@@ -390,7 +407,9 @@ class UsageManager:
|
|
| 390 |
|
| 391 |
return "daily"
|
| 392 |
|
| 393 |
-
def _get_usage_count(
|
|
|
|
|
|
|
| 394 |
"""
|
| 395 |
Get the current usage count for a model from the appropriate usage structure.
|
| 396 |
|
|
@@ -401,9 +420,12 @@ class UsageManager:
|
|
| 401 |
Args:
|
| 402 |
key: Credential identifier
|
| 403 |
model: Model name
|
|
|
|
|
|
|
|
|
|
| 404 |
|
| 405 |
Returns:
|
| 406 |
-
Usage count
|
| 407 |
"""
|
| 408 |
if self._usage_data is None:
|
| 409 |
return 0
|
|
@@ -412,15 +434,12 @@ class UsageManager:
|
|
| 412 |
reset_mode = self._get_reset_mode(key)
|
| 413 |
|
| 414 |
if reset_mode == "per_model":
|
| 415 |
-
# New per-model structure: key_data["models"][model][
|
| 416 |
-
return key_data.get("models", {}).get(model, {}).get(
|
| 417 |
else:
|
| 418 |
-
# Legacy structure: key_data["daily"]["models"][model][
|
| 419 |
return (
|
| 420 |
-
key_data.get("daily", {})
|
| 421 |
-
.get("models", {})
|
| 422 |
-
.get(model, {})
|
| 423 |
-
.get("success_count", 0)
|
| 424 |
)
|
| 425 |
|
| 426 |
# =========================================================================
|
|
|
|
| 329 |
|
| 330 |
return 1
|
| 331 |
|
| 332 |
+
# Providers where request_count should be used for credential selection
|
| 333 |
+
# instead of success_count (because failed requests also consume quota)
|
| 334 |
+
_REQUEST_COUNT_PROVIDERS = {"antigravity"}
|
| 335 |
+
|
| 336 |
def _get_grouped_usage_count(self, key: str, model: str) -> int:
|
| 337 |
"""
|
| 338 |
Get usage count for credential selection, considering quota groups.
|
|
|
|
| 343 |
Weights are applied per-model to account for models that consume more quota
|
| 344 |
per request (e.g., Opus might count 2x compared to Sonnet).
|
| 345 |
|
| 346 |
+
For providers in _REQUEST_COUNT_PROVIDERS (e.g., antigravity), uses
|
| 347 |
+
request_count instead of success_count since failed requests also
|
| 348 |
+
consume quota.
|
| 349 |
+
|
| 350 |
Args:
|
| 351 |
key: Credential identifier
|
| 352 |
model: Model name (with provider prefix, e.g., "antigravity/claude-sonnet-4-5")
|
|
|
|
| 354 |
Returns:
|
| 355 |
Weighted combined usage if grouped, otherwise individual model usage
|
| 356 |
"""
|
| 357 |
+
# Determine usage field based on provider
|
| 358 |
+
# Some providers (antigravity) count failed requests against quota
|
| 359 |
+
provider = self._get_provider_from_credential(key)
|
| 360 |
+
usage_field = (
|
| 361 |
+
"request_count"
|
| 362 |
+
if provider in self._REQUEST_COUNT_PROVIDERS
|
| 363 |
+
else "success_count"
|
| 364 |
+
)
|
| 365 |
+
|
| 366 |
# Check if model is in a quota group
|
| 367 |
group = self._get_model_quota_group(key, model)
|
| 368 |
|
|
|
|
| 373 |
# Sum weighted usage across all models in the group
|
| 374 |
total_weighted_usage = 0
|
| 375 |
for grouped_model in grouped_models:
|
| 376 |
+
usage = self._get_usage_count(key, grouped_model, usage_field)
|
| 377 |
weight = self._get_model_usage_weight(key, grouped_model)
|
| 378 |
total_weighted_usage += usage * weight
|
| 379 |
return total_weighted_usage
|
| 380 |
|
| 381 |
# Not grouped - return individual model usage (no weight applied)
|
| 382 |
+
return self._get_usage_count(key, model, usage_field)
|
| 383 |
|
| 384 |
def _get_usage_field_name(self, credential: str) -> str:
|
| 385 |
"""
|
|
|
|
| 407 |
|
| 408 |
return "daily"
|
| 409 |
|
| 410 |
+
def _get_usage_count(
|
| 411 |
+
self, key: str, model: str, field: str = "success_count"
|
| 412 |
+
) -> int:
|
| 413 |
"""
|
| 414 |
Get the current usage count for a model from the appropriate usage structure.
|
| 415 |
|
|
|
|
| 420 |
Args:
|
| 421 |
key: Credential identifier
|
| 422 |
model: Model name
|
| 423 |
+
field: The field to read for usage count (default: "success_count").
|
| 424 |
+
Use "request_count" for providers where failed requests also
|
| 425 |
+
consume quota (e.g., antigravity).
|
| 426 |
|
| 427 |
Returns:
|
| 428 |
+
Usage count for the model in the current window/period
|
| 429 |
"""
|
| 430 |
if self._usage_data is None:
|
| 431 |
return 0
|
|
|
|
| 434 |
reset_mode = self._get_reset_mode(key)
|
| 435 |
|
| 436 |
if reset_mode == "per_model":
|
| 437 |
+
# New per-model structure: key_data["models"][model][field]
|
| 438 |
+
return key_data.get("models", {}).get(model, {}).get(field, 0)
|
| 439 |
else:
|
| 440 |
+
# Legacy structure: key_data["daily"]["models"][model][field]
|
| 441 |
return (
|
| 442 |
+
key_data.get("daily", {}).get("models", {}).get(model, {}).get(field, 0)
|
|
|
|
|
|
|
|
|
|
| 443 |
)
|
| 444 |
|
| 445 |
# =========================================================================
|