Coverage for transformer_lens/model_bridge/sources/transformers/helpers.py: 67%

108 statements  

« prev     ^ index     » next       coverage.py v7.10.1, created at 2026-09-21 19:27 +0000

1"""HF-loader-specific helpers: model-class selection, modality processor loading, checkpoint revision resolution, registry discovery.""" 

2from __future__ import annotations 

3 

4from typing import Any 

5 

6import transformers 

7from transformers import ( 

8 AutoModelForCausalLM, 

9 AutoModelForMaskedLM, 

10 AutoModelForSeq2SeqLM, 

11) 

12 

13from transformer_lens.tools.model_registry.checkpoints import get_checkpoint_labels 

14 

15 

16def get_hf_model_class_for_architecture(architecture: str): 

17 """Pick the correct HuggingFace ``AutoModel*`` class for the architecture.""" 

18 from transformer_lens.utilities.architectures import ( 

19 AUDIO_ARCHITECTURES, 

20 AUDIO_CLASSIFICATION_ARCHITECTURES, 

21 AUDIO_TEXT_ARCHITECTURES, 

22 BASE_AUTOMODEL_ARCHITECTURES, 

23 MASKED_LM_ARCHITECTURES, 

24 MULTIMODAL_ARCHITECTURES, 

25 SEQ2SEQ_ARCHITECTURES, 

26 VISION_ARCHITECTURES, 

27 VISION_CLASSIFICATION_ARCHITECTURES, 

28 ) 

29 

30 if architecture in SEQ2SEQ_ARCHITECTURES or architecture in AUDIO_TEXT_ARCHITECTURES: 

31 return AutoModelForSeq2SeqLM 

32 elif architecture in MASKED_LM_ARCHITECTURES: 

33 return AutoModelForMaskedLM 

34 elif architecture in MULTIMODAL_ARCHITECTURES: 

35 from transformers import AutoModelForImageTextToText 

36 

37 return AutoModelForImageTextToText 

38 elif architecture in BASE_AUTOMODEL_ARCHITECTURES: 38 ↛ 39line 38 didn't jump to line 39 because the condition on line 38 was never true

39 from transformers import AutoModel 

40 

41 return AutoModel 

42 elif architecture in AUDIO_CLASSIFICATION_ARCHITECTURES: 

43 from transformers import AutoModelForAudioClassification 

44 

45 return AutoModelForAudioClassification 

46 elif architecture in AUDIO_ARCHITECTURES: 

47 if "ForCTC" in architecture: 47 ↛ 48line 47 didn't jump to line 48 because the condition on line 47 was never true

48 from transformers import AutoModelForCTC 

49 

50 return AutoModelForCTC 

51 from transformers import AutoModel 

52 

53 return AutoModel 

54 elif architecture in VISION_ARCHITECTURES: 

55 if architecture in VISION_CLASSIFICATION_ARCHITECTURES: 

56 from transformers import AutoModelForImageClassification 

57 

58 return AutoModelForImageClassification 

59 from transformers import AutoModel 

60 

61 return AutoModel 

62 else: 

63 return AutoModelForCausalLM 

64 

65 

66# Modality flag → HF auto-loader class, applied in order. Last write wins on 

67# bridge.processor: multimodal → audio → vision. The flags are disjoint today 

68# (vit.py is the only is_visual_model adapter and it doesn't set is_multimodal), 

69# but the order must be preserved for future dual-flag adapters. 

70_MODALITY_PROCESSOR_LOADERS: list[tuple[str, str]] = [ 

71 ("is_multimodal", "AutoProcessor"), 

72 ("is_audio_model", "AutoFeatureExtractor"), 

73 ("is_visual_model", "AutoImageProcessor"), 

74] 

75 

76 

77def _ensure_torchvision() -> bool: 

78 """Import torchvision, installing it on the fly if missing; True when importable.""" 

79 try: 

80 import torchvision # noqa: F401 

81 

82 return True 

83 except Exception: 

84 pass 

85 import importlib 

86 import shutil 

87 import subprocess 

88 import sys 

89 

90 try: 

91 if shutil.which("uv"): 

92 subprocess.check_call(["uv", "pip", "install", "torchvision", "-q"]) 

93 else: 

94 subprocess.check_call([sys.executable, "-m", "pip", "install", "torchvision", "-q"]) 

95 importlib.invalidate_caches() 

96 return True 

97 except Exception: 

98 return False 

99 

100 

101def load_modality_processor( 

102 bridge: Any, 

103 cfg: Any, 

104 model_name: str, 

105 trust_remote_code: bool, 

106 token: str | None, 

107) -> None: 

108 """Attach the modality preprocessor to ``bridge.processor`` per the cfg's modality flags. 

109 

110 Best-effort: each loader failure is swallowed so a missing/broken processor 

111 never blocks the boot itself. 

112 """ 

113 for cfg_flag, loader_name in _MODALITY_PROCESSOR_LOADERS: 

114 if not getattr(cfg, cfg_flag, False): 

115 continue 

116 try: 

117 loader = getattr(transformers, loader_name) 

118 bridge.processor = loader.from_pretrained( 

119 model_name, 

120 token=token, 

121 trust_remote_code=trust_remote_code, 

122 ) 

123 except Exception: 

124 # Some AutoProcessors need torchvision (e.g. LlavaOnevision); install and retry. 

125 if loader_name != "AutoProcessor" or not _ensure_torchvision(): 

126 continue 

127 try: 

128 bridge.processor = transformers.AutoProcessor.from_pretrained( 

129 model_name, 

130 token=token, 

131 trust_remote_code=trust_remote_code, 

132 ) 

133 except Exception: 

134 pass 

135 

136 

137# Known training-checkpoint revision conventions on HF Hub. 

138_CHECKPOINT_REVISION_FORMATS: dict[str, str] = { 

139 "EleutherAI/pythia": "step{value}", 

140 "stanford-crfm": "checkpoint-{value}", 

141} 

142 

143 

144def _resolve_checkpoint_to_revision( 

145 model_name: str, 

146 checkpoint_index: int | None, 

147 checkpoint_value: int | None, 

148) -> str: 

149 """Convert a checkpoint index/value into an HF revision string, validated against ``get_checkpoint_labels``.""" 

150 if checkpoint_index is None and checkpoint_value is None: 

151 raise ValueError("Must specify either checkpoint_index or checkpoint_value.") 

152 

153 format_str: str | None = None 

154 for prefix, fmt in _CHECKPOINT_REVISION_FORMATS.items(): 

155 if model_name.startswith(prefix): 

156 format_str = fmt 

157 break 

158 if format_str is None: 

159 raise ValueError( 

160 f"Model {model_name!r} does not have a known checkpoint revision convention. " 

161 f"Pass revision= directly if your model uses HF revisions. Known checkpoint " 

162 f"families: {list(_CHECKPOINT_REVISION_FORMATS.keys())}." 

163 ) 

164 

165 labels, _ = get_checkpoint_labels(model_name) 

166 if checkpoint_value is not None: 

167 if checkpoint_value not in labels: 

168 raise ValueError( 

169 f"checkpoint_value={checkpoint_value} not in available checkpoints for " 

170 f"{model_name!r}. {len(labels)} labels available, " 

171 f"first/last: {labels[0]}..{labels[-1]}." 

172 ) 

173 else: 

174 assert checkpoint_index is not None # narrowed by initial guard 

175 # Negative indices count from the end, matching the legacy loader. 

176 if not -len(labels) <= checkpoint_index < len(labels): 

177 raise ValueError( 

178 f"checkpoint_index={checkpoint_index} out of range " 

179 f"[-{len(labels)}, {len(labels)}) for {model_name!r}." 

180 ) 

181 checkpoint_value = labels[checkpoint_index] 

182 return format_str.format(value=checkpoint_value) 

183 

184 

185def list_supported_models( 

186 architecture: str | None = None, 

187 verified_only: bool = False, 

188) -> list[str]: 

189 """List all models supported by TransformerLens. 

190 

191 Args: 

192 architecture: Filter by architecture ID (e.g., "GPT2LMHeadModel"). 

193 verified_only: If True, only return verified-to-work models. 

194 

195 Returns: 

196 List of model IDs. 

197 """ 

198 try: 

199 from transformer_lens.tools.model_registry import api 

200 

201 models = api.get_supported_models(architecture=architecture, verified_only=verified_only) 

202 return [m.model_id for m in models] 

203 except ImportError: 

204 return [] 

205 except Exception: 

206 return [] 

207 

208 

209def check_model_support(model_id: str) -> dict: 

210 """Detailed support info for a model: ``is_supported``, ``architecture_id``, ``verified``, ``suggestion``.""" 

211 try: 

212 from transformer_lens.tools.model_registry import api 

213 

214 is_supported = api.is_model_supported(model_id) 

215 

216 if is_supported: 

217 model_info = api.get_model_info(model_id) 

218 return { 

219 "is_supported": True, 

220 "architecture_id": model_info.architecture_id, 

221 "status": model_info.status, 

222 "verified_date": ( 

223 model_info.verified_date.isoformat() if model_info.verified_date else None 

224 ), 

225 "suggestion": None, 

226 } 

227 else: 

228 suggestion = api.suggest_similar_model(model_id) 

229 return { 

230 "is_supported": False, 

231 "architecture_id": None, 

232 "verified": False, 

233 "verified_date": None, 

234 "suggestion": suggestion, 

235 } 

236 except ImportError: 

237 return { 

238 "is_supported": None, 

239 "architecture_id": None, 

240 "verified": False, 

241 "verified_date": None, 

242 "suggestion": None, 

243 "error": "Model registry not available", 

244 } 

245 except Exception as e: 

246 return { 

247 "is_supported": None, 

248 "architecture_id": None, 

249 "verified": False, 

250 "verified_date": None, 

251 "suggestion": None, 

252 "error": str(e), 

253 }