mirror of
https://github.com/usestrix/strix.git
synced 2026-09-09 22:31:07 +00:00
fix(cli): wait for the import warm-up before importing the agents SDK on the main thread
The warm-up thread imports strix.core.runner while warm_up_llm and preflight_model_connection import agents.models.interface. Both walk the agents SDK graph from different entry points, CPython fails one side to break the import-lock cycle, and the orphan purge then removes agents.* from sys.modules while the main thread is still importing it, crashing strix -n with KeyError: 'agents.models'.
This commit is contained in:
parent
afa7c4a77f
commit
7f46dd17d3
4 changed files with 46 additions and 2 deletions
|
|
@ -41,6 +41,7 @@ from strix.interface.update_check import (
|
|||
from strix.interface.utils import (
|
||||
build_final_stats_text,
|
||||
)
|
||||
from strix.llm.warmup import start_import_warmup, wait_for_import_warmup
|
||||
from strix.telemetry import posthog, scarf
|
||||
from strix.telemetry.logging import configure_dependency_logging
|
||||
|
||||
|
|
@ -135,6 +136,8 @@ def _subscription_error_hint(exc: BaseException) -> str | None:
|
|||
|
||||
|
||||
async def warm_up_llm(show_model_warning: bool = True) -> None:
|
||||
wait_for_import_warmup()
|
||||
|
||||
from agents.models.interface import ModelTracing
|
||||
|
||||
from strix.config.models import (
|
||||
|
|
@ -450,8 +453,6 @@ def main() -> None:
|
|||
|
||||
sys.exit(run_cloud(sys.argv[2:]))
|
||||
|
||||
from strix.llm.warmup import start_import_warmup
|
||||
|
||||
start_import_warmup()
|
||||
|
||||
args = parse_arguments()
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ from strix.interface.utils import (
|
|||
stage_api_specs,
|
||||
write_fetched_collection,
|
||||
)
|
||||
from strix.llm.warmup import wait_for_import_warmup
|
||||
from strix.telemetry import posthog, scarf
|
||||
from strix.utils.api_spec import (
|
||||
SpecParseError,
|
||||
|
|
@ -64,6 +65,8 @@ async def preflight_model_connection(
|
|||
settings: Settings | None = None,
|
||||
) -> None:
|
||||
"""Verify the configured model route before starting a scan."""
|
||||
wait_for_import_warmup()
|
||||
|
||||
from agents.models.interface import ModelTracing
|
||||
|
||||
from strix.config.models import StrixProvider, configure_sdk_model_defaults
|
||||
|
|
|
|||
|
|
@ -80,3 +80,17 @@ def start_import_warmup(modules: tuple[str, ...] = WARMUP_MODULES) -> threading.
|
|||
)
|
||||
_thread.start()
|
||||
return _thread
|
||||
|
||||
|
||||
def wait_for_import_warmup(timeout: float | None = None) -> None:
|
||||
"""Block until the warm-up thread has finished, if one was started.
|
||||
|
||||
Call this before the first import of a warmed module on another thread.
|
||||
Two threads walking the same package graph hold each other's import locks,
|
||||
CPython breaks the cycle by failing one side, and the failed side's
|
||||
orphan purge can remove a package the other thread is still importing.
|
||||
"""
|
||||
with _lock:
|
||||
thread = _thread
|
||||
if thread is not None and thread is not threading.current_thread():
|
||||
thread.join(timeout)
|
||||
|
|
|
|||
|
|
@ -92,6 +92,32 @@ def test_failed_warm_import_purges_orphaned_submodules() -> None:
|
|||
assert result.returncode == 0, result.stderr
|
||||
|
||||
|
||||
def test_wait_for_import_warmup_lets_main_thread_import_the_agents_graph() -> None:
|
||||
result = _run(
|
||||
"""
|
||||
import sys
|
||||
|
||||
from strix.llm.warmup import start_import_warmup, wait_for_import_warmup
|
||||
|
||||
# Same shape as the CLI: warm-up starts, then the main thread needs a
|
||||
# module from the middle of the agents graph before it has finished.
|
||||
start_import_warmup()
|
||||
wait_for_import_warmup()
|
||||
|
||||
from agents.models.interface import ModelTracing # noqa: F401
|
||||
|
||||
assert "agents" in sys.modules
|
||||
assert "agents.models" in sys.modules
|
||||
assert "strix.core.runner" in sys.modules
|
||||
"""
|
||||
)
|
||||
assert result.returncode == 0, result.stderr
|
||||
|
||||
|
||||
def test_wait_for_import_warmup_is_a_no_op_without_a_thread() -> None:
|
||||
warmup.wait_for_import_warmup(timeout=0)
|
||||
|
||||
|
||||
def test_purge_does_not_touch_preexisting_or_healthy_modules() -> None:
|
||||
before = frozenset(sys.modules) - {"strix.llm.warmup"}
|
||||
warmup._purge_orphaned_modules(before)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue