From 459d7694d348046d5598d1560ba276f47e5aef7f Mon Sep 17 00:00:00 2001 From: helix4u <4317663+helix4u@users.noreply.github.com> Date: Wed, 27 May 2026 22:47:14 -0600 Subject: [PATCH] fix(agent): preload jiter native parser --- agent/__init__.py | 2 ++ agent/jiter_preload.py | 39 +++++++++++++++++++++++++++++++ tests/agent/test_jiter_preload.py | 25 ++++++++++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 agent/jiter_preload.py create mode 100644 tests/agent/test_jiter_preload.py diff --git a/agent/__init__.py b/agent/__init__.py index aaa2d74d14a..41136f9b639 100644 --- a/agent/__init__.py +++ b/agent/__init__.py @@ -4,3 +4,5 @@ These modules contain pure utility functions and self-contained classes that were previously embedded in the 3,600-line run_agent.py. Extracting them makes run_agent.py focused on the AIAgent orchestrator class. """ + +from . import jiter_preload as _jiter_preload # noqa: F401 diff --git a/agent/jiter_preload.py b/agent/jiter_preload.py new file mode 100644 index 00000000000..787e45afa61 --- /dev/null +++ b/agent/jiter_preload.py @@ -0,0 +1,39 @@ +"""Best-effort early import for the OpenAI SDK's native streaming parser. + +The OpenAI SDK imports ``jiter`` while constructing streaming chat-completion +responses. On some Windows installs the native extension can be imported +directly from the Hermes venv, but the first import fails when it happens later +inside the threaded streaming request path. Loading it once during agent +package import avoids that import-order failure while preserving the normal +SDK error path for genuinely missing or broken installs. +""" + +from __future__ import annotations + +import importlib + +_JITER_PRELOADED = False +_JITER_PRELOAD_ERROR: Exception | None = None + + +def preload_jiter_native_extension() -> bool: + """Import jiter's native extension early if it is available.""" + + global _JITER_PRELOADED, _JITER_PRELOAD_ERROR + + if _JITER_PRELOADED: + return True + + try: + importlib.import_module("jiter.jiter") + from jiter import from_json as _from_json # noqa: F401 + except Exception as exc: + _JITER_PRELOAD_ERROR = exc + return False + + _JITER_PRELOADED = True + _JITER_PRELOAD_ERROR = None + return True + + +preload_jiter_native_extension() diff --git a/tests/agent/test_jiter_preload.py b/tests/agent/test_jiter_preload.py new file mode 100644 index 00000000000..2fd358b5b71 --- /dev/null +++ b/tests/agent/test_jiter_preload.py @@ -0,0 +1,25 @@ +from __future__ import annotations + +import importlib +import sys + +from agent import jiter_preload + + +def test_preload_jiter_native_extension_loads_sdk_parser_dependency(): + assert jiter_preload.preload_jiter_native_extension() is True + assert "jiter.jiter" in sys.modules + + +def test_preload_jiter_native_extension_is_best_effort(monkeypatch): + monkeypatch.setattr(jiter_preload, "_JITER_PRELOADED", False) + + def _raise_missing(name: str): + assert name == "jiter.jiter" + raise ModuleNotFoundError(name) + + monkeypatch.setattr(importlib, "import_module", _raise_missing) + + assert jiter_preload.preload_jiter_native_extension() is False + assert jiter_preload._JITER_PRELOADED is False + assert isinstance(jiter_preload._JITER_PRELOAD_ERROR, ModuleNotFoundError)