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:
Teknium
2026-09-10 06:03:40 -07:00
parent 2feb546828
commit 7e0b5cd235
6 changed files with 106 additions and 9 deletions
+19
View File
@@ -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()