mirror of
https://github.com/nesquena/hermes-webui.git
synced 2026-07-16 20:50:18 +00:00
fix(#4022): preserve different-endpoint config base_url override (Codex CORE)
Codex caught a regression: the contributor's _runtime_preferred_base_url preferred the runtime base_url for ALL non-custom providers, which would clobber an explicit providers.<id>.base_url override pointing at a different host/port (LM Studio at a LAN IP, an OpenRouter mirror). Now only prefer the runtime URL when it's the SAME scheme+host+port as the configured one (the #3895 /v1-dedup case is path-only on the same host); a genuinely different configured endpoint wins. Added _same_base_url_endpoint() + 2 regression tests (different-endpoint preserved, same-endpoint normalization).
This commit is contained in:
+48
-2
@@ -237,17 +237,58 @@ def _resolve_custom_provider_runtime_overrides(
|
||||
return resolved_provider, resolved_api_key, resolved_base_url
|
||||
|
||||
|
||||
def _same_base_url_endpoint(url_a: str, url_b: str) -> bool:
|
||||
"""True if two base URLs point at the same scheme+host+port endpoint.
|
||||
|
||||
Used to decide whether a runtime base_url is just a normalized form of the
|
||||
configured one (e.g. OpenCode-Go's ``/v1`` de-duplication on the same host)
|
||||
versus a genuinely different endpoint (an explicit ``providers.<id>.base_url``
|
||||
override at a different host/port that must be preserved). Path/query are
|
||||
intentionally ignored — the normalization #3895 fixes is path-only.
|
||||
"""
|
||||
from urllib.parse import urlsplit
|
||||
try:
|
||||
a = urlsplit((url_a or "").strip())
|
||||
b = urlsplit((url_b or "").strip())
|
||||
except Exception:
|
||||
return False
|
||||
_default_port = {"http": 80, "https": 443}
|
||||
a_host = (a.hostname or "").lower()
|
||||
b_host = (b.hostname or "").lower()
|
||||
a_scheme = (a.scheme or "").lower()
|
||||
b_scheme = (b.scheme or "").lower()
|
||||
a_port = a.port or _default_port.get(a_scheme)
|
||||
b_port = b.port or _default_port.get(b_scheme)
|
||||
return bool(a_host) and a_host == b_host and a_scheme == b_scheme and a_port == b_port
|
||||
|
||||
|
||||
def _runtime_preferred_base_url(
|
||||
runtime_provider: dict | None,
|
||||
resolved_provider: str | None,
|
||||
configured_base_url: str | None,
|
||||
) -> str | None:
|
||||
"""Prefer runtime-normalized URLs except for named custom endpoints."""
|
||||
"""Prefer the runtime-normalized base_url, but never override an explicit
|
||||
configured endpoint that points somewhere genuinely different.
|
||||
|
||||
The #3895 bug was that WebUI used the *configured* base_url (which can carry a
|
||||
duplicated ``/v1``) instead of the runtime provider's per-model-normalized
|
||||
base_url, 404ing OpenCode-Go. But blindly preferring the runtime URL would
|
||||
clobber a legitimate ``providers.<id>.base_url`` override (e.g. LM Studio at a
|
||||
LAN IP, an OpenRouter mirror). So:
|
||||
- no runtime URL -> keep configured
|
||||
- no configured URL -> use runtime (all we have)
|
||||
- named ``custom:`` endpoint -> configured wins (then runtime as fallback)
|
||||
- same scheme+host+port -> runtime wins (it's the normalized/corrected
|
||||
form of the same endpoint — the #3895 case)
|
||||
- different endpoint -> configured override wins (no regression)
|
||||
"""
|
||||
runtime_base_url = None
|
||||
if isinstance(runtime_provider, dict):
|
||||
runtime_base_url = runtime_provider.get("base_url")
|
||||
if not runtime_base_url:
|
||||
return configured_base_url
|
||||
if not configured_base_url:
|
||||
return runtime_base_url
|
||||
|
||||
provider_id = str(
|
||||
resolved_provider
|
||||
@@ -256,7 +297,12 @@ def _runtime_preferred_base_url(
|
||||
).strip().lower()
|
||||
if provider_id.startswith("custom:"):
|
||||
return configured_base_url or runtime_base_url
|
||||
return runtime_base_url
|
||||
|
||||
# An explicit configured override at a DIFFERENT endpoint must be preserved;
|
||||
# only prefer the runtime URL when it's the same endpoint (path-normalized).
|
||||
if _same_base_url_endpoint(configured_base_url, runtime_base_url):
|
||||
return runtime_base_url
|
||||
return configured_base_url
|
||||
|
||||
|
||||
def _is_fallback_lifecycle_message(kind: str, message: str) -> bool:
|
||||
|
||||
@@ -35,6 +35,40 @@ def test_runtime_preferred_base_url_uses_runtime_for_custom_provider_without_con
|
||||
) == "https://runtime.example.com"
|
||||
|
||||
|
||||
def test_runtime_preferred_base_url_preserves_different_endpoint_config_override():
|
||||
"""Codex CORE regression guard: an explicit providers.<id>.base_url override
|
||||
at a DIFFERENT host/port must NOT be clobbered by the runtime default. Only a
|
||||
same-endpoint runtime URL (the #3895 /v1-dedup case) should win."""
|
||||
# LM Studio pinned at a LAN IP — must be preserved over the runtime localhost default.
|
||||
assert streaming._runtime_preferred_base_url(
|
||||
{"provider": "lmstudio", "base_url": "http://localhost:1234/v1"},
|
||||
"lmstudio",
|
||||
"http://10.0.0.5:1234/v1",
|
||||
) == "http://10.0.0.5:1234/v1"
|
||||
# OpenRouter mirror override preserved over the runtime canonical host.
|
||||
assert streaming._runtime_preferred_base_url(
|
||||
{"provider": "openrouter", "base_url": "https://openrouter.ai/api/v1"},
|
||||
"openrouter",
|
||||
"https://my-mirror.example.com/api/v1",
|
||||
) == "https://my-mirror.example.com/api/v1"
|
||||
|
||||
|
||||
def test_runtime_preferred_base_url_prefers_runtime_for_same_endpoint_path_normalization():
|
||||
"""The #3895 case: same scheme+host+port, runtime just strips a duplicated
|
||||
path segment — runtime (corrected) form wins."""
|
||||
assert streaming._runtime_preferred_base_url(
|
||||
{"provider": "opencode-go", "base_url": "https://opencode.example.com/zen/go"},
|
||||
"opencode-go",
|
||||
"https://opencode.example.com/zen/go/v1",
|
||||
) == "https://opencode.example.com/zen/go"
|
||||
# Same host different port = different endpoint → configured wins.
|
||||
assert streaming._runtime_preferred_base_url(
|
||||
{"provider": "someprov", "base_url": "https://host.example.com:8080/v1"},
|
||||
"someprov",
|
||||
"https://host.example.com:9090/v1",
|
||||
) == "https://host.example.com:9090/v1"
|
||||
|
||||
|
||||
def test_streaming_passes_target_model_and_prefers_runtime_base_url(monkeypatch):
|
||||
captured = {}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user