fix(codex): honor HERMES_CODEX_BASE_URL on pooled and aux codex client resolution

This commit is contained in:
joaomarcos
2026-09-14 13:23:09 -03:00
committed by Teknium
parent 7e06625687
commit b62bb2a3d5
4 changed files with 54 additions and 2 deletions
+3 -2
View File
@@ -2787,13 +2787,14 @@ def _build_codex_client(model: str) -> Tuple[Optional[Any], Optional[str]]:
return None, None return None, None
pool_present, entry = _select_pool_entry("openai-codex") pool_present, entry = _select_pool_entry("openai-codex")
codex_token = _pool_runtime_api_key(entry) if pool_present else None codex_token = _pool_runtime_api_key(entry) if pool_present else None
codex_override = os.getenv("HERMES_CODEX_BASE_URL", "").strip().rstrip("/")
if codex_token: if codex_token:
base_url = _pool_runtime_base_url(entry, _CODEX_AUX_BASE_URL) or _CODEX_AUX_BASE_URL base_url = codex_override or _pool_runtime_base_url(entry, _CODEX_AUX_BASE_URL) or _CODEX_AUX_BASE_URL
else: else:
codex_token = _read_codex_access_token() codex_token = _read_codex_access_token()
if not codex_token: if not codex_token:
return None, None return None, None
base_url = _CODEX_AUX_BASE_URL base_url = codex_override or _CODEX_AUX_BASE_URL
logger.debug("Auxiliary client: Codex OAuth (%s via Responses API)", model) logger.debug("Auxiliary client: Codex OAuth (%s via Responses API)", model)
real_client = _create_openai_client( real_client = _create_openai_client(
api_key=codex_token, base_url=base_url, api_key=codex_token, base_url=base_url,
+7
View File
@@ -449,6 +449,13 @@ def _pool_entry_mode_and_url(provider, entry, model_cfg, effective_model, base_u
base_url = _config_base_url_for_provider(model_cfg, provider) or base_url base_url = _config_base_url_for_provider(model_cfg, provider) or base_url
if provider in _POOL_ENTRY_SIMPLE_MODES: if provider in _POOL_ENTRY_SIMPLE_MODES:
api_mode, default_url = _POOL_ENTRY_SIMPLE_MODES[provider] api_mode, default_url = _POOL_ENTRY_SIMPLE_MODES[provider]
if provider == "openai-codex":
# Pool entries retain the canonical ChatGPT URL, but the profile-wide
# HERMES_CODEX_BASE_URL override must apply consistently to every
# credential source, including pooled OAuth credentials.
override_url = get_secret_str("HERMES_CODEX_BASE_URL", "").strip().rstrip("/")
if override_url:
return api_mode, override_url
return api_mode, base_url or (default_url() if callable(default_url) else default_url) return api_mode, base_url or (default_url() if callable(default_url) else default_url)
if provider == "anthropic": if provider == "anthropic":
return "anthropic_messages", _anthropic_cfg_base_url(model_cfg) or base_url or _ANTHROPIC_DEFAULT_BASE_URL return "anthropic_messages", _anthropic_cfg_base_url(model_cfg) or base_url or _ANTHROPIC_DEFAULT_BASE_URL
+20
View File
@@ -724,6 +724,26 @@ class TestBuildCodexClient:
assert mock_openai.call_args.kwargs["api_key"] == "codex-auth-token" assert mock_openai.call_args.kwargs["api_key"] == "codex-auth-token"
assert mock_openai.call_args.kwargs["base_url"] == "https://chatgpt.com/backend-api/codex" assert mock_openai.call_args.kwargs["base_url"] == "https://chatgpt.com/backend-api/codex"
def test_profile_codex_base_url_overrides_pool_endpoint(self, monkeypatch):
"""Auxiliary Codex calls use the same profile endpoint override as the main client."""
entry = SimpleNamespace(
runtime_api_key="codex-pool-token",
runtime_base_url="https://chatgpt.com/backend-api/codex",
)
with (
patch("agent.auxiliary_client._select_pool_entry", return_value=(True, entry)),
patch("agent.auxiliary_client.OpenAI") as mock_openai,
):
monkeypatch.setenv("HERMES_CODEX_BASE_URL", "http://127.0.0.1:8787/v1")
mock_openai.return_value = MagicMock()
from agent.auxiliary_client import _build_codex_client
client, model = _build_codex_client("gpt-5.4")
assert client is not None
assert model == "gpt-5.4"
assert mock_openai.call_args.kwargs["base_url"] == "http://127.0.0.1:8787/v1"
def test_rejects_missing_model(self): def test_rejects_missing_model(self):
"""Callers must pass an explicit model; no hardcoded default.""" """Callers must pass an explicit model; no hardcoded default."""
from agent.auxiliary_client import _build_codex_client from agent.auxiliary_client import _build_codex_client
@@ -86,6 +86,30 @@ def test_resolve_runtime_provider_uses_credential_pool(monkeypatch):
assert resolved["source"] == "manual" assert resolved["source"] == "manual"
def test_codex_pool_honors_hermes_codex_base_url(monkeypatch):
"""The profile-wide Codex endpoint override must apply to pool credentials too."""
class _Entry:
access_token = "pool-token"
source = "manual"
base_url = "https://chatgpt.com/backend-api/codex"
class _Pool:
def has_credentials(self):
return True
def select(self):
return _Entry()
monkeypatch.setattr(rp, "resolve_provider", lambda *a, **k: "openai-codex")
monkeypatch.setattr(rp, "load_pool", lambda provider: _Pool())
monkeypatch.setenv("HERMES_CODEX_BASE_URL", "http://127.0.0.1:8787/v1")
resolved = rp.resolve_runtime_provider(requested="openai-codex")
assert resolved["provider"] == "openai-codex"
assert resolved["base_url"] == "http://127.0.0.1:8787/v1"
class TestCustomProviderPoolLoopbackNoKeyExemption: class TestCustomProviderPoolLoopbackNoKeyExemption:
"""Regression for issue #86864: legacy custom_providers configs often """Regression for issue #86864: legacy custom_providers configs often
used short/placeholder api_keys ('123', 'm') for local no-auth used short/placeholder api_keys ('123', 'm') for local no-auth