diff --git a/strix/config/codex.py b/strix/config/codex.py index ea477fa8..e507bee7 100644 --- a/strix/config/codex.py +++ b/strix/config/codex.py @@ -394,7 +394,7 @@ def get_subscription_client() -> AsyncOpenAI: async def reset_subscription_client() -> None: """Close the cached client so the next ``get_subscription_client`` builds a fresh one. Its pooled connections are bound to the event loop that opened them.""" - global _subscription_client # noqa: PLW0603 + global _subscription_client client, _subscription_client = _subscription_client, None if client is not None: await client.close() diff --git a/strix/interface/main.py b/strix/interface/main.py index 287133ef..9169d739 100644 --- a/strix/interface/main.py +++ b/strix/interface/main.py @@ -259,21 +259,27 @@ async def warm_up_llm(show_model_warning: bool = True) -> None: # The scan runs under a different event loop, and pooled connections # opened here would still be bound to this one. Close the shared # clients now so the scan starts from fresh ones. - with contextlib.suppress(Exception): + try: from agents.models import openai_provider http_client = openai_provider._http_client # pyright: ignore[reportPrivateUsage] openai_provider._http_client = None # pyright: ignore[reportPrivateUsage] if http_client is not None: await http_client.aclose() - with contextlib.suppress(Exception): + except Exception: + logger.debug("Closing the SDK HTTP client after warm-up failed", exc_info=True) + try: await codex.reset_subscription_client() + except Exception: + logger.debug("Closing the subscription client after warm-up failed", exc_info=True) # LiteLLM keys its client cache by event loop; this only releases the # warm-up's sockets. - with contextlib.suppress(Exception): + try: import litellm await litellm.close_litellm_async_clients() # type: ignore[attr-defined,no-untyped-call] + except Exception: + logger.debug("Closing LiteLLM clients after warm-up failed", exc_info=True) def display_completion_message(args: argparse.Namespace, results_path: Path) -> None: diff --git a/tests/test_warm_up_event_loop.py b/tests/test_warm_up_event_loop.py index a93901f7..a1a26f0d 100644 --- a/tests/test_warm_up_event_loop.py +++ b/tests/test_warm_up_event_loop.py @@ -98,17 +98,20 @@ class _KeepAliveHandler(BaseHTTPRequestHandler): async def _stream_turn(client: AsyncOpenAI) -> str: """One streamed scan turn. The SDK's model retry disables the OpenAI client's own retries, so a stale connection is not quietly replaced by a fresh one.""" - stream = await client.with_options(max_retries=0).chat.completions.create( - model="gw-model", - messages=[{"role": "user", "content": "go"}], - stream=True, - ) - text = "" - async for chunk in stream: - if chunk.choices and chunk.choices[0].delta.content: - text += chunk.choices[0].delta.content - await stream.close() - return text + try: + stream = await client.with_options(max_retries=0).chat.completions.create( + model="gw-model", + messages=[{"role": "user", "content": "go"}], + stream=True, + ) + text = "" + async for chunk in stream: + if chunk.choices and chunk.choices[0].delta.content: + text += chunk.choices[0].delta.content + await stream.close() + return text + finally: + await client.close() @pytest.fixture