hyper-models 0.3.0__tar.gz → 0.3.2__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.0 → hyper_models-0.3.2}/PKG-INFO +9 -7
  2. {hyper_models-0.3.0 → hyper_models-0.3.2}/README.md +4 -4
  3. {hyper_models-0.3.0 → hyper_models-0.3.2}/pyproject.toml +8 -2
  4. {hyper_models-0.3.0 → hyper_models-0.3.2}/src/hyper_models/__init__.py +10 -1
  5. {hyper_models-0.3.0 → hyper_models-0.3.2}/src/hyper_models/models.py +3 -1
  6. {hyper_models-0.3.0 → hyper_models-0.3.2}/src/hyper_models/registry.py +12 -4
  7. {hyper_models-0.3.0 → hyper_models-0.3.2}/src/hyper_models/torch_models.py +46 -7
  8. {hyper_models-0.3.0 → hyper_models-0.3.2}/.gitignore +0 -0
  9. {hyper_models-0.3.0 → hyper_models-0.3.2}/LICENSE +0 -0
  10. {hyper_models-0.3.0 → hyper_models-0.3.2}/src/hyper3_clip/__init__.py +0 -0
  11. {hyper_models-0.3.0 → hyper_models-0.3.2}/src/hyper3_clip/models/__init__.py +0 -0
  12. {hyper_models-0.3.0 → hyper_models-0.3.2}/src/hyper3_clip/models/encoders.py +0 -0
  13. {hyper_models-0.3.0 → hyper_models-0.3.2}/src/hyper3_clip/models/experimental.py +0 -0
  14. {hyper_models-0.3.0 → hyper_models-0.3.2}/src/hyper3_clip/models/himo.py +0 -0
  15. {hyper_models-0.3.0 → hyper_models-0.3.2}/src/hyper3_clip/models/hyper3_clip.py +0 -0
  16. {hyper_models-0.3.0 → hyper_models-0.3.2}/src/hyper3_clip/models/lorentz.py +0 -0
  17. {hyper_models-0.3.0 → hyper_models-0.3.2}/src/hyper3_clip/models/losses.py +0 -0
  18. {hyper_models-0.3.0 → hyper_models-0.3.2}/src/hyper3_clip/models/objectives.py +0 -0
  19. {hyper_models-0.3.0 → hyper_models-0.3.2}/src/hyper3_clip/models/tren.py +0 -0
  20. {hyper_models-0.3.0 → hyper_models-0.3.2}/src/hyper3_clip/training/__init__.py +0 -0
  21. {hyper_models-0.3.0 → hyper_models-0.3.2}/src/hyper3_clip/training/distributed.py +0 -0
  22. {hyper_models-0.3.0 → hyper_models-0.3.2}/src/hyper_models/loader.py +0 -0
  23. {hyper_models-0.3.0 → hyper_models-0.3.2}/src/hyper_models/loaders.py +0 -0
  24. {hyper_models-0.3.0 → hyper_models-0.3.2}/src/hyper_models/preprocessing.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: hyper-models
3
- Version: 0.3.0
3
+ Version: 0.3.2
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
@@ -22,11 +22,13 @@ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
22
22
  Requires-Python: >=3.10
23
23
  Requires-Dist: huggingface-hub>=1.0
24
24
  Requires-Dist: numpy>=1.26
25
- Requires-Dist: onnxruntime>=1.20
25
+ Requires-Dist: onnxruntime<1.24,>=1.20; python_version < '3.11'
26
+ Requires-Dist: onnxruntime>=1.20; python_version >= '3.11'
26
27
  Requires-Dist: pillow>=10.0
27
28
  Provides-Extra: dev
28
29
  Requires-Dist: pytest>=7.0; extra == 'dev'
29
30
  Requires-Dist: ruff>=0.1; extra == 'dev'
31
+ Requires-Dist: tomli>=2.0; (python_version < '3.11') and extra == 'dev'
30
32
  Provides-Extra: ml
31
33
  Requires-Dist: protobuf>=4.25.0; extra == 'ml'
32
34
  Requires-Dist: pyyaml>=6.0.0; extra == 'ml'
@@ -85,7 +87,7 @@ from PIL import Image
85
87
 
86
88
  # List available models
87
89
  hyper_models.list_models()
88
- # ['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']
89
91
 
90
92
  # Inspect supported internal loader kinds
91
93
  hyper_models.list_loaders()
@@ -128,7 +130,7 @@ This keeps callers on one stable API:
128
130
  ```python
129
131
  model = hyper_models.load("hycoclip-vit-s")
130
132
  model = hyper_models.load("uncha-vit-b")
131
- model = hyper_models.load("hyper3-clip-v0.5")
133
+ model = hyper_models.load("hyper3-clip-v1")
132
134
  ```
133
135
 
134
136
  Callers do not need to know which internal loader is used, except for optional
@@ -157,7 +159,7 @@ HyperView's simple path remains torch-free. If you use the default ONNX-backed
157
159
  `hyper-models` entries or the default `embed-anything` provider, HyperView does
158
160
  not need PyTorch. PyTorch is only needed when you explicitly select a
159
161
  torch-backed catalog entry such as `uncha-vit-s`, `uncha-vit-b`, or
160
- `hyper3-clip-v0.5`.
162
+ `hyper3-clip-v1`.
161
163
 
162
164
  ## Models
163
165
 
@@ -171,7 +173,7 @@ torch-backed catalog entry such as `uncha-vit-s`, `uncha-vit-b`, or
171
173
  | `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) |
172
174
  | `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) |
173
175
  | `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) |
174
- | `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) |
176
+ | `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) |
175
177
  | `hyp-vit` | — | [CVPR 2022](https://arxiv.org/abs/2203.10833) | [htdt/hyp_metric](https://github.com/htdt/hyp_metric) |
176
178
  | `hie` | — | [CVPR 2020](https://arxiv.org/abs/1904.02239) | [leymir/hyperbolic-image-embeddings](https://github.com/leymir/hyperbolic-image-embeddings) |
177
179
  | `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,7 +89,7 @@ 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
@@ -118,7 +118,7 @@ HyperView's simple path remains torch-free. If you use the default ONNX-backed
118
118
  `hyper-models` entries or the default `embed-anything` provider, HyperView does
119
119
  not need PyTorch. PyTorch is only needed when you explicitly select a
120
120
  torch-backed catalog entry such as `uncha-vit-s`, `uncha-vit-b`, or
121
- `hyper3-clip-v0.5`.
121
+ `hyper3-clip-v1`.
122
122
 
123
123
  ## Models
124
124
 
@@ -132,7 +132,7 @@ torch-backed catalog entry such as `uncha-vit-s`, `uncha-vit-b`, or
132
132
  | `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
133
  | `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
134
  | `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) |
135
+ | `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
136
  | `hyp-vit` | — | [CVPR 2022](https://arxiv.org/abs/2203.10833) | [htdt/hyp_metric](https://github.com/htdt/hyp_metric) |
137
137
  | `hie` | — | [CVPR 2020](https://arxiv.org/abs/1904.02239) | [leymir/hyperbolic-image-embeddings](https://github.com/leymir/hyperbolic-image-embeddings) |
138
138
  | `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.0"
3
+ version = "0.3.2"
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" }
@@ -20,7 +20,11 @@ classifiers = [
20
20
  ]
21
21
  dependencies = [
22
22
  "numpy>=1.26",
23
- "onnxruntime>=1.20",
23
+ # onnxruntime stopped publishing cp310 wheels after 1.23.x; without this
24
+ # split a universal resolution picks a release that cannot install on the
25
+ # 3.10 this package still supports.
26
+ "onnxruntime>=1.20,<1.24; python_version<'3.11'",
27
+ "onnxruntime>=1.20; python_version>='3.11'",
24
28
  "huggingface-hub>=1.0",
25
29
  "pillow>=10.0",
26
30
  ]
@@ -29,6 +33,8 @@ dependencies = [
29
33
  dev = [
30
34
  "pytest>=7.0",
31
35
  "ruff>=0.1",
36
+ # tomllib is stdlib from 3.11; the version test reads pyproject.toml on 3.10 too.
37
+ "tomli>=2.0; python_version<'3.11'",
32
38
  ]
33
39
  ml = [
34
40
  "torch>=2.9.1",
@@ -11,6 +11,9 @@ Example:
11
11
  >>> model.dim # 513
12
12
  """
13
13
 
14
+ from importlib.metadata import PackageNotFoundError
15
+ from importlib.metadata import version as _installed_version
16
+
14
17
  from hyper_models.loader import load
15
18
  from hyper_models.loaders import list_loaders
16
19
  from hyper_models.models import ONNXModel
@@ -27,4 +30,10 @@ __all__ = [
27
30
  "ImageConfig",
28
31
  "preprocess_images",
29
32
  ]
30
- __version__ = "0.3.0"
33
+ try:
34
+ # Read the installed distribution so this cannot drift from pyproject.toml:
35
+ # a hand-maintained literal here stayed at 0.3.0 through the 0.3.1 release,
36
+ # and the demo Dockerfiles print this value to confirm what they installed.
37
+ __version__ = _installed_version("hyper-models")
38
+ except PackageNotFoundError: # running from a source tree that was never installed
39
+ __version__ = "0.0.0.dev0"
@@ -37,7 +37,9 @@ class ONNXModel:
37
37
  if self._session is None:
38
38
  import onnxruntime as ort
39
39
 
40
- self._session = ort.InferenceSession(str(self._path), providers=["CPUExecutionProvider"])
40
+ self._session = ort.InferenceSession(
41
+ str(self._path), providers=["CPUExecutionProvider"]
42
+ )
41
43
 
42
44
  def encode(self, inputs: np.ndarray) -> np.ndarray:
43
45
  """Encode preprocessed inputs (B, C, H, W) to embeddings (B, D)."""
@@ -31,6 +31,7 @@ class ModelInfo:
31
31
  description: str = ""
32
32
  input_name: str = "image"
33
33
  output_name: str | None = None
34
+ modalities: tuple[str, ...] = ("image",) # what the loaded model can encode
34
35
  image_config: ImageConfig = field(default_factory=ImageConfig)
35
36
 
36
37
 
@@ -99,17 +100,18 @@ _MODELS: dict[str, ModelInfo] = {
99
100
  optional_dependencies=("ml",),
100
101
  description="UNCHA ViT-B/16 checkpoint (HF .pth, torch inference)",
101
102
  ),
102
- "hyper3-clip-v0.5": ModelInfo(
103
- name="hyper3-clip-v0.5",
103
+ "hyper3-clip-v1": ModelInfo(
104
+ name="hyper3-clip-v1",
104
105
  geometry="hyperboloid",
105
106
  dim=513,
106
- hub_id="hyper3labs/hyper3-clip-v0.5",
107
+ hub_id="hyper3labs/hyper3-clip-v1",
107
108
  hub_path="model.safetensors",
108
109
  hub_patterns=("config.yaml", "model.safetensors"),
109
110
  license="Unknown",
110
111
  loader="hyper3-clip-torch",
111
112
  optional_dependencies=("ml",),
112
- description="Hyper3-CLIP v0.5 ViT-B image encoder (HF safetensors, torch inference)",
113
+ modalities=("image", "text"),
114
+ description="Hyper3-CLIP v1 ViT-B image+text encoder (HF safetensors, torch inference)",
113
115
  image_config=ImageConfig(
114
116
  size=224,
115
117
  interpolation="bicubic",
@@ -130,6 +132,11 @@ _MODELS: dict[str, ModelInfo] = {
130
132
  ),
131
133
  }
132
134
 
135
+ _ALIASES = {
136
+ # Compatibility for callers that selected the model before its v1 rename.
137
+ "hyper3-clip-v0.5": "hyper3-clip-v1",
138
+ }
139
+
133
140
 
134
141
  def list_models(geometry: str | None = None) -> list[str]:
135
142
  """List available model names, optionally filtered by geometry."""
@@ -140,6 +147,7 @@ def list_models(geometry: str | None = None) -> list[str]:
140
147
 
141
148
  def get_model_info(name: str) -> ModelInfo:
142
149
  """Get metadata for a model. Raises KeyError if not found."""
150
+ name = _ALIASES.get(name, name)
143
151
  if name not in _MODELS:
144
152
  raise KeyError(f"Model '{name}' not found. Available: {', '.join(_MODELS.keys())}")
145
153
  return _MODELS[name]
@@ -232,6 +232,30 @@ class UNCHATorchModel:
232
232
  return self.encode(preprocess_images(images, self._image_config))
233
233
 
234
234
 
235
+ _CHECKPOINT_TEXT_PREFIX = "text_encoder.backbone.text_model."
236
+ _MODEL_TEXT_PREFIX = "text_encoder.backbone."
237
+
238
+
239
+ def _align_text_tower_keys(state: dict[str, Any]) -> dict[str, Any]:
240
+ """Match published Hyper3-CLIP text weights to the model's parameter names.
241
+
242
+ The checkpoint stores the text tower under the full Hugging Face
243
+ ``CLIPTextModel`` path, while :class:`Hyper3CLIP` binds its backbone one
244
+ level deeper, at ``CLIPTextModel.text_model``. Without this rename the text
245
+ tower silently loads no pretrained weights and every text embedding comes
246
+ from a randomly initialised encoder.
247
+ """
248
+
249
+ return {
250
+ (
251
+ _MODEL_TEXT_PREFIX + key[len(_CHECKPOINT_TEXT_PREFIX) :]
252
+ if key.startswith(_CHECKPOINT_TEXT_PREFIX)
253
+ else key
254
+ ): value
255
+ for key, value in state.items()
256
+ }
257
+
258
+
235
259
  class Hyper3ClipTorchModel:
236
260
  """Hyper3-CLIP catalog entry runtime using torch for image inference."""
237
261
 
@@ -297,14 +321,11 @@ class Hyper3ClipTorchModel:
297
321
  model_config["vision_pretrained"] = False
298
322
  model_config["text_pretrained"] = False
299
323
  model = Hyper3CLIP(**model_config)
300
- state = load_file(self._checkpoint_path, device="cpu")
324
+ state = _align_text_tower_keys(load_file(self._checkpoint_path, device="cpu"))
301
325
  load_result = model.load_state_dict(state, strict=False)
302
- missing_non_text = [
303
- key for key in load_result.missing_keys if not key.startswith("text_encoder.")
304
- ]
305
- if missing_non_text:
306
- missing = ", ".join(missing_non_text[:8])
307
- raise RuntimeError(f"Hyper3-CLIP checkpoint missing required image keys: {missing}")
326
+ if load_result.missing_keys:
327
+ missing = ", ".join(load_result.missing_keys[:8])
328
+ raise RuntimeError(f"Hyper3-CLIP checkpoint missing required keys: {missing}")
308
329
  model.to(self._device)
309
330
  model.eval()
310
331
  self._model = model
@@ -326,3 +347,21 @@ class Hyper3ClipTorchModel:
326
347
  def encode_images(self, images: list[Image.Image]) -> np.ndarray:
327
348
  """Encode PIL images to Hyper3-CLIP embeddings (B, D)."""
328
349
  return self.encode(preprocess_images(images, self._image_config))
350
+
351
+ def encode_texts(self, texts: list[str]) -> np.ndarray:
352
+ """Encode text queries into the same hyperboloid as the images (B, D)."""
353
+ self._ensure_model()
354
+
355
+ assert self._model is not None
356
+ assert self._torch is not None
357
+ assert self._device is not None
358
+
359
+ encoded = self._model.tokenizer(
360
+ list(texts), padding=True, truncation=True, return_tensors="pt"
361
+ )
362
+ input_ids = encoded["input_ids"].to(self._device)
363
+ attention_mask = encoded["attention_mask"].to(self._device)
364
+ with self._torch.inference_mode():
365
+ emb = self._model.encode_text(input_ids, attention_mask)
366
+
367
+ return np.asarray(emb.detach().cpu().numpy(), dtype=np.float32)
File without changes
File without changes