fix(codex): proxy override survives credential rotation and model.base_url is honoured

HERMES_CODEX_BASE_URL was applied at pool resolution and on the auxiliary
clients, but two paths still sent the openai-codex provider back to the
default ChatGPT backend:

- credential rotation: client_lifecycle._swap_credential adopts
  PooledCredential.runtime_base_url, which for openai-codex was the pool
  row's stored canonical URL, so the first 401/429 rotation silently left
  the proxy. The override now lives in runtime_base_url, the one place every
  reader of a Codex pool row (resolution and rotation) goes through.
- model.base_url: the openai-codex branch of _pool_entry_mode_and_url
  returned before the generic model.base_url block. It now honours
  model.base_url under model.provider: openai-codex when the pool row still
  carries the canonical URL (env override keeps precedence).

Slim port of #40924 onto the current layout (the original patched
run_agent._swap_credential, the pool seeder and a new auth.py helper; the
seeder half landed in b62bb2a3d5, the helper is replaced by the
profile-scoped get_secret_str read the landed fixes already use).

Fixes #40913
This commit is contained in:
rjshrjndrn
2026-09-19 01:00:13 -07:00
committed by Teknium
parent 85b2a3df6c
commit 7034628c92
3 changed files with 11 additions and 1 deletions

View File

@@ -19,7 +19,7 @@ from typing import Any, Callable, Dict, Iterable, List, Optional, Set, Tuple
from hermes_constants import OPENROUTER_BASE_URL
from hermes_cli.config import load_env
from agent.secret_scope import get_secret as _get_secret
from agent.secret_scope import get_secret as _get_secret, get_secret_str
from agent.retry_utils import reset_delay_from_message
from agent.credential_persistence import (
fingerprint_secret_value,
@@ -297,6 +297,11 @@ class PooledCredential:
def runtime_base_url(self) -> Optional[str]:
if self.provider == "nous":
return self.inference_base_url or self.base_url
if self.provider == "openai-codex":
# Pool rows keep the canonical ChatGPT URL; the profile-scoped proxy override must win
# for every reader of the row — initial resolution AND a 401/429 rotation
# (client_lifecycle._swap_credential), or a rotation silently leaves the proxy.
return get_secret_str("HERMES_CODEX_BASE_URL", "").strip().rstrip("/") or self.base_url
return self.base_url

View File

@@ -0,0 +1 @@
rjshrjndrn

View File

@@ -492,6 +492,10 @@ def _pool_entry_mode_and_url(provider, entry, model_cfg, effective_model, base_u
override_url = get_secret_str("HERMES_CODEX_BASE_URL", "").strip().rstrip("/")
if override_url:
return api_mode, override_url
# model.base_url is the secondary proxy override (same rule as the generic tail below:
# only when the pool row still carries the canonical URL).
if base_url in ("", default_url):
base_url = _config_base_url_for_provider(model_cfg, provider) or base_url
return api_mode, base_url or (default_url() if callable(default_url) else default_url)
if provider == "anthropic":
return "anthropic_messages", _anthropic_cfg_base_url(model_cfg) or base_url or _ANTHROPIC_DEFAULT_BASE_URL