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:
@@ -41,7 +41,9 @@ from src import tool_presets
|
|||||||
|
|
||||||
from src.context_presets import ContextPresetManager
|
from src.context_presets import ContextPresetManager
|
||||||
from src.file_cache import ASTParser
|
from src.file_cache import ASTParser
|
||||||
|
from src.io_pool import make_io_pool
|
||||||
from src.models import GenerateRequest, ConfirmRequest
|
from src.models import GenerateRequest, ConfirmRequest
|
||||||
|
from src.warmup import WarmupManager
|
||||||
|
|
||||||
|
|
||||||
def parse_symbols(text: str) -> list[str]:
|
def parse_symbols(text: str) -> list[str]:
|
||||||
@@ -810,6 +812,11 @@ class AppController:
|
|||||||
self._rag_engine_lock: threading.Lock = threading.Lock()
|
self._rag_engine_lock: threading.Lock = threading.Lock()
|
||||||
self._project_switch_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 ---
|
# --- Internal State ---
|
||||||
self._ai_status: str = "idle"
|
self._ai_status: str = "idle"
|
||||||
self._mma_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 = threading.Thread(target=self._run_event_loop, daemon=True)
|
||||||
self._loop_thread.start()
|
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:
|
def shutdown(self) -> None:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -2094,6 +2159,8 @@ class AppController:
|
|||||||
self.event_queue.put("shutdown", None)
|
self.event_queue.put("shutdown", None)
|
||||||
if self._loop_thread and self._loop_thread.is_alive():
|
if self._loop_thread and self._loop_thread.is_alive():
|
||||||
self._loop_thread.join(timeout=2.0)
|
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:
|
def _init_ai_and_hooks(self, app: Any = None) -> None:
|
||||||
from src import api_hooks
|
from src import api_hooks
|
||||||
|
|||||||
Reference in New Issue
Block a user