From fc32a38c3a6d6361e9a4385449cc782944e61320 Mon Sep 17 00:00:00 2001 From: pierrenode <298902573+pierrenode@users.noreply.github.com> Date: Mon, 3 Aug 2026 16:40:48 +0530 Subject: [PATCH] fix(model_metadata): rewrite localhost->IPv4 for the remaining local probe sites fetch_endpoint_model_metadata's generic (non-LM-Studio) /models fetch and its llama.cpp /v1/props context-length follow-up built request URLs straight from the unrewritten candidate, unlike every other local-probe site. Both retained the multi-second dual-stack IPv6 connect penalty that _localhost_to_ipv4() exists to skip (measured on macOS: localhost 32.9ms vs 127.0.0.1 0.1ms on a dead port; ~2s on Windows). normalized stays the cache key so caching behavior is unchanged; only the outbound request target is rewritten. Re-derived from PR #61528 onto current main (original no longer applied cleanly). --- agent/model_metadata.py | 8 +++- tests/agent/test_probe_cache_followups.py | 55 +++++++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/agent/model_metadata.py b/agent/model_metadata.py index f89d25f4c8dd7..be1efb163ed48 100644 --- a/agent/model_metadata.py +++ b/agent/model_metadata.py @@ -1184,7 +1184,11 @@ def fetch_endpoint_model_metadata( last_error = exc for candidate in candidates: - url = candidate.rstrip("/") + "/models" + # normalized/candidates stay unrewritten (cache key stability); only + # the outbound request target is IPv4-resolved to skip the multi-second + # dual-stack IPv6 connect timeout (see _localhost_to_ipv4). + request_candidate = _localhost_to_ipv4(candidate) + url = request_candidate.rstrip("/") + "/models" response = None try: response = requests.get( @@ -1230,7 +1234,7 @@ def fetch_endpoint_model_metadata( if is_llamacpp: try: # Try /v1/props first (current llama.cpp); fall back to /props for older builds - base = candidate.rstrip("/").replace("/v1", "") + base = request_candidate.rstrip("/").replace("/v1", "") _verify = _resolve_requests_verify() props_resp = requests.get(base + "/v1/props", headers=headers, timeout=5, verify=_verify) if not props_resp.ok: diff --git a/tests/agent/test_probe_cache_followups.py b/tests/agent/test_probe_cache_followups.py index 27d306ae2d642..f0a88a64e1cd7 100644 --- a/tests/agent/test_probe_cache_followups.py +++ b/tests/agent/test_probe_cache_followups.py @@ -186,6 +186,61 @@ class TestLocalhostIPv4SiblingSites: assert client.post.call_args[0][0].startswith("http://127.0.0.1:11434") + def test_fetch_endpoint_model_metadata_generic_probe_uses_ipv4(self): + """The generic (non-LM-Studio) /models fetch loop must also rewrite + localhost->127.0.0.1 before probing, like the LM Studio branch above.""" + from agent import model_metadata + from agent.model_metadata import fetch_endpoint_model_metadata + + model_metadata._endpoint_model_metadata_cache.clear() + model_metadata._endpoint_model_metadata_cache_time.clear() + + resp = MagicMock() + resp.status_code = 200 + resp.raise_for_status = MagicMock() + resp.json.return_value = {"data": []} + + with patch("agent.model_metadata.detect_local_server_type", return_value=None), \ + patch("agent.model_metadata.requests.get", return_value=resp) as mock_get: + fetch_endpoint_model_metadata("http://localhost:8000/v1") + + assert mock_get.call_args[0][0].startswith("http://127.0.0.1:8000") + + def test_fetch_endpoint_model_metadata_llamacpp_props_followup_uses_ipv4(self): + """The llama.cpp /props context-length follow-up must also rewrite + localhost->127.0.0.1 before probing, not just the initial /models call.""" + from agent import model_metadata + from agent.model_metadata import fetch_endpoint_model_metadata + + model_metadata._endpoint_model_metadata_cache.clear() + model_metadata._endpoint_model_metadata_cache_time.clear() + + models_resp = MagicMock() + models_resp.status_code = 200 + models_resp.raise_for_status = MagicMock() + models_resp.json.return_value = { + "data": [{"id": "llama-3-8b", "owned_by": "llamacpp"}], + } + + props_resp = MagicMock() + props_resp.ok = True + props_resp.json.return_value = { + "default_generation_settings": {"n_ctx": 32768}, + "model_alias": "llama-3-8b", + } + + with patch("agent.model_metadata.detect_local_server_type", return_value=None), \ + patch( + "agent.model_metadata.requests.get", + side_effect=[models_resp, props_resp], + ) as mock_get: + result = fetch_endpoint_model_metadata("http://localhost:8000/v1") + + assert mock_get.call_count == 2 + props_call_url = mock_get.call_args_list[1][0][0] + assert props_call_url.startswith("http://127.0.0.1:8000") + assert result["llama-3-8b"]["context_length"] == 32768 + class TestContextCacheKeyNormalization: