File size: 2,510 Bytes
728fc83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
"""
models/base_loader.py
─────────────────────
Abstract contract that every stage loader must satisfy.
Stage runners in pipeline.py interact only with this interface —
swapping models is just swapping loaders.
"""

from __future__ import annotations

import abc
import logging
from typing import Any

import torch

logger = logging.getLogger(__name__)


class BaseLoader(abc.ABC):
    """
    Lifecycle
    ---------
    1. Instantiate with model_id and kwargs.
    2. Call  .load()  once to download/initialise weights.
    3. Call  .run(**inputs)  as many times as needed.
    4. Call  .unload()  when done to free memory.
    """

    def __init__(self, model_id: str, device: torch.device, **kwargs: Any) -> None:
        self.model_id = model_id
        self.device = device
        self.kwargs = kwargs
        self._loaded = False
        self.logger = logging.getLogger(self.__class__.__name__)

    # ── Required interface ────────────────────────────────────────────────────

    @abc.abstractmethod
    def load(self) -> None:
        """Download weights and move model to self.device."""

    @abc.abstractmethod
    def run(self, **inputs: Any) -> dict[str, Any]:
        """
        Execute the model.

        Inputs and outputs are stage-specific dictionaries; see each
        concrete loader for the expected keys.
        """

    # ── Optional hooks ────────────────────────────────────────────────────────

    def unload(self) -> None:
        """Release model weights and clear GPU cache. Override if needed."""
        from utils.device import clear_cache
        for attr in ("model", "pipe", "processor", "feature_extractor"):
            if hasattr(self, attr):
                delattr(self, attr)
        self._loaded = False
        clear_cache()
        self.logger.info("Unloaded %s", self.__class__.__name__)

    def is_loaded(self) -> bool:
        return self._loaded

    # ── Helpers ───────────────────────────────────────────────────────────────

    def __repr__(self) -> str:
        return f"{self.__class__.__name__}(model_id={self.model_id!r}, device={self.device})"