Private
Public Access
0
0

refactor(app_controller): remove top-level fastapi imports; lift _require_warmed to shared module

Phase 4 T4.1-T4.4 of startup_speedup_20260606 track.

DEVIATION FROM ORIGINAL SPEC: spec.md said fastapi was in src/api_hooks.py
but it was actually in src/app_controller.py (lines 17, 21). api_hooks.py
uses stdlib http.server. Phase 4 target corrected to app_controller.

LIFTED _require_warmed TO SHARED MODULE: created src/module_loader.py to
avoid duplicating the lookup logic and the cross-module import smell
(app_controller -> ai_client). src/ai_client.py re-exports it so the
T3.1 test (which asserts hasattr(src.ai_client, '_require_warmed'))
continues to work.

src/app_controller.py changes:
- Added 'from __future__ import annotations' (enables lazy type annotations;
  -> FastAPI return type now a forward reference)
- Removed 'from fastapi import FastAPI, Depends, HTTPException' (line 17)
- Removed 'from fastapi.security.api_key import APIKeyHeader' (line 21)
- Added 'from src.module_loader import _require_warmed' (cross-module via
  shared utility, not via ai_client)
- create_api(): added lookups at top of function body
- 7 _api_* helper functions (_api_get_key, _api_generate, _api_stream,
  _api_confirm_action, _api_get_session, _api_delete_session,
  _api_get_context): added 'HTTPException = _require_warmed(...).HTTPException'
  at top of each function body

EFFECTIVENESS:
- import src.app_controller no longer triggers fastapi import (saves ~470ms
  in main thread; only loaded when --enable-test-hooks is set)
- When --enable-test-hooks is set, the AppController's warmup pre-loads
  fastapi on the _io_pool, so create_api()'s lookup is O(1)

TESTS:
- tests/test_app_controller_no_top_level_fastapi.py: 4/4 PASS (was 3 RED + 1 pass)
- tests/test_ai_client_no_top_level_sdk_imports.py: 9/9 still PASS (re-export works)
- tests/test_app_controller_mcp.py, test_app_controller_offloading.py: pass
- tests/test_headless_service.py: 10/11 PASS (1 pre-existing failure
  test_generate_endpoint is a circular-import issue in google.genai,
  reproduces identically on stashed pre-Phase-4 state - NOT a regression
  from this change)
- tests/test_hooks.py: pass

NEXT: Phase 5 (feature-gated GUI module imports - command palette, NERV
theme, markdown table), then Phase 6 (ad-hoc threads -> _io_pool).
This commit is contained in:
2026-06-06 16:34:46 -04:00
parent 7fb13fbf4b
commit 3849d30441
4 changed files with 168 additions and 21 deletions
+5 -18
View File
@@ -52,24 +52,11 @@ from src.tool_bias import ToolBiasEngine
from src.tool_presets import ToolPresetManager
def _require_warmed(name: str) -> Any:
"""Return a heavy module that the AppController's warmup should have loaded.
Heavy SDKs (anthropic, google.genai, openai, google.genai.types,
requests) are warmed on AppController's _io_pool at startup. This
function expects them to already be in sys.modules and just returns
the cached module object. If the module is NOT in sys.modules (e.g.
in tests where warmup didn't run), falls back to importlib so the
call still works.
In production: this is an O(1) sys.modules lookup. The 1+ second
import cost is paid during startup on a bg thread, NOT on the first
user-triggered AI call.
"""
mod = sys.modules.get(name)
if mod is not None:
return mod
return importlib.import_module(name)
# _require_warmed lives in src/module_loader.py to avoid duplicating the
# lookup logic across files that need heavy modules. Re-exported here so
# existing call sites and the T3.1 test (which asserts
# hasattr(src.ai_client, '_require_warmed')) continue to work.
from src.module_loader import _require_warmed # noqa: E402,F401
_provider: str = "gemini"