From 93aa01c71c696b121001cc2d1812126f488c485c Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Sat, 4 Apr 2026 12:07:43 -0700 Subject: [PATCH] fix: use main provider model for auxiliary tasks on non-aggregator providers (#5091) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Users on direct API-key providers (Alibaba, DeepSeek, ZAI, etc.) without an OpenRouter or Nous key would get broken auxiliary tasks (compression, vision, etc.) because _resolve_auto() only tried aggregator providers first, then fell back to iterating PROVIDER_REGISTRY with wrong default model names. Now _resolve_auto() checks the user's main provider first. If it's not an aggregator (OpenRouter/Nous), it uses their main model directly for all auxiliary tasks. Aggregator users still get the cheap gemini-flash model as before. Adds _read_main_provider() to read model.provider from config.yaml, mirroring the existing _read_main_model(). Reported by SkyLinx — Alibaba Coding Plan user getting 400 errors from google/gemini-3-flash-preview being sent to DashScope. --- agent/auxiliary_client.py | 46 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/agent/auxiliary_client.py b/agent/auxiliary_client.py index bfbf20b5d..3832ac736 100644 --- a/agent/auxiliary_client.py +++ b/agent/auxiliary_client.py @@ -697,6 +697,25 @@ def _read_main_model() -> str: return "" +def _read_main_provider() -> str: + """Read the user's configured main provider from config.yaml. + + Returns the lowercase provider id (e.g. "alibaba", "openrouter") or "" + if not configured. + """ + try: + from hermes_cli.config import load_config + cfg = load_config() + model_cfg = cfg.get("model", {}) + if isinstance(model_cfg, dict): + provider = model_cfg.get("provider", "") + if isinstance(provider, str) and provider.strip(): + return provider.strip().lower() + except Exception: + pass + return "" + + def _resolve_custom_runtime() -> Tuple[Optional[str], Optional[str]]: """Resolve the active custom/main endpoint the same way the main CLI does. @@ -855,10 +874,35 @@ _AUTO_PROVIDER_LABELS = { } +_AGGREGATOR_PROVIDERS = frozenset({"openrouter", "nous"}) + + def _resolve_auto() -> Tuple[Optional[OpenAI], Optional[str]]: - """Full auto-detection chain: OpenRouter → Nous → custom → Codex → API-key → None.""" + """Full auto-detection chain. + + Priority: + 1. If the user's main provider is NOT an aggregator (OpenRouter / Nous), + use their main provider + main model directly. This ensures users on + Alibaba, DeepSeek, ZAI, etc. get auxiliary tasks handled by the same + provider they already have credentials for — no OpenRouter key needed. + 2. OpenRouter → Nous → custom → Codex → API-key providers (original chain). + """ global auxiliary_is_nous auxiliary_is_nous = False # Reset — _try_nous() will set True if it wins + + # ── Step 1: non-aggregator main provider → use main model directly ── + main_provider = _read_main_provider() + main_model = _read_main_model() + if (main_provider and main_model + and main_provider not in _AGGREGATOR_PROVIDERS + and main_provider not in ("auto", "custom", "")): + client, resolved = resolve_provider_client(main_provider, main_model) + if client is not None: + logger.info("Auxiliary auto-detect: using main provider %s (%s)", + main_provider, resolved or main_model) + return client, resolved or main_model + + # ── Step 2: aggregator / fallback chain ────────────────────────────── tried = [] for try_fn in (_try_openrouter, _try_nous, _try_custom_endpoint, _try_codex, _resolve_api_key_provider):