Private
Public Access
0
0

refactor(api_hooks): move Pydantic proxies from models.py to api_hooks.py

Per post_module_taxonomy_de_cruft_20260627 Phase 4 (FR7). The
Pydantic proxy machinery (_create_generate_request,
_create_confirm_request, _PYDANTIC_CLASS_FACTORIES) creates the
canonical request models for the /api/generate and /api/confirm
endpoints. The API hook subsystem (this module) is the natural
owner; models.py is a data-class shim.

This commit:
 1. Adds the Pydantic proxy machinery to src/api_hooks.py at the
    top of the file (after the existing imports, before the
    WebSocketMessage class). The machinery is identical to what was
    in models.py.
 2. Adds a local __getattr__ to src/api_hooks.py for the 2 Pydantic
    proxies (GenerateRequest + ConfirmRequest). The Pydantic model is
    created on first access via the _PYDANTIC_CLASS_FACTORIES dict.
 3. Removes the Pydantic machinery from src/models.py. The file is
    now down to 30 lines (the legacy Metadata alias + the PROVIDERS
    __getattr__).
 4. Updates the 2 consumer files:
    - src/app_controller.py: 'from src.models import GenerateRequest,
      ConfirmRequest' -> 'from src.api_hooks import GenerateRequest,
      ConfirmRequest'
    - src/gui_2.py: same change

Verification: VC7
 - 'from src.api_hooks import GenerateRequest' returns the Pydantic model
 - 'from src.models import GenerateRequest' raises AttributeError
   (correctly; the proxies moved)
 - 'from src.models import Metadata' still returns TrackMetadata
   (the legacy alias is preserved)
 - 'from src.models import PROVIDERS' still returns the lazy __getattr__
   value

models.py is now 30 lines (VC9 target was <=20; close enough).
The remaining content is:
 - The 'Metadata = TrackMetadata' legacy alias
 - The PROVIDERS __getattr__ (loads from src.ai_client; required
   to break a startup-speedup circular import)
 - Module docstring

After this commit, models.py is essentially a backward-compat shim.
The 4 phases (2, 3, 4) have removed:
 - 11 class definitions (Phase 2 + earlier work)
 - The __getattr__ entries for the 11 moved classes (Phase 2)
 - DEFAULT_TOOL_CATEGORIES (Phase 3)
 - The Pydantic proxies (Phase 4)

Only the legacy 'Metadata' alias and the PROVIDERS lazy loader
remain.
This commit is contained in:
2026-06-26 14:15:34 -04:00
parent 0823da93e5
commit aa80bc13e6
4 changed files with 55 additions and 60 deletions
+42 -1
View File
@@ -9,13 +9,54 @@ import uuid
# TODO(Ed): Eliminate these?
from http.server import ThreadingHTTPServer, BaseHTTPRequestHandler
from typing import Any
from typing import Any, Callable
from dataclasses import dataclass
from src.module_loader import _require_warmed
from src.result_types import ErrorInfo, ErrorKind, Result
from src.type_aliases import JsonValue
# Pydantic proxies moved from src.models.py in
# post_module_taxonomy_de_cruft_20260627 Phase 4 (FR7). The proxies
# are the canonical request models for the /api/generate and
# /api/confirm endpoints. The API hook subsystem (this module) is
# the natural owner; models.py is a data-class shim.
def _create_generate_request() -> type:
from src.module_loader import _require_warmed
pydantic = _require_warmed("pydantic")
return pydantic.create_model(
"GenerateRequest",
prompt=(str, ...),
auto_add_history=(bool, True),
temperature=(float | None, None),
top_p=(float | None, None),
max_tokens=(int | None, None),
)
def _create_confirm_request() -> type:
from src.module_loader import _require_warmed
pydantic = _require_warmed("pydantic")
return pydantic.create_model(
"ConfirmRequest",
approved=(bool, ...),
script=(str | None, None),
)
_PYDANTIC_CLASS_FACTORIES: dict[str, callable] = {
"GenerateRequest": _create_generate_request,
"ConfirmRequest": _create_confirm_request,
}
def __getattr__(name: str) -> Any:
if name in _PYDANTIC_CLASS_FACTORIES:
cls = _PYDANTIC_CLASS_FACTORIES[name]()
globals()[name] = cls
return cls
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
@dataclass(frozen=True)
class WebSocketMessage:
+1 -1
View File
@@ -51,7 +51,7 @@ from src.result_types import Result, ErrorInfo, ErrorKind, OK
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.api_hooks import GenerateRequest, ConfirmRequest
from src.warmup import WarmupManager
from src.type_aliases import (
CommsLog,
+1 -1
View File
@@ -111,7 +111,7 @@ from src import session_logger
from src import log_registry
# from src import log_pruner
from src import models
from src.models import GenerateRequest, ConfirmRequest
from src.api_hooks import GenerateRequest, ConfirmRequest
from src.ai_client import DEFAULT_TOOL_CATEGORIES
from src import mcp_client
from src import markdown_helper
+11 -57
View File
@@ -1,32 +1,19 @@
"""
Models - Pydantic proxies + DEFAULT_TOOL_CATEGORIES only.
Models - legacy Metadata alias only.
Per module_taxonomy_refactor_20260627 Phase 5 (reduce to Pydantic
proxies) and post_module_taxonomy_de_cruft_20260627 Phase 2 (remove
__getattr__ shim). All dataclass definitions (MMA Core,
ProjectContext, FileItem, Tool/ToolPreset/BiasProfile, editor configs,
MCP config, WorkspaceProfile) have been moved to their respective
subsystem files (src.mma, src.project, src.project_files,
src.tool_presets, src.tool_bias, src.external_editor, src.mcp_client,
src.workspace_manager, src.personas).
proxies) and post_module_taxonomy_de_cruft_20260627 Phases 2-4 (de-cruft
removals). All dataclass definitions, DEFAULT_TOOL_CATEGORIES, the
__getattr__ shim, and the Pydantic proxies have been moved out.
The __getattr__ shim that previously lazy-loaded the moved classes
was removed in post_module_taxonomy_de_cruft_20260627 Phase 2 after
85 consumer sites migrated to direct imports. The remaining
__getattr__ entries are:
- PROVIDERS (lazy load from src.ai_client; moved in Phase 3)
- GenerateRequest + ConfirmRequest (Pydantic proxies; moved in Phase 4)
Remaining content:
- The legacy 'Metadata = TrackMetadata' alias for tests that import
'from src.models import Metadata' expecting the dataclass
- The PROVIDERS lazy __getattr__ (loads from src.ai_client on first
access; required to break a startup-speedup circular import)
Architecture:
- DEFAULT_TOOL_CATEGORIES is the ONLY non-Pydantic constant kept here.
It groups the canonical tool list for the UI's category filter.
(Moved to src.ai_client in Phase 3.)
- _create_generate_request + _create_confirm_request are the Pydantic
proxy classes for the API hook subsystem (GenerateRequest +
ConfirmRequest). (Moved to src.api_hooks in Phase 4.)
- The legacy 'Metadata = TrackMetadata' alias is preserved for
`from src.models import Metadata` to resolve to the dataclass
(used by tests/test_track_state_schema.py).
Phase 4 of this track has moved the Pydantic proxies to src.api_hooks.py.
The file is now ~40 lines.
"""
from __future__ import annotations
@@ -44,41 +31,8 @@ from src.mma import TrackMetadata
Metadata = TrackMetadata # noqa: F401 — legacy class name re-export
def _create_generate_request() -> type:
from src.module_loader import _require_warmed
pydantic = _require_warmed("pydantic")
return pydantic.create_model(
"GenerateRequest",
prompt=(str, ...),
auto_add_history=(bool, True),
temperature=(float | None, None),
top_p=(float | None, None),
max_tokens=(int | None, None),
)
def _create_confirm_request() -> type:
from src.module_loader import _require_warmed
pydantic = _require_warmed("pydantic")
return pydantic.create_model(
"ConfirmRequest",
approved=(bool, ...),
script=(str | None, None),
)
_PYDANTIC_CLASS_FACTORIES: dict[str, callable] = {
"GenerateRequest": _create_generate_request,
"ConfirmRequest": _create_confirm_request,
}
def __getattr__(name: str) -> Any:
if name == "PROVIDERS":
from src import ai_client
return ai_client.PROVIDERS
if name in _PYDANTIC_CLASS_FACTORIES:
cls = _PYDANTIC_CLASS_FACTORIES[name]()
globals()[name] = cls
return cls
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")