fix(codex): honor HERMES_CODEX_BASE_URL on pooled and aux codex client resolution
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user