XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet
公开
关注 0 Fork 0 Star 1
返回提交历史

XFEstudio/gpt4free

perf(provider): add circuit breaker and health cooldown to IterListProvider

c5b0f461
Anand Mall <anand@example.com>
提交于

代码差异

2 个文件 +54 -2
Modified g4f/Provider/__init__.py +2 -1
@@ -1,7 +1,7 @@
1 1 from __future__ import annotations
2 2
3 3 from ..providers.types import BaseProvider, ProviderType
4 from ..providers.retry_provider import RetryProvider, IterListProvider, RotatedProvider
4 from ..providers.retry_provider import RetryProvider, IterListProvider, RotatedProvider, ProviderCircuitBreaker
5 5 from ..providers.base_provider import AsyncProvider, AsyncGeneratorProvider
6 6 from ..providers.create_images import CreateImagesProvider
7 7
@@ -511,6 +511,7 @@ __all__ = [
511 511 "RetryProvider",
512 512 "IterListProvider",
513 513 "RotatedProvider",
514 "ProviderCircuitBreaker",
514 515 "AsyncProvider",
515 516 "AsyncGeneratorProvider",
516 517 "CreateImagesProvider",
Modified g4f/providers/retry_provider.py +52 -1
@@ -1,5 +1,7 @@
1 1 from __future__ import annotations
2 2
3 import time
4 import os
3 5 import random
4 6
5 7 from ..typing import Dict, Type, List, Messages, AsyncResult
@@ -140,6 +142,43 @@ class RotatedProvider(BaseRetryProvider):
140 142 raise_exceptions(exceptions)
141 143
142 144
145 class ProviderCircuitBreaker:
146 """Tracks provider failure states to avoid repeatedly querying unavailable providers."""
147 _failures: Dict[str, int] = {}
148 _cooldowns: Dict[str, float] = {}
149 COOLDOWN_SECONDS: float = 60.0
150 MAX_CONSECUTIVE_FAILURES: int = 3
151
152 @classmethod
153 def is_available(cls, provider_name: str) -> bool:
154 if os.environ.get("G4F_DISABLE_CIRCUIT_BREAKER", "").lower() in ("1", "true"):
155 return True
156 if provider_name in cls._cooldowns:
157 if time.time() < cls._cooldowns[provider_name]:
158 return False
159 cls._cooldowns.pop(provider_name, None)
160 cls._failures[provider_name] = cls.MAX_CONSECUTIVE_FAILURES - 1
161 return True
162
163 @classmethod
164 def record_failure(cls, provider_name: str, cooldown: Optional[float] = None):
165 cls._failures[provider_name] = cls._failures.get(provider_name, 0) + 1
166 if cls._failures[provider_name] >= cls.MAX_CONSECUTIVE_FAILURES:
167 cooldown_time = cooldown or cls.COOLDOWN_SECONDS
168 cls._cooldowns[provider_name] = time.time() + cooldown_time
169 debug.log(f"Provider {provider_name} entered cooldown for {cooldown_time}s")
170
171 @classmethod
172 def record_success(cls, provider_name: str):
173 cls._failures.pop(provider_name, None)
174 cls._cooldowns.pop(provider_name, None)
175
176 @classmethod
177 def reset(cls):
178 cls._failures.clear()
179 cls._cooldowns.clear()
180
181
143 182 class IterListProvider(BaseRetryProvider):
144 183 def __init__(
145 184 self, providers: List[Type[BaseProvider]] = [], shuffle: bool = True
@@ -191,8 +230,10 @@ class IterListProvider(BaseRetryProvider):
191 230 if is_content(chunk):
192 231 started = True
193 232 if started:
233 ProviderCircuitBreaker.record_success(provider.__name__)
194 234 return
195 235 except Exception as e:
236 ProviderCircuitBreaker.record_failure(provider.__name__)
196 237 exceptions[provider.__name__] = e
197 238 debug.error(f"{provider.__name__}:", e)
198 239 if started:
@@ -215,7 +256,17 @@ class IterListProvider(BaseRetryProvider):
215 256
216 257 if self.shuffle:
217 258 random.shuffle(resolved_providers)
218 return resolved_providers
259
260 available_providers = []
261 cooling_down_providers = []
262 for p in resolved_providers:
263 p_name = getattr(p, "__name__", str(p))
264 if ProviderCircuitBreaker.is_available(p_name):
265 available_providers.append(p)
266 else:
267 cooling_down_providers.append(p)
268
269 return available_providers + cooling_down_providers
219 270
220 271
221 272 class RetryProvider(IterListProvider):