Skip to content

Model Adapter Registry

The model adapter registry declares which model backends SafeLens can build, what capabilities each backend exposes, and how model cache/download plans are resolved without loading weights.

List supported adapters:

safelens models list-supported
safelens models list-supported --json
safelens models list-architectures --json
safelens models list-transformerlens --json

Inspect a model name without downloading it:

safelens inspect-model --model Qwen/Qwen3-8B
safelens inspect-model --model gpt2
safelens inspect-model --model Qwen/Qwen3-8B --json

Use it from Python:

from SafeLens.utils import get_model_adapter_registry

registry = get_model_adapter_registry()
print(registry.list_supported())
print(registry.inspect_model("Qwen/Qwen3-8B"))

Model adapter registry, capability declarations, and cache planning.

ModelAdapterCapabilities dataclass

Static capabilities declared by a model adapter.

Source code in src/SafeLens/utils/model_registry.py
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
@dataclass(frozen=True)
class ModelAdapterCapabilities:
    """Static capabilities declared by a model adapter."""

    supported_hooks: tuple[str, ...] = ()
    supported_patches: tuple[str, ...] = ()
    supports_attention_pattern: bool = False
    supports_attention_scores: bool = False
    supports_local_path: bool = False
    supports_remote_download: bool = False
    cache_policy: str = "provider-default"
    notes: tuple[str, ...] = ()

    def to_dict(self) -> dict[str, Any]:
        """Return a JSON-serializable representation."""
        return {
            "supported_hooks": list(self.supported_hooks),
            "supported_patches": list(self.supported_patches),
            "supports_attention_pattern": self.supports_attention_pattern,
            "supports_attention_scores": self.supports_attention_scores,
            "supports_local_path": self.supports_local_path,
            "supports_remote_download": self.supports_remote_download,
            "cache_policy": self.cache_policy,
            "notes": list(self.notes),
        }

to_dict()

Return a JSON-serializable representation.

Source code in src/SafeLens/utils/model_registry.py
59
60
61
62
63
64
65
66
67
68
69
70
def to_dict(self) -> dict[str, Any]:
    """Return a JSON-serializable representation."""
    return {
        "supported_hooks": list(self.supported_hooks),
        "supported_patches": list(self.supported_patches),
        "supports_attention_pattern": self.supports_attention_pattern,
        "supports_attention_scores": self.supports_attention_scores,
        "supports_local_path": self.supports_local_path,
        "supports_remote_download": self.supports_remote_download,
        "cache_policy": self.cache_policy,
        "notes": list(self.notes),
    }

ModelAdapterRegistry

Registry for model adapters keyed by model.source.

Source code in src/SafeLens/utils/model_registry.py
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
class ModelAdapterRegistry:
    """Registry for model adapters keyed by `model.source`."""

    def __init__(self) -> None:
        self._adapters: dict[str, ModelAdapterSpec] = {}
        self._aliases: dict[str, str] = {}

    def register(self, spec: ModelAdapterSpec, *, replace: bool = False) -> None:
        """Register one model adapter spec."""
        names = tuple(_normalize_source(name) for name in spec.all_names)
        for name in names:
            if not replace and (name in self._adapters or name in self._aliases):
                raise ValueError(f"Model adapter source {name!r} is already registered.")
        self._adapters[_normalize_source(spec.name)] = spec
        for alias in names[1:]:
            self._aliases[alias] = _normalize_source(spec.name)

    def get(self, source: str) -> ModelAdapterSpec:
        """Return the adapter for a `model.source` value."""
        normalized = _normalize_source(source)
        primary = self._aliases.get(normalized, normalized)
        try:
            return self._adapters[primary]
        except KeyError as exc:
            available = ", ".join(self.source_names())
            raise KeyError(
                f"Unsupported model source {source!r}. Available sources: {available}."
            ) from exc

    def source_names(self) -> list[str]:
        """Return primary source names and aliases."""
        return sorted({*self._adapters, *self._aliases})

    def list_supported(self) -> list[dict[str, Any]]:
        """Return supported adapter metadata."""
        return [
            spec.to_dict() for spec in sorted(self._adapters.values(), key=lambda item: item.name)
        ]

    def create(self, config: ModelLoadConfig) -> ModelWrapper:
        """Create a wrapper from a validated model config."""
        return self.get(config.source).build(config)

    def inspect_model(
        self,
        model_name: str,
        *,
        source: str | None = None,
        config: ModelLoadConfig | None = None,
    ) -> dict[str, Any]:
        """Inspect static adapter support for a model name without loading weights."""
        if source is not None:
            spec = self.get(source)
        else:
            spec = self._best_match(model_name)
        effective_config = config or ModelLoadConfig(source=spec.name, name=model_name)
        payload = spec.inspect(model_name, effective_config)
        payload.setdefault("model", model_name)
        payload.setdefault("source", spec.name)
        payload.setdefault("adapter", spec.to_dict())
        payload.setdefault("download_plan", resolve_model_download_plan(effective_config).to_dict())
        return payload

    def _best_match(self, model_name: str) -> ModelAdapterSpec:
        matches = [spec for spec in self._adapters.values() if spec.matches_model_name(model_name)]
        if not matches:
            if _looks_like_local_path(model_name):
                return self.get("local")
            return self.get("huggingface")
        return sorted(matches, key=lambda item: item.priority, reverse=True)[0]

create(config)

Create a wrapper from a validated model config.

Source code in src/SafeLens/utils/model_registry.py
146
147
148
def create(self, config: ModelLoadConfig) -> ModelWrapper:
    """Create a wrapper from a validated model config."""
    return self.get(config.source).build(config)

get(source)

Return the adapter for a model.source value.

Source code in src/SafeLens/utils/model_registry.py
124
125
126
127
128
129
130
131
132
133
134
def get(self, source: str) -> ModelAdapterSpec:
    """Return the adapter for a `model.source` value."""
    normalized = _normalize_source(source)
    primary = self._aliases.get(normalized, normalized)
    try:
        return self._adapters[primary]
    except KeyError as exc:
        available = ", ".join(self.source_names())
        raise KeyError(
            f"Unsupported model source {source!r}. Available sources: {available}."
        ) from exc

inspect_model(model_name, *, source=None, config=None)

Inspect static adapter support for a model name without loading weights.

Source code in src/SafeLens/utils/model_registry.py
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
def inspect_model(
    self,
    model_name: str,
    *,
    source: str | None = None,
    config: ModelLoadConfig | None = None,
) -> dict[str, Any]:
    """Inspect static adapter support for a model name without loading weights."""
    if source is not None:
        spec = self.get(source)
    else:
        spec = self._best_match(model_name)
    effective_config = config or ModelLoadConfig(source=spec.name, name=model_name)
    payload = spec.inspect(model_name, effective_config)
    payload.setdefault("model", model_name)
    payload.setdefault("source", spec.name)
    payload.setdefault("adapter", spec.to_dict())
    payload.setdefault("download_plan", resolve_model_download_plan(effective_config).to_dict())
    return payload

list_supported()

Return supported adapter metadata.

Source code in src/SafeLens/utils/model_registry.py
140
141
142
143
144
def list_supported(self) -> list[dict[str, Any]]:
    """Return supported adapter metadata."""
    return [
        spec.to_dict() for spec in sorted(self._adapters.values(), key=lambda item: item.name)
    ]

register(spec, *, replace=False)

Register one model adapter spec.

Source code in src/SafeLens/utils/model_registry.py
114
115
116
117
118
119
120
121
122
def register(self, spec: ModelAdapterSpec, *, replace: bool = False) -> None:
    """Register one model adapter spec."""
    names = tuple(_normalize_source(name) for name in spec.all_names)
    for name in names:
        if not replace and (name in self._adapters or name in self._aliases):
            raise ValueError(f"Model adapter source {name!r} is already registered.")
    self._adapters[_normalize_source(spec.name)] = spec
    for alias in names[1:]:
        self._aliases[alias] = _normalize_source(spec.name)

source_names()

Return primary source names and aliases.

Source code in src/SafeLens/utils/model_registry.py
136
137
138
def source_names(self) -> list[str]:
    """Return primary source names and aliases."""
    return sorted({*self._adapters, *self._aliases})

ModelAdapterSpec dataclass

Registered model adapter entry.

Source code in src/SafeLens/utils/model_registry.py
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
@dataclass(frozen=True)
class ModelAdapterSpec:
    """Registered model adapter entry."""

    name: str
    display_name: str
    aliases: tuple[str, ...]
    description: str
    capabilities: ModelAdapterCapabilities
    build: AdapterBuilder
    inspect: AdapterInspector
    matches_model_name: ModelNameMatcher = lambda _model_name: False
    dependencies: tuple[str, ...] = ()
    model_name_patterns: tuple[str, ...] = ()
    priority: int = 0

    @property
    def all_names(self) -> tuple[str, ...]:
        """Return primary name and aliases."""
        return (self.name, *self.aliases)

    def to_dict(self) -> dict[str, Any]:
        """Return public metadata for CLI and docs."""
        return {
            "name": self.name,
            "display_name": self.display_name,
            "aliases": list(self.aliases),
            "description": self.description,
            "dependencies": list(self.dependencies),
            "model_name_patterns": list(self.model_name_patterns),
            "capabilities": self.capabilities.to_dict(),
        }

all_names property

Return primary name and aliases.

to_dict()

Return public metadata for CLI and docs.

Source code in src/SafeLens/utils/model_registry.py
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def to_dict(self) -> dict[str, Any]:
    """Return public metadata for CLI and docs."""
    return {
        "name": self.name,
        "display_name": self.display_name,
        "aliases": list(self.aliases),
        "description": self.description,
        "dependencies": list(self.dependencies),
        "model_name_patterns": list(self.model_name_patterns),
        "capabilities": self.capabilities.to_dict(),
    }

ModelDownloadPlan dataclass

Resolved loading and cache plan for one model backend.

Source code in src/SafeLens/utils/model_registry.py
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
@dataclass(frozen=True)
class ModelDownloadPlan:
    """Resolved loading and cache plan for one model backend."""

    source: str
    model_name: str
    pretrained_path: str
    cache_dir: str | None = None
    local_dir: str | None = None
    revision: str | None = None
    uses_network: bool = False
    provider: str = "local"

    def to_dict(self) -> dict[str, Any]:
        """Return a JSON-serializable representation."""
        return {
            "source": self.source,
            "model_name": self.model_name,
            "pretrained_path": self.pretrained_path,
            "cache_dir": self.cache_dir,
            "local_dir": self.local_dir,
            "revision": self.revision,
            "uses_network": self.uses_network,
            "provider": self.provider,
        }

to_dict()

Return a JSON-serializable representation.

Source code in src/SafeLens/utils/model_registry.py
32
33
34
35
36
37
38
39
40
41
42
43
def to_dict(self) -> dict[str, Any]:
    """Return a JSON-serializable representation."""
    return {
        "source": self.source,
        "model_name": self.model_name,
        "pretrained_path": self.pretrained_path,
        "cache_dir": self.cache_dir,
        "local_dir": self.local_dir,
        "revision": self.revision,
        "uses_network": self.uses_network,
        "provider": self.provider,
    }

get_model_adapter_registry()

Return the built-in model adapter registry.

Source code in src/SafeLens/utils/model_registry.py
182
183
184
def get_model_adapter_registry() -> ModelAdapterRegistry:
    """Return the built-in model adapter registry."""
    return BUILTIN_MODEL_ADAPTER_REGISTRY

resolve_model_download_plan(config)

Resolve a unified provider/cache plan without downloading the model.

Source code in src/SafeLens/utils/model_registry.py
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
def resolve_model_download_plan(config: ModelLoadConfig) -> ModelDownloadPlan:
    """Resolve a unified provider/cache plan without downloading the model."""
    source = _normalize_source(config.source)
    if source in {"dummy", "mock", "none"} or config.name.lower() in {"dummy", "mock", "none"}:
        return ModelDownloadPlan(
            source="dummy",
            model_name=config.name,
            pretrained_path=config.name,
            uses_network=False,
            provider="memory",
        )
    if source == "local":
        pretrained_path = str(Path(config.local_dir or config.name).expanduser())
        return ModelDownloadPlan(
            source="local",
            model_name=config.name,
            pretrained_path=pretrained_path,
            local_dir=pretrained_path,
            revision=config.revision,
            uses_network=False,
            provider="local",
        )
    if source in {"huggingface", "hf", "qwen3", "qwen3_dense", "qwen3-dense"}:
        primary_source = "qwen3_dense" if source in {"qwen3", "qwen3-dense"} else source
        provider_cache = _cache_dir(config, "huggingface")
        return ModelDownloadPlan(
            source=primary_source,
            model_name=config.name,
            pretrained_path=config.name,
            cache_dir=provider_cache,
            revision=config.revision,
            uses_network=True,
            provider="huggingface",
        )
    if source in {"transformer_lens", "transformerlens", "tl", "hooked_transformer"}:
        from SafeLens.utils.transformer_lens_support import (
            resolve_transformer_lens_compatible_model_name,
        )

        if config.local_dir is not None or _looks_like_local_path(config.name):
            pretrained_path = str(Path(config.local_dir or config.name).expanduser())
            return ModelDownloadPlan(
                source="transformer_lens",
                model_name=config.name,
                pretrained_path=pretrained_path,
                local_dir=pretrained_path,
                revision=config.revision,
                uses_network=False,
                provider="local",
            )
        return ModelDownloadPlan(
            source="transformer_lens",
            model_name=config.name,
            pretrained_path=resolve_transformer_lens_compatible_model_name(config.name),
            cache_dir=_cache_dir(config, "transformer_lens_compatible"),
            revision=config.revision,
            uses_network=True,
            provider="huggingface",
        )
    if source in {"modelscope", "ms"}:
        return ModelDownloadPlan(
            source="modelscope",
            model_name=config.name,
            pretrained_path=config.local_dir or config.name,
            cache_dir=_cache_dir(config, "modelscope"),
            local_dir=config.local_dir,
            revision=config.revision,
            uses_network=True,
            provider="modelscope",
        )
    return ModelDownloadPlan(
        source=source,
        model_name=config.name,
        pretrained_path=config.name,
        cache_dir=config.cache_dir,
        local_dir=config.local_dir,
        revision=config.revision,
        uses_network=True,
        provider=source,
    )