fix(preflight): name the dedupe endpoint setting in its timeout message and test the dedupe warm-up

preflight_request takes api_base_setting so a dedupe model that timed out
points the user at DEDUPE_LLM_API_BASE, not LLM_API_BASE. warm_up_llm is
now exercised with a dedicated dedupe model: own headers, same preflight
timeout, resolved through resolve_dedupe_model.
This commit is contained in:
Ahmed Allam 2026-10-04 19:35:01 +00:00 • committed by Ahmed Allam
parent 3527d1f81d
commit 0c33f5776f
3 changed files with 53 additions and 3 deletions

View file

@ -169,6 +169,7 @@ async def warm_up_llm() -> None:
model_name=dedupe_model,
extra_headers=settings.dedupe.extra_headers,
timeout=llm.preflight_timeout,
api_base_setting="DEDUPE_LLM_API_BASE",
)
logger.info("LLM warm-up succeeded for dedupe model %s", dedupe_model)

View file

@ -117,6 +117,7 @@ async def preflight_model_connection(
model_name=model_name,
extra_headers=resolved_settings.llm.extra_headers,
timeout=resolved_settings.llm.preflight_timeout,
api_base_setting="LLM_API_BASE",
)
@ -126,8 +127,13 @@ async def preflight_request(
model_name: str,
extra_headers: dict[str, str] | None,
timeout: int,
api_base_setting: str,
) -> None:
"""Send one tiny request to ``model`` and fail if it does not answer in ``timeout`` seconds."""
"""Send one tiny request to ``model`` and fail if it does not answer in ``timeout`` seconds.
``api_base_setting`` names the environment variable that points at this
model's endpoint, so the timeout message sends the user to the right one.
"""
from agents.models.interface import ModelTracing
from strix.core.inputs import make_model_settings
@ -158,8 +164,8 @@ async def preflight_request(
)
except TimeoutError:
raise TimeoutError(
f"{model_name} did not answer within {timeout}s "
"(LLM_PREFLIGHT_TIMEOUT). Check LLM_API_BASE and that the endpoint is reachable."
f"{model_name} did not answer within {timeout}s (LLM_PREFLIGHT_TIMEOUT). "
f"Check {api_base_setting} and that the endpoint is reachable."
) from None

View file

@ -8,6 +8,7 @@ from typing import TYPE_CHECKING, Any
import pytest
import strix.interface.main as cli_main
from strix.config import load_settings, loader
from strix.interface import scan_setup
from strix.interface.scan_setup import preflight_model_connection, preflight_request
@ -56,12 +57,14 @@ def test_preflight_request_fails_after_its_own_timeout_with_a_clear_message() ->
model_name="openai/gpt-4o",
extra_headers=None,
timeout=1,
api_base_setting="DEDUPE_LLM_API_BASE",
)
)
assert time.monotonic() - started < 5
message = str(excinfo.value)
assert "openai/gpt-4o did not answer within 1s" in message
assert "LLM_PREFLIGHT_TIMEOUT" in message
assert "Check DEDUPE_LLM_API_BASE" in message
assert model.request_timeouts == [1]
@ -82,3 +85,43 @@ def test_preflight_model_connection_uses_the_preflight_timeout(
asyncio.run(preflight_model_connection("openai/gpt-4o", settings=load_settings()))
assert seen["timeout"] == 12
assert seen["model_name"] == "openai/gpt-4o"
assert seen["api_base_setting"] == "LLM_API_BASE"
def test_warm_up_checks_the_dedupe_model_with_its_own_headers_and_the_preflight_timeout(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
monkeypatch.setenv("STRIX_LLM", "openai/gpt-4o")
monkeypatch.setenv("LLM_API_KEY", "sk-test")
monkeypatch.setenv("LLM_EXTRA_HEADERS", '{"X-Main": "1"}')
monkeypatch.setenv("LLM_TIMEOUT", "600")
monkeypatch.setenv("LLM_PREFLIGHT_TIMEOUT", "9")
monkeypatch.setenv("STRIX_DEDUPE_MODEL", "anthropic/claude-sonnet-4-5")
monkeypatch.setenv("DEDUPE_LLM_EXTRA_HEADERS", '{"X-Dedupe": "1"}')
_fresh_settings(monkeypatch, tmp_path)
dedupe_model = object()
calls: list[tuple[object, dict[str, Any]]] = []
async def record(model: Any, **kwargs: Any) -> None:
calls.append((model, kwargs))
monkeypatch.setattr(scan_setup, "preflight_request", record)
monkeypatch.setattr(cli_main, "preflight_request", record)
monkeypatch.setattr(
"strix.report.dedupe.resolve_dedupe_model", lambda _dedupe, _name: dedupe_model
)
asyncio.run(cli_main.warm_up_llm())
assert [kwargs["model_name"] for _, kwargs in calls] == [
"openai/gpt-4o",
"anthropic/claude-sonnet-4-5",
]
assert [kwargs["timeout"] for _, kwargs in calls] == [9, 9]
assert [kwargs["extra_headers"] for _, kwargs in calls] == [{"X-Main": "1"}, {"X-Dedupe": "1"}]
assert [kwargs["api_base_setting"] for _, kwargs in calls] == [
"LLM_API_BASE",
"DEDUPE_LLM_API_BASE",
]
assert calls[1][0] is dedupe_model