Treat CoderAI broker warm-up as wait-and-retry, not a provider failure

CoderAI workers often run on small/edge hardware that drops its broker
session while cooling down to avoid overheating. A missing broker session
("No active CoderAI broker session...") was treated as a hard error:
record_failure() was called in both the provider handler and the rotation
loop, so three of them in one request disabled the provider for a 300s
cooldown — exactly when it just needed a moment to warm back up.

Now this transient condition is handled gracefully:
- coderai.py no longer records a failure when the error is a missing broker
  session (in any request path), so it never counts toward the disable
  threshold.
- The rotation handler detects the CoderAI warm-up condition and waits
  CODERAI_WARMUP_WAIT_SECONDS (10s) before retrying the same provider, up to
  CODERAI_MAX_WARMUP_WAITS (3) times, fully transparent to the client. Only
  after that does it fail over to the next provider — still without recording
  a failure.

Bump version to 0.99.76.
Co-Authored-By: 's avatarClaude Opus 4.8 <noreply@anthropic.com>
parent 03f3e110
...@@ -55,7 +55,7 @@ from .auth.qwen import QwenOAuth2 ...@@ -55,7 +55,7 @@ from .auth.qwen import QwenOAuth2
from .handlers import RequestHandler, RotationHandler, AutoselectHandler from .handlers import RequestHandler, RotationHandler, AutoselectHandler
from .utils import count_messages_tokens, split_messages_into_chunks, get_max_request_tokens_for_model from .utils import count_messages_tokens, split_messages_into_chunks, get_max_request_tokens_for_model
__version__ = "0.99.75" __version__ = "0.99.76"
__all__ = [ __all__ = [
# Config # Config
"config", "config",
......
...@@ -65,6 +65,14 @@ from .prompt_analysis import analyze_prompt_payload ...@@ -65,6 +65,14 @@ from .prompt_analysis import analyze_prompt_payload
_autoselect_result_cache: dict = {} _autoselect_result_cache: dict = {}
_autoselect_result_cache_ttl: int = 3600 # seconds _autoselect_result_cache_ttl: int = 3600 # seconds
# CoderAI providers often run on small/edge hardware that drops its broker session
# while cooling down. Treat a missing broker session as a transient "warming up"
# state: wait CODERAI_WARMUP_WAIT_SECONDS and retry the same provider, up to
# CODERAI_MAX_WARMUP_WAITS times, instead of recording a failure (which would
# disable the provider for a long cooldown).
CODERAI_WARMUP_WAIT_SECONDS: float = 10.0
CODERAI_MAX_WARMUP_WAITS: int = 3
# Registry for proxied content — shared across all RequestHandler instances. # Registry for proxied content — shared across all RequestHandler instances.
# Values: {"type": "broker", "provider_id": str, "path": str} # Values: {"type": "broker", "provider_id": str, "path": str}
# or {"type": "http", "url": str} # or {"type": "http", "url": str}
...@@ -3481,6 +3489,7 @@ class RotationHandler: ...@@ -3481,6 +3489,7 @@ class RotationHandler:
max_retries = 5 max_retries = 5
tried_models = [] # Track which models have been tried tried_models = [] # Track which models have been tried
model_retry_counts = {} # Track retry count per model model_retry_counts = {} # Track retry count per model
coderai_warmup_counts = {} # Track CoderAI "warming up" waits per model
last_error = None last_error = None
successful_model = None successful_model = None
successful_handler = None successful_handler = None
...@@ -3898,6 +3907,32 @@ class RotationHandler: ...@@ -3898,6 +3907,32 @@ class RotationHandler:
except Exception as e: except Exception as e:
last_error = str(e) last_error = str(e)
# CoderAI "warming up": the broker session is temporarily gone while
# the (often small/edge) worker cools down. This is NOT a provider
# fault — do not record a failure (which would disable the provider for
# a long cooldown). Instead wait a few seconds and retry the same
# provider, up to a bounded number of times, fully transparent to the
# client.
if self._is_coderai_warming_error(e, provider_id):
warmup_count = coderai_warmup_counts.get(model_key, 0)
if warmup_count < CODERAI_MAX_WARMUP_WAITS:
coderai_warmup_counts[model_key] = warmup_count + 1
logger.warning(
f"CoderAI provider {provider_id} is warming up (no broker session); "
f"waiting {CODERAI_WARMUP_WAIT_SECONDS:.0f}s and retrying "
f"(warmup {warmup_count + 1}/{CODERAI_MAX_WARMUP_WAITS}) — not recorded as a failure"
)
await asyncio.sleep(CODERAI_WARMUP_WAIT_SECONDS)
continue
logger.warning(
f"CoderAI provider {provider_id} still warming up after "
f"{warmup_count} wait(s); failing over to the next provider "
f"(still not recorded as a failure)"
)
tried_models.append(current_model)
continue
handler.record_failure() handler.record_failure()
# Increment retry count for this model # Increment retry count for this model
...@@ -4295,6 +4330,28 @@ class RotationHandler: ...@@ -4295,6 +4330,28 @@ class RotationHandler:
return True return True
return False return False
def _is_coderai_warming_error(self, error, provider_id) -> bool:
"""Return True when the error is a CoderAI provider that is merely 'warming up'.
CoderAI workers often run on small/edge hardware that disconnects its broker
session while cooling down (to avoid overheating). A missing broker session is
therefore a transient condition, not a provider fault: instead of recording a
failure (which would count toward disabling the provider for a long cooldown),
the rotation should wait a few seconds and retry the same provider.
"""
msg = str(error or '')
if 'No active CoderAI broker session' not in msg:
return False
# The message is already CoderAI-specific; confirm the provider type when we
# can, but don't require it (the snapshot lookup may be unavailable).
try:
ptype = (self._get_provider_type(provider_id) or '').lower()
if ptype and ptype != 'coderai':
return False
except Exception:
pass
return True
async def _prime_stream(self, response): async def _prime_stream(self, response):
"""Pull the first chunk of a streaming response so that immediate provider """Pull the first chunk of a streaming response so that immediate provider
errors are raised here (within the rotation retry loop) instead of after the errors are raised here (within the rotation retry loop) instead of after the
......
...@@ -502,8 +502,14 @@ class CoderAIProviderHandler(BaseProviderHandler): ...@@ -502,8 +502,14 @@ class CoderAIProviderHandler(BaseProviderHandler):
if not stream: if not stream:
self.record_success() self.record_success()
return response return response
except Exception: except Exception as e:
self.record_failure() # A missing broker session means the CoderAI worker (often small/edge
# hardware) is warming up / cooling down to avoid overheating — a
# transient condition, not a provider fault. Don't record a failure for
# it (that would count toward disabling the provider for a long
# cooldown); the rotation handler waits a few seconds and retries.
if 'No active CoderAI broker session' not in str(e):
self.record_failure()
raise raise
async def get_models(self) -> List[Model]: async def get_models(self) -> List[Model]:
......
...@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" ...@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "aisbf" name = "aisbf"
version = "0.99.75" version = "0.99.76"
description = "AISBF - AI Service Broker Framework || AI Should Be Free - A modular proxy server for managing multiple AI provider integrations" description = "AISBF - AI Service Broker Framework || AI Should Be Free - A modular proxy server for managing multiple AI provider integrations"
readme = "README.md" readme = "README.md"
license = "GPL-3.0-or-later" license = "GPL-3.0-or-later"
......
...@@ -106,7 +106,7 @@ class InstallCommand(_install): ...@@ -106,7 +106,7 @@ class InstallCommand(_install):
setup( setup(
name="aisbf", name="aisbf",
version="0.99.75", version="0.99.76",
author="AISBF Contributors", author="AISBF Contributors",
author_email="stefy@nexlab.net", author_email="stefy@nexlab.net",
description="AISBF - AI Service Broker Framework || AI Should Be Free - A modular proxy server for managing multiple AI provider integrations", description="AISBF - AI Service Broker Framework || AI Should Be Free - A modular proxy server for managing multiple AI provider integrations",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment