This commit is contained in:
Samir Hanna Verza 2026-08-27 17:19:48 -04:00 committed by GitHub
commit f30f784b86
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 73 additions and 2 deletions

View file

@ -433,10 +433,13 @@ def main() -> None:
from strix.llm.warmup import start_import_warmup
start_import_warmup()
# parse_arguments() first so --version/--help/argparse errors exit before
# the (heavier) import warm-up runs. Warm-up then overlaps with the I/O
# startup below (update check, Docker checks, image pull).
args = parse_arguments()
start_import_warmup()
start_background_check()
if not args.non_interactive and prompt_update_if_available(Console()):
if is_binary_install() and sys.platform != "win32":

View file

@ -38,6 +38,24 @@ def _warm(modules: tuple[str, ...]) -> None:
logger.debug("Import warm-up for %r failed", name, exc_info=True)
def _preimport_thread_unsafe_sdk() -> None:
"""Import the ``agents`` SDK once, on the caller's thread, before the daemon
warm-up starts.
``openai-agents`` has an internal import cycle (``agents.agent_output`` <->
``agents.agent``) that is not thread-safe: when two threads import the
``agents`` package concurrently, CPython's deadlock-avoidance can hand one
of them a partially initialized module, raising ``ImportError: cannot
import name 'AgentOutputSchemaBase' ... (circular import)``. Fully importing
the package single-threaded here closes that race window before the daemon
warm-up thread (and the main thread's later report import) touch it.
"""
try:
importlib.import_module("agents")
except Exception: # noqa: BLE001 - a failed warm-up must never fail the run.
logger.debug("Pre-import of agents SDK failed", exc_info=True)
def start_import_warmup(modules: tuple[str, ...] = WARMUP_MODULES) -> threading.Thread:
"""Start importing the heavy scan dependencies in the background, once.
@ -48,6 +66,7 @@ def start_import_warmup(modules: tuple[str, ...] = WARMUP_MODULES) -> threading.
with _lock:
if _thread is not None:
return _thread
_preimport_thread_unsafe_sdk()
_thread = threading.Thread(
target=_warm, args=(modules,), name="strix-import-warmup", daemon=True
)

49
tests/test_warmup.py Normal file
View file

@ -0,0 +1,49 @@
from __future__ import annotations
import subprocess
import sys
import textwrap
from pathlib import Path
PROJECT_ROOT = Path(__file__).resolve().parents[1]
def test_start_import_warmup_preimports_agents_sdk_synchronously() -> None:
"""``start_import_warmup`` must import the thread-unsafe ``agents`` SDK on
the caller's thread before spawning the daemon warm-up thread.
``openai-agents`` has an internal import cycle that is not thread-safe: if
the warm-up thread and the main thread import the ``agents`` package
concurrently, CPython can hand one of them a partially initialized module
(``ImportError: cannot import name 'AgentOutputSchemaBase' ... circular
import``). Warming ``agents`` synchronously closes that race window.
Run in a fresh interpreter with an empty ``modules`` set so the daemon
thread warms nothing: ``agents`` can then only be in ``sys.modules`` because
the synchronous pre-import ran.
"""
child = textwrap.dedent(
"""
import sys
assert "agents" not in sys.modules
from strix.llm.warmup import start_import_warmup
# Importing the warm-up module alone must not pull the agents SDK.
assert "agents" not in sys.modules
start_import_warmup(modules=())
assert "agents" in sys.modules, "agents SDK was not pre-imported synchronously"
print("OK")
"""
)
result = subprocess.run( # noqa: S603
[sys.executable, "-c", child],
cwd=PROJECT_ROOT,
check=False,
capture_output=True,
text=True,
)
assert result.returncode == 0, result.stdout + result.stderr
assert "OK" in result.stdout