167448edca
The gateway's startup warm-up (_warm_turn_machinery_sync) primed the run_agent import graph, tool schemas and context files, but left the process-local Python toolchain probe to the first AIAgent. That agent starts the probe worker in its constructor and its first system-prompt build then blocks in get_environment_probe_line() on the python/pip subprocesses (~250 ms measured) before the first model request — with local models too, so provider metadata warm-up (#105999) does not cover it. Call the same resolver from the warm-up: it owns the agent.environment_probe opt-out check here, and the resolver itself owns remote-backend omission, the single worker, its cache and the bounded fail-open wait, so the first turn simply finds the line cached. No agent is constructed and no session prompt is frozen at startup; a disabled probe or a remote terminal backend leaves the host untouched. Slim redo of #106075 by @francip: same mechanism, folded into the existing sync warm-up instead of a new module + asyncio.gather + second suppress block; the two behavior tests are kept, the concurrency tests dropped. Co-authored-by: Franci Penov <49422+francip@users.noreply.github.com>
48 lines
1.8 KiB
Python
48 lines
1.8 KiB
Python
"""The gateway boot warm-up primes the local toolchain probe the first prompt build reads (#106064)."""
|
|
|
|
import pytest
|
|
|
|
import model_tools
|
|
from gateway import run as gateway_run
|
|
from tools import env_probe
|
|
from tools.terminal_scope import reset_terminal_scope, set_terminal_scope
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _probe_cache(monkeypatch):
|
|
monkeypatch.setattr(model_tools, "get_tool_definitions", lambda **_: [])
|
|
env_probe._reset_cache_for_tests()
|
|
yield
|
|
env_probe._reset_cache_for_tests()
|
|
|
|
|
|
def _warm(tmp_path, monkeypatch, agent_section: dict, backend: str) -> None:
|
|
monkeypatch.setenv("HERMES_HOME", str(tmp_path))
|
|
(tmp_path / "config.yaml").write_text(f"agent: {agent_section!r}\n", encoding="utf-8")
|
|
token = set_terminal_scope({"TERMINAL_ENV": backend})
|
|
try:
|
|
gateway_run._warm_turn_machinery_sync()
|
|
finally:
|
|
reset_terminal_scope(token)
|
|
|
|
|
|
def test_warmup_leaves_probe_cached_for_first_prompt(tmp_path, monkeypatch):
|
|
calls = []
|
|
monkeypatch.setattr(env_probe, "_build_probe_line", lambda: calls.append(1) or "Python toolchain: fixture.")
|
|
|
|
_warm(tmp_path, monkeypatch, {}, "local")
|
|
|
|
assert env_probe._PROBE_DONE.is_set()
|
|
assert env_probe.get_environment_probe_line() == "Python toolchain: fixture."
|
|
assert calls == [1] # single worker; the first turn reuses the cache
|
|
|
|
|
|
@pytest.mark.parametrize("agent_section,backend", [({"environment_probe": False}, "local"), ({}, "ssh")])
|
|
def test_warmup_skips_probe_when_disabled_or_remote(tmp_path, monkeypatch, agent_section, backend):
|
|
monkeypatch.setattr(env_probe, "_build_probe_line", lambda: pytest.fail("host inspected"))
|
|
|
|
_warm(tmp_path, monkeypatch, agent_section, backend)
|
|
|
|
assert not env_probe._PROBE_DONE.is_set()
|
|
assert env_probe._PROBE_THREAD is None
|