Private
Public Access
0
0

feat(app_controller): wire _io_pool + warmup + 5 public delegation methods

Phase 2 Task T2.5 of the startup_speedup_20260606 track.

In AppController.__init__, right after the lock init (and before the
heavy subsystem construction that follows), create the shared _io_pool
and WarmupManager, then submit the warmup list. The warmup runs
concurrently with the rest of __init__, so by the time __init__
returns, the heavy modules are loaded (or in flight).

Changes:
  - Add imports: from src.io_pool import make_io_pool,
    from src.warmup import WarmupManager
  - In __init__, after the locks block, add:
      self._io_pool = make_io_pool()
      self._warmup = WarmupManager(self._io_pool)
      self._warmup.submit(self._compute_warmup_list())
  - Add _compute_warmup_list() method: returns ['google.genai',
    'anthropic', 'openai', 'requests', 'src.command_palette',
    'src.theme_nerv', 'src.theme_nerv_fx', 'src.markdown_table',
    'numpy'] always, plus ['fastapi', 'fastapi.security.api_key']
    if self.test_hooks_enabled
  - Add public delegation methods: warmup_status(), is_warmup_done(),
    wait_for_warmup(timeout), on_warmup(callback)
  - In shutdown(), add self._io_pool.shutdown(wait=False)

The warmup currently is a no-op for the heavy modules already imported
at the top of app_controller.py (fastapi, requests, etc. are
already in sys.modules). The infrastructure is in place; Phase 3 will
remove the top-level imports so the warmup actually does work.

Verified: all 18 tests pass (test_io_pool + test_warmup + existing
test_app_controller_mcp + test_app_controller_offloading).
This commit is contained in:
2026-06-06 14:48:51 -04:00
parent 1354679e33
commit 922c5ad9ab
+67
View File
@@ -41,7 +41,9 @@ from src import tool_presets
from src.context_presets import ContextPresetManager
from src.file_cache import ASTParser
from src.io_pool import make_io_pool
from src.models import GenerateRequest, ConfirmRequest
from src.warmup import WarmupManager
def parse_symbols(text: str) -> list[str]:
@@ -810,6 +812,11 @@ class AppController:
self._rag_engine_lock: threading.Lock = threading.Lock()
self._project_switch_lock: threading.Lock = threading.Lock()
# --- Shared background pool + proactive warmup (startup_speedup_20260606) ---
self._io_pool = make_io_pool()
self._warmup = WarmupManager(self._io_pool)
self._warmup.submit(self._compute_warmup_list())
# --- Internal State ---
self._ai_status: str = "idle"
self._mma_status: str = "idle"
@@ -2080,6 +2087,64 @@ class AppController:
self._loop_thread = threading.Thread(target=self._run_event_loop, daemon=True)
self._loop_thread.start()
def _compute_warmup_list(self) -> list[str]:
"""
Returns the list of modules to warm on the _io_pool at startup.
[SDM: src/app_controller.py:_compute_warmup_list]
"""
modules: list[str] = [
"google.genai",
"anthropic",
"openai",
"requests",
"src.command_palette",
"src.theme_nerv",
"src.theme_nerv_fx",
"src.markdown_table",
"numpy",
]
if getattr(self, "test_hooks_enabled", False):
modules.extend([
"fastapi",
"fastapi.security.api_key",
])
return modules
def warmup_status(self) -> dict:
"""
Snapshot of the warmup progress. {pending, completed, failed}.
Cheap (lock-guarded copy). Polled by the GUI status indicator.
[SDM: src/app_controller.py:warmup_status]
"""
return self._warmup.status()
def is_warmup_done(self) -> bool:
"""
True once all warmup jobs have completed (or failed).
[SDM: src/app_controller.py:is_warmup_done]
"""
return self._warmup.is_done()
def wait_for_warmup(self, timeout: Optional[float] = None) -> bool:
"""
Block until warmup completes. Returns True on done, False on timeout.
[SDM: src/app_controller.py:wait_for_warmup]
"""
return self._warmup.wait(timeout=timeout)
def on_warmup_complete(self, callback: Callable[[dict], None]) -> None:
"""
Register a callback for warmup completion. If already done, fires
immediately on the calling thread.
[SDM: src/app_controller.py:on_warmup_complete]
"""
self._warmup.on_complete(callback)
def shutdown(self) -> None:
"""
@@ -2094,6 +2159,8 @@ class AppController:
self.event_queue.put("shutdown", None)
if self._loop_thread and self._loop_thread.is_alive():
self._loop_thread.join(timeout=2.0)
if hasattr(self, "_io_pool") and self._io_pool is not None:
self._io_pool.shutdown(wait=False)
def _init_ai_and_hooks(self, app: Any = None) -> None:
from src import api_hooks