mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-15 14:22:43 +00:00
perf(model-metadata): resolve localhost to IPv4 in detect_local_server_type
On Windows, `localhost` resolves to both ::1 (IPv6) and 127.0.0.1 (IPv4).
httpx tries IPv6 first, hanging 2 sec per probe when the server binds IPv4
only. detect_local_server_type() is called 3+ times during init, each with
a new httpx.Client, compounding to ~14s of dead time.
Replace localhost with 127.0.0.1 inside the function before connecting.
The function is only called for local endpoints (callers guard with
is_local_endpoint()), so IPv6 loopback adds no diagnostic value.
Measured: 19.9s → 4.0s on Windows with a local proxy on 127.0.0.1:8317.
(cherry picked from commit a075d3194b)
This commit is contained in:
parent
c889941916
commit
91ece5c2fc
2 changed files with 70 additions and 1 deletions
|
|
@ -648,10 +648,19 @@ def detect_local_server_type(base_url: str, api_key: str = "") -> Optional[str]:
|
|||
import httpx
|
||||
|
||||
normalized = _normalize_base_url(base_url)
|
||||
|
||||
# Resolve localhost to IPv4 to avoid 2s IPv6 timeout on Windows dual-stack.
|
||||
# Applied to ``normalized`` before deriving server/LM Studio URLs AND
|
||||
# before the cache lookup, so localhost and 127.0.0.1 share a cache entry.
|
||||
normalized = normalized.replace("://localhost:", "://127.0.0.1:")
|
||||
normalized = normalized.replace("://localhost/", "://127.0.0.1/")
|
||||
if normalized.endswith("://localhost"):
|
||||
normalized = normalized[:-len("localhost")] + "127.0.0.1"
|
||||
|
||||
server_url = normalized
|
||||
if server_url.endswith("/v1"):
|
||||
server_url = server_url[:-3]
|
||||
lmstudio_url = _lmstudio_server_root(base_url)
|
||||
lmstudio_url = _lmstudio_server_root(normalized)
|
||||
|
||||
cached = _endpoint_probe_path_cache.get(server_url)
|
||||
if cached is not None:
|
||||
|
|
|
|||
|
|
@ -507,6 +507,66 @@ class TestDetectLocalServerTypeAuth:
|
|||
assert client_mock.get.call_args_list[0].args[0] == "http://localhost:1234/api/v1/models"
|
||||
|
||||
|
||||
class TestDetectLocalServerTypeLocalhostIPv4:
|
||||
"""detect_local_server_type should resolve localhost to 127.0.0.1."""
|
||||
|
||||
def test_localhost_resolved_to_ipv4(self):
|
||||
"""Probes should use 127.0.0.1, not localhost, to avoid IPv6 timeout."""
|
||||
from agent.model_metadata import detect_local_server_type
|
||||
|
||||
resp = MagicMock()
|
||||
resp.status_code = 200
|
||||
|
||||
client_mock = MagicMock()
|
||||
client_mock.__enter__ = lambda s: client_mock
|
||||
client_mock.__exit__ = MagicMock(return_value=False)
|
||||
client_mock.get.return_value = resp
|
||||
|
||||
with patch("httpx.Client", return_value=client_mock):
|
||||
detect_local_server_type("http://localhost:8317/v1")
|
||||
|
||||
for call in client_mock.get.call_args_list:
|
||||
url = call[0][0]
|
||||
assert "localhost" not in url, f"Probe URL still uses localhost: {url}"
|
||||
assert "127.0.0.1" in url
|
||||
|
||||
def test_non_localhost_urls_unchanged(self):
|
||||
"""Non-localhost URLs should not be modified."""
|
||||
from agent.model_metadata import detect_local_server_type
|
||||
|
||||
client_mock = MagicMock()
|
||||
client_mock.__enter__ = lambda s: client_mock
|
||||
client_mock.__exit__ = MagicMock(return_value=False)
|
||||
resp = MagicMock()
|
||||
resp.status_code = 404
|
||||
client_mock.get.return_value = resp
|
||||
|
||||
with patch("httpx.Client", return_value=client_mock):
|
||||
detect_local_server_type("http://192.168.1.100:8080")
|
||||
|
||||
for call in client_mock.get.call_args_list:
|
||||
url = call[0][0]
|
||||
assert "192.168.1.100" in url
|
||||
|
||||
def test_127_0_0_1_urls_unchanged(self):
|
||||
"""URLs already using 127.0.0.1 should pass through unchanged."""
|
||||
from agent.model_metadata import detect_local_server_type
|
||||
|
||||
client_mock = MagicMock()
|
||||
client_mock.__enter__ = lambda s: client_mock
|
||||
client_mock.__exit__ = MagicMock(return_value=False)
|
||||
resp = MagicMock()
|
||||
resp.status_code = 404
|
||||
client_mock.get.return_value = resp
|
||||
|
||||
with patch("httpx.Client", return_value=client_mock):
|
||||
detect_local_server_type("http://127.0.0.1:8317")
|
||||
|
||||
for call in client_mock.get.call_args_list:
|
||||
url = call[0][0]
|
||||
assert "127.0.0.1" in url
|
||||
|
||||
|
||||
class TestFetchEndpointModelMetadataLmStudio:
|
||||
"""fetch_endpoint_model_metadata should use LM Studio's native models endpoint."""
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue