fix(auxiliary): auto never bills a provider the user did not select
With a main provider selected, an unusable main route (expired xAI/Codex OAuth token, 401/402/429 mid-session) fell through the built-in discovery chain (OpenRouter -> Nous -> custom -> api-key) and quietly ran every compression, title and memory-flush call on whichever OTHER account was still logged in. Reported as "using Grok on my Premium+ sub, my Nous Portal balance kept draining" — the chat visibly stayed on Grok while the side tasks were billed elsewhere, and re-logging into X did not help because the aux side never consulted the selected provider. The discovery chain is now reserved for installs with no selected main provider (`model.provider: auto` / unset). Otherwise the ladder is main -> auxiliary.<task>.fallback_chain -> fallback_providers -> refuse with a warning naming the dead provider and the fix. Both entry points gate on the same predicate: the resolve-time route and the mid-request payment/auth hop (_try_payment_fallback). Existing chain tests that asserted the hop now pin `provider=auto`, the one case where discovery is still the contract.
This commit is contained in:
@@ -3827,6 +3827,8 @@ def _try_payment_fallback(
|
||||
provider (and the main-provider path when it maps to the same backend). Returns (client, model, label) or (None, None, "")."""
|
||||
skip = failed_provider.lower().strip()
|
||||
main_provider = _read_main_provider()
|
||||
if not _discovery_chain_allowed(main_provider, task):
|
||||
return None, None, ""
|
||||
skip_labels = {skip}
|
||||
if main_provider and main_provider.lower() in skip:
|
||||
skip_labels.add(main_provider.lower())
|
||||
@@ -4200,6 +4202,21 @@ def _try_main_provider_route(
|
||||
return client, resolved or main_model, resolved_provider
|
||||
|
||||
|
||||
def _discovery_chain_allowed(main_provider: str, task: Optional[str] = None) -> bool:
|
||||
"""The built-in discovery chain is a convenience for installs with NO selected main provider.
|
||||
Once the user picked one, every auxiliary route must be a provider they configured (main,
|
||||
``auxiliary.<task>``, ``fallback_providers``); guessing "whatever else is logged in" bills an
|
||||
account they never pointed this session at (xAI OAuth session with a dead token → every
|
||||
compression silently charged to a Nous Portal balance)."""
|
||||
if (main_provider or "").strip().lower() in {"", "auto"}:
|
||||
return True
|
||||
logger.warning(
|
||||
"Auxiliary %s: main provider %s is unavailable and no fallback_chain / fallback_providers is "
|
||||
"configured — refusing to guess another logged-in provider. Re-authenticate (`hermes model`) "
|
||||
"or declare a fallback.", task or "call", main_provider)
|
||||
return False
|
||||
|
||||
|
||||
def _try_discovery_chain() -> Tuple[Optional[OpenAI], Optional[str], str]:
|
||||
"""Step 3: hardcoded aggregator/fallback chain, skipping unhealthy providers."""
|
||||
tried = []
|
||||
@@ -4249,6 +4266,8 @@ def _resolve_auto_route(
|
||||
task, main_provider or "auto", reason="main provider unavailable")
|
||||
if fb_client is not None:
|
||||
return fb_client, fb_model, fb_label
|
||||
if not _discovery_chain_allowed(main_provider, task):
|
||||
return None, None, ""
|
||||
return _try_discovery_chain()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user