Spaces:
Paused
Paused
Mirrowel
commited on
Commit
·
aaab6f8
1
Parent(s):
97ae153
refactor(rotator): randomize key selection and streamline daily stats reset
Browse filesRandomize API key selection lists in `RotatingClient` to ensure a fair distribution of keys, especially when multiple keys have similar usage statistics. This prevents deterministic selection biases and promotes better load balancing.
Consolidate the daily usage statistics reset logic into a dedicated `_reset_daily_stats_if_needed` method within the `UsageManager`. This centralizes and clarifies the reset mechanism, removing inline and potentially inconsistent checks.
src/rotator_library/client.py
CHANGED
|
@@ -291,7 +291,13 @@ class RotatingClient:
|
|
| 291 |
|
| 292 |
# Establish a global deadline for the entire request lifecycle.
|
| 293 |
deadline = time.time() + self.global_timeout
|
| 294 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 295 |
tried_keys = set()
|
| 296 |
last_exception = None
|
| 297 |
kwargs = self._convert_model_params(**kwargs)
|
|
@@ -439,7 +445,11 @@ class RotatingClient:
|
|
| 439 |
"""A dedicated generator for retrying streaming completions with full request preparation and per-key retries."""
|
| 440 |
model = kwargs.get("model")
|
| 441 |
provider = model.split('/')[0]
|
| 442 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 443 |
deadline = time.time() + self.global_timeout
|
| 444 |
tried_keys = set()
|
| 445 |
last_exception = None
|
|
@@ -465,7 +475,7 @@ class RotatingClient:
|
|
| 465 |
|
| 466 |
lib_logger.info(f"Acquiring key for model {model}. Tried keys: {len(tried_keys)}/{len(keys_for_provider)}")
|
| 467 |
current_key = await self.usage_manager.acquire_key(
|
| 468 |
-
available_keys=keys_to_try,
|
| 469 |
model=model,
|
| 470 |
deadline=deadline
|
| 471 |
)
|
|
|
|
| 291 |
|
| 292 |
# Establish a global deadline for the entire request lifecycle.
|
| 293 |
deadline = time.time() + self.global_timeout
|
| 294 |
+
|
| 295 |
+
# Create a mutable copy of the keys and shuffle it to ensure
|
| 296 |
+
# that the key selection is randomized, which is crucial when
|
| 297 |
+
# multiple keys have the same usage stats.
|
| 298 |
+
keys_for_provider = list(self.api_keys[provider])
|
| 299 |
+
random.shuffle(keys_for_provider)
|
| 300 |
+
|
| 301 |
tried_keys = set()
|
| 302 |
last_exception = None
|
| 303 |
kwargs = self._convert_model_params(**kwargs)
|
|
|
|
| 445 |
"""A dedicated generator for retrying streaming completions with full request preparation and per-key retries."""
|
| 446 |
model = kwargs.get("model")
|
| 447 |
provider = model.split('/')[0]
|
| 448 |
+
|
| 449 |
+
# Create a mutable copy of the keys and shuffle it.
|
| 450 |
+
keys_for_provider = list(self.api_keys[provider])
|
| 451 |
+
random.shuffle(keys_for_provider)
|
| 452 |
+
|
| 453 |
deadline = time.time() + self.global_timeout
|
| 454 |
tried_keys = set()
|
| 455 |
last_exception = None
|
|
|
|
| 475 |
|
| 476 |
lib_logger.info(f"Acquiring key for model {model}. Tried keys: {len(tried_keys)}/{len(keys_for_provider)}")
|
| 477 |
current_key = await self.usage_manager.acquire_key(
|
| 478 |
+
available_keys=keys_to_try,
|
| 479 |
model=model,
|
| 480 |
deadline=deadline
|
| 481 |
)
|
src/rotator_library/usage_manager.py
CHANGED
|
@@ -134,6 +134,7 @@ class UsageManager:
|
|
| 134 |
respecting a global deadline.
|
| 135 |
"""
|
| 136 |
await self._lazy_init()
|
|
|
|
| 137 |
self._initialize_key_states(available_keys)
|
| 138 |
|
| 139 |
# This loop continues as long as the global deadline has not been met.
|
|
@@ -238,10 +239,6 @@ class UsageManager:
|
|
| 238 |
today_utc_str = datetime.now(timezone.utc).date().isoformat()
|
| 239 |
key_data = self._usage_data.setdefault(key, {"daily": {"date": today_utc_str, "models": {}}, "global": {"models": {}}, "model_cooldowns": {}, "failures": {}})
|
| 240 |
|
| 241 |
-
# Perform a just-in-time daily reset if the date has changed.
|
| 242 |
-
if key_data["daily"].get("date") != today_utc_str:
|
| 243 |
-
key_data["daily"] = {"date": today_utc_str, "models": {}}
|
| 244 |
-
|
| 245 |
# Always record a success and reset failures
|
| 246 |
model_failures = key_data.setdefault("failures", {}).setdefault(model, {})
|
| 247 |
model_failures["consecutive_failures"] = 0
|
|
|
|
| 134 |
respecting a global deadline.
|
| 135 |
"""
|
| 136 |
await self._lazy_init()
|
| 137 |
+
await self._reset_daily_stats_if_needed()
|
| 138 |
self._initialize_key_states(available_keys)
|
| 139 |
|
| 140 |
# This loop continues as long as the global deadline has not been met.
|
|
|
|
| 239 |
today_utc_str = datetime.now(timezone.utc).date().isoformat()
|
| 240 |
key_data = self._usage_data.setdefault(key, {"daily": {"date": today_utc_str, "models": {}}, "global": {"models": {}}, "model_cooldowns": {}, "failures": {}})
|
| 241 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 242 |
# Always record a success and reset failures
|
| 243 |
model_failures = key_data.setdefault("failures", {}).setdefault(model, {})
|
| 244 |
model_failures["consecutive_failures"] = 0
|