mirror of
https://github.com/nesquena/hermes-webui.git
synced 2026-05-25 11:10:18 +00:00
Stage 326: PR #1953 — fix(config): skip #1776 provider peel for custom host:port slugs by @lucky-yonug
This commit is contained in:
+48
-5
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user