Stage 326: PR #1953 — fix(config): skip #1776 provider peel for custom host:port slugs by @lucky-yonug

This commit is contained in:
nesquena-hermes
2026-05-09 18:16:16 +00:00
2 changed files with 86 additions and 5 deletions
+48 -5
View File
@@ -1430,6 +1430,44 @@ def _base_url_points_at_local_server(base_url: str) -> bool:
return False
def _custom_slug_rest_looks_like_host_port(rest: str) -> bool:
"""True when ``custom:<rest>`` is an endpoint-style slug ``host:port``.
WebUI sometimes derives ``custom:10.8.71.41:8080`` from ``base_url`` authority.
The #1776 peel must not treat that middle colon as part of an eaten model
segment — otherwise ``@custom:10.8.71.41:8080:Qwen3`` wrongly becomes model
``8080:Qwen3``.
"""
rest = str(rest or "").strip()
if ":" not in rest:
return False
host, port_s = rest.rsplit(":", 1)
if not host or ":" in host:
return False
if not port_s.isdigit():
return False
try:
port_n = int(port_s)
except ValueError:
return False
if not (1 <= port_n <= 65535):
return False
try:
import ipaddress
ipaddress.ip_address(host)
return True
except ValueError:
pass
hl = host.lower()
if hl == "localhost":
return True
# Typical DNS hostname used as proxy slug (contains at least one label dot).
if "." in host:
return True
return False
def resolve_model_provider(model_id: str) -> tuple:
"""Resolve model name, provider, and base_url for AIAgent.
@@ -1516,15 +1554,20 @@ def resolve_model_provider(model_id: str) -> tuple:
# ("@custom:my-key:some-model:free"), rsplit yields
# provider_hint="custom:my-key:some-model", bare_model="free", and the
# custom-prefix guard below skips the split-fallback. Detect the
# over-split structurally — custom hints carry exactly one segment after
# "custom:", so any provider_hint with 2+ colons that starts with
# "custom:" has eaten part of the model name. Peel one segment back.
# over-split structurally — custom hints normally carry one slug segment
# after ``custom:``. If ``provider_hint`` has extra ``:`` tokens because the
# model ID contained tags like ``:free``, peel one segment back (#1776).
#
# Exception: ``custom:<ip-or-host>:<port>`` is a single logical slug derived
# from OpenAI ``base_url`` authority and contains no eaten model segments.
if model_id.startswith("@") and ":" in model_id:
inner = model_id[1:]
provider_hint, bare_model = inner.rsplit(":", 1)
if provider_hint.startswith("custom:") and provider_hint.count(":") >= 2:
provider_hint, extra = provider_hint.rsplit(":", 1)
bare_model = f"{extra}:{bare_model}"
_slug_rest = provider_hint[len("custom:"):]
if not _custom_slug_rest_looks_like_host_port(_slug_rest):
provider_hint, extra = provider_hint.rsplit(":", 1)
bare_model = f"{extra}:{bare_model}"
elif (provider_hint not in _PROVIDER_MODELS
and provider_hint not in _PROVIDER_DISPLAY
and not provider_hint.startswith("custom:")):
@@ -170,3 +170,41 @@ def test_custom_provider_slashed_model_with_free_suffix_1776():
model, provider, _ = resolve_model_provider(qualified)
assert provider == "custom:my-key"
assert model == "org/model:free"
def test_custom_provider_ipv4_port_slug_no_false_peel():
"""host:port in custom slug must not trigger #1776 peel — avoids ``8080:model``."""
qualified = "@custom:10.8.71.41:8080:Qwen3-235B"
model, provider, _ = resolve_model_provider(qualified)
assert provider == "custom:10.8.71.41:8080"
assert model == "Qwen3-235B"
def test_custom_provider_hostname_port_slug_no_false_peel():
qualified = "@custom:proxy.internal:8443:Qwen3-235B"
model, provider, _ = resolve_model_provider(qualified)
assert provider == "custom:proxy.internal:8443"
assert model == "Qwen3-235B"
def test_custom_provider_localhost_port_slug_no_false_peel():
qualified = "@custom:localhost:11434:llama3.2"
model, provider, _ = resolve_model_provider(qualified)
assert provider == "custom:localhost:11434"
assert model == "llama3.2"
def test_model_with_provider_context_custom_ipv4_port_roundtrip():
"""Mirrors WebUI /start payload: bare model + custom:<host>:<port> provider."""
import api.config as cfg_mod
old = dict(cfg_mod.cfg.get("model", {}))
cfg_mod.cfg["model"] = {"provider": "custom", "default": "gpt-5.5"}
try:
wrapped = model_with_provider_context("Qwen3-235B", "custom:10.8.71.41:8080")
assert wrapped == "@custom:10.8.71.41:8080:Qwen3-235B"
model, provider, _ = resolve_model_provider(wrapped)
assert provider == "custom:10.8.71.41:8080"
assert model == "Qwen3-235B"
finally:
cfg_mod.cfg["model"] = old