hyper-models 0.3.1__tar.gz → 0.3.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. {hyper_models-0.3.1 → hyper_models-0.3.3}/PKG-INFO +10 -5
  2. {hyper_models-0.3.1 → hyper_models-0.3.3}/README.md +9 -4
  3. {hyper_models-0.3.1 → hyper_models-0.3.3}/pyproject.toml +1 -1
  4. {hyper_models-0.3.1 → hyper_models-0.3.3}/src/hyper3_clip/models/encoders.py +21 -4
  5. {hyper_models-0.3.1 → hyper_models-0.3.3}/src/hyper3_clip/models/hyper3_clip.py +7 -1
  6. {hyper_models-0.3.1 → hyper_models-0.3.3}/src/hyper_models/preprocessing.py +13 -8
  7. {hyper_models-0.3.1 → hyper_models-0.3.3}/src/hyper_models/registry.py +20 -6
  8. {hyper_models-0.3.1 → hyper_models-0.3.3}/src/hyper_models/torch_models.py +45 -5
  9. {hyper_models-0.3.1 → hyper_models-0.3.3}/.gitignore +0 -0
  10. {hyper_models-0.3.1 → hyper_models-0.3.3}/LICENSE +0 -0
  11. {hyper_models-0.3.1 → hyper_models-0.3.3}/src/hyper3_clip/__init__.py +0 -0
  12. {hyper_models-0.3.1 → hyper_models-0.3.3}/src/hyper3_clip/models/__init__.py +0 -0
  13. {hyper_models-0.3.1 → hyper_models-0.3.3}/src/hyper3_clip/models/experimental.py +0 -0
  14. {hyper_models-0.3.1 → hyper_models-0.3.3}/src/hyper3_clip/models/himo.py +0 -0
  15. {hyper_models-0.3.1 → hyper_models-0.3.3}/src/hyper3_clip/models/lorentz.py +0 -0
  16. {hyper_models-0.3.1 → hyper_models-0.3.3}/src/hyper3_clip/models/losses.py +0 -0
  17. {hyper_models-0.3.1 → hyper_models-0.3.3}/src/hyper3_clip/models/objectives.py +0 -0
  18. {hyper_models-0.3.1 → hyper_models-0.3.3}/src/hyper3_clip/models/tren.py +0 -0
  19. {hyper_models-0.3.1 → hyper_models-0.3.3}/src/hyper3_clip/training/__init__.py +0 -0
  20. {hyper_models-0.3.1 → hyper_models-0.3.3}/src/hyper3_clip/training/distributed.py +0 -0
  21. {hyper_models-0.3.1 → hyper_models-0.3.3}/src/hyper_models/__init__.py +0 -0
  22. {hyper_models-0.3.1 → hyper_models-0.3.3}/src/hyper_models/loader.py +0 -0
  23. {hyper_models-0.3.1 → hyper_models-0.3.3}/src/hyper_models/loaders.py +0 -0
  24. {hyper_models-0.3.1 → hyper_models-0.3.3}/src/hyper_models/models.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: hyper-models
3
- Version: 0.3.1
3
+ Version: 0.3.3
4
4
  Summary: A model zoo for non-Euclidean embedding models (hyperbolic, spherical, product manifolds)
5
5
  Project-URL: Homepage, https://github.com/Hyper3Labs/hyper-models
6
6
  Project-URL: Repository, https://github.com/Hyper3Labs/hyper-models
@@ -87,7 +87,7 @@ from PIL import Image
87
87
 
88
88
  # List available models
89
89
  hyper_models.list_models()
90
- # ['hycoclip-vit-s', 'hycoclip-vit-b', 'meru-vit-s', 'meru-vit-b', 'uncha-vit-s', 'uncha-vit-b', 'hyper3-clip-v0.5']
90
+ # ['hycoclip-vit-s', 'hycoclip-vit-b', 'meru-vit-s', 'meru-vit-b', 'uncha-vit-s', 'uncha-vit-b', 'hyper3-clip-v1']
91
91
 
92
92
  # Inspect supported internal loader kinds
93
93
  hyper_models.list_loaders()
@@ -130,12 +130,17 @@ This keeps callers on one stable API:
130
130
  ```python
131
131
  model = hyper_models.load("hycoclip-vit-s")
132
132
  model = hyper_models.load("uncha-vit-b")
133
- model = hyper_models.load("hyper3-clip-v0.5")
133
+ model = hyper_models.load("hyper3-clip-v1")
134
134
  ```
135
135
 
136
136
  Callers do not need to know which internal loader is used, except for optional
137
137
  dependency installation when choosing entries that need `hyper-models[ml]`.
138
138
 
139
+ For `hyper3-clip-v1`, `encode_images(images)` and `encode_texts(texts)` return
140
+ 513-coordinate Lorentz embeddings in the same space. The loader downloads the
141
+ model's runtime configuration, weights, and tokenizer together. Complete the
142
+ model's Hugging Face access form and run `hf auth login` before the first download.
143
+
139
144
  ### HyperView integration
140
145
 
141
146
  HyperView auto-detects `hyper-models` names and routes them to the `hyper-models` provider.
@@ -159,7 +164,7 @@ HyperView's simple path remains torch-free. If you use the default ONNX-backed
159
164
  `hyper-models` entries or the default `embed-anything` provider, HyperView does
160
165
  not need PyTorch. PyTorch is only needed when you explicitly select a
161
166
  torch-backed catalog entry such as `uncha-vit-s`, `uncha-vit-b`, or
162
- `hyper3-clip-v0.5`.
167
+ `hyper3-clip-v1`.
163
168
 
164
169
  ## Models
165
170
 
@@ -173,7 +178,7 @@ torch-backed catalog entry such as `uncha-vit-s`, `uncha-vit-b`, or
173
178
  | `meru-vit-b` | [![HF](https://img.shields.io/badge/🤗-HuggingFace-yellow)](https://huggingface.co/mnm-matin/hyperbolic-clip/tree/main/meru-vit-b) | [ICML 2023](https://arxiv.org/abs/2304.09172) | [facebookresearch/meru](https://github.com/facebookresearch/meru) |
174
179
  | `uncha-vit-s` | [![HF](https://img.shields.io/badge/🤗-HuggingFace-yellow)](https://huggingface.co/hayeonkim/uncha/blob/main/uncha_vit_s.pth) | [CVPR 2026](https://arxiv.org/abs/2603.22042) | [jeeit17/UNCHA](https://github.com/jeeit17/UNCHA) |
175
180
  | `uncha-vit-b` | [![HF](https://img.shields.io/badge/🤗-HuggingFace-yellow)](https://huggingface.co/hayeonkim/uncha/blob/main/uncha_vit_b.pth) | [CVPR 2026](https://arxiv.org/abs/2603.22042) | [jeeit17/UNCHA](https://github.com/jeeit17/UNCHA) |
176
- | `hyper3-clip-v0.5` | [![HF](https://img.shields.io/badge/🤗-HuggingFace-yellow)](https://huggingface.co/hyper3labs/hyper3-clip-v0.5) | — | [Hyper3Labs/hyper3-clip](https://github.com/Hyper3Labs/hyper3-clip) |
181
+ | `hyper3-clip-v1` | [![HF](https://img.shields.io/badge/🤗-HuggingFace-yellow)](https://huggingface.co/hyper3labs/hyper3-clip-v1) | — | [Hyper3Labs/hyper3-clip](https://github.com/Hyper3Labs/hyper3-clip) |
177
182
  | `hyp-vit` | — | [CVPR 2022](https://arxiv.org/abs/2203.10833) | [htdt/hyp_metric](https://github.com/htdt/hyp_metric) |
178
183
  | `hie` | — | [CVPR 2020](https://arxiv.org/abs/1904.02239) | [leymir/hyperbolic-image-embeddings](https://github.com/leymir/hyperbolic-image-embeddings) |
179
184
  | `hcnn` | — | [ICLR 2024](https://openreview.net/forum?id=ekz1hN5QNh) | [kschwethelm/HyperbolicCV](https://github.com/kschwethelm/HyperbolicCV) |
@@ -46,7 +46,7 @@ from PIL import Image
46
46
 
47
47
  # List available models
48
48
  hyper_models.list_models()
49
- # ['hycoclip-vit-s', 'hycoclip-vit-b', 'meru-vit-s', 'meru-vit-b', 'uncha-vit-s', 'uncha-vit-b', 'hyper3-clip-v0.5']
49
+ # ['hycoclip-vit-s', 'hycoclip-vit-b', 'meru-vit-s', 'meru-vit-b', 'uncha-vit-s', 'uncha-vit-b', 'hyper3-clip-v1']
50
50
 
51
51
  # Inspect supported internal loader kinds
52
52
  hyper_models.list_loaders()
@@ -89,12 +89,17 @@ This keeps callers on one stable API:
89
89
  ```python
90
90
  model = hyper_models.load("hycoclip-vit-s")
91
91
  model = hyper_models.load("uncha-vit-b")
92
- model = hyper_models.load("hyper3-clip-v0.5")
92
+ model = hyper_models.load("hyper3-clip-v1")
93
93
  ```
94
94
 
95
95
  Callers do not need to know which internal loader is used, except for optional
96
96
  dependency installation when choosing entries that need `hyper-models[ml]`.
97
97
 
98
+ For `hyper3-clip-v1`, `encode_images(images)` and `encode_texts(texts)` return
99
+ 513-coordinate Lorentz embeddings in the same space. The loader downloads the
100
+ model's runtime configuration, weights, and tokenizer together. Complete the
101
+ model's Hugging Face access form and run `hf auth login` before the first download.
102
+
98
103
  ### HyperView integration
99
104
 
100
105
  HyperView auto-detects `hyper-models` names and routes them to the `hyper-models` provider.
@@ -118,7 +123,7 @@ HyperView's simple path remains torch-free. If you use the default ONNX-backed
118
123
  `hyper-models` entries or the default `embed-anything` provider, HyperView does
119
124
  not need PyTorch. PyTorch is only needed when you explicitly select a
120
125
  torch-backed catalog entry such as `uncha-vit-s`, `uncha-vit-b`, or
121
- `hyper3-clip-v0.5`.
126
+ `hyper3-clip-v1`.
122
127
 
123
128
  ## Models
124
129
 
@@ -132,7 +137,7 @@ torch-backed catalog entry such as `uncha-vit-s`, `uncha-vit-b`, or
132
137
  | `meru-vit-b` | [![HF](https://img.shields.io/badge/🤗-HuggingFace-yellow)](https://huggingface.co/mnm-matin/hyperbolic-clip/tree/main/meru-vit-b) | [ICML 2023](https://arxiv.org/abs/2304.09172) | [facebookresearch/meru](https://github.com/facebookresearch/meru) |
133
138
  | `uncha-vit-s` | [![HF](https://img.shields.io/badge/🤗-HuggingFace-yellow)](https://huggingface.co/hayeonkim/uncha/blob/main/uncha_vit_s.pth) | [CVPR 2026](https://arxiv.org/abs/2603.22042) | [jeeit17/UNCHA](https://github.com/jeeit17/UNCHA) |
134
139
  | `uncha-vit-b` | [![HF](https://img.shields.io/badge/🤗-HuggingFace-yellow)](https://huggingface.co/hayeonkim/uncha/blob/main/uncha_vit_b.pth) | [CVPR 2026](https://arxiv.org/abs/2603.22042) | [jeeit17/UNCHA](https://github.com/jeeit17/UNCHA) |
135
- | `hyper3-clip-v0.5` | [![HF](https://img.shields.io/badge/🤗-HuggingFace-yellow)](https://huggingface.co/hyper3labs/hyper3-clip-v0.5) | — | [Hyper3Labs/hyper3-clip](https://github.com/Hyper3Labs/hyper3-clip) |
140
+ | `hyper3-clip-v1` | [![HF](https://img.shields.io/badge/🤗-HuggingFace-yellow)](https://huggingface.co/hyper3labs/hyper3-clip-v1) | — | [Hyper3Labs/hyper3-clip](https://github.com/Hyper3Labs/hyper3-clip) |
136
141
  | `hyp-vit` | — | [CVPR 2022](https://arxiv.org/abs/2203.10833) | [htdt/hyp_metric](https://github.com/htdt/hyp_metric) |
137
142
  | `hie` | — | [CVPR 2020](https://arxiv.org/abs/1904.02239) | [leymir/hyperbolic-image-embeddings](https://github.com/leymir/hyperbolic-image-embeddings) |
138
143
  | `hcnn` | — | [ICLR 2024](https://openreview.net/forum?id=ekz1hN5QNh) | [kschwethelm/HyperbolicCV](https://github.com/kschwethelm/HyperbolicCV) |
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "hyper-models"
3
- version = "0.3.1"
3
+ version = "0.3.3"
4
4
  description = "A model zoo for non-Euclidean embedding models (hyperbolic, spherical, product manifolds)"
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -10,6 +10,7 @@ from transformers import (
10
10
  CLIPTextConfig,
11
11
  CLIPTextModel,
12
12
  CLIPTextModelWithProjection,
13
+ CLIPTokenizerFast,
13
14
  CLIPVisionConfig,
14
15
  CLIPVisionModel,
15
16
  CLIPVisionModelWithProjection,
@@ -99,7 +100,14 @@ class VisionEncoder(nn.Module):
99
100
 
100
101
 
101
102
  class TextEncoder(nn.Module):
102
- def __init__(self, model_name: str, pretrained: bool = True, pooling: str = "auto") -> None:
103
+ def __init__(
104
+ self,
105
+ model_name: str,
106
+ pretrained: bool = True,
107
+ pooling: str = "auto",
108
+ text_config: dict | None = None,
109
+ tokenizer_name_or_path: str | None = None,
110
+ ) -> None:
103
111
  super().__init__()
104
112
  if pooling not in {"auto", "pooler", "cls", "mean"}:
105
113
  raise ValueError(
@@ -107,10 +115,19 @@ class TextEncoder(nn.Module):
107
115
  )
108
116
  self.kind = "hf_text"
109
117
  self.pooling = pooling
110
- tokenizer_name = model_name.removeprefix("hf_clip_projected:")
111
- self.tokenizer = AutoTokenizer.from_pretrained(tokenizer_name)
118
+ tokenizer_name = tokenizer_name_or_path or model_name.removeprefix("hf_clip_projected:")
119
+ self.tokenizer = (
120
+ CLIPTokenizerFast.from_pretrained(tokenizer_name)
121
+ if text_config is not None
122
+ else AutoTokenizer.from_pretrained(tokenizer_name)
123
+ )
112
124
  model_name_lower = model_name.lower()
113
- if model_name.startswith("hf_clip_projected:"):
125
+ if text_config is not None:
126
+ if pretrained or text_config.get("model_type") != "clip_text_model":
127
+ raise ValueError("Bundled text_config requires a non-pretrained CLIP text encoder")
128
+ self.backbone = CLIPTextModel(CLIPTextConfig.from_dict(text_config))
129
+ self.output_dim = self.backbone.config.hidden_size
130
+ elif model_name.startswith("hf_clip_projected:"):
114
131
  self.kind = "hf_clip_projected"
115
132
  projected_model_name = model_name.removeprefix("hf_clip_projected:")
116
133
  if pretrained:
@@ -101,6 +101,8 @@ class Hyper3CLIP(ExperimentalObjectiveMixin, nn.Module):
101
101
  fuse_beta_query_encoder_forwards: bool = False,
102
102
  group_beta_query_pooling: bool = False,
103
103
  objective_autocast_dtype: str = "float32",
104
+ text_config: dict | None = None,
105
+ tokenizer_name_or_path: str | None = None,
104
106
  ) -> None:
105
107
  super().__init__()
106
108
  if objective not in {"hycoclip", "uncha", "proclip"}:
@@ -206,7 +208,11 @@ class Hyper3CLIP(ExperimentalObjectiveMixin, nn.Module):
206
208
  self.phyclip_num_factors = 0
207
209
  self.vision_encoder = VisionEncoder(vision_backbone, pretrained=vision_pretrained)
208
210
  self.text_encoder = TextEncoder(
209
- text_model_name, pretrained=text_pretrained, pooling=text_pooling
211
+ text_model_name,
212
+ pretrained=text_pretrained,
213
+ pooling=text_pooling,
214
+ text_config=text_config,
215
+ tokenizer_name_or_path=tokenizer_name_or_path,
210
216
  )
211
217
  self.tokenizer = self.text_encoder.tokenizer
212
218
  self.embed_dim = embed_dim
@@ -27,6 +27,7 @@ class ImageConfig:
27
27
  rescale: float = 1.0 / 255.0
28
28
  mean: tuple[float, float, float] | None = None
29
29
  std: tuple[float, float, float] | None = None
30
+ resize_mode: Literal["center_crop", "squash"] = "center_crop"
30
31
 
31
32
 
32
33
  def preprocess_images(images: list[Image.Image], config: ImageConfig | None = None) -> np.ndarray:
@@ -47,14 +48,18 @@ def preprocess_images(images: list[Image.Image], config: ImageConfig | None = No
47
48
  if img.mode != "RGB":
48
49
  img = img.convert("RGB")
49
50
 
50
- # Resize shortest side, then center crop
51
- w, h = img.size
52
- scale = config.size / min(w, h)
53
- img = img.resize((int(round(w * scale)), int(round(h * scale))), resample=resample)
54
-
55
- w, h = img.size
56
- left, top = (w - config.size) // 2, (h - config.size) // 2
57
- img = img.crop((left, top, left + config.size, top + config.size))
51
+ if config.resize_mode == "squash":
52
+ img = img.resize((config.size, config.size), resample=resample)
53
+ elif config.resize_mode == "center_crop":
54
+ # Resize shortest side, then center crop.
55
+ w, h = img.size
56
+ scale = config.size / min(w, h)
57
+ img = img.resize((int(round(w * scale)), int(round(h * scale))), resample=resample)
58
+ w, h = img.size
59
+ left, top = (w - config.size) // 2, (h - config.size) // 2
60
+ img = img.crop((left, top, left + config.size, top + config.size))
61
+ else:
62
+ raise ValueError(f"Unknown resize mode: {config.resize_mode!r}")
58
63
 
59
64
  # To float32 CHW
60
65
  arr = np.asarray(img, dtype=np.float32) * config.rescale
@@ -100,21 +100,29 @@ _MODELS: dict[str, ModelInfo] = {
100
100
  optional_dependencies=("ml",),
101
101
  description="UNCHA ViT-B/16 checkpoint (HF .pth, torch inference)",
102
102
  ),
103
- "hyper3-clip-v0.5": ModelInfo(
104
- name="hyper3-clip-v0.5",
103
+ "hyper3-clip-v1": ModelInfo(
104
+ name="hyper3-clip-v1",
105
105
  geometry="hyperboloid",
106
106
  dim=513,
107
- hub_id="hyper3labs/hyper3-clip-v0.5",
107
+ hub_id="hyper3labs/hyper3-clip-v1",
108
108
  hub_path="model.safetensors",
109
- hub_patterns=("config.yaml", "model.safetensors"),
110
- license="Unknown",
109
+ hub_patterns=(
110
+ "config.json",
111
+ "model.safetensors",
112
+ "tokenizer.json",
113
+ "tokenizer_config.json",
114
+ "vocab.json",
115
+ "merges.txt",
116
+ ),
117
+ license="OpenMDW-1.0",
111
118
  loader="hyper3-clip-torch",
112
119
  optional_dependencies=("ml",),
113
120
  modalities=("image", "text"),
114
- description="Hyper3-CLIP v0.5 ViT-B image+text encoder (HF safetensors, torch inference)",
121
+ description="Hyper3-CLIP v1 ViT-B image+text encoder (HF safetensors, torch inference)",
115
122
  image_config=ImageConfig(
116
123
  size=224,
117
124
  interpolation="bicubic",
125
+ resize_mode="squash",
118
126
  mean=(0.485, 0.456, 0.406),
119
127
  std=(0.229, 0.224, 0.225),
120
128
  ),
@@ -132,6 +140,11 @@ _MODELS: dict[str, ModelInfo] = {
132
140
  ),
133
141
  }
134
142
 
143
+ _ALIASES = {
144
+ # Compatibility for callers that selected the model before its v1 rename.
145
+ "hyper3-clip-v0.5": "hyper3-clip-v1",
146
+ }
147
+
135
148
 
136
149
  def list_models(geometry: str | None = None) -> list[str]:
137
150
  """List available model names, optionally filtered by geometry."""
@@ -142,6 +155,7 @@ def list_models(geometry: str | None = None) -> list[str]:
142
155
 
143
156
  def get_model_info(name: str) -> ModelInfo:
144
157
  """Get metadata for a model. Raises KeyError if not found."""
158
+ name = _ALIASES.get(name, name)
145
159
  if name not in _MODELS:
146
160
  raise KeyError(f"Model '{name}' not found. Available: {', '.join(_MODELS.keys())}")
147
161
  return _MODELS[name]
@@ -6,6 +6,8 @@ They do not create a separate public provider surface.
6
6
 
7
7
  from __future__ import annotations
8
8
 
9
+ import json
10
+ from dataclasses import replace
9
11
  from pathlib import Path
10
12
  from typing import Any
11
13
 
@@ -257,7 +259,7 @@ def _align_text_tower_keys(state: dict[str, Any]) -> dict[str, Any]:
257
259
 
258
260
 
259
261
  class Hyper3ClipTorchModel:
260
- """Hyper3-CLIP catalog entry runtime using torch for image inference."""
262
+ """Hyper3-CLIP image/text inference returning native Lorentz embeddings."""
261
263
 
262
264
  def __init__(
263
265
  self,
@@ -269,11 +271,14 @@ class Hyper3ClipTorchModel:
269
271
  device: str | None = None,
270
272
  ) -> None:
271
273
  self._checkpoint_path = checkpoint_path
272
- self._config_path = checkpoint_path.with_name("config.yaml")
274
+ self._config_path = checkpoint_path.with_name("config.json")
275
+ if not self._config_path.is_file():
276
+ self._config_path = checkpoint_path.with_name("config.yaml")
273
277
  self.geometry = geometry
274
278
  self.dim = dim
275
279
  self._image_config = image_config or ImageConfig()
276
280
  self._device_override = device
281
+ self._max_text_length = 77
277
282
 
278
283
  self._torch = None
279
284
  self._device = None
@@ -316,11 +321,41 @@ class Hyper3ClipTorchModel:
316
321
  self._torch = torch
317
322
  self._device = self._resolve_device(torch)
318
323
 
319
- config = yaml.safe_load(self._config_path.read_text(encoding="utf-8"))
320
- model_config = dict(config["model"])
324
+ if self._config_path.suffix == ".json":
325
+ config = json.loads(self._config_path.read_text(encoding="utf-8"))
326
+ model_config = {
327
+ key: config[key]
328
+ for key in (
329
+ "vision_backbone",
330
+ "text_model_name",
331
+ "embed_dim",
332
+ "curv_init",
333
+ "learn_curv",
334
+ )
335
+ }
336
+ # These constructor arguments affect training losses only.
337
+ model_config.update(
338
+ entail_weight=0.0, inter_aperture_scale=0.0, intra_aperture_scale=0.0
339
+ )
340
+ model_config["text_config"] = config["text_config"]
341
+ model_config["tokenizer_name_or_path"] = str(self._checkpoint_path.parent)
342
+ else:
343
+ config = yaml.safe_load(self._config_path.read_text(encoding="utf-8"))
344
+ model_config = dict(config["model"])
345
+ data_config = config.get("data", config)
346
+ self._image_config = replace(
347
+ self._image_config,
348
+ size=int(data_config.get("image_size", self._image_config.size)),
349
+ resize_mode="squash",
350
+ )
351
+ self._max_text_length = int(data_config.get("max_text_length", 77))
321
352
  model_config["vision_pretrained"] = False
322
353
  model_config["text_pretrained"] = False
323
354
  model = Hyper3CLIP(**model_config)
355
+ if model.embed_dim + 1 != self.dim:
356
+ raise ValueError("Hyper3-CLIP embedding dimension does not match the catalog entry")
357
+ model.curv_min = float(config.get("curvature_min", model.curv_min))
358
+ model.curv_max = float(config.get("curvature_max", model.curv_max))
324
359
  state = _align_text_tower_keys(load_file(self._checkpoint_path, device="cpu"))
325
360
  load_result = model.load_state_dict(state, strict=False)
326
361
  if load_result.missing_keys:
@@ -346,6 +381,7 @@ class Hyper3ClipTorchModel:
346
381
 
347
382
  def encode_images(self, images: list[Image.Image]) -> np.ndarray:
348
383
  """Encode PIL images to Hyper3-CLIP embeddings (B, D)."""
384
+ self._ensure_model()
349
385
  return self.encode(preprocess_images(images, self._image_config))
350
386
 
351
387
  def encode_texts(self, texts: list[str]) -> np.ndarray:
@@ -357,7 +393,11 @@ class Hyper3ClipTorchModel:
357
393
  assert self._device is not None
358
394
 
359
395
  encoded = self._model.tokenizer(
360
- list(texts), padding=True, truncation=True, return_tensors="pt"
396
+ list(texts),
397
+ padding=True,
398
+ truncation=True,
399
+ max_length=self._max_text_length,
400
+ return_tensors="pt",
361
401
  )
362
402
  input_ids = encoded["input_ids"].to(self._device)
363
403
  attention_mask = encoded["attention_mask"].to(self._device)
File without changes
File without changes