hbauzan Cursor commited on
Commit
8a761c1
·
1 Parent(s): 538585c

feat(api): add POST /project UMAP projection for Galaxy VIEW

Browse files

Backend dimensionality reduction seam (umap-learn) so Galaxy can place tokens from embeddings without re-encoding; pca/tsne return 501 until later epics.

Co-authored-by: Cursor <cursoragent@cursor.com>

architecture_spec.md CHANGED
@@ -17,6 +17,7 @@ VHectorLab 3D is a 3D semantic vector visualizer and vector arithmetic explorer.
17
  - `POST /tokenize`: Returns tokenization details.
18
  - `POST /arithmetic`: Computes $V_{res} = V_A - V_B + V_C$ and returns top-$K$ nearest vocabulary words and component vectors.
19
  - `POST /compare`: Batch-encodes 1–1024 texts, L2-normalizes embeddings, and returns per-item cosine vs the first token (anchor).
 
20
 
21
  ### Data Contracts
22
 
@@ -62,3 +63,32 @@ Returns:
62
  }
63
  ```
64
  `cosine_vs_first` is $\text{dot}(\hat{e}_i, \hat{e}_0)$ on L2-normalized embeddings. Frontend reorders may recompute scores in memory without re-calling `/compare`.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  - `POST /tokenize`: Returns tokenization details.
18
  - `POST /arithmetic`: Computes $V_{res} = V_A - V_B + V_C$ and returns top-$K$ nearest vocabulary words and component vectors.
19
  - `POST /compare`: Batch-encodes 1–1024 texts, L2-normalizes embeddings, and returns per-item cosine vs the first token (anchor).
20
+ - `POST /project`: Projects precomputed embedding vectors to 2D/3D (does **not** re-encode text). v1 method = **`umap` only**; `pca` / `tsne` → **501**; other methods → **400**. Default seed `42`. When `dim > 50`, internal PCA→50 before UMAP. Positions are zero-mean and RMS-scaled server-side.
21
 
22
  ### Data Contracts
23
 
 
63
  }
64
  ```
65
  `cosine_vs_first` is $\text{dot}(\hat{e}_i, \hat{e}_0)$ on L2-normalized embeddings. Frontend reorders may recompute scores in memory without re-calling `/compare`.
66
+
67
+ #### Project (Galaxy / UMAP)
68
+ ```json
69
+ {
70
+ "vectors": [[0.01, "..."], ["..."]],
71
+ "method": "umap",
72
+ "n_components": 3,
73
+ "seed": 42,
74
+ "params": { "n_neighbors": 15, "min_dist": 0.1, "metric": "cosine" }
75
+ }
76
+ ```
77
+ Constraints: `len(vectors)` ∈ 1..1024; uniform row dim; `n_components` ∈ {2, 3}; `method` = `umap` (green path).
78
+
79
+ Returns:
80
+ ```json
81
+ {
82
+ "method": "umap",
83
+ "n_components": 3,
84
+ "positions": [[x, y, z], "..."],
85
+ "meta": {
86
+ "seed": 42,
87
+ "n_neighbors": 15,
88
+ "min_dist": 0.1,
89
+ "metric": "cosine",
90
+ "pre_pca_dims": 50
91
+ }
92
+ }
93
+ ```
94
+ `meta.pre_pca_dims` is present only when the internal PCA pre-step ran. Encoding stays on `/compare` (or SAE encode); `/project` is additive.
backend/projection.py ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Dimensionality reduction for Galaxy VIEW.
3
+
4
+ Deep seam: project_embeddings(vectors, ...) → positions + meta.
5
+ UMAP only in v1; PCA/t-SNE raise ProjectError(501).
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import Any
11
+
12
+ import numpy as np
13
+
14
+ PRE_PCA_DIMS = 50
15
+ MAX_VECTORS = 1024
16
+ DEFAULT_N_NEIGHBORS = 15
17
+ DEFAULT_MIN_DIST = 0.1
18
+ DEFAULT_METRIC = "cosine"
19
+
20
+
21
+ class ProjectError(Exception):
22
+ """Validation / capability error with HTTP-ish status code."""
23
+
24
+ def __init__(self, message: str, status_code: int = 400) -> None:
25
+ super().__init__(message)
26
+ self.message = message
27
+ self.status_code = status_code
28
+
29
+
30
+ def _as_matrix(vectors: list[list[float]] | np.ndarray) -> np.ndarray:
31
+ if isinstance(vectors, np.ndarray):
32
+ mat = np.asarray(vectors, dtype=np.float64)
33
+ else:
34
+ if not vectors:
35
+ raise ProjectError("vectors list cannot be empty (need 1..1024)")
36
+ lengths = {len(row) for row in vectors}
37
+ if len(lengths) != 1:
38
+ raise ProjectError("all vectors must have the same dimension")
39
+ if next(iter(lengths)) < 1:
40
+ raise ProjectError("vector dimension must be >= 1")
41
+ mat = np.asarray(vectors, dtype=np.float64)
42
+
43
+ if mat.ndim != 2:
44
+ raise ProjectError("vectors must be a 2D matrix [n, dim]")
45
+ n, dim = mat.shape
46
+ if n < 1 or n > MAX_VECTORS:
47
+ raise ProjectError(f"len(vectors) must be in 1..{MAX_VECTORS}, got {n}")
48
+ if dim < 1:
49
+ raise ProjectError("vector dimension must be >= 1")
50
+ if not np.isfinite(mat).all():
51
+ raise ProjectError("vectors must contain only finite values")
52
+ return mat
53
+
54
+
55
+ def _normalize_positions(pos: np.ndarray) -> np.ndarray:
56
+ """Zero-mean; scale so RMS distance from origin is 1 (stable camera defaults)."""
57
+ centered = pos - pos.mean(axis=0, keepdims=True)
58
+ rms = float(np.sqrt(np.mean(np.sum(centered**2, axis=1))))
59
+ if rms < 1e-12:
60
+ return centered
61
+ return centered / rms
62
+
63
+
64
+ def _resolve_umap_params(
65
+ n_samples: int, params: dict[str, Any] | None
66
+ ) -> dict[str, Any]:
67
+ raw = params or {}
68
+ n_neighbors = int(raw.get("n_neighbors", DEFAULT_N_NEIGHBORS))
69
+ min_dist = float(raw.get("min_dist", DEFAULT_MIN_DIST))
70
+ metric = str(raw.get("metric", DEFAULT_METRIC))
71
+
72
+ # UMAP requires n_neighbors < n_samples
73
+ max_nn = max(2, n_samples - 1)
74
+ n_neighbors = max(n_neighbors, 2)
75
+ n_neighbors = min(n_neighbors, max_nn)
76
+
77
+ if min_dist < 0.0:
78
+ raise ProjectError("params.min_dist must be >= 0")
79
+
80
+ return {
81
+ "n_neighbors": n_neighbors,
82
+ "min_dist": min_dist,
83
+ "metric": metric,
84
+ }
85
+
86
+
87
+ def project_embeddings(
88
+ vectors: list[list[float]] | np.ndarray,
89
+ *,
90
+ method: str = "umap",
91
+ n_components: int = 3,
92
+ seed: int = 42,
93
+ params: dict[str, Any] | None = None,
94
+ ) -> dict[str, Any]:
95
+ """
96
+ Project embedding rows to 2D/3D.
97
+
98
+ Returns:
99
+ { method, n_components, positions, meta }
100
+ """
101
+ method_l = (method or "").strip().lower()
102
+ if method_l in ("pca", "tsne"):
103
+ raise ProjectError(
104
+ f"method '{method_l}' is not implemented yet; use 'umap'",
105
+ status_code=501,
106
+ )
107
+ if method_l != "umap":
108
+ raise ProjectError(
109
+ f"unsupported method '{method}'; v1 accepts 'umap' only "
110
+ "(pca/tsne coming later)",
111
+ status_code=400,
112
+ )
113
+
114
+ if n_components not in (2, 3):
115
+ raise ProjectError("n_components must be 2 or 3")
116
+
117
+ mat = _as_matrix(vectors)
118
+ n_samples, dim = mat.shape
119
+
120
+ # Need at least n_components + 1 points for a meaningful UMAP; allow smaller
121
+ # with clamped neighbors (smoke / edge cases).
122
+ if n_samples < 3:
123
+ raise ProjectError("UMAP requires at least 3 vectors")
124
+
125
+ umap_params = _resolve_umap_params(n_samples, params)
126
+ pre_pca_dims: int | None = None
127
+ work = mat
128
+
129
+ if dim > PRE_PCA_DIMS:
130
+ from sklearn.decomposition import PCA
131
+
132
+ pca_dims = min(PRE_PCA_DIMS, n_samples - 1, dim)
133
+ if pca_dims < n_components:
134
+ raise ProjectError(
135
+ f"cannot pre-reduce: need at least {n_components} PCA dims "
136
+ f"(n={n_samples}, dim={dim})"
137
+ )
138
+ pca = PCA(n_components=pca_dims, random_state=seed)
139
+ work = pca.fit_transform(mat)
140
+ pre_pca_dims = pca_dims
141
+
142
+ import umap
143
+
144
+ reducer = umap.UMAP(
145
+ n_components=n_components,
146
+ n_neighbors=umap_params["n_neighbors"],
147
+ min_dist=umap_params["min_dist"],
148
+ metric=umap_params["metric"],
149
+ random_state=seed,
150
+ n_jobs=1,
151
+ )
152
+ embedded = reducer.fit_transform(work)
153
+ positions = _normalize_positions(np.asarray(embedded, dtype=np.float64))
154
+
155
+ meta: dict[str, Any] = {
156
+ "seed": seed,
157
+ "n_neighbors": umap_params["n_neighbors"],
158
+ "min_dist": umap_params["min_dist"],
159
+ "metric": umap_params["metric"],
160
+ }
161
+ if pre_pca_dims is not None:
162
+ meta["pre_pca_dims"] = pre_pca_dims
163
+
164
+ return {
165
+ "method": "umap",
166
+ "n_components": n_components,
167
+ "positions": positions.tolist(),
168
+ "meta": meta,
169
+ }
backend/pyproject.toml CHANGED
@@ -13,6 +13,7 @@ dependencies = [
13
  "httpx>=0.27.0",
14
  "huggingface-hub>=0.21.0",
15
  "orjson>=3.11.9",
 
16
  ]
17
 
18
  [project.optional-dependencies]
 
13
  "httpx>=0.27.0",
14
  "huggingface-hub>=0.21.0",
15
  "orjson>=3.11.9",
16
+ "umap-learn==0.5.12",
17
  ]
18
 
19
  [project.optional-dependencies]
backend/routers/core.py CHANGED
@@ -1,10 +1,11 @@
1
  """
2
  Core API Router for VHectorLab 3D.
3
- Provides /health, /embed, /tokenize, and /arithmetic endpoints.
4
  """
5
 
6
- from typing import Any
7
 
 
8
  from backend.state import state
9
  from fastapi import APIRouter, HTTPException
10
  from pydantic import BaseModel, Field
@@ -42,6 +43,32 @@ class CompareRequest(BaseModel):
42
  )
43
 
44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  @router.get("/health")
46
  def health_check() -> dict[str, Any]:
47
  return {
@@ -117,3 +144,21 @@ def perform_compare(req: CompareRequest) -> dict[str, Any]:
117
  except Exception as e: # noqa: BLE001
118
  raise HTTPException(status_code=500, detail=str(e))
119
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  """
2
  Core API Router for VHectorLab 3D.
3
+ Provides /health, /embed, /tokenize, /arithmetic, /compare, and /project endpoints.
4
  """
5
 
6
+ from typing import Any, Literal
7
 
8
+ from backend.projection import ProjectError, project_embeddings
9
  from backend.state import state
10
  from fastapi import APIRouter, HTTPException
11
  from pydantic import BaseModel, Field
 
43
  )
44
 
45
 
46
+ class ProjectParams(BaseModel):
47
+ n_neighbors: int = Field(default=15, ge=2, le=200)
48
+ min_dist: float = Field(default=0.1, ge=0.0)
49
+ metric: str = Field(default="cosine")
50
+
51
+
52
+ class ProjectRequest(BaseModel):
53
+ vectors: list[list[float]] = Field(
54
+ ...,
55
+ description="Precomputed embedding rows (1..1024); does not re-encode text",
56
+ )
57
+ method: str = Field(
58
+ default="umap",
59
+ description="Projection method; v1 accepts 'umap' only",
60
+ )
61
+ n_components: Literal[2, 3] = Field(
62
+ default=3,
63
+ description="Output dimensionality (Galaxy uses 3; 2 reserved for future 2D VIEW)",
64
+ )
65
+ seed: int = Field(default=42, description="RNG seed for reproducibility")
66
+ params: ProjectParams | None = Field(
67
+ default=None,
68
+ description="UMAP hyperparameters (n_neighbors, min_dist, metric)",
69
+ )
70
+
71
+
72
  @router.get("/health")
73
  def health_check() -> dict[str, Any]:
74
  return {
 
144
  except Exception as e: # noqa: BLE001
145
  raise HTTPException(status_code=500, detail=str(e))
146
 
147
+
148
+ @router.post("/project")
149
+ def perform_project(req: ProjectRequest) -> dict[str, Any]:
150
+ """Project embedding vectors to 2D/3D (UMAP). Does not encode text."""
151
+ params = req.params.model_dump() if req.params is not None else None
152
+ try:
153
+ return project_embeddings(
154
+ req.vectors,
155
+ method=req.method,
156
+ n_components=req.n_components,
157
+ seed=req.seed,
158
+ params=params,
159
+ )
160
+ except ProjectError as e:
161
+ raise HTTPException(status_code=e.status_code, detail=e.message)
162
+ except Exception as e: # noqa: BLE001
163
+ raise HTTPException(status_code=500, detail=str(e))
164
+
backend/tests/test_project.py ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Tests for POST /project (UMAP dimensionality reduction)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from contextlib import asynccontextmanager
6
+
7
+ import numpy as np
8
+ import pytest
9
+ from fastapi import FastAPI
10
+ from fastapi.middleware.cors import CORSMiddleware
11
+ from fastapi.testclient import TestClient
12
+
13
+
14
+ @pytest.fixture()
15
+ def client():
16
+ """Lightweight app with core router only (no model load)."""
17
+ from backend.routers.core import router as core_router
18
+
19
+ @asynccontextmanager
20
+ async def _noop_lifespan(_app):
21
+ yield
22
+
23
+ app = FastAPI(lifespan=_noop_lifespan)
24
+ app.add_middleware(
25
+ CORSMiddleware,
26
+ allow_origins=["*"],
27
+ allow_credentials=False,
28
+ allow_methods=["*"],
29
+ allow_headers=["*"],
30
+ )
31
+ app.include_router(core_router)
32
+ app.include_router(core_router, prefix="/api")
33
+
34
+ with TestClient(app) as c:
35
+ yield c
36
+
37
+
38
+ def _random_vectors(n: int = 24, dim: int = 32, seed: int = 0) -> list[list[float]]:
39
+ rng = np.random.default_rng(seed)
40
+ mat = rng.standard_normal((n, dim)).astype(np.float64)
41
+ norms = np.linalg.norm(mat, axis=1, keepdims=True)
42
+ norms[norms == 0] = 1e-9
43
+ return (mat / norms).tolist()
44
+
45
+
46
+ def test_project_rejects_empty_vectors(client: TestClient):
47
+ res = client.post(
48
+ "/project",
49
+ json={"vectors": [], "method": "umap", "n_components": 3, "seed": 42},
50
+ )
51
+ assert res.status_code == 400
52
+ assert "empty" in res.json()["detail"].lower() or "1" in res.json()["detail"]
53
+
54
+
55
+ def test_project_rejects_too_many_vectors(client: TestClient):
56
+ vecs = _random_vectors(n=2, dim=4)
57
+ bloated = vecs * 513 # 1026
58
+ res = client.post(
59
+ "/project",
60
+ json={"vectors": bloated, "method": "umap", "n_components": 3},
61
+ )
62
+ assert res.status_code == 400
63
+ assert "1024" in res.json()["detail"]
64
+
65
+
66
+ def test_project_rejects_inconsistent_dims(client: TestClient):
67
+ res = client.post(
68
+ "/project",
69
+ json={
70
+ "vectors": [[1.0, 0.0], [0.0, 1.0, 0.0]],
71
+ "method": "umap",
72
+ "n_components": 2,
73
+ },
74
+ )
75
+ assert res.status_code == 400
76
+ assert "dim" in res.json()["detail"].lower()
77
+
78
+
79
+ def test_project_rejects_invalid_n_components(client: TestClient):
80
+ res = client.post(
81
+ "/project",
82
+ json={"vectors": _random_vectors(8, 8), "method": "umap", "n_components": 4},
83
+ )
84
+ assert res.status_code == 422 # pydantic Field constraint
85
+
86
+
87
+ @pytest.mark.parametrize("method", ["pca", "tsne"])
88
+ def test_project_pca_tsne_not_implemented(client: TestClient, method: str):
89
+ res = client.post(
90
+ "/project",
91
+ json={
92
+ "vectors": _random_vectors(12, 8),
93
+ "method": method,
94
+ "n_components": 3,
95
+ "seed": 42,
96
+ },
97
+ )
98
+ assert res.status_code == 501
99
+ detail = res.json()["detail"].lower()
100
+ assert method in detail
101
+ assert "not implemented" in detail or "umap" in detail
102
+
103
+
104
+ def test_project_rejects_unknown_method(client: TestClient):
105
+ res = client.post(
106
+ "/project",
107
+ json={
108
+ "vectors": _random_vectors(12, 8),
109
+ "method": "mds",
110
+ "n_components": 3,
111
+ },
112
+ )
113
+ assert res.status_code == 400
114
+ assert "umap" in res.json()["detail"].lower()
115
+
116
+
117
+ def test_project_umap_smoke_3d_seeded(client: TestClient):
118
+ vectors = _random_vectors(n=20, dim=16, seed=7)
119
+ payload = {
120
+ "vectors": vectors,
121
+ "method": "umap",
122
+ "n_components": 3,
123
+ "seed": 42,
124
+ "params": {"n_neighbors": 5, "min_dist": 0.1, "metric": "cosine"},
125
+ }
126
+ res = client.post("/project", json=payload)
127
+ assert res.status_code == 200, res.text
128
+ data = res.json()
129
+ assert data["method"] == "umap"
130
+ assert data["n_components"] == 3
131
+ assert len(data["positions"]) == 20
132
+ assert all(len(p) == 3 for p in data["positions"])
133
+
134
+ meta = data["meta"]
135
+ assert meta["seed"] == 42
136
+ assert meta["n_neighbors"] == 5
137
+ assert meta["min_dist"] == pytest.approx(0.1)
138
+ assert meta["metric"] == "cosine"
139
+
140
+ # Server-side normalize: approximately zero-mean
141
+ pos = np.asarray(data["positions"], dtype=np.float64)
142
+ means = pos.mean(axis=0)
143
+ assert np.allclose(means, 0.0, atol=1e-5)
144
+
145
+ # Same inputs → same positions (seeded)
146
+ res2 = client.post("/project", json=payload)
147
+ assert res2.status_code == 200
148
+ pos2 = np.asarray(res2.json()["positions"], dtype=np.float64)
149
+ assert np.allclose(pos, pos2, atol=1e-5)
150
+
151
+
152
+ def test_project_umap_n_components_2(client: TestClient):
153
+ vectors = _random_vectors(n=16, dim=12, seed=3)
154
+ res = client.post(
155
+ "/project",
156
+ json={
157
+ "vectors": vectors,
158
+ "method": "umap",
159
+ "n_components": 2,
160
+ "seed": 42,
161
+ "params": {"n_neighbors": 5},
162
+ },
163
+ )
164
+ assert res.status_code == 200
165
+ data = res.json()
166
+ assert data["n_components"] == 2
167
+ assert all(len(p) == 2 for p in data["positions"])
168
+
169
+
170
+ def test_project_umap_pre_pca_when_high_dim(client: TestClient):
171
+ """dim > 50 triggers internal PCA→50 before UMAP; meta reports pre_pca_dims."""
172
+ vectors = _random_vectors(n=60, dim=128, seed=1)
173
+ res = client.post(
174
+ "/project",
175
+ json={
176
+ "vectors": vectors,
177
+ "method": "umap",
178
+ "n_components": 3,
179
+ "seed": 42,
180
+ "params": {"n_neighbors": 8},
181
+ },
182
+ )
183
+ assert res.status_code == 200, res.text
184
+ meta = res.json()["meta"]
185
+ assert meta["pre_pca_dims"] == 50
186
+
187
+
188
+ def test_project_module_unit_rejects_method():
189
+ from backend.projection import ProjectError, project_embeddings
190
+
191
+ with pytest.raises(ProjectError) as ei:
192
+ project_embeddings(
193
+ _random_vectors(10, 8),
194
+ method="pca",
195
+ n_components=3,
196
+ seed=42,
197
+ )
198
+ assert ei.value.status_code == 501
backend/uv.lock CHANGED
@@ -314,6 +314,38 @@ wheels = [
314
  { url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071, upload-time = "2025-12-15T08:41:44.973Z" },
315
  ]
316
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
317
  [[package]]
318
  name = "markdown-it-py"
319
  version = "4.2.0"
@@ -463,6 +495,43 @@ wheels = [
463
  { url = "https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762", size = 2068504, upload-time = "2025-12-08T17:02:38.159Z" },
464
  ]
465
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
466
  [[package]]
467
  name = "numpy"
468
  version = "2.2.6"
@@ -533,6 +602,7 @@ name = "numpy"
533
  version = "2.4.6"
534
  source = { registry = "https://pypi.org/simple" }
535
  resolution-markers = [
 
536
  "python_full_version == '3.11.*'",
537
  ]
538
  sdist = { url = "https://files.pythonhosted.org/packages/d0/ad/fed0499ce6a338d2a03ebae59cd15093910c8875328855781952abf6c2fe/numpy-2.4.6.tar.gz", hash = "sha256:f3a3570c4a2a16746ac2c31a7c7c7b0c186b95ce902e33db6f28094ed7387dda", size = 20735807, upload-time = "2026-05-18T23:37:14.07Z" }
@@ -610,60 +680,6 @@ wheels = [
610
  { url = "https://files.pythonhosted.org/packages/15/ce/e5ec180bc41812edcd8daeb8639d205622c0e8c02259d8ab25a0201b3c2a/numpy-2.4.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2803abfebfc990042cd494d8ce2d5f82e9d847af6d35ec486923aa19dbad5e73", size = 12504263, upload-time = "2026-05-18T23:37:09.715Z" },
611
  ]
612
 
613
- [[package]]
614
- name = "numpy"
615
- version = "2.5.1"
616
- source = { registry = "https://pypi.org/simple" }
617
- resolution-markers = [
618
- "python_full_version >= '3.12'",
619
- ]
620
- sdist = { url = "https://files.pythonhosted.org/packages/22/fd/89965aa4ac08c74998539fcbf24fa3540f3e15237fbeb6bcf9c908f4aade/numpy-2.5.1.tar.gz", hash = "sha256:a48a113e6afea91f5608793bafa7ef2ad481fefbda87ec5069f483de61cb9fa3", size = 20755553, upload-time = "2026-07-04T17:08:00.933Z" }
621
- wheels = [
622
- { url = "https://files.pythonhosted.org/packages/62/7b/14687aa674250e5e546f616f486b0d56d3631cd5b2415739141ce40bdcea/numpy-2.5.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2c889b56fe48b1018f764b0eec8df59ab654e9148aa91faa12596043500de277", size = 16801574, upload-time = "2026-07-04T17:06:12.423Z" },
623
- { url = "https://files.pythonhosted.org/packages/e1/19/cc5bb2a3f2913d27d6dbb2c78d25921fabaedc6741d4a5a615a11f3c5bf3/numpy-2.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ab451b59c5643c570974c43aef780703ef1d3b4965d2be07afd530615a9358d1", size = 11772250, upload-time = "2026-07-04T17:06:15.726Z" },
624
- { url = "https://files.pythonhosted.org/packages/42/77/fdf34a71dd30f54979b18603bee915e0aaf825b07afe79acd60b04b691e2/numpy-2.5.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:78798bd5b9ad744056af8efa90e3b9ddaa53272a0848a483084a1cc0a13b2dc0", size = 5331516, upload-time = "2026-07-04T17:06:17.913Z" },
625
- { url = "https://files.pythonhosted.org/packages/ce/e2/eb7efa015b4cce41e2517bf182a7fce0d7d5b9d9ed76a29bfa0f4fe4505c/numpy-2.5.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:2ae0ca40bcb22d6ba59c1dfd5446f49940b0f2d821fde133f10dda11f816b84e", size = 6664863, upload-time = "2026-07-04T17:06:20.02Z" },
626
- { url = "https://files.pythonhosted.org/packages/a9/4b/a2b32dd94ee9ffbeecb28152240042a3949db33b1c834d44090b80e1b3b8/numpy-2.5.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:61ac47e772e6b8ea489e1d2f441a34c5c3ac17327e7ce294cbdf535795ad4e75", size = 15167977, upload-time = "2026-07-04T17:06:21.621Z" },
627
- { url = "https://files.pythonhosted.org/packages/b8/a9/6e73d68500f80773f65f0654ea932019d6694329a0eb0ed0533de38df376/numpy-2.5.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:59fda5e192b570217ec2580c96f00e9a7e12ef6866a900eb089b62c1a32545ca", size = 16672469, upload-time = "2026-07-04T17:06:24.064Z" },
628
- { url = "https://files.pythonhosted.org/packages/24/7d/ad3e59015135f5261c95fd4cafeff159c955febd83a99a1d9250c4233815/numpy-2.5.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f7119ebff1a9829e9f431a4f9d28e703023bb6b9fe7c8f724467dbfc27c94ab3", size = 16527531, upload-time = "2026-07-04T17:06:26.69Z" },
629
- { url = "https://files.pythonhosted.org/packages/83/d0/a39b2fbcde9cb17a1dac678f254b33a6336298af9df338824c685425d5e8/numpy-2.5.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e824c2acf8862052246be5a44c15da1777940c60d010dd2aab897824d9c430f9", size = 18431940, upload-time = "2026-07-04T17:06:29.521Z" },
630
- { url = "https://files.pythonhosted.org/packages/04/12/cff070947791c1ed425ff76413189adbdc2fbe215eba7ce7fa454a03c7f8/numpy-2.5.1-cp312-cp312-win32.whl", hash = "sha256:08d60c810432eb83360958dea0999ac4cfb94531ea8efcbf0b7f277c2068aeb2", size = 6066764, upload-time = "2026-07-04T17:06:32.571Z" },
631
- { url = "https://files.pythonhosted.org/packages/65/66/53f31807a48a750f9d748da273bc3fcedd12b27ff1f3e373bfec55ef2dc0/numpy-2.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:f7d60026c0bdb1380e83bfa7a0419c4577ee4b9a08880afcb6dadeb74c649fa2", size = 12430966, upload-time = "2026-07-04T17:06:34.926Z" },
632
- { url = "https://files.pythonhosted.org/packages/2b/2a/d1a88066b1c14186f5d3c0d18c94f17b064511982bab0578d49ee9d43c29/numpy-2.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:17a25e09640602e10bc8de0e6fa2b3fd68eedd84ba6d7842dc8f32f9ab87bd0b", size = 10350488, upload-time = "2026-07-04T17:06:37.785Z" },
633
- { url = "https://files.pythonhosted.org/packages/eb/07/ec2a3f0c91761581d4b7104a740791800025983f9a4dc4e73f91a99aeac4/numpy-2.5.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0bfebd8695f9863592fe744be833a258120b14a9f39da255e8aa8fade2c0ddd1", size = 16796419, upload-time = "2026-07-04T17:06:40.37Z" },
634
- { url = "https://files.pythonhosted.org/packages/ab/ab/ddb499fc4f8780354395face5b65c7fd107bcd6e1d667a5f07d046956f6f/numpy-2.5.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:30b44a6b53a7ae63c54c089a8726e5563ed302716c5b7ccc85afade40b0e7ff6", size = 11765832, upload-time = "2026-07-04T17:06:42.768Z" },
635
- { url = "https://files.pythonhosted.org/packages/88/b3/3c28c558a09fc72100c646dac6d2fce8e834c471b0edca01a29996706117/numpy-2.5.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:6165343f81b56ef8f514f396989e529b61d9dc709b99421b07e9f3e698e2287d", size = 5325143, upload-time = "2026-07-04T17:06:45.466Z" },
636
- { url = "https://files.pythonhosted.org/packages/5e/0e/ce19b985bb15c596f4f05954e76cccc77c845083b3b8f938a6c68e523128/numpy-2.5.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4939237038ada79308dda3204ac6462df056b5672b2e25db1149cf873668b3e1", size = 6659749, upload-time = "2026-07-04T17:06:47.288Z" },
637
- { url = "https://files.pythonhosted.org/packages/2e/20/1ee6614d64332a1bba6411f38e68cb79eec1b2459e20a623777c5c5492a2/numpy-2.5.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c6759f538fb912fc46de0a6b1758ccf7b57bc7c7ebebc23974fdac3de8db0cd", size = 15164716, upload-time = "2026-07-04T17:06:49.494Z" },
638
- { url = "https://files.pythonhosted.org/packages/ed/a7/2bcd3fdbb87804755c35b729bf8709d62025c5f4cfd7d5b2415997097515/numpy-2.5.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9726558e8db4a5bf7929a70ae50f63abda4daf0efe810e3bfbab95976f75fc1a", size = 16661440, upload-time = "2026-07-04T17:06:52.061Z" },
639
- { url = "https://files.pythonhosted.org/packages/fc/d7/a41e3310c886fe457d36e670bbf24fae411aca8a7b6ad92a32afd924077c/numpy-2.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3935f3b419b244a02732676fa5317a9193cc596a4c0646db07e5b421229ac9f7", size = 16526305, upload-time = "2026-07-04T17:06:54.605Z" },
640
- { url = "https://files.pythonhosted.org/packages/53/75/4333a9a707c1edd3a4e1a0c58eca52c0f31e55089fa80db02b5565b24df7/numpy-2.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dc932a65ded7ce9013d120845a2514dcccb1a67bfc8deb8d37633762951904a6", size = 18423008, upload-time = "2026-07-04T17:06:57.54Z" },
641
- { url = "https://files.pythonhosted.org/packages/ee/90/e314a32b1c11a2ffe818ddad3a57b50b4b6e1b6c487192eb50cdef0415d0/numpy-2.5.1-cp313-cp313-win32.whl", hash = "sha256:4b4ff1608417eb7a59da7b967bbb798cacfe071d2caf526a24281cd562072ed9", size = 6063885, upload-time = "2026-07-04T17:07:00.14Z" },
642
- { url = "https://files.pythonhosted.org/packages/10/70/800b3fca480af32df9e8ea9f3d4a0c8feb4b32d7f195d174eabbda4829ad/numpy-2.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:6c3fe51bc6a16453d452997053454f309e8e0ed7b42d6b361ce4ac8c32913d74", size = 12425674, upload-time = "2026-07-04T17:07:02.387Z" },
643
- { url = "https://files.pythonhosted.org/packages/8b/0b/196350c122f50f6ca56846f2d71efd5e0d24b7b2e07355e019b2e2c7a11e/numpy-2.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:f7feb014281029e628ba2d5a007407443b06e418b6fe451d1e2adcbc8eba0107", size = 10350256, upload-time = "2026-07-04T17:07:04.878Z" },
644
- { url = "https://files.pythonhosted.org/packages/db/f4/731b6085a83faf6ca843394cbd5e217280c214399f7e8b21b9f552af0ae2/numpy-2.5.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:7c786fe9a5bbe360022e584c5a34cf6b54265c71bd7ec8ac3d8fec38968071f8", size = 16795063, upload-time = "2026-07-04T17:07:07.374Z" },
645
- { url = "https://files.pythonhosted.org/packages/bf/64/0e215f2048dd11a55bb989ed41b3585ef57452404e638d703a211a3e4157/numpy-2.5.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:32985c896d897419ef8da6917872d80b78ad0ea26d85b23245c7366ffde76d75", size = 11776652, upload-time = "2026-07-04T17:07:09.907Z" },
646
- { url = "https://files.pythonhosted.org/packages/b5/59/2b844c7a6e9deff69b404a66221e1542937734f65d5e6e39411876053862/numpy-2.5.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:efd736408cc97c79b9e6917338dfc8f06013b2274f992e96b1d9a81a71e2a2c2", size = 5335944, upload-time = "2026-07-04T17:07:12.227Z" },
647
- { url = "https://files.pythonhosted.org/packages/86/51/9bf7cb2cabcebc9e017e4ec7e6322b378317a542c08b4cb68479c1efc716/numpy-2.5.1-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:ab84dc6b074fa881cae55bea94cc4f68e285181ba7f32497bf7dee6b1496165b", size = 6656266, upload-time = "2026-07-04T17:07:14.368Z" },
648
- { url = "https://files.pythonhosted.org/packages/83/3e/fb7615b211b82a32f44d5180a6d421b61f84d4fadd578b48ba4ac34e189f/numpy-2.5.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:caf3e317d33d60c37986b452613f4ab51246d0691350c03d0cb4a898627f4a95", size = 15179720, upload-time = "2026-07-04T17:07:16.272Z" },
649
- { url = "https://files.pythonhosted.org/packages/41/5f/0f992cb24560673496c5d68de61913b57166ce530ffda07c1f280e0cc464/numpy-2.5.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:54ad769f17bc2d833b620851989f62054fb9ab93c969d9e1dc3c8e3d56beea21", size = 16664835, upload-time = "2026-07-04T17:07:19.021Z" },
650
- { url = "https://files.pythonhosted.org/packages/a2/2f/97d6475ee91afe2587797d09446f9d3e475ad4cb681662d824809327b75a/numpy-2.5.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c12afb53450fa976d4c681c50a7423729a4c51c0465ed9f32b8a9cabbc472373", size = 16539135, upload-time = "2026-07-04T17:07:22.015Z" },
651
- { url = "https://files.pythonhosted.org/packages/c4/5b/4db81e4ba0be7e2776b1de68c82aa862c7f8ec27e1b4927d4ae075e20678/numpy-2.5.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e8c11c405efc5ff6816d5983c96cdfa215bab3428961243af3ff59b228490438", size = 18426684, upload-time = "2026-07-04T17:07:24.941Z" },
652
- { url = "https://files.pythonhosted.org/packages/1f/64/c0ba2d90724d450279a7df8f32057241070250a26a7e2b5337d77347f481/numpy-2.5.1-cp314-cp314-win32.whl", hash = "sha256:f2479a47f8d5932d1718168a681ad6e536a9df484c83cfcf9de365e164537ace", size = 6116103, upload-time = "2026-07-04T17:07:27.622Z" },
653
- { url = "https://files.pythonhosted.org/packages/c1/1a/837f9ed7405adcd7a40538792eb169eddd8fa5630c16a1ef49dae71a30f4/numpy-2.5.1-cp314-cp314-win_amd64.whl", hash = "sha256:24d0eb82c0541d3415a33425db64ae439dffccd7b4dbcb30e7c35120205c506a", size = 12562177, upload-time = "2026-07-04T17:07:29.887Z" },
654
- { url = "https://files.pythonhosted.org/packages/22/ed/49707938b6dd0a78a9178dd93227dc89e4c11af47f5c798d70366e8d0483/numpy-2.5.1-cp314-cp314-win_arm64.whl", hash = "sha256:5a4c988b38d261deeeaad9954e3deb091ad905c94e8bb6708654ef1d97f286b0", size = 10627739, upload-time = "2026-07-04T17:07:32.568Z" },
655
- { url = "https://files.pythonhosted.org/packages/a6/c7/bb4b882cfe7f299cbc8b66e42e7dd78cf9d14e40f9469fc5e3db7e15b3bd/numpy-2.5.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a33276be12fa045805f477f22482088b66bb758ffbe89a9d21457de863a32e22", size = 11894709, upload-time = "2026-07-04T17:07:34.941Z" },
656
- { url = "https://files.pythonhosted.org/packages/40/3f/5af7f4a7f6224aef48017aa82bb6174c7a659d724be0c75017b7e64a55b4/numpy-2.5.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:f089d7b00756190aacf1f5d34bdf38c3c430ac82b4f868f8cede73380460fce7", size = 5453810, upload-time = "2026-07-04T17:07:37.495Z" },
657
- { url = "https://files.pythonhosted.org/packages/20/c9/3474309bc94d634d3f9c3eddf03250ecb8c22cd948ef16fef69a77cc5d7b/numpy-2.5.1-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:09e9bfd8d2cf479c7d174804fb3811c53a8e9f20a37444008606b57d6b7a826d", size = 6761189, upload-time = "2026-07-04T17:07:39.563Z" },
658
- { url = "https://files.pythonhosted.org/packages/90/8a/558ae39fdd55d7e7f7fef9a84a6e964ac6b23edbd2a07e52bb084500507d/numpy-2.5.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e68d8dd1e7eba712948f2053a29ec86917bc70ba1358df869d9f06649ef9cf09", size = 15225039, upload-time = "2026-07-04T17:07:41.682Z" },
659
- { url = "https://files.pythonhosted.org/packages/63/27/ca7392b2d030277bdf0273e7d23255b3ee57d57a7c170a6f4fb3981e1e5d/numpy-2.5.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:99d5095fa265a0c4152e7bb12759e14381ef5496152f1ce58f44bdf55c44beb4", size = 16701306, upload-time = "2026-07-04T17:07:44.611Z" },
660
- { url = "https://files.pythonhosted.org/packages/02/42/03d53ae7996c44d4374a8262e9dc41671fd56cbb98f7d47ef85cf5da4c6b/numpy-2.5.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ab87a91b3cc3382b8956095bd8f95e00cf679bb81554339be1a2ba404a1473c1", size = 16589955, upload-time = "2026-07-04T17:07:47.694Z" },
661
- { url = "https://files.pythonhosted.org/packages/7b/15/6c1784ae469640e65db111e9a34b3d0f14d91e8a38b9ce34810ced370dbb/numpy-2.5.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:224ca51130ef7da85bea2191625181cb4f337f9cb64b471f10c1a12aa8b60077", size = 18464252, upload-time = "2026-07-04T17:07:50.684Z" },
662
- { url = "https://files.pythonhosted.org/packages/94/a8/f98e50356cf167df656c526c2dfeec2d7dde182f2a3da4b458a5938e2776/numpy-2.5.1-cp314-cp314t-win32.whl", hash = "sha256:6eab239876581b2b3c5a242281b6007bbdbcd1c7085d7709bb57c5929b11e6bf", size = 6263298, upload-time = "2026-07-04T17:07:53.445Z" },
663
- { url = "https://files.pythonhosted.org/packages/72/ac/96ae880cdecad0b3275d9359fcec72667b49a4863c9f12942e43679dda02/numpy-2.5.1-cp314-cp314t-win_amd64.whl", hash = "sha256:83ce9c80d5b521b0d77ddcbe5447c218d247929b6cc056ca5351342accfff0af", size = 12748623, upload-time = "2026-07-04T17:07:55.384Z" },
664
- { url = "https://files.pythonhosted.org/packages/a1/5a/4d2b1601df3602dba7a14f3348ba9bfe94a18adb428e693df6154c293831/numpy-2.5.1-cp314-cp314t-win_arm64.whl", hash = "sha256:5a6db61f9aaa57e369905c67d852045d3c4f7126405b29d09b19dec118e9c9cb", size = 10697674, upload-time = "2026-07-04T17:07:58.506Z" },
665
- ]
666
-
667
  [[package]]
668
  name = "nvidia-cublas"
669
  version = "13.1.1.3"
@@ -1055,6 +1071,25 @@ wheels = [
1055
  { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" },
1056
  ]
1057
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1058
  [[package]]
1059
  name = "pytest"
1060
  version = "9.1.1"
@@ -1378,8 +1413,7 @@ resolution-markers = [
1378
  dependencies = [
1379
  { name = "joblib", marker = "python_full_version >= '3.11'" },
1380
  { name = "narwhals", marker = "python_full_version >= '3.11'" },
1381
- { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" },
1382
- { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
1383
  { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" },
1384
  { name = "scipy", version = "1.18.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
1385
  { name = "threadpoolctl", marker = "python_full_version >= '3.11'" },
@@ -1559,7 +1593,7 @@ resolution-markers = [
1559
  "python_full_version >= '3.12'",
1560
  ]
1561
  dependencies = [
1562
- { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
1563
  ]
1564
  sdist = { url = "https://files.pythonhosted.org/packages/a7/25/c2700dfaf6442b4effaa91af24ebce5dc9d31bb4a69706313aae70d72cd0/scipy-1.18.0.tar.gz", hash = "sha256:67b2ad2ad54c72ca6d04975a9b2df8c3638c34ddd5b28738e94fc2b57929d378", size = 30774447, upload-time = "2026-06-19T15:01:43.456Z" }
1565
  wheels = [
@@ -1612,8 +1646,7 @@ source = { registry = "https://pypi.org/simple" }
1612
  dependencies = [
1613
  { name = "huggingface-hub" },
1614
  { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
1615
- { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" },
1616
- { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
1617
  { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
1618
  { name = "scikit-learn", version = "1.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
1619
  { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
@@ -1832,8 +1865,7 @@ source = { registry = "https://pypi.org/simple" }
1832
  dependencies = [
1833
  { name = "huggingface-hub" },
1834
  { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
1835
- { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" },
1836
- { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
1837
  { name = "packaging" },
1838
  { name = "pyyaml" },
1839
  { name = "regex" },
@@ -1902,6 +1934,27 @@ wheels = [
1902
  { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" },
1903
  ]
1904
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1905
  [[package]]
1906
  name = "uvicorn"
1907
  version = "0.52.1"
@@ -1925,11 +1978,11 @@ dependencies = [
1925
  { name = "httpx" },
1926
  { name = "huggingface-hub" },
1927
  { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
1928
- { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" },
1929
- { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
1930
  { name = "orjson" },
1931
  { name = "sentence-transformers" },
1932
  { name = "torch" },
 
1933
  { name = "uvicorn" },
1934
  ]
1935
 
@@ -1952,6 +2005,7 @@ requires-dist = [
1952
  { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.3.0" },
1953
  { name = "sentence-transformers", specifier = ">=2.5.0" },
1954
  { name = "torch", specifier = ">=2.2.0" },
 
1955
  { name = "uvicorn", specifier = ">=0.28.0" },
1956
  ]
1957
  provides-extras = ["dev"]
 
314
  { url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071, upload-time = "2025-12-15T08:41:44.973Z" },
315
  ]
316
 
317
+ [[package]]
318
+ name = "llvmlite"
319
+ version = "0.48.0"
320
+ source = { registry = "https://pypi.org/simple" }
321
+ sdist = { url = "https://files.pythonhosted.org/packages/dc/a0/acc8ffcd5bdc63df0097e22c719bfcd61b604358343089313a8aebbb24ab/llvmlite-0.48.0.tar.gz", hash = "sha256:543b19f9ef8f3c7c60d1468191e4ee1b1537bf9f8a3d56f64c0ddd98de92edd2", size = 184016, upload-time = "2026-07-02T20:20:05.308Z" }
322
+ wheels = [
323
+ { url = "https://files.pythonhosted.org/packages/a2/4e/32543c42568fb321b3bdfcf9106e4116ab8f5a7bbcfd9ecf5569b0c07d83/llvmlite-0.48.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:614aad57df707e3172efd5165f2aa7da6a0c6897e40dce590bf756396815ba76", size = 40480650, upload-time = "2026-07-01T18:41:01.945Z" },
324
+ { url = "https://files.pythonhosted.org/packages/a9/0d/6aa48abd423067139a129d1434b77bbcc56080db51d12a88510bb491ca3d/llvmlite-0.48.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:13532f248960ba888ad5ab8150494e2f3a3d20e5f59f264e63741ea5b0ba844c", size = 59890118, upload-time = "2026-07-01T18:41:10.608Z" },
325
+ { url = "https://files.pythonhosted.org/packages/5a/c7/aa917444d871a79608af49149de1b28764e87d2ab41f933c5cd02431d03d/llvmlite-0.48.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ee0c77685a18f5fca994ae21d0763007fca5c5c64b41de37accc78b69079176", size = 58343459, upload-time = "2026-07-01T18:41:06.21Z" },
326
+ { url = "https://files.pythonhosted.org/packages/c5/2b/ceee1cdc263617109d514ac4d1b31f10a282662740ff7d5777baae25b3b5/llvmlite-0.48.0-cp310-cp310-win_amd64.whl", hash = "sha256:02853fe4214acb3780fc920c3fee10564b61d58a35e1b78afcc8a546c2deaba3", size = 41864734, upload-time = "2026-07-01T18:41:14.746Z" },
327
+ { url = "https://files.pythonhosted.org/packages/9a/55/595981f14fbae9ba966feb12af552b1fe69889e44e64ac883a731ed335e0/llvmlite-0.48.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:56a7e24607d3f02d7b1bae8d29c7e1e423d53143d68b072999777f19678fe77b", size = 40480651, upload-time = "2026-07-01T18:41:18.438Z" },
328
+ { url = "https://files.pythonhosted.org/packages/26/08/0109d1b9cb3f4603f3890e30bc66c65332b79185f12a045343b2ae431f67/llvmlite-0.48.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6fa532d6bb3fd3f0803567c736401c54aecfe1a396d3ad25d2440d220e09f0e7", size = 59890118, upload-time = "2026-07-01T18:41:28.184Z" },
329
+ { url = "https://files.pythonhosted.org/packages/02/eb/c5281be180c789cdffbf45b671884c57d7e61345ef3b0f643a4965e108e8/llvmlite-0.48.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:979a66a3f28a02565383ff463527dce78e9b856298872a361283132488e83591", size = 58343458, upload-time = "2026-07-01T18:41:23.397Z" },
330
+ { url = "https://files.pythonhosted.org/packages/aa/f7/b3222b13f2d424dae3c9e63fde476af25ebccf1f3faf0b52d1b79fc15c70/llvmlite-0.48.0-cp311-cp311-win_amd64.whl", hash = "sha256:efaee0276e5e17c2b99b92e0c974bd484ef5977cf5dbc9168e82b71578edb47f", size = 41864734, upload-time = "2026-07-01T18:41:31.932Z" },
331
+ { url = "https://files.pythonhosted.org/packages/92/a2/28696a9e61e245d1a79816d29d106692a90a2b6e7d78c98b326db70827af/llvmlite-0.48.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:d66c3beb4209087ddd4cf4ed2a0856b6887e6a913bdcf1aacfec9851cf2cba4e", size = 40480651, upload-time = "2026-07-01T18:41:35.694Z" },
332
+ { url = "https://files.pythonhosted.org/packages/80/f2/72409351db66d0a317ec5087e076f31fb7b773a640db8a90ce6b5cac9edd/llvmlite-0.48.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:416fa4c2c66c2c6dc6d0a402648c19206e548efa0aa1eff01ad5cdad0af8217d", size = 59890118, upload-time = "2026-07-01T18:41:44.886Z" },
333
+ { url = "https://files.pythonhosted.org/packages/3a/27/5ae2f3722606360480707adb47f001ad89df8251d06b14ee80336e660b66/llvmlite-0.48.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f5e5a5131045b72345c71062ea1a91910dde913792b6c9b28ebb2c1c0a712e98", size = 58343459, upload-time = "2026-07-01T18:41:40.306Z" },
334
+ { url = "https://files.pythonhosted.org/packages/16/78/d824ffff7521cd140dc2006e44ce2bc82e64b48d1b32e90e956308c85a74/llvmlite-0.48.0-cp312-cp312-win_amd64.whl", hash = "sha256:d45c7541a80934ec6d8ab0defe67439494ecd2193cbf852a44ba827808976ac1", size = 41865022, upload-time = "2026-07-01T18:41:48.663Z" },
335
+ { url = "https://files.pythonhosted.org/packages/9c/23/fe9316d14626b42c73ef0b502e724705a6ee9450afe53759c0a99c37c2d7/llvmlite-0.48.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:a83a99ef0c05b4ccddf9b6218ed9fe84b653a0caf7c1d9dbe148d6d16c67f518", size = 40480652, upload-time = "2026-07-01T18:41:52.216Z" },
336
+ { url = "https://files.pythonhosted.org/packages/1b/4a/90715fa12006d681270b08d881195b6fab3ec39572e048764a1f7f59fed7/llvmlite-0.48.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8761b9e522f55207e24424fcd98370289eec2710bf8e915c82d1053f642450dc", size = 59890120, upload-time = "2026-07-01T18:42:00.748Z" },
337
+ { url = "https://files.pythonhosted.org/packages/70/5e/7b3e20d64650ca3c80af0cdb664ec4b575ec83d9d4dd05bea8bd31f9bbb6/llvmlite-0.48.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2fe5cb59b2063bfa039dcb8ca6481c0181bf552f340d10dcf61d7996a665556e", size = 58343457, upload-time = "2026-07-01T18:41:56.41Z" },
338
+ { url = "https://files.pythonhosted.org/packages/17/97/5a430055d1838cf1fb7a01cfa943300f5e4c026fc6333a522c5e4a03b0c1/llvmlite-0.48.0-cp313-cp313-win_amd64.whl", hash = "sha256:91c7e24e74cde3f02b88aa5acca678373f9e069f3b98531b3dbb3a142d9d10bb", size = 41865022, upload-time = "2026-07-01T18:42:04.57Z" },
339
+ { url = "https://files.pythonhosted.org/packages/8d/8e/8170f2e0c217f88069c333d85bb976e536b332aecfcce606ddbdb249385f/llvmlite-0.48.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:321f1ac39b462603f0b589751aecf2d237d056f6d005749c1752b6f23ec3f074", size = 40480650, upload-time = "2026-07-01T18:42:07.935Z" },
340
+ { url = "https://files.pythonhosted.org/packages/d6/e1/05b50692b647cac3c18200ac485b04f342f00ed173c9cc46767274469a15/llvmlite-0.48.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:05f0103c8f2f96a37441337e3643863c01b8e83e530aff38960dcb383c54a065", size = 59890115, upload-time = "2026-07-01T18:42:17.805Z" },
341
+ { url = "https://files.pythonhosted.org/packages/f7/c3/470b8c4ff9ae2db2f9cf5c3e73de76ed908a32788ae9eb5602d43e6a476b/llvmlite-0.48.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:37d66fae72802175b0bfe1ea06e624b51e2d7aee6c3c34bbd09739b8f88e8e0b", size = 58343457, upload-time = "2026-07-01T18:42:13.217Z" },
342
+ { url = "https://files.pythonhosted.org/packages/c9/2d/6a5171fb7236ac0895e1a02ccba3735bf291e8597239aa6421894d3c0ba8/llvmlite-0.48.0-cp314-cp314-win_amd64.whl", hash = "sha256:966dcab0a598e2bd8fb5f2cc082cf7b07bae564fc485a3a8692393caf986facf", size = 42986372, upload-time = "2026-07-01T18:42:21.483Z" },
343
+ { url = "https://files.pythonhosted.org/packages/94/e3/7a93e09c9f94e637ca90209ceef0334a9a1d45b0bdb7c92ff922d25d6187/llvmlite-0.48.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:7a5c413317050a1d67c34708bde97707f9b2257ef1017f7532d21fe7d9a9ff30", size = 40480654, upload-time = "2026-07-01T18:42:25.076Z" },
344
+ { url = "https://files.pythonhosted.org/packages/27/98/a29133b4728671a175f7d616fab8b1c6e1d8c269d1523581d3160697bfb1/llvmlite-0.48.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d9f952dff6c350c529997423d4fa43abae9722a884ac7ffafc37a3af676e7db", size = 59890119, upload-time = "2026-07-01T18:42:33.88Z" },
345
+ { url = "https://files.pythonhosted.org/packages/1a/cf/7aac11a1f1c7ec54b60c7f6814e87561fb6b55b2f290455d7941eb113420/llvmlite-0.48.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:054aa7d46595935565f276cf0c1659b4f10929c996dd4a606875fae26fba2a23", size = 58343460, upload-time = "2026-07-01T18:42:29.545Z" },
346
+ { url = "https://files.pythonhosted.org/packages/db/41/b96f440c7df5ebba07872cad4e30fbc3560387755b1ea0b629adb76d5ca8/llvmlite-0.48.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d0b3c61aac83b42fb48cc96bffbf57c81b82b2aa92276b7ed6420c814629a99a", size = 42986383, upload-time = "2026-07-01T18:42:37.544Z" },
347
+ ]
348
+
349
  [[package]]
350
  name = "markdown-it-py"
351
  version = "4.2.0"
 
495
  { url = "https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762", size = 2068504, upload-time = "2025-12-08T17:02:38.159Z" },
496
  ]
497
 
498
+ [[package]]
499
+ name = "numba"
500
+ version = "0.66.0"
501
+ source = { registry = "https://pypi.org/simple" }
502
+ dependencies = [
503
+ { name = "llvmlite" },
504
+ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
505
+ { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
506
+ ]
507
+ sdist = { url = "https://files.pythonhosted.org/packages/ae/a0/570e3dc53e5602b49108f62a13e529f1eec8bfc7ef37d49c825924dcf546/numba-0.66.0.tar.gz", hash = "sha256:b900e63a0e26c05ea9a6d5a3a5a0a177cb64c5011887bf43edb8c3ed2c38d363", size = 2806181, upload-time = "2026-07-01T23:12:46.36Z" }
508
+ wheels = [
509
+ { url = "https://files.pythonhosted.org/packages/2b/48/d139bde40f2359351bfe26ee1b261937f458ac177ab810d4f045ae1c9d92/numba-0.66.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:27951c47e0def9bf8afe580eb961102902e2fd23cb77924b7d9d7cc0f8b444cb", size = 2727368, upload-time = "2026-07-01T23:12:04.282Z" },
510
+ { url = "https://files.pythonhosted.org/packages/36/e4/b780bfa9191410da50ba249cb3248a75014e17f611e72709cbddcb21f42d/numba-0.66.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc408c54b450f41582f4be1608f8981c1dcc44c7f40355cc150dd93015753407", size = 3803554, upload-time = "2026-07-01T23:12:06.379Z" },
511
+ { url = "https://files.pythonhosted.org/packages/1c/b2/a051b96626bdf5c4d8fa6b8d450605c09638d85dc872ab63ef9a67096dca/numba-0.66.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7c14c044c06b453ec3fa7715dfe75425e2ba72c73377a7ffde6d9ec511dfd94c", size = 3510065, upload-time = "2026-07-01T23:12:08.051Z" },
512
+ { url = "https://files.pythonhosted.org/packages/34/01/24dcdc3e919522e2efbd92969c281ff40deb1d5f8a994bcd0057081c158c/numba-0.66.0-cp310-cp310-win_amd64.whl", hash = "sha256:2338cc0d43609fe448930848fd35a5bc688761b986f81b597a6f45cc0f8c9577", size = 2780379, upload-time = "2026-07-01T23:12:09.772Z" },
513
+ { url = "https://files.pythonhosted.org/packages/9e/02/970796b4daa709604cde22e87a7cda9bde473c278ea4a75f59fe38cee47f/numba-0.66.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:bbd531c327557a9004507fa6bff06c53ab51a7a5776b75261bb9cef1efe2b2ea", size = 2727049, upload-time = "2026-07-01T23:12:11.296Z" },
514
+ { url = "https://files.pythonhosted.org/packages/8c/99/33a6ed9c1a0b5e42efa98eb0edf617d61dca576c82625947377b1d4540c9/numba-0.66.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc6629becb21a867d85401ec89f426dd24c484a4193ade8a38309debfd1529ca", size = 3808870, upload-time = "2026-07-01T23:12:12.944Z" },
515
+ { url = "https://files.pythonhosted.org/packages/04/20/8c51126025211659235b8de2866dfa226984ae0c8273461a3cf374716741/numba-0.66.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aac69f3ccb8af100f5913c1241edc9692bad1cdd2508721713f426eb06c9a659", size = 3514498, upload-time = "2026-07-01T23:12:15.307Z" },
516
+ { url = "https://files.pythonhosted.org/packages/5e/c9/9476940bc6d5caf5c0cf2e4c5feecbf01244bbe6f914614082dd7a3e520e/numba-0.66.0-cp311-cp311-win_amd64.whl", hash = "sha256:fb601841d9e02e6237bb6522e36d0741614be3cfe2b482a6f00a41b5ba209443", size = 2780225, upload-time = "2026-07-01T23:12:16.924Z" },
517
+ { url = "https://files.pythonhosted.org/packages/62/a3/70deb7f88461c1cd5d16aa990c2380604102661a427667b8950dcdccc27f/numba-0.66.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:53ca5900b7cab15109796030113a6b28576bae5ad7bb507ad6dd1360ddd81ba4", size = 2727264, upload-time = "2026-07-01T23:12:18.669Z" },
518
+ { url = "https://files.pythonhosted.org/packages/2d/55/25c319845e9a4e08f16611ddbda56a192eb7b6ed13e1a2bff2da272ffb97/numba-0.66.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0999e3ee1b18c48e1fb51d11af35ef59852c7f4f50569c9550c25faef0616ad1", size = 3866252, upload-time = "2026-07-01T23:12:20.429Z" },
519
+ { url = "https://files.pythonhosted.org/packages/71/ef/a82d6fd6bf1b0fe461651e924d3647eeec9ac17f8eee4896264bf7480930/numba-0.66.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:efe0d2d5099790df945e0cb6e1b3104bd965d7bbfac50d62f1d5d1d6ade0825d", size = 3566974, upload-time = "2026-07-01T23:12:22.116Z" },
520
+ { url = "https://files.pythonhosted.org/packages/fc/eb/9e6171e378822ab191c7abcfd3d8cfc8644516f6c7834c22e210e4acc070/numba-0.66.0-cp312-cp312-win_amd64.whl", hash = "sha256:b075a4e7ebc43dc6294f223e2821659656209fd5e0ce53245877c23d66d6e1a9", size = 2797403, upload-time = "2026-07-01T23:12:23.724Z" },
521
+ { url = "https://files.pythonhosted.org/packages/03/52/176c02d005c5c5143cde10a85bbcdcb6236d9e34c3aac089380e0506cd1d/numba-0.66.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:380b2556a2019ccd1e956ae77dd257eaa39403f7520768b626d44b755112785e", size = 2727084, upload-time = "2026-07-01T23:12:25.434Z" },
522
+ { url = "https://files.pythonhosted.org/packages/44/b5/e930010965568fe7f2c6c962fd2849d458cb9f62c3ab7584af8a19a2b40a/numba-0.66.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:939316d5d8619751207b8972a67852b5a7646665298cb4de693cd6bf135152f4", size = 3873663, upload-time = "2026-07-01T23:12:27.308Z" },
523
+ { url = "https://files.pythonhosted.org/packages/d0/ec/5b51457cbe96e4831141d83e892e65191b23a1b78728456c62909d231ace/numba-0.66.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cdf506775d9f02eb92a87bf5c5b1e0d25506fd18cafd769f4ed914a8feac73e7", size = 3573529, upload-time = "2026-07-01T23:12:28.944Z" },
524
+ { url = "https://files.pythonhosted.org/packages/83/7e/cea7710e96913d3c7f2999f16db1b28e6c5be5171cbf40f77f98333a7243/numba-0.66.0-cp313-cp313-win_amd64.whl", hash = "sha256:c5bfe5350284509ab0474390321454c3a8627a188af5b68c910e83df3e2db4a7", size = 2797247, upload-time = "2026-07-01T23:12:30.774Z" },
525
+ { url = "https://files.pythonhosted.org/packages/96/7a/7e0e73550eb4e41ede6e72fb5371f4539537a4d770a3b73fa9b61aea0622/numba-0.66.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:46ae5f2b19e2af3c33c2df100306a90ea2f981c8158b0390f8bf6c20eee7357e", size = 2727296, upload-time = "2026-07-01T23:12:32.39Z" },
526
+ { url = "https://files.pythonhosted.org/packages/0f/26/885774c006de6620ed3d10f45d8e20fe0b8e6aad6d573211a2cbc8b3e528/numba-0.66.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e2b101f23b8b63978d334574d2039f27f0dccfe1d891756f33a2e2f3e4c88cf4", size = 3842720, upload-time = "2026-07-01T23:12:33.938Z" },
527
+ { url = "https://files.pythonhosted.org/packages/93/99/edebf7de890b73973d839dd971cf73734adfb81ffa1b4504f84b9059c3e5/numba-0.66.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:63b943eb2c9ba371908ce2cd6dfc643db51fc40f7966993376a1701bc922f537", size = 3543537, upload-time = "2026-07-01T23:12:35.566Z" },
528
+ { url = "https://files.pythonhosted.org/packages/66/c5/b46ad28ac3681d035ea21365c5e052149062e1a0a9affd0563d2760ea6ff/numba-0.66.0-cp314-cp314-win_amd64.whl", hash = "sha256:bd57790acd20f6a468e0ad333ef6b82355e309a92310fb7dff80e919f01a21a9", size = 2799250, upload-time = "2026-07-01T23:12:37.154Z" },
529
+ { url = "https://files.pythonhosted.org/packages/10/6f/5e77a7397a37dd16f57a7b72e7e470db5227b68e3639df0d13a8e674883d/numba-0.66.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:db7735d15ea17a283d6485b9fa3504769f78fd86e5146638ad5e8da57c031b9e", size = 2730342, upload-time = "2026-07-01T23:12:38.758Z" },
530
+ { url = "https://files.pythonhosted.org/packages/39/fd/e9c9680a3813f3d781c20e5d53c1074801b787d4feecca0472fdd7c05ce1/numba-0.66.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:651a2b53298340956db26ecbe7ab043106b50a40c2807e66d617a4917245a4ab", size = 3878695, upload-time = "2026-07-01T23:12:40.302Z" },
531
+ { url = "https://files.pythonhosted.org/packages/61/3a/9b363287b85fcd4537ea3878793822878b2ac1008a78159d2096fea628de/numba-0.66.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c1144ba1720ea59ad79f4f488ed54d149b2613b357f7e445678b7d0739c70e9", size = 3596323, upload-time = "2026-07-01T23:12:42.805Z" },
532
+ { url = "https://files.pythonhosted.org/packages/4c/f2/dca53d50b8f2289dd01954ace9da261e0487d5b74b188b4304e4ecc3492c/numba-0.66.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d426178fb991a85714c43112a8ea7b9d9579ea856ad8dcdb9c1c3941903ba5be", size = 2804772, upload-time = "2026-07-01T23:12:44.399Z" },
533
+ ]
534
+
535
  [[package]]
536
  name = "numpy"
537
  version = "2.2.6"
 
602
  version = "2.4.6"
603
  source = { registry = "https://pypi.org/simple" }
604
  resolution-markers = [
605
+ "python_full_version >= '3.12'",
606
  "python_full_version == '3.11.*'",
607
  ]
608
  sdist = { url = "https://files.pythonhosted.org/packages/d0/ad/fed0499ce6a338d2a03ebae59cd15093910c8875328855781952abf6c2fe/numpy-2.4.6.tar.gz", hash = "sha256:f3a3570c4a2a16746ac2c31a7c7c7b0c186b95ce902e33db6f28094ed7387dda", size = 20735807, upload-time = "2026-05-18T23:37:14.07Z" }
 
680
  { url = "https://files.pythonhosted.org/packages/15/ce/e5ec180bc41812edcd8daeb8639d205622c0e8c02259d8ab25a0201b3c2a/numpy-2.4.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2803abfebfc990042cd494d8ce2d5f82e9d847af6d35ec486923aa19dbad5e73", size = 12504263, upload-time = "2026-05-18T23:37:09.715Z" },
681
  ]
682
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
683
  [[package]]
684
  name = "nvidia-cublas"
685
  version = "13.1.1.3"
 
1071
  { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" },
1072
  ]
1073
 
1074
+ [[package]]
1075
+ name = "pynndescent"
1076
+ version = "0.6.0"
1077
+ source = { registry = "https://pypi.org/simple" }
1078
+ dependencies = [
1079
+ { name = "joblib" },
1080
+ { name = "llvmlite" },
1081
+ { name = "numba" },
1082
+ { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
1083
+ { name = "scikit-learn", version = "1.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
1084
+ { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
1085
+ { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" },
1086
+ { name = "scipy", version = "1.18.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
1087
+ ]
1088
+ sdist = { url = "https://files.pythonhosted.org/packages/4a/fb/7f58c397fb31666756457ee2ac4c0289ef2daad57f4ae4be8dec12f80b03/pynndescent-0.6.0.tar.gz", hash = "sha256:7ffde0fb5b400741e055a9f7d377e3702e02250616834231f6c209e39aac24f5", size = 2992987, upload-time = "2026-01-08T21:29:58.943Z" }
1089
+ wheels = [
1090
+ { url = "https://files.pythonhosted.org/packages/b2/e6/94145d714402fd5ade00b5661f2d0ab981219e07f7db9bfa16786cdb9c04/pynndescent-0.6.0-py3-none-any.whl", hash = "sha256:dc8c74844e4c7f5cbd1e0cd6909da86fdc789e6ff4997336e344779c3d5538ef", size = 73511, upload-time = "2026-01-08T21:29:57.306Z" },
1091
+ ]
1092
+
1093
  [[package]]
1094
  name = "pytest"
1095
  version = "9.1.1"
 
1413
  dependencies = [
1414
  { name = "joblib", marker = "python_full_version >= '3.11'" },
1415
  { name = "narwhals", marker = "python_full_version >= '3.11'" },
1416
+ { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
 
1417
  { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" },
1418
  { name = "scipy", version = "1.18.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
1419
  { name = "threadpoolctl", marker = "python_full_version >= '3.11'" },
 
1593
  "python_full_version >= '3.12'",
1594
  ]
1595
  dependencies = [
1596
+ { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
1597
  ]
1598
  sdist = { url = "https://files.pythonhosted.org/packages/a7/25/c2700dfaf6442b4effaa91af24ebce5dc9d31bb4a69706313aae70d72cd0/scipy-1.18.0.tar.gz", hash = "sha256:67b2ad2ad54c72ca6d04975a9b2df8c3638c34ddd5b28738e94fc2b57929d378", size = 30774447, upload-time = "2026-06-19T15:01:43.456Z" }
1599
  wheels = [
 
1646
  dependencies = [
1647
  { name = "huggingface-hub" },
1648
  { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
1649
+ { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
 
1650
  { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
1651
  { name = "scikit-learn", version = "1.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
1652
  { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
 
1865
  dependencies = [
1866
  { name = "huggingface-hub" },
1867
  { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
1868
+ { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
 
1869
  { name = "packaging" },
1870
  { name = "pyyaml" },
1871
  { name = "regex" },
 
1934
  { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" },
1935
  ]
1936
 
1937
+ [[package]]
1938
+ name = "umap-learn"
1939
+ version = "0.5.12"
1940
+ source = { registry = "https://pypi.org/simple" }
1941
+ dependencies = [
1942
+ { name = "numba" },
1943
+ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
1944
+ { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
1945
+ { name = "pynndescent" },
1946
+ { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
1947
+ { name = "scikit-learn", version = "1.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
1948
+ { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
1949
+ { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" },
1950
+ { name = "scipy", version = "1.18.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
1951
+ { name = "tqdm" },
1952
+ ]
1953
+ sdist = { url = "https://files.pythonhosted.org/packages/02/ee/af4171241117f85c74b5ca6448ea1033cc28d599c13651d67289bacd4083/umap_learn-0.5.12.tar.gz", hash = "sha256:6aff02ecac5f2aad9f3c65ee518d7ae93e1a985ae38721fdcffceee4232c33c7", size = 96672, upload-time = "2026-04-08T20:03:54.012Z" }
1954
+ wheels = [
1955
+ { url = "https://files.pythonhosted.org/packages/1b/98/f63318ccbe75c810011fe9233884c5d348d94d90005de1b79e5f93bef9c0/umap_learn-0.5.12-py3-none-any.whl", hash = "sha256:f2a85d2a2adcb52b541bed9b27a23ca169b56bb1b23283abeebfb8dfb8a42fe5", size = 91849, upload-time = "2026-04-08T20:03:52.561Z" },
1956
+ ]
1957
+
1958
  [[package]]
1959
  name = "uvicorn"
1960
  version = "0.52.1"
 
1978
  { name = "httpx" },
1979
  { name = "huggingface-hub" },
1980
  { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
1981
+ { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
 
1982
  { name = "orjson" },
1983
  { name = "sentence-transformers" },
1984
  { name = "torch" },
1985
+ { name = "umap-learn" },
1986
  { name = "uvicorn" },
1987
  ]
1988
 
 
2005
  { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.3.0" },
2006
  { name = "sentence-transformers", specifier = ">=2.5.0" },
2007
  { name = "torch", specifier = ">=2.2.0" },
2008
+ { name = "umap-learn", specifier = "==0.5.12" },
2009
  { name = "uvicorn", specifier = ">=0.28.0" },
2010
  ]
2011
  provides-extras = ["dev"]