fix(xai): honor model.base_url when the pool row is still the registry host

Env-seeded xAI credentials keep https://api.x.ai/v1, so a configured relay
never receives the request. Apply model.base_url only in that case.
This commit is contained in:
686f6c61
2026-09-24 14:54:39 +02:00
committed by Austin Pickett
parent 30565b2db2
commit 4033c97d35
2 changed files with 67 additions and 0 deletions

View File

@@ -534,6 +534,13 @@ def _pool_entry_mode_and_url(provider, entry, model_cfg, effective_model, base_u
# 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
if provider == "xai":
# Env-seeded rows keep the registry host. model.base_url is the relay
# override, and only while the row is still that host — an explicit
# per-credential endpoint stays authoritative (#121347).
canonical = (PROVIDER_REGISTRY["xai"].inference_base_url or "").rstrip("/")
if base_url.rstrip("/") in ("", canonical):
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

View File

@@ -163,6 +163,66 @@ def test_codex_pool_honors_model_base_url(monkeypatch):
assert resolved["api_mode"] == "codex_responses"
def _xai_pool(url):
class _Entry:
access_token = "pool-token"
source = "env:XAI_API_KEY"
base_url = url
class _Pool:
def has_credentials(self):
return True
def select(self, **_kwargs):
return _Entry()
return _Pool()
def test_xai_pool_honors_model_base_url_when_row_is_registry_host(monkeypatch):
"""#121347: an env-seeded xAI row keeps https://api.x.ai/v1. model.base_url is the
relay override, same as the other API-key providers, and must not be shadowed."""
monkeypatch.setattr(rp, "resolve_provider", lambda *a, **k: "xai")
monkeypatch.setattr(rp, "load_pool", lambda provider: _xai_pool("https://api.x.ai/v1"))
monkeypatch.delenv("XAI_BASE_URL", raising=False)
monkeypatch.setattr(rp, "_get_model_config", lambda: {
"provider": "xai", "default": "grok-4", "base_url": "http://127.0.0.1:8765/v1/"})
resolved = rp.resolve_runtime_provider(requested="xai")
assert resolved["provider"] == "xai"
assert resolved["api_key"] == "pool-token"
assert resolved["base_url"] == "http://127.0.0.1:8765/v1"
assert resolved["api_mode"] == "codex_responses"
def test_xai_pool_keeps_explicit_credential_endpoint(monkeypatch):
"""A pool row that is not the registry host is an explicit endpoint and wins over model.base_url."""
monkeypatch.setattr(rp, "resolve_provider", lambda *a, **k: "xai")
monkeypatch.setattr(rp, "load_pool", lambda provider: _xai_pool("https://relay.example/v1"))
monkeypatch.delenv("XAI_BASE_URL", raising=False)
monkeypatch.setattr(rp, "_get_model_config", lambda: {
"provider": "xai", "default": "grok-4", "base_url": "http://127.0.0.1:8765/v1"})
resolved = rp.resolve_runtime_provider(requested="xai")
assert resolved["base_url"] == "https://relay.example/v1"
assert resolved["api_mode"] == "codex_responses"
def test_xai_pool_ignores_another_providers_base_url(monkeypatch):
"""A stale model.base_url saved for a different provider must not receive the xAI key."""
monkeypatch.setattr(rp, "resolve_provider", lambda *a, **k: "xai")
monkeypatch.setattr(rp, "load_pool", lambda provider: _xai_pool("https://api.x.ai/v1"))
monkeypatch.delenv("XAI_BASE_URL", raising=False)
monkeypatch.setattr(rp, "_get_model_config", lambda: {
"provider": "deepseek", "default": "deepseek-v4-pro", "base_url": "http://127.0.0.1:8765/v1"})
resolved = rp.resolve_runtime_provider(requested="xai")
assert resolved["base_url"] == "https://api.x.ai/v1"
class TestCustomProviderPoolLoopbackNoKeyExemption:
"""Regression for issue #86864: legacy custom_providers configs often
used short/placeholder api_keys ('123', 'm') for local no-auth