Embeddable hybrid graph-vector database in OCaml
0

Configure Feed

Select the types of activity you want to include in your feed.

graph vis paper demo

+4872 -45
+14
.gitignore
··· 8 8 *.prof 9 9 callgrind* 10 10 datasets/ 11 + 12 + demo/frontend/node_modules/ 13 + demo/frontend/dist/ 14 + bun.lock 15 + demo/backend/__pycache__/ 16 + demo/backend/src/arxiv_demo/__pycache__/ 17 + demo/backend/.venv/ 18 + demo/backend/*.egg-info/ 19 + demo/data/ 20 + ingest_state.json 21 + *.pyc 22 + .mypy_cache/ 23 + .ruff_cache/ 24 + gvecdb.cap
+61
demo/README.md
··· 1 + # arXiv Explorer 2 + 3 + full-stack demo for gvecdb, ingests arXiv papers as a graph with vector embeddings, provides a web UI for semantic search and interactive graph exploration 4 + 5 + ## Architecture 6 + 7 + ``` 8 + [React/TypeScript frontend] <--REST--> [FastAPI backend] <--Cap'n Proto RPC--> [gvecdb-server] 9 + ``` 10 + 11 + ## Data model 12 + 13 + **Nodes:** `paper` (title, abstract, year, arxiv_id, categories, doi, journal_ref, submitted_date, page_count, figure_count, version_count, comments), `author` (name, paper_count) 14 + 15 + **Edges:** `authored` (author -> paper, with position), `cites` (paper -> paper) 16 + 17 + **Vectors:** `abstract_embedding` and `title_embedding` (384-dim, all-MiniLM-L6-v2) 18 + 19 + ## Setup 20 + 21 + ### Prerequisites 22 + 23 + - OCaml with opam 24 + - Python 3.12+ with [uv](https://github.com/astral-sh/uv) 25 + - [Bun](https://bun.sh) 26 + - arXiv metadata snapshot (JSON lines from [Kaggle](https://www.kaggle.com/datasets/Cornell-University/arxiv)) 27 + 28 + ### 1. Start gvecdb-server 29 + 30 + ```bash 31 + dune build 32 + dune exec server/main.exe -- --db demo/data/arxiv.db \ 33 + --capnp-listen-address unix:/tmp/gvecdb.sock \ 34 + --capnp-secret-key-file "" --capnp-disable-tls 35 + ``` 36 + 37 + ### 2. Ingest data 38 + 39 + ```bash 40 + cd demo/backend 41 + uv sync 42 + uv run python -m arxiv_demo.ingest \ 43 + --arxiv-json ~/data/arxiv-metadata-oai-snapshot.json \ 44 + --limit 50000 \ 45 + --socket /tmp/gvecdb.sock 46 + ``` 47 + 48 + ### 3. Start backend 49 + 50 + ```bash 51 + cd demo/backend 52 + uv run uvicorn arxiv_demo.api:app --port 8000 53 + ``` 54 + 55 + ### 4. Start frontend 56 + 57 + ```bash 58 + cd demo/frontend 59 + bun install 60 + bun run dev 61 + ```
+37
demo/backend/pyproject.toml
··· 1 + [project] 2 + name = "arxiv-demo" 3 + version = "0.1.0" 4 + description = "arXiv research paper explorer demo for gvecdb" 5 + requires-python = ">=3.12" 6 + dependencies = [ 7 + "fastapi>=0.115", 8 + "uvicorn[standard]>=0.32", 9 + "pycapnp>=2.0.0", 10 + "sentence-transformers>=3.0", 11 + "pydantic>=2.9", 12 + "httpx>=0.28", 13 + "rich>=13.0", 14 + ] 15 + 16 + [project.scripts] 17 + ingest = "arxiv_demo.ingest:main" 18 + 19 + [build-system] 20 + requires = ["hatchling"] 21 + build-backend = "hatchling.build" 22 + 23 + [tool.hatch.build.targets.wheel] 24 + packages = ["src/arxiv_demo"] 25 + 26 + [tool.ruff] 27 + line-length = 100 28 + target-version = "py312" 29 + 30 + [tool.ruff.lint] 31 + select = ["E", "F", "I", "N", "UP", "B", "A", "SIM", "TCH"] 32 + 33 + [tool.mypy] 34 + python_version = "3.12" 35 + strict = true 36 + warn_return_any = true 37 + warn_unused_configs = true
+29
demo/backend/schemas/arxiv.capnp
··· 1 + @0xb8e3f4a1c2d5e6f7; 2 + 3 + struct Paper { 4 + title @0 :Text; 5 + abstract @1 :Text; 6 + year @2 :UInt16; 7 + arxivId @3 :Text; 8 + categories @4 :Text; 9 + doi @5 :Text; 10 + journalRef @6 :Text; 11 + submittedDate @7 :Text; 12 + pageCount @8 :UInt16; 13 + figureCount @9 :UInt16; 14 + versionCount @10 :UInt8; 15 + comments @11 :Text; 16 + } 17 + 18 + struct Author { 19 + name @0 :Text; 20 + paperCount @1 :UInt32; 21 + } 22 + 23 + struct Authored { 24 + position @0 :UInt8; 25 + } 26 + 27 + struct Cites { 28 + context @0 :Text; 29 + }
+1
demo/backend/schemas/gvecdb_api.capnp
··· 1 + ../../../server/gvecdb_api.capnp
demo/backend/src/arxiv_demo/__init__.py

This is a binary file and will not be displayed.

+392
demo/backend/src/arxiv_demo/api.py
··· 1 + from __future__ import annotations 2 + 3 + import asyncio 4 + import logging 5 + import os 6 + from collections import deque 7 + from contextlib import asynccontextmanager 8 + from typing import AsyncIterator 9 + 10 + import capnp 11 + from fastapi import FastAPI, HTTPException, Query 12 + from fastapi.middleware.cors import CORSMiddleware 13 + 14 + from arxiv_demo.embeddings import embed_to_bytes 15 + from arxiv_demo.gvecdb_client import ( 16 + EdgeInfo, 17 + GvecdbClient, 18 + KnnResult, 19 + METRIC_COSINE, 20 + PaperProps, 21 + ) 22 + from arxiv_demo.models import ( 23 + AuthorDetail, 24 + AuthorRef, 25 + GraphData, 26 + GraphEdge, 27 + GraphNode, 28 + PaperDetail, 29 + PaperRef, 30 + PaperSummary, 31 + SearchResponse, 32 + ) 33 + 34 + logger = logging.getLogger(__name__) 35 + 36 + SOCKET_PATH = os.environ.get("GVECDB_SOCKET", "/tmp/gvecdb.sock") 37 + VALID_TAGS = {"abstract_embedding", "title_embedding"} 38 + MAX_NEIGHBOURHOOD_NODES = 200 39 + 40 + _client: GvecdbClient | None = None 41 + _client_lock = asyncio.Lock() 42 + 43 + 44 + async def get_client() -> GvecdbClient: 45 + global _client # noqa: PLW0603 46 + if _client is not None: 47 + return _client 48 + async with _client_lock: 49 + if _client is not None: 50 + return _client 51 + _client = await GvecdbClient.connect(SOCKET_PATH) 52 + return _client 53 + 54 + 55 + @asynccontextmanager 56 + async def lifespan(_app: FastAPI) -> AsyncIterator[None]: 57 + async with capnp.kj_loop(): # type: ignore[no-untyped-call] 58 + yield 59 + global _client # noqa: PLW0603 60 + _client = None 61 + 62 + 63 + app = FastAPI(title="arXiv gvecdb Demo", version="0.1.0", lifespan=lifespan) 64 + 65 + app.add_middleware( 66 + CORSMiddleware, 67 + allow_origins=["*"], 68 + allow_methods=["*"], 69 + allow_headers=["*"], 70 + ) 71 + 72 + 73 + def _validate_tag(tag: str) -> None: 74 + if tag not in VALID_TAGS: 75 + raise HTTPException( 76 + status_code=400, 77 + detail=f"Invalid tag '{tag}'. Must be one of: {', '.join(sorted(VALID_TAGS))}", 78 + ) 79 + 80 + 81 + def _paper_summary_fields(data: PaperProps) -> dict[str, str | int]: 82 + return { 83 + "doi": data["doi"], 84 + "journal_ref": data["journal_ref"], 85 + "submitted_date": data["submitted_date"], 86 + "page_count": data["page_count"], 87 + "figure_count": data["figure_count"], 88 + "version_count": data["version_count"], 89 + } 90 + 91 + 92 + async def _get_paper_authors(client: GvecdbClient, paper_node_id: int) -> list[AuthorRef]: 93 + inbound = await client.get_inbound_edges(paper_node_id) 94 + authored_edges = [e for e in inbound if e.edge_type == "authored"] 95 + if not authored_edges: 96 + return [] 97 + 98 + async def _fetch_one(edge: EdgeInfo) -> AuthorRef | None: 99 + try: 100 + author_props, edge_props = await asyncio.gather( 101 + client.get_node_props(edge.src), 102 + client.get_edge_props(edge.id), 103 + ) 104 + author_data = GvecdbClient.decode_author_props(author_props) 105 + authored_data = GvecdbClient.decode_authored_props(edge_props) 106 + return AuthorRef( 107 + node_id=edge.src, 108 + name=author_data["name"], 109 + position=authored_data["position"], 110 + ) 111 + except RuntimeError: 112 + logger.warning("failed to fetch author for edge %d", edge.id) 113 + return None 114 + 115 + results = await asyncio.gather(*[_fetch_one(e) for e in authored_edges]) 116 + authors = [a for a in results if a is not None] 117 + authors.sort(key=lambda a: a.position) 118 + return authors 119 + 120 + 121 + async def _get_paper_summary( 122 + client: GvecdbClient, node_id: int, score: float = 0.0 123 + ) -> PaperSummary: 124 + props = await client.get_node_props(node_id) 125 + data = GvecdbClient.decode_paper_props(props) 126 + authors = await _get_paper_authors(client, node_id) 127 + return PaperSummary( 128 + node_id=node_id, 129 + arxiv_id=data["arxiv_id"], 130 + title=data["title"], 131 + authors=[a.name for a in authors], 132 + year=data["year"], 133 + categories=data["categories"], 134 + score=score, 135 + **_paper_summary_fields(data), 136 + ) 137 + 138 + 139 + async def _fetch_paper_ref(client: GvecdbClient, node_id: int) -> PaperRef | None: 140 + try: 141 + props = await client.get_node_props(node_id) 142 + data = GvecdbClient.decode_paper_props(props) 143 + return PaperRef( 144 + node_id=node_id, 145 + arxiv_id=data["arxiv_id"], 146 + title=data["title"], 147 + year=data["year"], 148 + ) 149 + except RuntimeError: 150 + logger.warning("failed to fetch paper ref for node %d", node_id) 151 + return None 152 + 153 + 154 + def _apply_filters( 155 + papers: list[PaperSummary], 156 + year_min: int | None, 157 + year_max: int | None, 158 + category: str | None, 159 + published_only: bool, 160 + ) -> list[PaperSummary]: 161 + result = papers 162 + if year_min is not None: 163 + result = [p for p in result if p.year >= year_min] 164 + if year_max is not None: 165 + result = [p for p in result if p.year <= year_max] 166 + if category: 167 + cats = set(category.split(",")) 168 + result = [p for p in result if set(p.categories.split()) & cats] 169 + if published_only: 170 + result = [p for p in result if p.doi] 171 + return result 172 + 173 + 174 + @app.get("/api/search", response_model=SearchResponse) 175 + async def search( 176 + q: str, 177 + k: int = Query(default=20, ge=1, le=100), 178 + tag: str = Query(default="abstract_embedding"), 179 + year_min: int | None = Query(default=None), 180 + year_max: int | None = Query(default=None), 181 + category: str | None = Query(default=None), 182 + published_only: bool = Query(default=False), 183 + ) -> SearchResponse: 184 + _validate_tag(tag) 185 + client = await get_client() 186 + query_vec = await asyncio.to_thread(embed_to_bytes, q) 187 + # overfetch when filtering so we still return k results after post-filter 188 + fetch_k = k * 3 if (year_min or year_max or category or published_only) else k 189 + results = await client.knn_hnsw( 190 + vector_tag=tag, 191 + query=query_vec, 192 + k=min(fetch_k, 100), 193 + ef=max(fetch_k * 4, 64), 194 + metric=METRIC_COSINE, 195 + ) 196 + 197 + async def _fetch_result(r: KnnResult) -> PaperSummary | None: 198 + try: 199 + return await _get_paper_summary(client, r.owner_id, score=1.0 - r.distance) 200 + except RuntimeError: 201 + logger.warning("failed to fetch paper for knn result owner %d", r.owner_id) 202 + return None 203 + 204 + summaries = await asyncio.gather(*[_fetch_result(r) for r in results]) 205 + papers = [p for p in summaries if p is not None] 206 + papers = _apply_filters(papers, year_min, year_max, category, published_only) 207 + 208 + return SearchResponse(query=q, tag=tag, results=papers[:k]) 209 + 210 + 211 + @app.get("/api/paper/by-node/{node_id}", response_model=PaperDetail) 212 + async def get_paper_by_node(node_id: int) -> PaperDetail: 213 + client = await get_client() 214 + 215 + try: 216 + props = await client.get_node_props(node_id) 217 + except RuntimeError as e: 218 + raise HTTPException(status_code=404, detail=str(e)) from e 219 + 220 + data = GvecdbClient.decode_paper_props(props) 221 + 222 + authors, outbound, inbound = await asyncio.gather( 223 + _get_paper_authors(client, node_id), 224 + client.get_outbound_edges(node_id), 225 + client.get_inbound_edges(node_id), 226 + ) 227 + 228 + cite_edges = [e for e in outbound if e.edge_type == "cites"] 229 + cited_by_edges = [e for e in inbound if e.edge_type == "cites"] 230 + 231 + all_refs = await asyncio.gather( 232 + *[_fetch_paper_ref(client, e.dst) for e in cite_edges], 233 + *[_fetch_paper_ref(client, e.src) for e in cited_by_edges], 234 + ) 235 + 236 + n_cite = len(cite_edges) 237 + cites = [r for r in all_refs[:n_cite] if r is not None] 238 + cited_by = [r for r in all_refs[n_cite:] if r is not None] 239 + 240 + return PaperDetail( 241 + node_id=node_id, 242 + arxiv_id=data["arxiv_id"], 243 + title=data["title"], 244 + abstract=data["abstract"], 245 + authors=authors, 246 + year=data["year"], 247 + categories=data["categories"], 248 + cites=cites, 249 + cited_by=cited_by, 250 + doi=data["doi"], 251 + journal_ref=data["journal_ref"], 252 + submitted_date=data["submitted_date"], 253 + page_count=data["page_count"], 254 + figure_count=data["figure_count"], 255 + version_count=data["version_count"], 256 + comments=data["comments"], 257 + ) 258 + 259 + 260 + @app.get("/api/author/{node_id}", response_model=AuthorDetail) 261 + async def get_author(node_id: int) -> AuthorDetail: 262 + client = await get_client() 263 + 264 + try: 265 + props = await client.get_node_props(node_id) 266 + except RuntimeError as e: 267 + raise HTTPException(status_code=404, detail=str(e)) from e 268 + 269 + data = GvecdbClient.decode_author_props(props) 270 + outbound = await client.get_outbound_edges(node_id) 271 + authored_edges = [e for e in outbound if e.edge_type == "authored"] 272 + 273 + results = await asyncio.gather( 274 + *[_fetch_paper_ref(client, e.dst) for e in authored_edges] 275 + ) 276 + papers = [p for p in results if p is not None] 277 + 278 + return AuthorDetail( 279 + node_id=node_id, 280 + name=data["name"], 281 + paper_count=data["paper_count"], 282 + papers=papers, 283 + ) 284 + 285 + 286 + @app.get("/api/graph/neighbourhood/{node_id}", response_model=GraphData) 287 + async def get_neighbourhood( 288 + node_id: int, 289 + depth: int = Query(default=1, ge=1, le=3), 290 + ) -> GraphData: 291 + client = await get_client() 292 + 293 + visited_nodes: dict[int, GraphNode] = {} 294 + visited_edges: dict[int, GraphEdge] = {} 295 + queue: deque[tuple[int, int]] = deque([(node_id, 0)]) 296 + seen: set[int] = {node_id} 297 + 298 + while queue: 299 + if len(visited_nodes) >= MAX_NEIGHBOURHOOD_NODES: 300 + break 301 + 302 + current_id, current_depth = queue.popleft() 303 + 304 + if current_id not in visited_nodes: 305 + try: 306 + info = await client.get_node_info(current_id) 307 + props = await client.get_node_props(current_id) 308 + if info.node_type == "paper": 309 + data = GvecdbClient.decode_paper_props(props) 310 + visited_nodes[current_id] = GraphNode( 311 + id=current_id, 312 + type="paper", 313 + label=data["title"][:60], 314 + metadata={ 315 + "year": data["year"], 316 + "arxiv_id": data["arxiv_id"], 317 + "categories": data["categories"], 318 + }, 319 + ) 320 + elif info.node_type == "author": 321 + data = GvecdbClient.decode_author_props(props) 322 + visited_nodes[current_id] = GraphNode( 323 + id=current_id, 324 + type="author", 325 + label=data["name"], 326 + metadata={"paper_count": data["paper_count"]}, 327 + ) 328 + except RuntimeError: 329 + logger.warning("failed to fetch node %d in neighbourhood", current_id) 330 + continue 331 + 332 + if current_depth >= depth: 333 + continue 334 + 335 + try: 336 + outbound, inbound = await asyncio.gather( 337 + client.get_outbound_edges(current_id), 338 + client.get_inbound_edges(current_id), 339 + ) 340 + except RuntimeError: 341 + logger.warning("failed to fetch edges for node %d", current_id) 342 + continue 343 + 344 + for edge in outbound + inbound: 345 + if edge.id not in visited_edges: 346 + visited_edges[edge.id] = GraphEdge( 347 + id=edge.id, source=edge.src, target=edge.dst, type=edge.edge_type 348 + ) 349 + 350 + neighbour = edge.dst if edge.src == current_id else edge.src 351 + if neighbour not in seen: 352 + seen.add(neighbour) 353 + queue.append((neighbour, current_depth + 1)) 354 + 355 + all_node_ids = set(visited_nodes.keys()) 356 + filtered_edges = [ 357 + e for e in visited_edges.values() if e.source in all_node_ids and e.target in all_node_ids 358 + ] 359 + 360 + return GraphData(nodes=list(visited_nodes.values()), edges=filtered_edges) 361 + 362 + 363 + @app.get("/api/similar/{node_id}", response_model=list[PaperSummary]) 364 + async def get_similar( 365 + node_id: int, 366 + k: int = Query(default=10, ge=1, le=50), 367 + tag: str = Query(default="abstract_embedding"), 368 + ) -> list[PaperSummary]: 369 + _validate_tag(tag) 370 + client = await get_client() 371 + 372 + props = await client.get_node_props(node_id) 373 + data = GvecdbClient.decode_paper_props(props) 374 + 375 + text = data["abstract"] if tag == "abstract_embedding" else data["title"] 376 + query_vec = await asyncio.to_thread(embed_to_bytes, text) 377 + 378 + results = await client.knn_hnsw( 379 + vector_tag=tag, query=query_vec, k=k + 1, ef=max(k * 4, 64), metric=METRIC_COSINE 380 + ) 381 + 382 + filtered = [r for r in results if r.owner_id != node_id][:k] 383 + 384 + async def _fetch_similar(r: KnnResult) -> PaperSummary | None: 385 + try: 386 + return await _get_paper_summary(client, r.owner_id, score=1.0 - r.distance) 387 + except RuntimeError: 388 + logger.warning("failed to fetch similar paper %d", r.owner_id) 389 + return None 390 + 391 + summaries = await asyncio.gather(*[_fetch_similar(r) for r in filtered]) 392 + return [p for p in summaries if p is not None]
+41
demo/backend/src/arxiv_demo/embeddings.py
··· 1 + from __future__ import annotations 2 + 3 + from typing import TYPE_CHECKING 4 + 5 + from arxiv_demo.gvecdb_client import pack_float32_vector 6 + 7 + if TYPE_CHECKING: 8 + from sentence_transformers import SentenceTransformer 9 + 10 + MODEL_NAME = "all-MiniLM-L6-v2" 11 + EMBEDDING_DIM = 384 12 + 13 + _model: SentenceTransformer | None = None 14 + 15 + 16 + def get_model() -> SentenceTransformer: 17 + global _model # noqa: PLW0603 18 + if _model is None: 19 + from sentence_transformers import SentenceTransformer 20 + 21 + _model = SentenceTransformer(MODEL_NAME) 22 + return _model 23 + 24 + 25 + def embed_texts(texts: list[str]) -> list[list[float]]: 26 + model = get_model() 27 + embeddings = model.encode(texts, normalize_embeddings=True, show_progress_bar=False) 28 + return [row.tolist() for row in embeddings] 29 + 30 + 31 + def embed_single(text: str) -> list[float]: 32 + return embed_texts([text])[0] 33 + 34 + 35 + def embed_to_bytes(text: str) -> bytes: 36 + return pack_float32_vector(embed_single(text)) 37 + 38 + 39 + def embed_batch_to_bytes(texts: list[str]) -> list[bytes]: 40 + vecs = embed_texts(texts) 41 + return [pack_float32_vector(v) for v in vecs]
+357
demo/backend/src/arxiv_demo/gvecdb_client.py
··· 1 + from __future__ import annotations 2 + 3 + import struct 4 + from pathlib import Path 5 + from typing import Any, TypedDict 6 + 7 + import capnp 8 + 9 + 10 + class PaperProps(TypedDict): 11 + title: str 12 + abstract: str 13 + year: int 14 + arxiv_id: str 15 + categories: str 16 + doi: str 17 + journal_ref: str 18 + submitted_date: str 19 + page_count: int 20 + figure_count: int 21 + version_count: int 22 + comments: str 23 + 24 + 25 + class AuthorProps(TypedDict): 26 + name: str 27 + paper_count: int 28 + 29 + 30 + class AuthoredProps(TypedDict): 31 + position: int 32 + 33 + 34 + SCHEMAS_DIR = Path(__file__).resolve().parent.parent.parent / "schemas" 35 + API_SCHEMA = SCHEMAS_DIR / "gvecdb_api.capnp" 36 + ARXIV_SCHEMA = SCHEMAS_DIR / "arxiv.capnp" 37 + 38 + gvecdb_api = capnp.load(str(API_SCHEMA)) # type: ignore[no-untyped-call] 39 + arxiv_schema = capnp.load(str(ARXIV_SCHEMA)) # type: ignore[no-untyped-call] 40 + 41 + NODE_SCHEMA_KIND = 0 42 + EDGE_SCHEMA_KIND = 1 43 + 44 + METRIC_EUCLIDEAN = 0 45 + METRIC_COSINE = 1 46 + METRIC_DOT_PRODUCT = 2 47 + 48 + 49 + class EdgeInfo: 50 + __slots__ = ("id", "edge_type", "src", "dst") 51 + 52 + def __init__(self, id: int, edge_type: str, src: int, dst: int) -> None: 53 + self.id = id 54 + self.edge_type = edge_type 55 + self.src = src 56 + self.dst = dst 57 + 58 + 59 + class NodeInfo: 60 + __slots__ = ("id", "node_type") 61 + 62 + def __init__(self, id: int, node_type: str) -> None: 63 + self.id = id 64 + self.node_type = node_type 65 + 66 + 67 + class KnnResult: 68 + __slots__ = ("vector_id", "owner_kind", "owner_id", "vector_tag", "distance") 69 + 70 + def __init__( 71 + self, 72 + vector_id: int, 73 + owner_kind: int, 74 + owner_id: int, 75 + vector_tag: str, 76 + distance: float, 77 + ) -> None: 78 + self.vector_id = vector_id 79 + self.owner_kind = owner_kind 80 + self.owner_id = owner_id 81 + self.vector_tag = vector_tag 82 + self.distance = distance 83 + 84 + 85 + def _check_error(result: Any) -> None: 86 + err = result.error 87 + if err: 88 + raise RuntimeError(f"gvecdb error: {err}") 89 + 90 + 91 + def pack_float32_vector(vec: list[float]) -> bytes: 92 + return struct.pack(f"<{len(vec)}f", *vec) 93 + 94 + 95 + class GvecdbClient: 96 + def __init__(self, client: Any, connection: Any) -> None: 97 + self._client = client 98 + self._connection = connection 99 + 100 + @classmethod 101 + async def connect(cls, socket_path: str) -> GvecdbClient: 102 + conn = await capnp.AsyncIoStream.create_unix_connection( # type: ignore[no-untyped-call] 103 + path=socket_path 104 + ) 105 + tpc = capnp.TwoPartyClient(conn) # type: ignore[no-untyped-call] 106 + bootstrap: Any = tpc.bootstrap().cast_as(gvecdb_api.Gvecdb) # type: ignore[no-untyped-call] 107 + return cls(bootstrap, conn) 108 + 109 + async def register_schemas(self) -> None: 110 + schema_data = ARXIV_SCHEMA.read_bytes() 111 + registrations = [ 112 + (NODE_SCHEMA_KIND, "paper", "Paper"), 113 + (NODE_SCHEMA_KIND, "author", "Author"), 114 + (EDGE_SCHEMA_KIND, "authored", "Authored"), 115 + (EDGE_SCHEMA_KIND, "cites", "Cites"), 116 + ] 117 + for kind, type_name, struct_name in registrations: 118 + result = await self._client.registerSchemaFromCapnp( 119 + kind=kind, 120 + typeName=type_name, 121 + capnpSchema=schema_data, 122 + structName=struct_name, 123 + ) 124 + _check_error(result) 125 + 126 + async def create_node(self, node_type: str) -> int: 127 + result = await self._client.createNode(nodeType=node_type) 128 + _check_error(result) 129 + return int(result.nodeId) 130 + 131 + async def delete_node(self, node_id: int) -> None: 132 + result = await self._client.deleteNode(nodeId=node_id) 133 + _check_error(result) 134 + 135 + async def get_node_info(self, node_id: int) -> NodeInfo: 136 + result = await self._client.getNodeInfo(nodeId=node_id) 137 + _check_error(result) 138 + info = result.info 139 + return NodeInfo(id=int(info.id), node_type=str(info.nodeType)) 140 + 141 + async def create_edge(self, edge_type: str, src: int, dst: int) -> int: 142 + result = await self._client.createEdge(edgeType=edge_type, src=src, dst=dst) 143 + _check_error(result) 144 + return int(result.edgeId) 145 + 146 + async def get_edge_info(self, edge_id: int) -> EdgeInfo: 147 + result = await self._client.getEdgeInfo(edgeId=edge_id) 148 + _check_error(result) 149 + info = result.info 150 + return EdgeInfo( 151 + id=int(info.id), 152 + edge_type=str(info.edgeType), 153 + src=int(info.src), 154 + dst=int(info.dst), 155 + ) 156 + 157 + async def set_node_props(self, node_id: int, node_type: str, props_bytes: bytes) -> None: 158 + result = await self._client.setNodeProps( 159 + nodeId=node_id, nodeType=node_type, props=props_bytes 160 + ) 161 + _check_error(result) 162 + 163 + async def get_node_props(self, node_id: int) -> bytes: 164 + result = await self._client.getNodeProps(nodeId=node_id) 165 + _check_error(result) 166 + return bytes(result.props) 167 + 168 + async def set_edge_props(self, edge_id: int, edge_type: str, props_bytes: bytes) -> None: 169 + result = await self._client.setEdgeProps( 170 + edgeId=edge_id, edgeType=edge_type, props=props_bytes 171 + ) 172 + _check_error(result) 173 + 174 + async def get_edge_props(self, edge_id: int) -> bytes: 175 + result = await self._client.getEdgeProps(edgeId=edge_id) 176 + _check_error(result) 177 + return bytes(result.props) 178 + 179 + async def read_node_field(self, node_id: int, field_name: str) -> bytes: 180 + result = await self._client.readNodeField(nodeId=node_id, fieldName=field_name) 181 + _check_error(result) 182 + return bytes(result.value) 183 + 184 + async def read_edge_field(self, edge_id: int, field_name: str) -> bytes: 185 + result = await self._client.readEdgeField(edgeId=edge_id, fieldName=field_name) 186 + _check_error(result) 187 + return bytes(result.value) 188 + 189 + async def get_outbound_edges(self, node_id: int) -> list[EdgeInfo]: 190 + result = await self._client.getOutboundEdges(nodeId=node_id) 191 + _check_error(result) 192 + return [ 193 + EdgeInfo(id=int(e.id), edge_type=str(e.edgeType), src=int(e.src), dst=int(e.dst)) 194 + for e in result.edges 195 + ] 196 + 197 + async def get_inbound_edges(self, node_id: int) -> list[EdgeInfo]: 198 + result = await self._client.getInboundEdges(nodeId=node_id) 199 + _check_error(result) 200 + return [ 201 + EdgeInfo(id=int(e.id), edge_type=str(e.edgeType), src=int(e.src), dst=int(e.dst)) 202 + for e in result.edges 203 + ] 204 + 205 + async def create_vector( 206 + self, 207 + node_id: int, 208 + vector_tag: str, 209 + data: bytes, 210 + *, 211 + normalise: bool = False, 212 + metric: int = METRIC_COSINE, 213 + ) -> int: 214 + result = await self._client.createVector( 215 + nodeId=node_id, 216 + vectorTag=vector_tag, 217 + data=data, 218 + normalize=normalise, 219 + metric=metric, 220 + ) 221 + _check_error(result) 222 + return int(result.vectorId) 223 + 224 + async def knn_hnsw( 225 + self, 226 + vector_tag: str, 227 + query: bytes, 228 + k: int = 10, 229 + ef: int = 64, 230 + metric: int = METRIC_COSINE, 231 + ) -> list[KnnResult]: 232 + result = await self._client.knnHnsw( 233 + vectorTag=vector_tag, query=query, k=k, ef=ef, metric=metric 234 + ) 235 + _check_error(result) 236 + return [ 237 + KnnResult( 238 + vector_id=int(r.vectorId), 239 + owner_kind=int(r.ownerKind), 240 + owner_id=int(r.ownerId), 241 + vector_tag=str(r.vectorTag), 242 + distance=float(r.distance), 243 + ) 244 + for r in result.results 245 + ] 246 + 247 + @staticmethod 248 + def build_paper_props( 249 + title: str, 250 + abstract: str, 251 + year: int, 252 + arxiv_id: str, 253 + categories: str, 254 + doi: str = "", 255 + journal_ref: str = "", 256 + submitted_date: str = "", 257 + page_count: int = 0, 258 + figure_count: int = 0, 259 + version_count: int = 0, 260 + comments: str = "", 261 + ) -> bytes: 262 + paper = arxiv_schema.Paper.new_message() # type: ignore[no-untyped-call] 263 + paper.title = title 264 + paper.abstract = abstract 265 + paper.year = year 266 + paper.arxivId = arxiv_id 267 + paper.categories = categories 268 + paper.doi = doi 269 + paper.journalRef = journal_ref 270 + paper.submittedDate = submitted_date 271 + paper.pageCount = page_count 272 + paper.figureCount = figure_count 273 + paper.versionCount = version_count 274 + paper.comments = comments 275 + return paper.to_bytes() # type: ignore[no-any-return] 276 + 277 + @staticmethod 278 + def build_author_props(name: str, paper_count: int) -> bytes: 279 + author = arxiv_schema.Author.new_message() # type: ignore[no-untyped-call] 280 + author.name = name 281 + author.paperCount = paper_count 282 + return author.to_bytes() # type: ignore[no-any-return] 283 + 284 + @staticmethod 285 + def build_authored_props(position: int) -> bytes: 286 + authored = arxiv_schema.Authored.new_message() # type: ignore[no-untyped-call] 287 + authored.position = position 288 + return authored.to_bytes() # type: ignore[no-any-return] 289 + 290 + @staticmethod 291 + def build_cites_props(context: str = "") -> bytes: 292 + cites = arxiv_schema.Cites.new_message() # type: ignore[no-untyped-call] 293 + cites.context = context 294 + return cites.to_bytes() # type: ignore[no-any-return] 295 + 296 + @staticmethod 297 + def decode_paper_props(data: bytes) -> PaperProps: 298 + with arxiv_schema.Paper.from_bytes(data) as paper: # type: ignore[no-untyped-call] 299 + return PaperProps( 300 + title=str(paper.title), 301 + abstract=str(paper.abstract), 302 + year=int(paper.year), 303 + arxiv_id=str(paper.arxivId), 304 + categories=str(paper.categories), 305 + doi=str(paper.doi), 306 + journal_ref=str(paper.journalRef), 307 + submitted_date=str(paper.submittedDate), 308 + page_count=int(paper.pageCount), 309 + figure_count=int(paper.figureCount), 310 + version_count=int(paper.versionCount), 311 + comments=str(paper.comments), 312 + ) 313 + 314 + @staticmethod 315 + def decode_author_props(data: bytes) -> AuthorProps: 316 + with arxiv_schema.Author.from_bytes(data) as author: # type: ignore[no-untyped-call] 317 + return AuthorProps( 318 + name=str(author.name), 319 + paper_count=int(author.paperCount), 320 + ) 321 + 322 + @staticmethod 323 + def decode_authored_props(data: bytes) -> AuthoredProps: 324 + with arxiv_schema.Authored.from_bytes(data) as authored: # type: ignore[no-untyped-call] 325 + return AuthoredProps(position=int(authored.position)) 326 + 327 + @staticmethod 328 + def decode_field_value(data: bytes) -> str | int | float | bool | None: 329 + if not data: 330 + return None 331 + tag = data[0] 332 + payload = data[1:] 333 + if tag == 0: 334 + return None 335 + if tag == 1: 336 + return bool(payload[0]) if payload else False 337 + if tag == 2: 338 + # signed int8 339 + v = payload[0] if payload else 0 340 + return v - 256 if v > 127 else v 341 + if tag == 6: 342 + return payload[0] if payload else 0 343 + if tag in (3, 7): 344 + return struct.unpack("<H", payload[:2])[0] if len(payload) >= 2 else 0 345 + if tag in (4, 8): 346 + return struct.unpack("<i", payload[:4])[0] if len(payload) >= 4 else 0 347 + if tag in (5, 9): 348 + return struct.unpack("<q", payload[:8])[0] if len(payload) >= 8 else 0 349 + if tag == 10: 350 + return struct.unpack("<f", payload[:4])[0] if len(payload) >= 4 else 0.0 351 + if tag == 11: 352 + return struct.unpack("<d", payload[:8])[0] if len(payload) >= 8 else 0.0 353 + if tag == 12: 354 + return payload.decode("utf-8", errors="replace") 355 + if tag == 13: 356 + return payload.hex() 357 + return None
+503
demo/backend/src/arxiv_demo/ingest.py
··· 1 + from __future__ import annotations 2 + 3 + import argparse 4 + import asyncio 5 + import json 6 + import re 7 + import sys 8 + from collections.abc import Coroutine 9 + from email.utils import parsedate_to_datetime 10 + from pathlib import Path 11 + from typing import TypedDict 12 + 13 + import capnp 14 + import httpx 15 + from rich.console import Console 16 + from rich.progress import BarColumn, MofNCompleteColumn, Progress, TextColumn, TimeElapsedColumn 17 + 18 + from arxiv_demo.embeddings import embed_batch_to_bytes 19 + from arxiv_demo.gvecdb_client import METRIC_COSINE, GvecdbClient 20 + 21 + 22 + class ArxivRecord(TypedDict): 23 + id: str 24 + title: str 25 + abstract: str 26 + authors: list[str] 27 + categories: str 28 + year: int 29 + doi: str 30 + journal_ref: str 31 + submitted_date: str 32 + page_count: int 33 + figure_count: int 34 + version_count: int 35 + comments: str 36 + 37 + 38 + console = Console() 39 + 40 + EMBED_BATCH_SIZE = 64 41 + RPC_CONCURRENCY = 32 42 + MIN_YEAR = 2020 43 + 44 + OPENALEX_BATCH_SIZE = 50 45 + OPENALEX_EMAIL = "demo@gvecdb.dev" 46 + 47 + _RE_PAGES = re.compile(r"(\d+)\s*pages?", re.IGNORECASE) 48 + _RE_FIGURES = re.compile(r"(\d+)\s*figures?", re.IGNORECASE) 49 + 50 + 51 + def parse_authors(authors_parsed: list[list[str]]) -> list[str]: 52 + names: list[str] = [] 53 + for entry in authors_parsed: 54 + if not entry: 55 + continue 56 + last = entry[0].strip() if len(entry) > 0 else "" 57 + first = entry[1].strip() if len(entry) > 1 else "" 58 + if last: 59 + name = f"{first} {last}".strip() if first else last 60 + names.append(name) 61 + return names 62 + 63 + 64 + def parse_authors_string(authors_str: str) -> list[str]: 65 + return [a.strip() for a in authors_str.split(",") if a.strip()] 66 + 67 + 68 + def extract_year(update_date: str) -> int: 69 + m = re.match(r"(\d{4})", update_date) 70 + return int(m.group(1)) if m else 0 71 + 72 + 73 + def extract_page_count(comments: str) -> int: 74 + m = _RE_PAGES.search(comments) 75 + return min(int(m.group(1)), 65535) if m else 0 76 + 77 + 78 + def extract_figure_count(comments: str) -> int: 79 + m = _RE_FIGURES.search(comments) 80 + return min(int(m.group(1)), 65535) if m else 0 81 + 82 + 83 + def extract_submitted_date(versions: list[dict[str, str]]) -> str: 84 + if not versions: 85 + return "" 86 + created = versions[0].get("created", "") 87 + if not created: 88 + return "" 89 + try: 90 + dt = parsedate_to_datetime(created) 91 + return dt.strftime("%Y-%m-%d") 92 + except (ValueError, TypeError): 93 + return "" 94 + 95 + 96 + def load_papers( 97 + json_path: Path, 98 + limit: int, 99 + categories: set[str], 100 + ) -> list[ArxivRecord]: 101 + papers: list[ArxivRecord] = [] 102 + console.print(f"[bold]Loading papers from {json_path}...[/bold]") 103 + 104 + with open(json_path) as f: 105 + for line in f: 106 + if len(papers) >= limit: 107 + break 108 + try: 109 + record = json.loads(line) 110 + except json.JSONDecodeError: 111 + continue 112 + 113 + cats = record.get("categories", "") 114 + cat_set = set(cats.split()) 115 + if not cat_set & categories: 116 + continue 117 + 118 + year = extract_year(record.get("update_date", "")) 119 + if year < MIN_YEAR: 120 + continue 121 + 122 + if "authors_parsed" in record: 123 + authors = parse_authors(record["authors_parsed"]) 124 + else: 125 + authors = parse_authors_string(record.get("authors", "")) 126 + 127 + if not authors: 128 + continue 129 + 130 + abstract = record.get("abstract", "").strip().replace("\n", " ") 131 + title = record.get("title", "").strip().replace("\n", " ") 132 + if not abstract or not title: 133 + continue 134 + 135 + comments_raw = record.get("comments", "") or "" 136 + versions = record.get("versions", []) 137 + 138 + papers.append( 139 + ArxivRecord( 140 + id=record["id"], 141 + title=title, 142 + abstract=abstract, 143 + authors=authors, 144 + categories=cats, 145 + year=year, 146 + doi=record.get("doi", "") or "", 147 + journal_ref=record.get("journal-ref", "") or "", 148 + submitted_date=extract_submitted_date(versions), 149 + page_count=extract_page_count(comments_raw), 150 + figure_count=extract_figure_count(comments_raw), 151 + version_count=min(len(versions), 255), 152 + comments=comments_raw.strip().replace("\n", " "), 153 + ) 154 + ) 155 + 156 + console.print(f"[green]Loaded {len(papers)} papers[/green]") 157 + return papers 158 + 159 + 160 + async def fetch_citations( 161 + arxiv_ids: list[str], 162 + progress: Progress, 163 + ) -> dict[str, list[str]]: 164 + known_arxiv = set(arxiv_ids) 165 + # openalex id -> arxiv id, for papers in our dataset 166 + oa_to_arxiv: dict[str, str] = {} 167 + # openalex id -> list of referenced openalex ids 168 + oa_refs: dict[str, list[str]] = {} 169 + 170 + task = progress.add_task("Fetching citations from OpenAlex", total=len(arxiv_ids)) 171 + 172 + async with httpx.AsyncClient( 173 + timeout=30, 174 + headers={"User-Agent": f"gvecdb-demo/0.1 (mailto:{OPENALEX_EMAIL})"}, 175 + ) as http: 176 + for batch_start in range(0, len(arxiv_ids), OPENALEX_BATCH_SIZE): 177 + batch = arxiv_ids[batch_start : batch_start + OPENALEX_BATCH_SIZE] 178 + doi_filter = "|".join( 179 + f"https://doi.org/10.48550/arXiv.{aid}" for aid in batch 180 + ) 181 + 182 + try: 183 + resp = await http.get( 184 + "https://api.openalex.org/works", 185 + params={ 186 + "filter": f"doi:{doi_filter}", 187 + "select": "id,ids,referenced_works", 188 + "per_page": str(OPENALEX_BATCH_SIZE), 189 + }, 190 + ) 191 + resp.raise_for_status() 192 + data = resp.json() 193 + except (httpx.HTTPError, json.JSONDecodeError): 194 + progress.advance(task, len(batch)) 195 + continue 196 + 197 + for work in data.get("results", []): 198 + oa_id = work.get("id", "") 199 + doi = work.get("ids", {}).get("doi", "") 200 + lower_doi = doi.lower() 201 + if "10.48550/arxiv." in lower_doi: 202 + arxiv_id = lower_doi.split("10.48550/arxiv.")[-1] 203 + if arxiv_id in known_arxiv: 204 + oa_to_arxiv[oa_id] = arxiv_id 205 + 206 + refs = work.get("referenced_works", []) 207 + if refs: 208 + oa_refs[oa_id] = refs 209 + 210 + progress.advance(task, len(batch)) 211 + await asyncio.sleep(0.1) 212 + 213 + # any ref whose openalex id is already in oa_to_arxiv is a within-dataset citation 214 + citations: dict[str, list[str]] = {} 215 + for oa_id, refs in oa_refs.items(): 216 + src_arxiv = oa_to_arxiv.get(oa_id) 217 + if not src_arxiv: 218 + continue 219 + targets = [ 220 + oa_to_arxiv[ref_id] 221 + for ref_id in refs 222 + if ref_id in oa_to_arxiv and oa_to_arxiv[ref_id] != src_arxiv 223 + ] 224 + if targets: 225 + citations[src_arxiv] = targets 226 + 227 + total = sum(len(v) for v in citations.values()) 228 + console.print( 229 + f"[green]Found {total} citation edges across {len(citations)} papers[/green]" 230 + ) 231 + return citations 232 + 233 + 234 + async def ingest_papers( 235 + client: GvecdbClient, 236 + papers: list[ArxivRecord], 237 + state_file: Path, 238 + ) -> dict[str, int]: 239 + ingested_ids: set[str] = set() 240 + author_name_to_id: dict[str, int] = {} 241 + arxiv_id_to_node: dict[str, int] = {} 242 + 243 + if state_file.exists(): 244 + state = json.loads(state_file.read_text()) 245 + ingested_ids = set(state.get("ingested_ids", [])) 246 + author_name_to_id = {k: int(v) for k, v in state.get("author_map", {}).items()} 247 + arxiv_id_to_node = {k: int(v) for k, v in state.get("paper_map", {}).items()} 248 + console.print( 249 + f"[yellow]Resuming: {len(ingested_ids)} papers already ingested[/yellow]" 250 + ) 251 + 252 + remaining = [p for p in papers if p["id"] not in ingested_ids] 253 + if not remaining: 254 + console.print("[green]All papers already ingested[/green]") 255 + return arxiv_id_to_node 256 + 257 + console.print(f"[bold]{len(remaining)} papers to ingest[/bold]") 258 + 259 + author_paper_count: dict[str, int] = {} 260 + for p in remaining: 261 + for a in p["authors"]: 262 + author_paper_count[a] = author_paper_count.get(a, 0) + 1 263 + 264 + new_authors = [name for name in author_paper_count if name not in author_name_to_id] 265 + 266 + progress = Progress( 267 + TextColumn("[bold blue]{task.description}"), 268 + BarColumn(), 269 + MofNCompleteColumn(), 270 + TimeElapsedColumn(), 271 + console=console, 272 + ) 273 + 274 + def _save_state() -> None: 275 + state_file.write_text( 276 + json.dumps({ 277 + "ingested_ids": list(ingested_ids), 278 + "author_map": author_name_to_id, 279 + "paper_map": arxiv_id_to_node, 280 + }) 281 + ) 282 + 283 + with progress: 284 + if new_authors: 285 + task_authors = progress.add_task("Creating authors", total=len(new_authors)) 286 + 287 + async def _create_author(name: str) -> tuple[str, int]: 288 + node_id = await client.create_node("author") 289 + props = GvecdbClient.build_author_props(name, author_paper_count[name]) 290 + await client.set_node_props(node_id, "author", props) 291 + return name, node_id 292 + 293 + author_coros = [_create_author(name) for name in new_authors] 294 + sem = asyncio.Semaphore(RPC_CONCURRENCY) 295 + 296 + async def _bounded_author( 297 + coro: Coroutine[None, None, tuple[str, int]], 298 + ) -> tuple[str, int]: 299 + async with sem: 300 + result = await coro 301 + progress.advance(task_authors) 302 + return result 303 + 304 + author_results = await asyncio.gather( 305 + *[_bounded_author(c) for c in author_coros] 306 + ) 307 + for name, node_id in author_results: 308 + author_name_to_id[name] = node_id 309 + 310 + task_papers = progress.add_task("Ingesting papers", total=len(remaining)) 311 + 312 + for batch_start in range(0, len(remaining), EMBED_BATCH_SIZE): 313 + batch = remaining[batch_start : batch_start + EMBED_BATCH_SIZE] 314 + sem = asyncio.Semaphore(RPC_CONCURRENCY) 315 + 316 + async def _create_paper(p: ArxivRecord) -> tuple[str, int]: 317 + async with sem: 318 + node_id = await client.create_node("paper") 319 + props = GvecdbClient.build_paper_props( 320 + title=p["title"], 321 + abstract=p["abstract"], 322 + year=p["year"], 323 + arxiv_id=p["id"], 324 + categories=p["categories"], 325 + doi=p["doi"], 326 + journal_ref=p["journal_ref"], 327 + submitted_date=p["submitted_date"], 328 + page_count=p["page_count"], 329 + figure_count=p["figure_count"], 330 + version_count=p["version_count"], 331 + comments=p["comments"], 332 + ) 333 + await client.set_node_props(node_id, "paper", props) 334 + return p["id"], node_id 335 + 336 + paper_results = await asyncio.gather(*[_create_paper(p) for p in batch]) 337 + for arxiv_id, node_id in paper_results: 338 + arxiv_id_to_node[arxiv_id] = node_id 339 + 340 + edge_coros = [] 341 + for p in batch: 342 + paper_node = arxiv_id_to_node[p["id"]] 343 + for pos, author_name in enumerate(p["authors"]): 344 + author_node = author_name_to_id.get(author_name) 345 + if author_node is None: 346 + continue 347 + 348 + async def _create_edge( 349 + a_node: int = author_node, 350 + p_node: int = paper_node, 351 + position: int = pos, 352 + ) -> None: 353 + async with sem: 354 + edge_id = await client.create_edge("authored", a_node, p_node) 355 + props = GvecdbClient.build_authored_props(min(position, 255)) 356 + await client.set_edge_props(edge_id, "authored", props) 357 + 358 + edge_coros.append(_create_edge()) 359 + 360 + if edge_coros: 361 + await asyncio.gather(*edge_coros) 362 + 363 + abstracts = [p["abstract"] for p in batch] 364 + titles = [p["title"] for p in batch] 365 + abstract_vecs = embed_batch_to_bytes(abstracts) 366 + title_vecs = embed_batch_to_bytes(titles) 367 + 368 + vec_coros = [] 369 + for j, p in enumerate(batch): 370 + paper_node = arxiv_id_to_node[p["id"]] 371 + 372 + async def _store_vecs( 373 + node: int = paper_node, 374 + abs_vec: bytes = abstract_vecs[j], 375 + title_vec: bytes = title_vecs[j], 376 + ) -> None: 377 + async with sem: 378 + await client.create_vector( 379 + node, "abstract_embedding", abs_vec, metric=METRIC_COSINE 380 + ) 381 + await client.create_vector( 382 + node, "title_embedding", title_vec, metric=METRIC_COSINE 383 + ) 384 + 385 + vec_coros.append(_store_vecs()) 386 + 387 + await asyncio.gather(*vec_coros) 388 + 389 + for p in batch: 390 + ingested_ids.add(p["id"]) 391 + progress.advance(task_papers) 392 + _save_state() 393 + 394 + console.print(f" Papers: {len(arxiv_id_to_node)}") 395 + console.print(f" Authors: {len(author_name_to_id)}") 396 + return arxiv_id_to_node 397 + 398 + 399 + async def ingest_citations( 400 + client: GvecdbClient, 401 + arxiv_id_to_node: dict[str, int], 402 + progress: Progress, 403 + ) -> int: 404 + citations = await fetch_citations(list(arxiv_id_to_node.keys()), progress) 405 + 406 + total_cites = sum(len(v) for v in citations.values()) 407 + if total_cites == 0: 408 + return 0 409 + 410 + cite_task = progress.add_task("Creating citation edges", total=total_cites) 411 + sem = asyncio.Semaphore(RPC_CONCURRENCY) 412 + 413 + cite_coros = [] 414 + for src_arxiv, dst_arxivs in citations.items(): 415 + src_node = arxiv_id_to_node.get(src_arxiv) 416 + if src_node is None: 417 + continue 418 + for dst_arxiv in dst_arxivs: 419 + dst_node = arxiv_id_to_node.get(dst_arxiv) 420 + if dst_node is None: 421 + continue 422 + 423 + async def _create_cite(s: int = src_node, d: int = dst_node) -> None: 424 + async with sem: 425 + edge_id = await client.create_edge("cites", s, d) 426 + props = GvecdbClient.build_cites_props() 427 + await client.set_edge_props(edge_id, "cites", props) 428 + progress.advance(cite_task) 429 + 430 + cite_coros.append(_create_cite()) 431 + 432 + if cite_coros: 433 + await asyncio.gather(*cite_coros) 434 + 435 + return len(cite_coros) 436 + 437 + 438 + async def run( 439 + socket_path: str, 440 + papers: list[ArxivRecord], 441 + state_file: Path, 442 + skip_citations: bool = False, 443 + citations_only: bool = False, 444 + ) -> None: 445 + client = await GvecdbClient.connect(socket_path) 446 + 447 + console.print("[bold]Registering schemas...[/bold]") 448 + await client.register_schemas() 449 + console.print("[green]Schemas registered[/green]") 450 + 451 + if citations_only: 452 + if not state_file.exists(): 453 + console.print("[red]No state file found — run full ingest first[/red]") 454 + return 455 + state = json.loads(state_file.read_text()) 456 + arxiv_id_to_node = {k: int(v) for k, v in state.get("paper_map", {}).items()} 457 + console.print(f"[bold]Loading {len(arxiv_id_to_node)} papers from state[/bold]") 458 + else: 459 + arxiv_id_to_node = await ingest_papers(client, papers, state_file) 460 + 461 + if not skip_citations and arxiv_id_to_node: 462 + progress = Progress( 463 + TextColumn("[bold blue]{task.description}"), 464 + BarColumn(), 465 + MofNCompleteColumn(), 466 + TimeElapsedColumn(), 467 + console=console, 468 + ) 469 + with progress: 470 + n = await ingest_citations(client, arxiv_id_to_node, progress) 471 + console.print(f" Citation edges: {n}") 472 + 473 + console.print(f"\n[bold green]Done![/bold green]") 474 + 475 + 476 + def main() -> None: 477 + parser = argparse.ArgumentParser(description="Ingest arXiv data into gvecdb") 478 + parser.add_argument("--arxiv-json", type=Path, required=True) 479 + parser.add_argument("--socket", default="/tmp/gvecdb.sock") 480 + parser.add_argument("--limit", type=int, default=50000) 481 + parser.add_argument("--categories", default="cs.AI,cs.LG,cs.CL,cs.CV,cs.IR") 482 + parser.add_argument("--state-file", type=Path, default=Path("ingest_state.json")) 483 + parser.add_argument("--skip-citations", action="store_true") 484 + parser.add_argument( 485 + "--citations-only", action="store_true", 486 + help="skip paper ingest, only fetch citations (requires prior ingest state)", 487 + ) 488 + args = parser.parse_args() 489 + 490 + categories = set(args.categories.split(",")) 491 + papers = load_papers(args.arxiv_json, args.limit, categories) if not args.citations_only else [] 492 + 493 + if not papers and not args.citations_only: 494 + console.print("[red]No papers found matching criteria[/red]") 495 + sys.exit(1) 496 + 497 + asyncio.run( # type: ignore[no-untyped-call] 498 + capnp.run(run(args.socket, papers, args.state_file, args.skip_citations, args.citations_only)) 499 + ) 500 + 501 + 502 + if __name__ == "__main__": 503 + main()
+86
demo/backend/src/arxiv_demo/models.py
··· 1 + from __future__ import annotations 2 + 3 + from pydantic import BaseModel 4 + 5 + 6 + class PaperSummary(BaseModel): 7 + node_id: int 8 + arxiv_id: str 9 + title: str 10 + authors: list[str] 11 + year: int 12 + categories: str 13 + score: float = 0.0 14 + doi: str = "" 15 + journal_ref: str = "" 16 + submitted_date: str = "" 17 + page_count: int = 0 18 + figure_count: int = 0 19 + version_count: int = 0 20 + 21 + 22 + class PaperDetail(BaseModel): 23 + node_id: int 24 + arxiv_id: str 25 + title: str 26 + abstract: str 27 + authors: list[AuthorRef] 28 + year: int 29 + categories: str 30 + cites: list[PaperRef] 31 + cited_by: list[PaperRef] 32 + doi: str = "" 33 + journal_ref: str = "" 34 + submitted_date: str = "" 35 + page_count: int = 0 36 + figure_count: int = 0 37 + version_count: int = 0 38 + comments: str = "" 39 + 40 + 41 + class AuthorRef(BaseModel): 42 + node_id: int 43 + name: str 44 + position: int = 0 45 + 46 + 47 + class PaperRef(BaseModel): 48 + node_id: int 49 + arxiv_id: str 50 + title: str 51 + year: int = 0 52 + 53 + 54 + class AuthorDetail(BaseModel): 55 + node_id: int 56 + name: str 57 + paper_count: int 58 + papers: list[PaperRef] 59 + 60 + 61 + class GraphNode(BaseModel): 62 + id: int 63 + type: str 64 + label: str 65 + metadata: dict[str, str | int | float] = {} 66 + 67 + 68 + class GraphEdge(BaseModel): 69 + id: int 70 + source: int 71 + target: int 72 + type: str 73 + 74 + 75 + class GraphData(BaseModel): 76 + nodes: list[GraphNode] 77 + edges: list[GraphEdge] 78 + 79 + 80 + class SearchResponse(BaseModel): 81 + query: str 82 + tag: str 83 + results: list[PaperSummary] 84 + 85 + 86 + PaperDetail.model_rebuild()
+1470
demo/backend/uv.lock
··· 1 + version = 1 2 + requires-python = ">=3.12" 3 + 4 + [[package]] 5 + name = "annotated-doc" 6 + version = "0.0.4" 7 + source = { registry = "https://pypi.org/simple" } 8 + sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288 } 9 + wheels = [ 10 + { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303 }, 11 + ] 12 + 13 + [[package]] 14 + name = "annotated-types" 15 + version = "0.7.0" 16 + source = { registry = "https://pypi.org/simple" } 17 + sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } 18 + wheels = [ 19 + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, 20 + ] 21 + 22 + [[package]] 23 + name = "anyio" 24 + version = "4.13.0" 25 + source = { registry = "https://pypi.org/simple" } 26 + dependencies = [ 27 + { name = "idna" }, 28 + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, 29 + ] 30 + sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622 } 31 + wheels = [ 32 + { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353 }, 33 + ] 34 + 35 + [[package]] 36 + name = "arxiv-demo" 37 + version = "0.1.0" 38 + source = { editable = "." } 39 + dependencies = [ 40 + { name = "fastapi" }, 41 + { name = "httpx" }, 42 + { name = "pycapnp" }, 43 + { name = "pydantic" }, 44 + { name = "rich" }, 45 + { name = "sentence-transformers" }, 46 + { name = "uvicorn", extra = ["standard"] }, 47 + ] 48 + 49 + [package.metadata] 50 + requires-dist = [ 51 + { name = "fastapi", specifier = ">=0.115" }, 52 + { name = "httpx", specifier = ">=0.28" }, 53 + { name = "pycapnp", specifier = ">=2.0.0" }, 54 + { name = "pydantic", specifier = ">=2.9" }, 55 + { name = "rich", specifier = ">=13.0" }, 56 + { name = "sentence-transformers", specifier = ">=3.0" }, 57 + { name = "uvicorn", extras = ["standard"], specifier = ">=0.32" }, 58 + ] 59 + 60 + [[package]] 61 + name = "certifi" 62 + version = "2026.4.22" 63 + source = { registry = "https://pypi.org/simple" } 64 + sdist = { url = "https://files.pythonhosted.org/packages/25/ee/6caf7a40c36a1220410afe15a1cc64993a1f864871f698c0f93acb72842a/certifi-2026.4.22.tar.gz", hash = "sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580", size = 137077 } 65 + wheels = [ 66 + { url = "https://files.pythonhosted.org/packages/22/30/7cd8fdcdfbc5b869528b079bfb76dcdf6056b1a2097a662e5e8c04f42965/certifi-2026.4.22-py3-none-any.whl", hash = "sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a", size = 135707 }, 67 + ] 68 + 69 + [[package]] 70 + name = "click" 71 + version = "8.3.3" 72 + source = { registry = "https://pypi.org/simple" } 73 + dependencies = [ 74 + { name = "colorama", marker = "sys_platform == 'win32'" }, 75 + ] 76 + sdist = { url = "https://files.pythonhosted.org/packages/bb/63/f9e1ea081ce35720d8b92acde70daaedace594dc93b693c869e0d5910718/click-8.3.3.tar.gz", hash = "sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2", size = 328061 } 77 + wheels = [ 78 + { url = "https://files.pythonhosted.org/packages/ae/44/c1221527f6a71a01ec6fbad7fa78f1d50dfa02217385cf0fa3eec7087d59/click-8.3.3-py3-none-any.whl", hash = "sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613", size = 110502 }, 79 + ] 80 + 81 + [[package]] 82 + name = "colorama" 83 + version = "0.4.6" 84 + source = { registry = "https://pypi.org/simple" } 85 + sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } 86 + wheels = [ 87 + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, 88 + ] 89 + 90 + [[package]] 91 + name = "cuda-bindings" 92 + version = "13.2.0" 93 + source = { registry = "https://pypi.org/simple" } 94 + dependencies = [ 95 + { name = "cuda-pathfinder" }, 96 + ] 97 + wheels = [ 98 + { url = "https://files.pythonhosted.org/packages/52/c8/b2589d68acf7e3d63e2be330b84bc25712e97ed799affbca7edd7eae25d6/cuda_bindings-13.2.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e865447abfb83d6a98ad5130ed3c70b1fc295ae3eeee39fd07b4ddb0671b6788", size = 5722404 }, 99 + { url = "https://files.pythonhosted.org/packages/1f/92/f899f7bbb5617bb65ec52a6eac1e9a1447a86b916c4194f8a5001b8cde0c/cuda_bindings-13.2.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46d8776a55d6d5da9dd6e9858fba2efcda2abe6743871dee47dd06eb8cb6d955", size = 6320619 }, 100 + { url = "https://files.pythonhosted.org/packages/df/93/eef988860a3ca985f82c4f3174fc0cdd94e07331ba9a92e8e064c260337f/cuda_bindings-13.2.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6629ca2df6f795b784752409bcaedbd22a7a651b74b56a165ebc0c9dcbd504d0", size = 5614610 }, 101 + { url = "https://files.pythonhosted.org/packages/18/23/6db3aba46864aee357ab2415135b3fe3da7e9f1fa0221fa2a86a5968099c/cuda_bindings-13.2.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7dca0da053d3b4cc4869eff49c61c03f3c5dbaa0bcd712317a358d5b8f3f385d", size = 6149914 }, 102 + { url = "https://files.pythonhosted.org/packages/c0/87/87a014f045b77c6de5c8527b0757fe644417b184e5367db977236a141602/cuda_bindings-13.2.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6464b30f46692d6c7f65d4a0e0450d81dd29de3afc1bb515653973d01c2cd6e", size = 5685673 }, 103 + { url = "https://files.pythonhosted.org/packages/ee/5e/c0fe77a73aaefd3fff25ffaccaac69c5a63eafdf8b9a4c476626ef0ac703/cuda_bindings-13.2.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4af9f3e1be603fa12d5ad6cfca7844c9d230befa9792b5abdf7dd79979c3626", size = 6191386 }, 104 + { url = "https://files.pythonhosted.org/packages/5f/58/ed2c3b39c8dd5f96aa7a4abef0d47a73932c7a988e30f5fa428f00ed0da1/cuda_bindings-13.2.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df850a1ff8ce1b3385257b08e47b70e959932f5f432d0a4e46a355962b4e4771", size = 5507469 }, 105 + { url = "https://files.pythonhosted.org/packages/1f/01/0c941b112ceeb21439b05895eace78ca1aa2eaaf695c8521a068fd9b4c00/cuda_bindings-13.2.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8a16384c6494e5485f39314b0b4afb04bee48d49edb16d5d8593fd35bbd231b", size = 6059693 }, 106 + ] 107 + 108 + [[package]] 109 + name = "cuda-pathfinder" 110 + version = "1.5.4" 111 + source = { registry = "https://pypi.org/simple" } 112 + wheels = [ 113 + { url = "https://files.pythonhosted.org/packages/11/d0/c177e29701cf1d3008d7d2b16b5fc626592ce13bd535f8795c5f57187e0e/cuda_pathfinder-1.5.4-py3-none-any.whl", hash = "sha256:9563d3175ce1828531acf4b94e1c1c7d67208c347ca002493e2654878b26f4b7", size = 51657 }, 114 + ] 115 + 116 + [[package]] 117 + name = "cuda-toolkit" 118 + version = "13.0.2" 119 + source = { registry = "https://pypi.org/simple" } 120 + wheels = [ 121 + { url = "https://files.pythonhosted.org/packages/57/b2/453099f5f3b698d7d0eab38916aac44c7f76229f451709e2eb9db6615dcd/cuda_toolkit-13.0.2-py2.py3-none-any.whl", hash = "sha256:b198824cf2f54003f50d64ada3a0f184b42ca0846c1c94192fa269ecd97a66eb", size = 2364 }, 122 + ] 123 + 124 + [package.optional-dependencies] 125 + cublas = [ 126 + { name = "nvidia-cublas", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, 127 + ] 128 + cudart = [ 129 + { name = "nvidia-cuda-runtime", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, 130 + ] 131 + cufft = [ 132 + { name = "nvidia-cufft", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, 133 + ] 134 + cufile = [ 135 + { name = "nvidia-cufile", marker = "sys_platform == 'linux'" }, 136 + ] 137 + cupti = [ 138 + { name = "nvidia-cuda-cupti", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, 139 + ] 140 + curand = [ 141 + { name = "nvidia-curand", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, 142 + ] 143 + cusolver = [ 144 + { name = "nvidia-cusolver", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, 145 + ] 146 + cusparse = [ 147 + { name = "nvidia-cusparse", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, 148 + ] 149 + nvjitlink = [ 150 + { name = "nvidia-nvjitlink", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, 151 + ] 152 + nvrtc = [ 153 + { name = "nvidia-cuda-nvrtc", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, 154 + ] 155 + nvtx = [ 156 + { name = "nvidia-nvtx", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, 157 + ] 158 + 159 + [[package]] 160 + name = "fastapi" 161 + version = "0.136.1" 162 + source = { registry = "https://pypi.org/simple" } 163 + dependencies = [ 164 + { name = "annotated-doc" }, 165 + { name = "pydantic" }, 166 + { name = "starlette" }, 167 + { name = "typing-extensions" }, 168 + { name = "typing-inspection" }, 169 + ] 170 + sdist = { url = "https://files.pythonhosted.org/packages/5d/45/c130091c2dfa061bbfe3150f2a5091ef1adf149f2a8d2ae769ecaf6e99a2/fastapi-0.136.1.tar.gz", hash = "sha256:7af665ad7acfa0a3baf8983d393b6b471b9da10ede59c60045f49fbc89a0fa7f", size = 397448 } 171 + wheels = [ 172 + { url = "https://files.pythonhosted.org/packages/5a/ff/2e4eca3ade2c22fe1dea7043b8ee9dabe47753349eb1b56a202de8af6349/fastapi-0.136.1-py3-none-any.whl", hash = "sha256:a6e9d7eeada96c93a4d69cb03836b44fa34e2854accb7244a1ece36cd4781c3f", size = 117683 }, 173 + ] 174 + 175 + [[package]] 176 + name = "filelock" 177 + version = "3.29.0" 178 + source = { registry = "https://pypi.org/simple" } 179 + sdist = { url = "https://files.pythonhosted.org/packages/b5/fe/997687a931ab51049acce6fa1f23e8f01216374ea81374ddee763c493db5/filelock-3.29.0.tar.gz", hash = "sha256:69974355e960702e789734cb4871f884ea6fe50bd8404051a3530bc07809cf90", size = 57571 } 180 + wheels = [ 181 + { url = "https://files.pythonhosted.org/packages/81/47/dd9a212ef6e343a6857485ffe25bba537304f1913bdbed446a23f7f592e1/filelock-3.29.0-py3-none-any.whl", hash = "sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258", size = 39812 }, 182 + ] 183 + 184 + [[package]] 185 + name = "fsspec" 186 + version = "2026.3.0" 187 + source = { registry = "https://pypi.org/simple" } 188 + sdist = { url = "https://files.pythonhosted.org/packages/e1/cf/b50ddf667c15276a9ab15a70ef5f257564de271957933ffea49d2cdbcdfb/fsspec-2026.3.0.tar.gz", hash = "sha256:1ee6a0e28677557f8c2f994e3eea77db6392b4de9cd1f5d7a9e87a0ae9d01b41", size = 313547 } 189 + wheels = [ 190 + { url = "https://files.pythonhosted.org/packages/d5/1f/5f4a3cd9e4440e9d9bc78ad0a91a1c8d46b4d429d5239ebe6793c9fe5c41/fsspec-2026.3.0-py3-none-any.whl", hash = "sha256:d2ceafaad1b3457968ed14efa28798162f1638dbb5d2a6868a2db002a5ee39a4", size = 202595 }, 191 + ] 192 + 193 + [[package]] 194 + name = "h11" 195 + version = "0.16.0" 196 + source = { registry = "https://pypi.org/simple" } 197 + sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250 } 198 + wheels = [ 199 + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515 }, 200 + ] 201 + 202 + [[package]] 203 + name = "hf-xet" 204 + version = "1.4.3" 205 + source = { registry = "https://pypi.org/simple" } 206 + sdist = { url = "https://files.pythonhosted.org/packages/53/92/ec9ad04d0b5728dca387a45af7bc98fbb0d73b2118759f5f6038b61a57e8/hf_xet-1.4.3.tar.gz", hash = "sha256:8ddedb73c8c08928c793df2f3401ec26f95be7f7e516a7bee2fbb546f6676113", size = 670477 } 207 + wheels = [ 208 + { url = "https://files.pythonhosted.org/packages/72/43/724d307b34e353da0abd476e02f72f735cdd2bc86082dee1b32ea0bfee1d/hf_xet-1.4.3-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:7551659ba4f1e1074e9623996f28c3873682530aee0a846b7f2f066239228144", size = 3800935 }, 209 + { url = "https://files.pythonhosted.org/packages/2b/d2/8bee5996b699262edb87dbb54118d287c0e1b2fc78af7cdc41857ba5e3c4/hf_xet-1.4.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:bee693ada985e7045997f05f081d0e12c4c08bd7626dc397f8a7c487e6c04f7f", size = 3558942 }, 210 + { url = "https://files.pythonhosted.org/packages/c3/a1/e993d09cbe251196fb60812b09a58901c468127b7259d2bf0f68bf6088eb/hf_xet-1.4.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:21644b404bb0100fe3857892f752c4d09642586fd988e61501c95bbf44b393a3", size = 4207657 }, 211 + { url = "https://files.pythonhosted.org/packages/64/44/9eb6d21e5c34c63e5e399803a6932fa983cabdf47c0ecbcfe7ea97684b8c/hf_xet-1.4.3-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:987f09cfe418237812896a6736b81b1af02a3a6dcb4b4944425c4c4fca7a7cf8", size = 3986765 }, 212 + { url = "https://files.pythonhosted.org/packages/ea/7b/8ad6f16fdb82f5f7284a34b5ec48645bd575bdcd2f6f0d1644775909c486/hf_xet-1.4.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:60cf7fc43a99da0a853345cf86d23738c03983ee5249613a6305d3e57a5dca74", size = 4188162 }, 213 + { url = "https://files.pythonhosted.org/packages/1b/c4/39d6e136cbeea9ca5a23aad4b33024319222adbdc059ebcda5fc7d9d5ff4/hf_xet-1.4.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2815a49a7a59f3e2edf0cf113ae88e8cb2ca2a221bf353fb60c609584f4884d4", size = 4424525 }, 214 + { url = "https://files.pythonhosted.org/packages/46/f2/adc32dae6bdbc367853118b9878139ac869419a4ae7ba07185dc31251b76/hf_xet-1.4.3-cp313-cp313t-win_amd64.whl", hash = "sha256:42ee323265f1e6a81b0e11094564fb7f7e0ec75b5105ffd91ae63f403a11931b", size = 3671610 }, 215 + { url = "https://files.pythonhosted.org/packages/e2/19/25d897dcc3f81953e0c2cde9ec186c7a0fee413eb0c9a7a9130d87d94d3a/hf_xet-1.4.3-cp313-cp313t-win_arm64.whl", hash = "sha256:27c976ba60079fb8217f485b9c5c7fcd21c90b0367753805f87cb9f3cdc4418a", size = 3528529 }, 216 + { url = "https://files.pythonhosted.org/packages/ec/36/3e8f85ca9fe09b8de2b2e10c63b3b3353d7dda88a0b3d426dffbe7b8313b/hf_xet-1.4.3-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:5251d5ece3a81815bae9abab41cf7ddb7bcb8f56411bce0827f4a3071c92fdc6", size = 3801019 }, 217 + { url = "https://files.pythonhosted.org/packages/b5/9c/defb6cb1de28bccb7bd8d95f6e60f72a3d3fa4cb3d0329c26fb9a488bfe7/hf_xet-1.4.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1feb0f3abeacee143367c326a128a2e2b60868ec12a36c225afb1d6c5a05e6d2", size = 3558746 }, 218 + { url = "https://files.pythonhosted.org/packages/c1/bd/8d001191893178ff8e826e46ad5299446e62b93cd164e17b0ffea08832ec/hf_xet-1.4.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8b301fc150290ca90b4fccd079829b84bb4786747584ae08b94b4577d82fb791", size = 4207692 }, 219 + { url = "https://files.pythonhosted.org/packages/ce/48/6790b402803250e9936435613d3a78b9aaeee7973439f0918848dde58309/hf_xet-1.4.3-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:d972fbe95ddc0d3c0fc49b31a8a69f47db35c1e3699bf316421705741aab6653", size = 3986281 }, 220 + { url = "https://files.pythonhosted.org/packages/51/56/ea62552fe53db652a9099eda600b032d75554d0e86c12a73824bfedef88b/hf_xet-1.4.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c5b48db1ee344a805a1b9bd2cda9b6b65fe77ed3787bd6e87ad5521141d317cd", size = 4187414 }, 221 + { url = "https://files.pythonhosted.org/packages/7d/f5/bc1456d4638061bea997e6d2db60a1a613d7b200e0755965ec312dc1ef79/hf_xet-1.4.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:22bdc1f5fb8b15bf2831440b91d1c9bbceeb7e10c81a12e8d75889996a5c9da8", size = 4424368 }, 222 + { url = "https://files.pythonhosted.org/packages/e4/76/ab597bae87e1f06d18d3ecb8ed7f0d3c9a37037fc32ce76233d369273c64/hf_xet-1.4.3-cp314-cp314t-win_amd64.whl", hash = "sha256:0392c79b7cf48418cd61478c1a925246cf10639f4cd9d94368d8ca1e8df9ea07", size = 3672280 }, 223 + { url = "https://files.pythonhosted.org/packages/62/05/2e462d34e23a09a74d73785dbed71cc5dbad82a72eee2ad60a72a554155d/hf_xet-1.4.3-cp314-cp314t-win_arm64.whl", hash = "sha256:681c92a07796325778a79d76c67011764ecc9042a8c3579332b61b63ae512075", size = 3528945 }, 224 + { url = "https://files.pythonhosted.org/packages/ac/9f/9c23e4a447b8f83120798f9279d0297a4d1360bdbf59ef49ebec78fe2545/hf_xet-1.4.3-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:d0da85329eaf196e03e90b84c2d0aca53bd4573d097a75f99609e80775f98025", size = 3805048 }, 225 + { url = "https://files.pythonhosted.org/packages/0b/f8/7aacb8e5f4a7899d39c787b5984e912e6c18b11be136ef13947d7a66d265/hf_xet-1.4.3-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:e23717ce4186b265f69afa66e6f0069fe7efbf331546f5c313d00e123dc84583", size = 3562178 }, 226 + { url = "https://files.pythonhosted.org/packages/df/9a/a24b26dc8a65f0ecc0fe5be981a19e61e7ca963b85e062c083f3a9100529/hf_xet-1.4.3-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc360b70c815bf340ed56c7b8c63aacf11762a4b099b2fe2c9bd6d6068668c08", size = 4212320 }, 227 + { url = "https://files.pythonhosted.org/packages/53/60/46d493db155d2ee2801b71fb1b0fd67696359047fdd8caee2c914cc50c79/hf_xet-1.4.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:39f2d2e9654cd9b4319885733993807aab6de9dfbd34c42f0b78338d6617421f", size = 3991546 }, 228 + { url = "https://files.pythonhosted.org/packages/bc/f5/067363e1c96c6b17256910830d1b54099d06287e10f4ec6ec4e7e08371fc/hf_xet-1.4.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:49ad8a8cead2b56051aa84d7fce3e1335efe68df3cf6c058f22a65513885baac", size = 4193200 }, 229 + { url = "https://files.pythonhosted.org/packages/42/4b/53951592882d9c23080c7644542fda34a3813104e9e11fa1a7d82d419cb8/hf_xet-1.4.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7716d62015477a70ea272d2d68cd7cad140f61c52ee452e133e139abfe2c17ba", size = 4429392 }, 230 + { url = "https://files.pythonhosted.org/packages/8a/21/75a6c175b4e79662ad8e62f46a40ce341d8d6b206b06b4320d07d55b188c/hf_xet-1.4.3-cp37-abi3-win_amd64.whl", hash = "sha256:6b591fcad34e272a5b02607485e4f2a1334aebf1bc6d16ce8eb1eb8978ac2021", size = 3677359 }, 231 + { url = "https://files.pythonhosted.org/packages/8a/7c/44314ecd0e89f8b2b51c9d9e5e7a60a9c1c82024ac471d415860557d3cd8/hf_xet-1.4.3-cp37-abi3-win_arm64.whl", hash = "sha256:7c2c7e20bcfcc946dc67187c203463f5e932e395845d098cc2a93f5b67ca0b47", size = 3533664 }, 232 + ] 233 + 234 + [[package]] 235 + name = "httpcore" 236 + version = "1.0.9" 237 + source = { registry = "https://pypi.org/simple" } 238 + dependencies = [ 239 + { name = "certifi" }, 240 + { name = "h11" }, 241 + ] 242 + sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484 } 243 + wheels = [ 244 + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784 }, 245 + ] 246 + 247 + [[package]] 248 + name = "httptools" 249 + version = "0.7.1" 250 + source = { registry = "https://pypi.org/simple" } 251 + sdist = { url = "https://files.pythonhosted.org/packages/b5/46/120a669232c7bdedb9d52d4aeae7e6c7dfe151e99dc70802e2fc7a5e1993/httptools-0.7.1.tar.gz", hash = "sha256:abd72556974f8e7c74a259655924a717a2365b236c882c3f6f8a45fe94703ac9", size = 258961 } 252 + wheels = [ 253 + { url = "https://files.pythonhosted.org/packages/53/7f/403e5d787dc4942316e515e949b0c8a013d84078a915910e9f391ba9b3ed/httptools-0.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:38e0c83a2ea9746ebbd643bdfb521b9aa4a91703e2cd705c20443405d2fd16a5", size = 206280 }, 254 + { url = "https://files.pythonhosted.org/packages/2a/0d/7f3fd28e2ce311ccc998c388dd1c53b18120fda3b70ebb022b135dc9839b/httptools-0.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f25bbaf1235e27704f1a7b86cd3304eabc04f569c828101d94a0e605ef7205a5", size = 110004 }, 255 + { url = "https://files.pythonhosted.org/packages/84/a6/b3965e1e146ef5762870bbe76117876ceba51a201e18cc31f5703e454596/httptools-0.7.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2c15f37ef679ab9ecc06bfc4e6e8628c32a8e4b305459de7cf6785acd57e4d03", size = 517655 }, 256 + { url = "https://files.pythonhosted.org/packages/11/7d/71fee6f1844e6fa378f2eddde6c3e41ce3a1fb4b2d81118dd544e3441ec0/httptools-0.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7fe6e96090df46b36ccfaf746f03034e5ab723162bc51b0a4cf58305324036f2", size = 511440 }, 257 + { url = "https://files.pythonhosted.org/packages/22/a5/079d216712a4f3ffa24af4a0381b108aa9c45b7a5cc6eb141f81726b1823/httptools-0.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f72fdbae2dbc6e68b8239defb48e6a5937b12218e6ffc2c7846cc37befa84362", size = 495186 }, 258 + { url = "https://files.pythonhosted.org/packages/e9/9e/025ad7b65278745dee3bd0ebf9314934c4592560878308a6121f7f812084/httptools-0.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e99c7b90a29fd82fea9ef57943d501a16f3404d7b9ee81799d41639bdaae412c", size = 499192 }, 259 + { url = "https://files.pythonhosted.org/packages/6d/de/40a8f202b987d43afc4d54689600ff03ce65680ede2f31df348d7f368b8f/httptools-0.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:3e14f530fefa7499334a79b0cf7e7cd2992870eb893526fb097d51b4f2d0f321", size = 86694 }, 260 + { url = "https://files.pythonhosted.org/packages/09/8f/c77b1fcbfd262d422f12da02feb0d218fa228d52485b77b953832105bb90/httptools-0.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6babce6cfa2a99545c60bfef8bee0cc0545413cb0018f617c8059a30ad985de3", size = 202889 }, 261 + { url = "https://files.pythonhosted.org/packages/0a/1a/22887f53602feaa066354867bc49a68fc295c2293433177ee90870a7d517/httptools-0.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:601b7628de7504077dd3dcb3791c6b8694bbd967148a6d1f01806509254fb1ca", size = 108180 }, 262 + { url = "https://files.pythonhosted.org/packages/32/6a/6aaa91937f0010d288d3d124ca2946d48d60c3a5ee7ca62afe870e3ea011/httptools-0.7.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:04c6c0e6c5fb0739c5b8a9eb046d298650a0ff38cf42537fc372b28dc7e4472c", size = 478596 }, 263 + { url = "https://files.pythonhosted.org/packages/6d/70/023d7ce117993107be88d2cbca566a7c1323ccbaf0af7eabf2064fe356f6/httptools-0.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:69d4f9705c405ae3ee83d6a12283dc9feba8cc6aaec671b412917e644ab4fa66", size = 473268 }, 264 + { url = "https://files.pythonhosted.org/packages/32/4d/9dd616c38da088e3f436e9a616e1d0cc66544b8cdac405cc4e81c8679fc7/httptools-0.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:44c8f4347d4b31269c8a9205d8a5ee2df5322b09bbbd30f8f862185bb6b05346", size = 455517 }, 265 + { url = "https://files.pythonhosted.org/packages/1d/3a/a6c595c310b7df958e739aae88724e24f9246a514d909547778d776799be/httptools-0.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:465275d76db4d554918aba40bf1cbebe324670f3dfc979eaffaa5d108e2ed650", size = 458337 }, 266 + { url = "https://files.pythonhosted.org/packages/fd/82/88e8d6d2c51edc1cc391b6e044c6c435b6aebe97b1abc33db1b0b24cd582/httptools-0.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:322d00c2068d125bd570f7bf78b2d367dad02b919d8581d7476d8b75b294e3e6", size = 85743 }, 267 + { url = "https://files.pythonhosted.org/packages/34/50/9d095fcbb6de2d523e027a2f304d4551855c2f46e0b82befd718b8b20056/httptools-0.7.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:c08fe65728b8d70b6923ce31e3956f859d5e1e8548e6f22ec520a962c6757270", size = 203619 }, 268 + { url = "https://files.pythonhosted.org/packages/07/f0/89720dc5139ae54b03f861b5e2c55a37dba9a5da7d51e1e824a1f343627f/httptools-0.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7aea2e3c3953521c3c51106ee11487a910d45586e351202474d45472db7d72d3", size = 108714 }, 269 + { url = "https://files.pythonhosted.org/packages/b3/cb/eea88506f191fb552c11787c23f9a405f4c7b0c5799bf73f2249cd4f5228/httptools-0.7.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0e68b8582f4ea9166be62926077a3334064d422cf08ab87d8b74664f8e9058e1", size = 472909 }, 270 + { url = "https://files.pythonhosted.org/packages/e0/4a/a548bdfae6369c0d078bab5769f7b66f17f1bfaa6fa28f81d6be6959066b/httptools-0.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df091cf961a3be783d6aebae963cc9b71e00d57fa6f149025075217bc6a55a7b", size = 470831 }, 271 + { url = "https://files.pythonhosted.org/packages/4d/31/14df99e1c43bd132eec921c2e7e11cda7852f65619bc0fc5bdc2d0cb126c/httptools-0.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f084813239e1eb403ddacd06a30de3d3e09a9b76e7894dcda2b22f8a726e9c60", size = 452631 }, 272 + { url = "https://files.pythonhosted.org/packages/22/d2/b7e131f7be8d854d48cb6d048113c30f9a46dca0c9a8b08fcb3fcd588cdc/httptools-0.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7347714368fb2b335e9063bc2b96f2f87a9ceffcd9758ac295f8bbcd3ffbc0ca", size = 452910 }, 273 + { url = "https://files.pythonhosted.org/packages/53/cf/878f3b91e4e6e011eff6d1fa9ca39f7eb17d19c9d7971b04873734112f30/httptools-0.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:cfabda2a5bb85aa2a904ce06d974a3f30fb36cc63d7feaddec05d2050acede96", size = 88205 }, 274 + ] 275 + 276 + [[package]] 277 + name = "httpx" 278 + version = "0.28.1" 279 + source = { registry = "https://pypi.org/simple" } 280 + dependencies = [ 281 + { name = "anyio" }, 282 + { name = "certifi" }, 283 + { name = "httpcore" }, 284 + { name = "idna" }, 285 + ] 286 + sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } 287 + wheels = [ 288 + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, 289 + ] 290 + 291 + [[package]] 292 + name = "huggingface-hub" 293 + version = "1.12.0" 294 + source = { registry = "https://pypi.org/simple" } 295 + dependencies = [ 296 + { name = "filelock" }, 297 + { name = "fsspec" }, 298 + { name = "hf-xet", marker = "platform_machine == 'AMD64' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, 299 + { name = "httpx" }, 300 + { name = "packaging" }, 301 + { name = "pyyaml" }, 302 + { name = "tqdm" }, 303 + { name = "typer" }, 304 + { name = "typing-extensions" }, 305 + ] 306 + sdist = { url = "https://files.pythonhosted.org/packages/56/52/1b54cb569509c725a32c1315261ac9fd0e6b91bbbf74d86fca10d3376164/huggingface_hub-1.12.0.tar.gz", hash = "sha256:7c3fe85e24b652334e5d456d7a812cd9a071e75630fac4365d9165ab5e4a34b6", size = 763091 } 307 + wheels = [ 308 + { url = "https://files.pythonhosted.org/packages/7e/2b/ef03ddb96bd1123503c2bd6932001020292deea649e9bf4caa2cb65a85bf/huggingface_hub-1.12.0-py3-none-any.whl", hash = "sha256:d74939969585ee35748bd66de09baf84099d461bda7287cd9043bfb99b0e424d", size = 646806 }, 309 + ] 310 + 311 + [[package]] 312 + name = "idna" 313 + version = "3.13" 314 + source = { registry = "https://pypi.org/simple" } 315 + sdist = { url = "https://files.pythonhosted.org/packages/ce/cc/762dfb036166873f0059f3b7de4565e1b5bc3d6f28a414c13da27e442f99/idna-3.13.tar.gz", hash = "sha256:585ea8fe5d69b9181ec1afba340451fba6ba764af97026f92a91d4eef164a242", size = 194210 } 316 + wheels = [ 317 + { url = "https://files.pythonhosted.org/packages/5d/13/ad7d7ca3808a898b4612b6fe93cde56b53f3034dcde235acb1f0e1df24c6/idna-3.13-py3-none-any.whl", hash = "sha256:892ea0cde124a99ce773decba204c5552b69c3c67ffd5f232eb7696135bc8bb3", size = 68629 }, 318 + ] 319 + 320 + [[package]] 321 + name = "jinja2" 322 + version = "3.1.6" 323 + source = { registry = "https://pypi.org/simple" } 324 + dependencies = [ 325 + { name = "markupsafe" }, 326 + ] 327 + sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 } 328 + wheels = [ 329 + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, 330 + ] 331 + 332 + [[package]] 333 + name = "joblib" 334 + version = "1.5.3" 335 + source = { registry = "https://pypi.org/simple" } 336 + sdist = { url = "https://files.pythonhosted.org/packages/41/f2/d34e8b3a08a9cc79a50b2208a93dce981fe615b64d5a4d4abee421d898df/joblib-1.5.3.tar.gz", hash = "sha256:8561a3269e6801106863fd0d6d84bb737be9e7631e33aaed3fb9ce5953688da3", size = 331603 } 337 + wheels = [ 338 + { url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071 }, 339 + ] 340 + 341 + [[package]] 342 + name = "markdown-it-py" 343 + version = "4.0.0" 344 + source = { registry = "https://pypi.org/simple" } 345 + dependencies = [ 346 + { name = "mdurl" }, 347 + ] 348 + sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070 } 349 + wheels = [ 350 + { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321 }, 351 + ] 352 + 353 + [[package]] 354 + name = "markupsafe" 355 + version = "3.0.3" 356 + source = { registry = "https://pypi.org/simple" } 357 + sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313 } 358 + wheels = [ 359 + { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615 }, 360 + { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020 }, 361 + { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332 }, 362 + { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947 }, 363 + { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962 }, 364 + { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760 }, 365 + { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529 }, 366 + { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015 }, 367 + { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540 }, 368 + { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105 }, 369 + { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906 }, 370 + { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622 }, 371 + { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029 }, 372 + { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374 }, 373 + { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980 }, 374 + { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990 }, 375 + { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784 }, 376 + { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588 }, 377 + { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041 }, 378 + { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543 }, 379 + { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113 }, 380 + { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911 }, 381 + { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658 }, 382 + { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066 }, 383 + { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639 }, 384 + { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569 }, 385 + { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284 }, 386 + { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801 }, 387 + { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769 }, 388 + { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642 }, 389 + { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612 }, 390 + { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200 }, 391 + { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973 }, 392 + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619 }, 393 + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029 }, 394 + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408 }, 395 + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005 }, 396 + { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048 }, 397 + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821 }, 398 + { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606 }, 399 + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043 }, 400 + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747 }, 401 + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341 }, 402 + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073 }, 403 + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661 }, 404 + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069 }, 405 + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670 }, 406 + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598 }, 407 + { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261 }, 408 + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835 }, 409 + { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733 }, 410 + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672 }, 411 + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819 }, 412 + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426 }, 413 + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146 }, 414 + ] 415 + 416 + [[package]] 417 + name = "mdurl" 418 + version = "0.1.2" 419 + source = { registry = "https://pypi.org/simple" } 420 + sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } 421 + wheels = [ 422 + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, 423 + ] 424 + 425 + [[package]] 426 + name = "mpmath" 427 + version = "1.3.0" 428 + source = { registry = "https://pypi.org/simple" } 429 + sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106 } 430 + wheels = [ 431 + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198 }, 432 + ] 433 + 434 + [[package]] 435 + name = "networkx" 436 + version = "3.6.1" 437 + source = { registry = "https://pypi.org/simple" } 438 + sdist = { url = "https://files.pythonhosted.org/packages/6a/51/63fe664f3908c97be9d2e4f1158eb633317598cfa6e1fc14af5383f17512/networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509", size = 2517025 } 439 + wheels = [ 440 + { url = "https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762", size = 2068504 }, 441 + ] 442 + 443 + [[package]] 444 + name = "numpy" 445 + version = "2.4.4" 446 + source = { registry = "https://pypi.org/simple" } 447 + sdist = { url = "https://files.pythonhosted.org/packages/d7/9f/b8cef5bffa569759033adda9481211426f12f53299629b410340795c2514/numpy-2.4.4.tar.gz", hash = "sha256:2d390634c5182175533585cc89f3608a4682ccb173cc9bb940b2881c8d6f8fa0", size = 20731587 } 448 + wheels = [ 449 + { url = "https://files.pythonhosted.org/packages/28/05/32396bec30fb2263770ee910142f49c1476d08e8ad41abf8403806b520ce/numpy-2.4.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:15716cfef24d3a9762e3acdf87e27f58dc823d1348f765bbea6bef8c639bfa1b", size = 16689272 }, 450 + { url = "https://files.pythonhosted.org/packages/c5/f3/a983d28637bfcd763a9c7aafdb6d5c0ebf3d487d1e1459ffdb57e2f01117/numpy-2.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23cbfd4c17357c81021f21540da84ee282b9c8fba38a03b7b9d09ba6b951421e", size = 14699573 }, 451 + { url = "https://files.pythonhosted.org/packages/9b/fd/e5ecca1e78c05106d98028114f5c00d3eddb41207686b2b7de3e477b0e22/numpy-2.4.4-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:8b3b60bb7cba2c8c81837661c488637eee696f59a877788a396d33150c35d842", size = 5204782 }, 452 + { url = "https://files.pythonhosted.org/packages/de/2f/702a4594413c1a8632092beae8aba00f1d67947389369b3777aed783fdca/numpy-2.4.4-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:e4a010c27ff6f210ff4c6ef34394cd61470d01014439b192ec22552ee867f2a8", size = 6552038 }, 453 + { url = "https://files.pythonhosted.org/packages/7f/37/eed308a8f56cba4d1fdf467a4fc67ef4ff4bf1c888f5fc980481890104b1/numpy-2.4.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f9e75681b59ddaa5e659898085ae0eaea229d054f2ac0c7e563a62205a700121", size = 15670666 }, 454 + { url = "https://files.pythonhosted.org/packages/0a/0d/0e3ecece05b7a7e87ab9fb587855548da437a061326fff64a223b6dcb78a/numpy-2.4.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:81f4a14bee47aec54f883e0cad2d73986640c1590eb9bfaaba7ad17394481e6e", size = 16645480 }, 455 + { url = "https://files.pythonhosted.org/packages/34/49/f2312c154b82a286758ee2f1743336d50651f8b5195db18cdb63675ff649/numpy-2.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:62d6b0f03b694173f9fcb1fb317f7222fd0b0b103e784c6549f5e53a27718c44", size = 17020036 }, 456 + { url = "https://files.pythonhosted.org/packages/7b/e9/736d17bd77f1b0ec4f9901aaec129c00d59f5d84d5e79bba540ef12c2330/numpy-2.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fbc356aae7adf9e6336d336b9c8111d390a05df88f1805573ebb0807bd06fd1d", size = 18368643 }, 457 + { url = "https://files.pythonhosted.org/packages/63/f6/d417977c5f519b17c8a5c3bc9e8304b0908b0e21136fe43bf628a1343914/numpy-2.4.4-cp312-cp312-win32.whl", hash = "sha256:0d35aea54ad1d420c812bfa0385c71cd7cc5bcf7c65fed95fc2cd02fe8c79827", size = 5961117 }, 458 + { url = "https://files.pythonhosted.org/packages/2d/5b/e1deebf88ff431b01b7406ca3583ab2bbb90972bbe1c568732e49c844f7e/numpy-2.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:b5f0362dc928a6ecd9db58868fca5e48485205e3855957bdedea308f8672ea4a", size = 12320584 }, 459 + { url = "https://files.pythonhosted.org/packages/58/89/e4e856ac82a68c3ed64486a544977d0e7bdd18b8da75b78a577ca31c4395/numpy-2.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:846300f379b5b12cc769334464656bc882e0735d27d9726568bc932fdc49d5ec", size = 10221450 }, 460 + { url = "https://files.pythonhosted.org/packages/14/1d/d0a583ce4fefcc3308806a749a536c201ed6b5ad6e1322e227ee4848979d/numpy-2.4.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:08f2e31ed5e6f04b118e49821397f12767934cfdd12a1ce86a058f91e004ee50", size = 16684933 }, 461 + { url = "https://files.pythonhosted.org/packages/c1/62/2b7a48fbb745d344742c0277f01286dead15f3f68e4f359fbfcf7b48f70f/numpy-2.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e823b8b6edc81e747526f70f71a9c0a07ac4e7ad13020aa736bb7c9d67196115", size = 14694532 }, 462 + { url = "https://files.pythonhosted.org/packages/e5/87/499737bfba066b4a3bebff24a8f1c5b2dee410b209bc6668c9be692580f0/numpy-2.4.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4a19d9dba1a76618dd86b164d608566f393f8ec6ac7c44f0cc879011c45e65af", size = 5199661 }, 463 + { url = "https://files.pythonhosted.org/packages/cd/da/464d551604320d1491bc345efed99b4b7034143a85787aab78d5691d5a0e/numpy-2.4.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:d2a8490669bfe99a233298348acc2d824d496dee0e66e31b66a6022c2ad74a5c", size = 6547539 }, 464 + { url = "https://files.pythonhosted.org/packages/7d/90/8d23e3b0dafd024bf31bdec225b3bb5c2dbfa6912f8a53b8659f21216cbf/numpy-2.4.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:45dbed2ab436a9e826e302fcdcbe9133f9b0006e5af7168afb8963a6520da103", size = 15668806 }, 465 + { url = "https://files.pythonhosted.org/packages/d1/73/a9d864e42a01896bb5974475438f16086be9ba1f0d19d0bb7a07427c4a8b/numpy-2.4.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c901b15172510173f5cb310eae652908340f8dede90fff9e3bf6c0d8dfd92f83", size = 16632682 }, 466 + { url = "https://files.pythonhosted.org/packages/34/fb/14570d65c3bde4e202a031210475ae9cde9b7686a2e7dc97ee67d2833b35/numpy-2.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:99d838547ace2c4aace6c4f76e879ddfe02bb58a80c1549928477862b7a6d6ed", size = 17019810 }, 467 + { url = "https://files.pythonhosted.org/packages/8a/77/2ba9d87081fd41f6d640c83f26fb7351e536b7ce6dd9061b6af5904e8e46/numpy-2.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0aec54fd785890ecca25a6003fd9a5aed47ad607bbac5cd64f836ad8666f4959", size = 18357394 }, 468 + { url = "https://files.pythonhosted.org/packages/a2/23/52666c9a41708b0853fa3b1a12c90da38c507a3074883823126d4e9d5b30/numpy-2.4.4-cp313-cp313-win32.whl", hash = "sha256:07077278157d02f65c43b1b26a3886bce886f95d20aabd11f87932750dfb14ed", size = 5959556 }, 469 + { url = "https://files.pythonhosted.org/packages/57/fb/48649b4971cde70d817cf97a2a2fdc0b4d8308569f1dd2f2611959d2e0cf/numpy-2.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:5c70f1cc1c4efbe316a572e2d8b9b9cc44e89b95f79ca3331553fbb63716e2bf", size = 12317311 }, 470 + { url = "https://files.pythonhosted.org/packages/ba/d8/11490cddd564eb4de97b4579ef6bfe6a736cc07e94c1598590ae25415e01/numpy-2.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:ef4059d6e5152fa1a39f888e344c73fdc926e1b2dd58c771d67b0acfbf2aa67d", size = 10222060 }, 471 + { url = "https://files.pythonhosted.org/packages/99/5d/dab4339177a905aad3e2221c915b35202f1ec30d750dd2e5e9d9a72b804b/numpy-2.4.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4bbc7f303d125971f60ec0aaad5e12c62d0d2c925f0ab1273debd0e4ba37aba5", size = 14822302 }, 472 + { url = "https://files.pythonhosted.org/packages/eb/e4/0564a65e7d3d97562ed6f9b0fd0fb0a6f559ee444092f105938b50043876/numpy-2.4.4-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:4d6d57903571f86180eb98f8f0c839fa9ebbfb031356d87f1361be91e433f5b7", size = 5327407 }, 473 + { url = "https://files.pythonhosted.org/packages/29/8d/35a3a6ce5ad371afa58b4700f1c820f8f279948cca32524e0a695b0ded83/numpy-2.4.4-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:4636de7fd195197b7535f231b5de9e4b36d2c440b6e566d2e4e4746e6af0ca93", size = 6647631 }, 474 + { url = "https://files.pythonhosted.org/packages/f4/da/477731acbd5a58a946c736edfdabb2ac5b34c3d08d1ba1a7b437fa0884df/numpy-2.4.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ad2e2ef14e0b04e544ea2fa0a36463f847f113d314aa02e5b402fdf910ef309e", size = 15727691 }, 475 + { url = "https://files.pythonhosted.org/packages/e6/db/338535d9b152beabeb511579598418ba0212ce77cf9718edd70262cc4370/numpy-2.4.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a285b3b96f951841799528cd1f4f01cd70e7e0204b4abebac9463eecfcf2a40", size = 16681241 }, 476 + { url = "https://files.pythonhosted.org/packages/e2/a9/ad248e8f58beb7a0219b413c9c7d8151c5d285f7f946c3e26695bdbbe2df/numpy-2.4.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f8474c4241bc18b750be2abea9d7a9ec84f46ef861dbacf86a4f6e043401f79e", size = 17085767 }, 477 + { url = "https://files.pythonhosted.org/packages/b5/1a/3b88ccd3694681356f70da841630e4725a7264d6a885c8d442a697e1146b/numpy-2.4.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4e874c976154687c1f71715b034739b45c7711bec81db01914770373d125e392", size = 18403169 }, 478 + { url = "https://files.pythonhosted.org/packages/c2/c9/fcfd5d0639222c6eac7f304829b04892ef51c96a75d479214d77e3ce6e33/numpy-2.4.4-cp313-cp313t-win32.whl", hash = "sha256:9c585a1790d5436a5374bac930dad6ed244c046ed91b2b2a3634eb2971d21008", size = 6083477 }, 479 + { url = "https://files.pythonhosted.org/packages/d5/e3/3938a61d1c538aaec8ed6fd6323f57b0c2d2d2219512434c5c878db76553/numpy-2.4.4-cp313-cp313t-win_amd64.whl", hash = "sha256:93e15038125dc1e5345d9b5b68aa7f996ec33b98118d18c6ca0d0b7d6198b7e8", size = 12457487 }, 480 + { url = "https://files.pythonhosted.org/packages/97/6a/7e345032cc60501721ef94e0e30b60f6b0bd601f9174ebd36389a2b86d40/numpy-2.4.4-cp313-cp313t-win_arm64.whl", hash = "sha256:0dfd3f9d3adbe2920b68b5cd3d51444e13a10792ec7154cd0a2f6e74d4ab3233", size = 10292002 }, 481 + { url = "https://files.pythonhosted.org/packages/6e/06/c54062f85f673dd5c04cbe2f14c3acb8c8b95e3384869bb8cc9bff8cb9df/numpy-2.4.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:f169b9a863d34f5d11b8698ead99febeaa17a13ca044961aa8e2662a6c7766a0", size = 16684353 }, 482 + { url = "https://files.pythonhosted.org/packages/4c/39/8a320264a84404c74cc7e79715de85d6130fa07a0898f67fb5cd5bd79908/numpy-2.4.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2483e4584a1cb3092da4470b38866634bafb223cbcd551ee047633fd2584599a", size = 14704914 }, 483 + { url = "https://files.pythonhosted.org/packages/91/fb/287076b2614e1d1044235f50f03748f31fa287e3dbe6abeb35cdfa351eca/numpy-2.4.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:2d19e6e2095506d1736b7d80595e0f252d76b89f5e715c35e06e937679ea7d7a", size = 5210005 }, 484 + { url = "https://files.pythonhosted.org/packages/63/eb/fcc338595309910de6ecabfcef2419a9ce24399680bfb149421fa2df1280/numpy-2.4.4-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:6a246d5914aa1c820c9443ddcee9c02bec3e203b0c080349533fae17727dfd1b", size = 6544974 }, 485 + { url = "https://files.pythonhosted.org/packages/44/5d/e7e9044032a716cdfaa3fba27a8e874bf1c5f1912a1ddd4ed071bf8a14a6/numpy-2.4.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:989824e9faf85f96ec9c7761cd8d29c531ad857bfa1daa930cba85baaecf1a9a", size = 15684591 }, 486 + { url = "https://files.pythonhosted.org/packages/98/7c/21252050676612625449b4807d6b695b9ce8a7c9e1c197ee6216c8a65c7c/numpy-2.4.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:27a8d92cd10f1382a67d7cf4db7ce18341b66438bdd9f691d7b0e48d104c2a9d", size = 16637700 }, 487 + { url = "https://files.pythonhosted.org/packages/b1/29/56d2bbef9465db24ef25393383d761a1af4f446a1df9b8cded4fe3a5a5d7/numpy-2.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e44319a2953c738205bf3354537979eaa3998ed673395b964c1176083dd46252", size = 17035781 }, 488 + { url = "https://files.pythonhosted.org/packages/e3/2b/a35a6d7589d21f44cea7d0a98de5ddcbb3d421b2622a5c96b1edf18707c3/numpy-2.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e892aff75639bbef0d2a2cfd55535510df26ff92f63c92cd84ef8d4ba5a5557f", size = 18362959 }, 489 + { url = "https://files.pythonhosted.org/packages/64/c9/d52ec581f2390e0f5f85cbfd80fb83d965fc15e9f0e1aec2195faa142cde/numpy-2.4.4-cp314-cp314-win32.whl", hash = "sha256:1378871da56ca8943c2ba674530924bb8ca40cd228358a3b5f302ad60cf875fc", size = 6008768 }, 490 + { url = "https://files.pythonhosted.org/packages/fa/22/4cc31a62a6c7b74a8730e31a4274c5dc80e005751e277a2ce38e675e4923/numpy-2.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:715d1c092715954784bc79e1174fc2a90093dc4dc84ea15eb14dad8abdcdeb74", size = 12449181 }, 491 + { url = "https://files.pythonhosted.org/packages/70/2e/14cda6f4d8e396c612d1bf97f22958e92148801d7e4f110cabebdc0eef4b/numpy-2.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:2c194dd721e54ecad9ad387c1d35e63dce5c4450c6dc7dd5611283dda239aabb", size = 10496035 }, 492 + { url = "https://files.pythonhosted.org/packages/b1/e8/8fed8c8d848d7ecea092dc3469643f9d10bc3a134a815a3b033da1d2039b/numpy-2.4.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2aa0613a5177c264ff5921051a5719d20095ea586ca88cc802c5c218d1c67d3e", size = 14824958 }, 493 + { url = "https://files.pythonhosted.org/packages/05/1a/d8007a5138c179c2bf33ef44503e83d70434d2642877ee8fbb230e7c0548/numpy-2.4.4-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:42c16925aa5a02362f986765f9ebabf20de75cdefdca827d14315c568dcab113", size = 5330020 }, 494 + { url = "https://files.pythonhosted.org/packages/99/64/ffb99ac6ae93faf117bcbd5c7ba48a7f45364a33e8e458545d3633615dda/numpy-2.4.4-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:874f200b2a981c647340f841730fc3a2b54c9d940566a3c4149099591e2c4c3d", size = 6650758 }, 495 + { url = "https://files.pythonhosted.org/packages/6e/6e/795cc078b78a384052e73b2f6281ff7a700e9bf53bcce2ee579d4f6dd879/numpy-2.4.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9b39d38a9bd2ae1becd7eac1303d031c5c110ad31f2b319c6e7d98b135c934d", size = 15729948 }, 496 + { url = "https://files.pythonhosted.org/packages/5f/86/2acbda8cc2af5f3d7bfc791192863b9e3e19674da7b5e533fded124d1299/numpy-2.4.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b268594bccac7d7cf5844c7732e3f20c50921d94e36d7ec9b79e9857694b1b2f", size = 16679325 }, 497 + { url = "https://files.pythonhosted.org/packages/bc/59/cafd83018f4aa55e0ac6fa92aa066c0a1877b77a615ceff1711c260ffae8/numpy-2.4.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ac6b31e35612a26483e20750126d30d0941f949426974cace8e6b5c58a3657b0", size = 17084883 }, 498 + { url = "https://files.pythonhosted.org/packages/f0/85/a42548db84e65ece46ab2caea3d3f78b416a47af387fcbb47ec28e660dc2/numpy-2.4.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8e3ed142f2728df44263aaf5fb1f5b0b99f4070c553a0d7f033be65338329150", size = 18403474 }, 499 + { url = "https://files.pythonhosted.org/packages/ed/ad/483d9e262f4b831000062e5d8a45e342166ec8aaa1195264982bca267e62/numpy-2.4.4-cp314-cp314t-win32.whl", hash = "sha256:dddbbd259598d7240b18c9d87c56a9d2fb3b02fe266f49a7c101532e78c1d871", size = 6155500 }, 500 + { url = "https://files.pythonhosted.org/packages/c7/03/2fc4e14c7bd4ff2964b74ba90ecb8552540b6315f201df70f137faa5c589/numpy-2.4.4-cp314-cp314t-win_amd64.whl", hash = "sha256:a7164afb23be6e37ad90b2f10426149fd75aee07ca55653d2aa41e66c4ef697e", size = 12637755 }, 501 + { url = "https://files.pythonhosted.org/packages/58/78/548fb8e07b1a341746bfbecb32f2c268470f45fa028aacdbd10d9bc73aab/numpy-2.4.4-cp314-cp314t-win_arm64.whl", hash = "sha256:ba203255017337d39f89bdd58417f03c4426f12beed0440cfd933cb15f8669c7", size = 10566643 }, 502 + ] 503 + 504 + [[package]] 505 + name = "nvidia-cublas" 506 + version = "13.1.0.3" 507 + source = { registry = "https://pypi.org/simple" } 508 + wheels = [ 509 + { url = "https://files.pythonhosted.org/packages/e1/a5/fce49e2ae977e0ccc084e5adafceb4f0ac0c8333cb6863501618a7277f67/nvidia_cublas-13.1.0.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:c86fc7f7ae36d7528288c5d88098edcb7b02c633d262e7ddbb86b0ad91be5df2", size = 542851226 }, 510 + { url = "https://files.pythonhosted.org/packages/e7/44/423ac00af4dd95a5aeb27207e2c0d9b7118702149bf4704c3ddb55bb7429/nvidia_cublas-13.1.0.3-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:ee8722c1f0145ab246bccb9e452153b5e0515fd094c3678df50b2a0888b8b171", size = 423133236 }, 511 + ] 512 + 513 + [[package]] 514 + name = "nvidia-cuda-cupti" 515 + version = "13.0.85" 516 + source = { registry = "https://pypi.org/simple" } 517 + wheels = [ 518 + { url = "https://files.pythonhosted.org/packages/2a/2a/80353b103fc20ce05ef51e928daed4b6015db4aaa9162ed0997090fe2250/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_aarch64.whl", hash = "sha256:796bd679890ee55fb14a94629b698b6db54bcfd833d391d5e94017dd9d7d3151", size = 10310827 }, 519 + { url = "https://files.pythonhosted.org/packages/33/6d/737d164b4837a9bbd202f5ae3078975f0525a55730fe871d8ed4e3b952b0/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_x86_64.whl", hash = "sha256:4eb01c08e859bf924d222250d2e8f8b8ff6d3db4721288cf35d14252a4d933c8", size = 10715597 }, 520 + ] 521 + 522 + [[package]] 523 + name = "nvidia-cuda-nvrtc" 524 + version = "13.0.88" 525 + source = { registry = "https://pypi.org/simple" } 526 + wheels = [ 527 + { url = "https://files.pythonhosted.org/packages/c3/68/483a78f5e8f31b08fb1bb671559968c0ca3a065ac7acabfc7cee55214fd6/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:ad9b6d2ead2435f11cbb6868809d2adeeee302e9bb94bcf0539c7a40d80e8575", size = 90215200 }, 528 + { url = "https://files.pythonhosted.org/packages/b7/dc/6bb80850e0b7edd6588d560758f17e0550893a1feaf436807d64d2da040f/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d27f20a0ca67a4bb34268a5e951033496c5b74870b868bacd046b1b8e0c3267b", size = 43015449 }, 529 + ] 530 + 531 + [[package]] 532 + name = "nvidia-cuda-runtime" 533 + version = "13.0.96" 534 + source = { registry = "https://pypi.org/simple" } 535 + wheels = [ 536 + { url = "https://files.pythonhosted.org/packages/87/4f/17d7b9b8e285199c58ce28e31b5c5bbaa4d8271af06a89b6405258245de2/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ef9bcbe90493a2b9d810e43d249adb3d02e98dd30200d86607d8d02687c43f55", size = 2261060 }, 537 + { url = "https://files.pythonhosted.org/packages/2e/24/d1558f3b68b1d26e706813b1d10aa1d785e4698c425af8db8edc3dced472/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7f82250d7782aa23b6cfe765ecc7db554bd3c2870c43f3d1821f1d18aebf0548", size = 2243632 }, 538 + ] 539 + 540 + [[package]] 541 + name = "nvidia-cudnn-cu13" 542 + version = "9.19.0.56" 543 + source = { registry = "https://pypi.org/simple" } 544 + dependencies = [ 545 + { name = "nvidia-cublas" }, 546 + ] 547 + wheels = [ 548 + { url = "https://files.pythonhosted.org/packages/f1/84/26025437c1e6b61a707442184fa0c03d083b661adf3a3eecfd6d21677740/nvidia_cudnn_cu13-9.19.0.56-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:6ed29ffaee1176c612daf442e4dd6cfeb6a0caa43ddcbeb59da94953030b1be4", size = 433781201 }, 549 + { url = "https://files.pythonhosted.org/packages/a3/22/0b4b932655d17a6da1b92fa92ab12844b053bb2ac2475e179ba6f043da1e/nvidia_cudnn_cu13-9.19.0.56-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:d20e1734305e9d68889a96e3f35094d733ff1f83932ebe462753973e53a572bf", size = 366066321 }, 550 + ] 551 + 552 + [[package]] 553 + name = "nvidia-cufft" 554 + version = "12.0.0.61" 555 + source = { registry = "https://pypi.org/simple" } 556 + dependencies = [ 557 + { name = "nvidia-nvjitlink" }, 558 + ] 559 + wheels = [ 560 + { url = "https://files.pythonhosted.org/packages/8b/ae/f417a75c0259e85c1d2f83ca4e960289a5f814ed0cea74d18c353d3e989d/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2708c852ef8cd89d1d2068bdbece0aa188813a0c934db3779b9b1faa8442e5f5", size = 214053554 }, 561 + { url = "https://files.pythonhosted.org/packages/a8/2f/7b57e29836ea8714f81e9898409196f47d772d5ddedddf1592eadb8ab743/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6c44f692dce8fd5ffd3e3df134b6cdb9c2f72d99cf40b62c32dde45eea9ddad3", size = 214085489 }, 562 + ] 563 + 564 + [[package]] 565 + name = "nvidia-cufile" 566 + version = "1.15.1.6" 567 + source = { registry = "https://pypi.org/simple" } 568 + wheels = [ 569 + { url = "https://files.pythonhosted.org/packages/3f/70/4f193de89a48b71714e74602ee14d04e4019ad36a5a9f20c425776e72cd6/nvidia_cufile-1.15.1.6-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:08a3ecefae5a01c7f5117351c64f17c7c62efa5fffdbe24fc7d298da19cd0b44", size = 1223672 }, 570 + { url = "https://files.pythonhosted.org/packages/ab/73/cc4a14c9813a8a0d509417cf5f4bdaba76e924d58beb9864f5a7baceefbf/nvidia_cufile-1.15.1.6-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:bdc0deedc61f548bddf7733bdc216456c2fdb101d020e1ab4b88d232d5e2f6d1", size = 1136992 }, 571 + ] 572 + 573 + [[package]] 574 + name = "nvidia-curand" 575 + version = "10.4.0.35" 576 + source = { registry = "https://pypi.org/simple" } 577 + wheels = [ 578 + { url = "https://files.pythonhosted.org/packages/1e/72/7c2ae24fb6b63a32e6ae5d241cc65263ea18d08802aaae087d9f013335a2/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:133df5a7509c3e292aaa2b477afd0194f06ce4ea24d714d616ff36439cee349a", size = 61962106 }, 579 + { url = "https://files.pythonhosted.org/packages/a5/9f/be0a41ca4a4917abf5cb9ae0daff1a6060cc5de950aec0396de9f3b52bc5/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:1aee33a5da6e1db083fe2b90082def8915f30f3248d5896bcec36a579d941bfc", size = 59544258 }, 580 + ] 581 + 582 + [[package]] 583 + name = "nvidia-cusolver" 584 + version = "12.0.4.66" 585 + source = { registry = "https://pypi.org/simple" } 586 + dependencies = [ 587 + { name = "nvidia-cublas" }, 588 + { name = "nvidia-cusparse" }, 589 + { name = "nvidia-nvjitlink" }, 590 + ] 591 + wheels = [ 592 + { url = "https://files.pythonhosted.org/packages/c8/c3/b30c9e935fc01e3da443ec0116ed1b2a009bb867f5324d3f2d7e533e776b/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:02c2457eaa9e39de20f880f4bd8820e6a1cfb9f9a34f820eb12a155aa5bc92d2", size = 223467760 }, 593 + { url = "https://files.pythonhosted.org/packages/5f/67/cba3777620cdacb99102da4042883709c41c709f4b6323c10781a9c3aa34/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:0a759da5dea5c0ea10fd307de75cdeb59e7ea4fcb8add0924859b944babf1112", size = 200941980 }, 594 + ] 595 + 596 + [[package]] 597 + name = "nvidia-cusparse" 598 + version = "12.6.3.3" 599 + source = { registry = "https://pypi.org/simple" } 600 + dependencies = [ 601 + { name = "nvidia-nvjitlink" }, 602 + ] 603 + wheels = [ 604 + { url = "https://files.pythonhosted.org/packages/f8/94/5c26f33738ae35276672f12615a64bd008ed5be6d1ebcb23579285d960a9/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80bcc4662f23f1054ee334a15c72b8940402975e0eab63178fc7e670aa59472c", size = 162155568 }, 605 + { url = "https://files.pythonhosted.org/packages/fa/18/623c77619c31d62efd55302939756966f3ecc8d724a14dab2b75f1508850/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b3c89c88d01ee0e477cb7f82ef60a11a4bcd57b6b87c33f789350b59759360b", size = 145942937 }, 606 + ] 607 + 608 + [[package]] 609 + name = "nvidia-cusparselt-cu13" 610 + version = "0.8.0" 611 + source = { registry = "https://pypi.org/simple" } 612 + wheels = [ 613 + { url = "https://files.pythonhosted.org/packages/46/10/8dcd1175260706a2fc92a16a52e306b71d4c1ea0b0cc4a9484183399818a/nvidia_cusparselt_cu13-0.8.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:400c6ed1cf6780fc6efedd64ec9f1345871767e6a1a0a552a1ea0578117ea77c", size = 220791277 }, 614 + { url = "https://files.pythonhosted.org/packages/fd/53/43b0d71f4e702fa9733f8b4571fdca50a8813f1e450b656c239beff12315/nvidia_cusparselt_cu13-0.8.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:25e30a8a7323935d4ad0340b95a0b69926eee755767e8e0b1cf8dd85b197d3fd", size = 169884119 }, 615 + ] 616 + 617 + [[package]] 618 + name = "nvidia-nccl-cu13" 619 + version = "2.28.9" 620 + source = { registry = "https://pypi.org/simple" } 621 + wheels = [ 622 + { url = "https://files.pythonhosted.org/packages/39/55/1920646a2e43ffd4fc958536b276197ed740e9e0c54105b4bb3521591fc7/nvidia_nccl_cu13-2.28.9-py3-none-manylinux_2_18_aarch64.whl", hash = "sha256:01c873ba1626b54caa12272ed228dc5b2781545e0ae8ba3f432a8ef1c6d78643", size = 196561677 }, 623 + { url = "https://files.pythonhosted.org/packages/b0/b4/878fefaad5b2bcc6fcf8d474a25e3e3774bc5133e4b58adff4d0bca238bc/nvidia_nccl_cu13-2.28.9-py3-none-manylinux_2_18_x86_64.whl", hash = "sha256:e4553a30f34195f3fa1da02a6da3d6337d28f2003943aa0a3d247bbc25fefc42", size = 196493177 }, 624 + ] 625 + 626 + [[package]] 627 + name = "nvidia-nvjitlink" 628 + version = "13.0.88" 629 + source = { registry = "https://pypi.org/simple" } 630 + wheels = [ 631 + { url = "https://files.pythonhosted.org/packages/56/7a/123e033aaff487c77107195fa5a2b8686795ca537935a24efae476c41f05/nvidia_nvjitlink-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:13a74f429e23b921c1109976abefacc69835f2f433ebd323d3946e11d804e47b", size = 40713933 }, 632 + { url = "https://files.pythonhosted.org/packages/ab/2c/93c5250e64df4f894f1cbb397c6fd71f79813f9fd79d7cd61de3f97b3c2d/nvidia_nvjitlink-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e931536ccc7d467a98ba1d8b89ff7fa7f1fa3b13f2b0069118cd7f47bff07d0c", size = 38768748 }, 633 + ] 634 + 635 + [[package]] 636 + name = "nvidia-nvshmem-cu13" 637 + version = "3.4.5" 638 + source = { registry = "https://pypi.org/simple" } 639 + wheels = [ 640 + { url = "https://files.pythonhosted.org/packages/dc/0f/05cc9c720236dcd2db9c1ab97fff629e96821be2e63103569da0c9b72f19/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dc2a197f38e5d0376ad52cd1a2a3617d3cdc150fd5966f4aee9bcebb1d68fe9", size = 60215947 }, 641 + { url = "https://files.pythonhosted.org/packages/3c/35/a9bf80a609e74e3b000fef598933235c908fcefcef9026042b8e6dfde2a9/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:290f0a2ee94c9f3687a02502f3b9299a9f9fe826e6d0287ee18482e78d495b80", size = 60412546 }, 642 + ] 643 + 644 + [[package]] 645 + name = "nvidia-nvtx" 646 + version = "13.0.85" 647 + source = { registry = "https://pypi.org/simple" } 648 + wheels = [ 649 + { url = "https://files.pythonhosted.org/packages/c2/f3/d86c845465a2723ad7e1e5c36dcd75ddb82898b3f53be47ebd429fb2fa5d/nvidia_nvtx-13.0.85-py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4936d1d6780fbe68db454f5e72a42ff64d1fd6397df9f363ae786930fd5c1cd4", size = 148047 }, 650 + { url = "https://files.pythonhosted.org/packages/a8/64/3708a90d1ebe202ffdeb7185f878a3c84d15c2b2c31858da2ce0583e2def/nvidia_nvtx-13.0.85-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cb7780edb6b14107373c835bf8b72e7a178bac7367e23da7acb108f973f157a6", size = 148878 }, 651 + ] 652 + 653 + [[package]] 654 + name = "packaging" 655 + version = "26.2" 656 + source = { registry = "https://pypi.org/simple" } 657 + sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134 } 658 + wheels = [ 659 + { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195 }, 660 + ] 661 + 662 + [[package]] 663 + name = "pycapnp" 664 + version = "2.2.2" 665 + source = { registry = "https://pypi.org/simple" } 666 + sdist = { url = "https://files.pythonhosted.org/packages/85/7b/b2f356bc24220068beffc03e94062e8059a1383addb837303794398aec36/pycapnp-2.2.2.tar.gz", hash = "sha256:7f6c23c2283173a3cb6f1a5086dd0114779d508a7cd1b138d25a6357857d02b6", size = 730142 } 667 + wheels = [ 668 + { url = "https://files.pythonhosted.org/packages/8a/76/f8f81d32ddf950e934ec144facbc112e5acbef31a63ba5be0c5f34a00fd5/pycapnp-2.2.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8b86cb8ea5b8011b562c4e022325a826a62f91196ceb5aa33a766c0bea0b8fd3", size = 1605194 }, 669 + { url = "https://files.pythonhosted.org/packages/50/dd/a31be782d56a8648fef899f39aeeab867cf544a6b170871e3f4cbfc58af6/pycapnp-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2353531cfa669e3eeb99be9f993573341650276abec46676d687cc12b3e6b6d9", size = 1486613 }, 670 + { url = "https://files.pythonhosted.org/packages/aa/bf/8da830dda94eb7327c6508d6c26fbd964897d742f8c1c0ec48623f0c515b/pycapnp-2.2.2-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:ee27bdc78c7ccd8eaa0fe31e09f0ec4ef31deda3f475fc9373bb4b0de8083053", size = 5186701 }, 671 + { url = "https://files.pythonhosted.org/packages/8d/a1/13d0baa2f337f4f6fe8c2142646ba437a26b9c433f5d7ce016a912bad052/pycapnp-2.2.2-cp312-cp312-manylinux_2_28_i686.whl", hash = "sha256:a8ded808911d1d7a9a2197626c09eea6e269e74dc1276760789538b1efcf6cd5", size = 5239464 }, 672 + { url = "https://files.pythonhosted.org/packages/82/76/0451c64b5f0132e4b75a0afe8cec957c8bf8fa981264a7c0b264cb94663e/pycapnp-2.2.2-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:59e92e1db40041d82a95eab0bd8de2676ce50c6b97c1457e2edde4d134b6d046", size = 5542887 }, 673 + { url = "https://files.pythonhosted.org/packages/04/00/d025d68d9a5330d55cbe2d018091cacfef0835c3ad422eb6778c4525041f/pycapnp-2.2.2-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:ee1e9ac2f0b80fa892b922b60e36efc925d072ecf1204ba3e59d8d9ac7c3dc83", size = 5659696 }, 674 + { url = "https://files.pythonhosted.org/packages/58/b7/28f7c539a5f4cbaa12e55ec27d081d11473464230f2e801e4714606d3453/pycapnp-2.2.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:53273b385be78ed8ac997ff8697f2a4c760e93c190b509822a937de5531f4861", size = 5413827 }, 675 + { url = "https://files.pythonhosted.org/packages/3c/a7/83bc13d90675f0cee8a38d4ad8401bb2f8662c543b3a6622aeffb7b56b1e/pycapnp-2.2.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:812cbdd002bc542b63f969b85c6b9041dfdaf4185613635a6d4feea84c9092fa", size = 6046815 }, 676 + { url = "https://files.pythonhosted.org/packages/0d/8a/80f46baa1684bbcc4754ce22c5a44693a1209a64de6df2b256b85b8b8a97/pycapnp-2.2.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9c330218a44bd649b96f565dbf5326d183fdd20f9887bdedfeabd73f0366c2e1", size = 6367625 }, 677 + { url = "https://files.pythonhosted.org/packages/02/00/60e82eaf6b4e78d887157bf9f18234c852771cc575355e63d1114c4a5d79/pycapnp-2.2.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:796aa0ba18bcd4e6b2815471bbed059ad7ee8a815a30e81ac8a9aa030ec7818d", size = 6487265 }, 678 + { url = "https://files.pythonhosted.org/packages/57/6e/2dedd8f95dc22357c50a775ee2b8711b3d711f30344d244141e0e1962c3e/pycapnp-2.2.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:251a6abdd64b9b11d2a8e16fc365b922ef6ba6c968959b72a3a3d9d8ec8cc8d7", size = 6576699 }, 679 + { url = "https://files.pythonhosted.org/packages/2f/53/f7f69ed1d11ea30ea4f0f6d8319fbc18bc8781c480c118005e0a394492a7/pycapnp-2.2.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6aab811e0fcc27ae8bf5f04dedaa7e0af47e0d4db51d9c85ab0d2dad26a46bd7", size = 6344114 }, 680 + { url = "https://files.pythonhosted.org/packages/ab/78/ab78ee42797ff44c7e1fc0d1aa9396c6742cb05ff01a7cdf9c8f19e0defe/pycapnp-2.2.2-cp312-cp312-win32.whl", hash = "sha256:5061c85dd8f843b2656720ca6976d2a9b418845580c6f6d9602f7119fc2208d5", size = 1047207 }, 681 + { url = "https://files.pythonhosted.org/packages/4c/fb/6edf56d5144c476270fa8b2e6a660ef5a188fb0097193e342618fbcb0210/pycapnp-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:700eb8c77405222903af3fb5a371c0d766f86139c3d51f4bff41ccd6403b51f9", size = 1185178 }, 682 + { url = "https://files.pythonhosted.org/packages/ba/70/376c3f1be4ba453584bc96a9e6a7372486ce920cb9c0869c06066e77d626/pycapnp-2.2.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:90138fceca1e85ea3eaa0de6656e33c4bef1c8da3c191db0a5b5bacc969f7889", size = 1603886 }, 683 + { url = "https://files.pythonhosted.org/packages/b6/11/2728b563f3f25d826024136cd3aab39f5d1727195de5c90a2ba3d232e897/pycapnp-2.2.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cd0549036bfddb003f8e371c3c4ed3f56ac847953eb57cdd371100bb52afa64e", size = 1485731 }, 684 + { url = "https://files.pythonhosted.org/packages/77/1b/ab9bb376e7314b92dde24843fcf3d6459f10e48d95eb54d25912f973f5d7/pycapnp-2.2.2-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:7c5a8f6d96017a7bb7e202fa8920fcdad119deab0d761f9aca1e6a4755376cef", size = 5209046 }, 685 + { url = "https://files.pythonhosted.org/packages/5f/62/303548df0316740caad513e4b81b18b2db1990785f3f01c36fd19889e7d4/pycapnp-2.2.2-cp313-cp313-manylinux_2_28_i686.whl", hash = "sha256:bba31a8ba8ec32a04c5a14a5e469df7e1f1b85e169f49f7c2edfdfb78ec5075f", size = 5235782 }, 686 + { url = "https://files.pythonhosted.org/packages/5d/ec/df320105d17e118207f01208af1e505d48981856c07fe10b04a8feab39d9/pycapnp-2.2.2-cp313-cp313-manylinux_2_28_ppc64le.whl", hash = "sha256:2decdaeb517120e152f7d9ddede393a830087ac3f1024c5224f2ec58e1735abd", size = 5544647 }, 687 + { url = "https://files.pythonhosted.org/packages/03/47/bc3ea9b0d71cc3e0694993a5907ac56aab2c1ad803697be068fff55a0e1b/pycapnp-2.2.2-cp313-cp313-manylinux_2_28_s390x.whl", hash = "sha256:e879127ac9580005efcd51ec9ae903f1dd98954fb4ea88db9cf534e9a4afb379", size = 5596651 }, 688 + { url = "https://files.pythonhosted.org/packages/84/82/c500220d4eccca845a06f2f9d0747ad8043f8b893ef5278e2019cbc906cc/pycapnp-2.2.2-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:7092c2393191221b8ce1c03ddc1343d1ff26d36129a831017d2371867e9c09bb", size = 5393131 }, 689 + { url = "https://files.pythonhosted.org/packages/ed/61/be35f0b8d81cf107a9d0329d180f1fd8f5d6d75e117fdf7fcad779443f17/pycapnp-2.2.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:682812bf9ba4a60309b7150763cd5abed9341a31375398f3d2f60fee26143e13", size = 6071761 }, 690 + { url = "https://files.pythonhosted.org/packages/54/c0/d09da26ebb1bf0a130650a940b8e32f41219241c8d5f9c0891f15343dd9c/pycapnp-2.2.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6f321dbf33767bc2fd77dd66265177284697089f9e45b6dee7fd462b2b5cf918", size = 6369216 }, 691 + { url = "https://files.pythonhosted.org/packages/3d/58/a651de950437d8ef9fa91c3cce84ecd068e05011d421a6cf288f930f331c/pycapnp-2.2.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1d443d38f1cbeaec5b20b420117885fd5de812c5687e3bc8456d2e2c5cba371e", size = 6488273 }, 692 + { url = "https://files.pythonhosted.org/packages/4b/28/254f511272fde9fea1220807d759bcbdb47ecdff64ac860499be9aeddeaa/pycapnp-2.2.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:20a31fedb7ef30ec53d5d2d951a7cec8f639c954557093a3ba3d11aba5d174b4", size = 6542078 }, 693 + { url = "https://files.pythonhosted.org/packages/f9/b3/eea39216b7b59cefd21e6783e38a4d78db7e58c4f8f1f45e8351057432b7/pycapnp-2.2.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fbefb388464501899233d0b7d27256edd22c3cebb5a312da5d9f246671e9455e", size = 6333374 }, 694 + { url = "https://files.pythonhosted.org/packages/be/08/42ab52a3e7e5381d8fd7ae227ae75f54454f9e339d214ebe92763bb78115/pycapnp-2.2.2-cp313-cp313-win32.whl", hash = "sha256:a25e3b3ef40d430309acc7c024aa62b1ee6b2e7a85b341a8b7a8a6f8e29d4133", size = 1046173 }, 695 + { url = "https://files.pythonhosted.org/packages/34/d4/fec2023c4709d3631fe24e09dae2badc23d613f1288b4a6b398302945984/pycapnp-2.2.2-cp313-cp313-win_amd64.whl", hash = "sha256:db65c9ec9d69bac6d19092db43b5e1f3cb2c680810418fd1e02316f7ef157fc1", size = 1184414 }, 696 + ] 697 + 698 + [[package]] 699 + name = "pydantic" 700 + version = "2.13.3" 701 + source = { registry = "https://pypi.org/simple" } 702 + dependencies = [ 703 + { name = "annotated-types" }, 704 + { name = "pydantic-core" }, 705 + { name = "typing-extensions" }, 706 + { name = "typing-inspection" }, 707 + ] 708 + sdist = { url = "https://files.pythonhosted.org/packages/d9/e4/40d09941a2cebcb20609b86a559817d5b9291c49dd6f8c87e5feffbe703a/pydantic-2.13.3.tar.gz", hash = "sha256:af09e9d1d09f4e7fe37145c1f577e1d61ceb9a41924bf0094a36506285d0a84d", size = 844068 } 709 + wheels = [ 710 + { url = "https://files.pythonhosted.org/packages/f3/0a/fd7d723f8f8153418fb40cf9c940e82004fce7e987026b08a68a36dd3fe7/pydantic-2.13.3-py3-none-any.whl", hash = "sha256:6db14ac8dfc9a1e57f87ea2c0de670c251240f43cb0c30a5130e9720dc612927", size = 471981 }, 711 + ] 712 + 713 + [[package]] 714 + name = "pydantic-core" 715 + version = "2.46.3" 716 + source = { registry = "https://pypi.org/simple" } 717 + dependencies = [ 718 + { name = "typing-extensions" }, 719 + ] 720 + sdist = { url = "https://files.pythonhosted.org/packages/2a/ef/f7abb56c49382a246fd2ce9c799691e3c3e7175ec74b14d99e798bcddb1a/pydantic_core-2.46.3.tar.gz", hash = "sha256:41c178f65b8c29807239d47e6050262eb6bf84eb695e41101e62e38df4a5bc2c", size = 471412 } 721 + wheels = [ 722 + { url = "https://files.pythonhosted.org/packages/4b/cb/5b47425556ecc1f3fe18ed2a0083188aa46e1dd812b06e406475b3a5d536/pydantic_core-2.46.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:b11b59b3eee90a80a36701ddb4576d9ae31f93f05cb9e277ceaa09e6bf074a67", size = 2101946 }, 723 + { url = "https://files.pythonhosted.org/packages/a1/4f/2fb62c2267cae99b815bbf4a7b9283812c88ca3153ef29f7707200f1d4e5/pydantic_core-2.46.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:af8653713055ea18a3abc1537fe2ebc42f5b0bbb768d1eb79fd74eb47c0ac089", size = 1951612 }, 724 + { url = "https://files.pythonhosted.org/packages/50/6e/b7348fd30d6556d132cddd5bd79f37f96f2601fe0608afac4f5fb01ec0b3/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75a519dab6d63c514f3a81053e5266c549679e4aa88f6ec57f2b7b854aceb1b0", size = 1977027 }, 725 + { url = "https://files.pythonhosted.org/packages/82/11/31d60ee2b45540d3fb0b29302a393dbc01cd771c473f5b5147bcd353e593/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a6cd87cb1575b1ad05ba98894c5b5c96411ef678fa2f6ed2576607095b8d9789", size = 2063008 }, 726 + { url = "https://files.pythonhosted.org/packages/8a/db/3a9d1957181b59258f44a2300ab0f0be9d1e12d662a4f57bb31250455c52/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f80a55484b8d843c8ada81ebf70a682f3f00a3d40e378c06cf17ecb44d280d7d", size = 2233082 }, 727 + { url = "https://files.pythonhosted.org/packages/9c/e1/3277c38792aeb5cfb18c2f0c5785a221d9ff4e149abbe1184d53d5f72273/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3861f1731b90c50a3266316b9044f5c9b405eecb8e299b0a7120596334e4fe9c", size = 2304615 }, 728 + { url = "https://files.pythonhosted.org/packages/5e/d5/e3d9717c9eba10855325650afd2a9cba8e607321697f18953af9d562da2f/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb528e295ed31570ac3dcc9bfdd6e0150bc11ce6168ac87a8082055cf1a67395", size = 2094380 }, 729 + { url = "https://files.pythonhosted.org/packages/a1/20/abac35dedcbfd66c6f0b03e4e3564511771d6c9b7ede10a362d03e110d9b/pydantic_core-2.46.3-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:367508faa4973b992b271ba1494acaab36eb7e8739d1e47be5035fb1ea225396", size = 2135429 }, 730 + { url = "https://files.pythonhosted.org/packages/6c/a5/41bfd1df69afad71b5cf0535055bccc73022715ad362edbc124bc1e021d7/pydantic_core-2.46.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ad3c826fe523e4becf4fe39baa44286cff85ef137c729a2c5e269afbfd0905d", size = 2174582 }, 731 + { url = "https://files.pythonhosted.org/packages/79/65/38d86ea056b29b2b10734eb23329b7a7672ca604df4f2b6e9c02d4ee22fe/pydantic_core-2.46.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ec638c5d194ef8af27db69f16c954a09797c0dc25015ad6123eb2c73a4d271ca", size = 2187533 }, 732 + { url = "https://files.pythonhosted.org/packages/b6/55/a1129141678a2026badc539ad1dee0a71d06f54c2f06a4bd68c030ac781b/pydantic_core-2.46.3-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:28ed528c45446062ee66edb1d33df5d88828ae167de76e773a3c7f64bd14e976", size = 2332985 }, 733 + { url = "https://files.pythonhosted.org/packages/d7/60/cb26f4077719f709e54819f4e8e1d43f4091f94e285eb6bd21e1190a7b7c/pydantic_core-2.46.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aed19d0c783886d5bd86d80ae5030006b45e28464218747dcf83dabfdd092c7b", size = 2373670 }, 734 + { url = "https://files.pythonhosted.org/packages/6b/7e/c3f21882bdf1d8d086876f81b5e296206c69c6082551d776895de7801fa0/pydantic_core-2.46.3-cp312-cp312-win32.whl", hash = "sha256:06d5d8820cbbdb4147578c1fe7ffcd5b83f34508cb9f9ab76e807be7db6ff0a4", size = 1966722 }, 735 + { url = "https://files.pythonhosted.org/packages/57/be/6b5e757b859013ebfbd7adba02f23b428f37c86dcbf78b5bb0b4ffd36e99/pydantic_core-2.46.3-cp312-cp312-win_amd64.whl", hash = "sha256:c3212fda0ee959c1dd04c60b601ec31097aaa893573a3a1abd0a47bcac2968c1", size = 2072970 }, 736 + { url = "https://files.pythonhosted.org/packages/bf/f8/a989b21cc75e9a32d24192ef700eea606521221a89faa40c919ce884f2b1/pydantic_core-2.46.3-cp312-cp312-win_arm64.whl", hash = "sha256:f1f8338dd7a7f31761f1f1a3c47503a9a3b34eea3c8b01fa6ee96408affb5e72", size = 2035963 }, 737 + { url = "https://files.pythonhosted.org/packages/9b/3c/9b5e8eb9821936d065439c3b0fb1490ffa64163bfe7e1595985a47896073/pydantic_core-2.46.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:12bc98de041458b80c86c56b24df1d23832f3e166cbaff011f25d187f5c62c37", size = 2102109 }, 738 + { url = "https://files.pythonhosted.org/packages/91/97/1c41d1f5a19f241d8069f1e249853bcce378cdb76eec8ab636d7bc426280/pydantic_core-2.46.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:85348b8f89d2c3508b65b16c3c33a4da22b8215138d8b996912bb1532868885f", size = 1951820 }, 739 + { url = "https://files.pythonhosted.org/packages/30/b4/d03a7ae14571bc2b6b3c7b122441154720619afe9a336fa3a95434df5e2f/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1105677a6df914b1fb71a81b96c8cce7726857e1717d86001f29be06a25ee6f8", size = 1977785 }, 740 + { url = "https://files.pythonhosted.org/packages/ae/0c/4086f808834b59e3c8f1aa26df8f4b6d998cdcf354a143d18ef41529d1fe/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:87082cd65669a33adeba5470769e9704c7cf026cc30afb9cc77fd865578ebaad", size = 2062761 }, 741 + { url = "https://files.pythonhosted.org/packages/fa/71/a649be5a5064c2df0db06e0a512c2281134ed2fcc981f52a657936a7527c/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60e5f66e12c4f5212d08522963380eaaeac5ebd795826cfd19b2dfb0c7a52b9c", size = 2232989 }, 742 + { url = "https://files.pythonhosted.org/packages/a2/84/7756e75763e810b3a710f4724441d1ecc5883b94aacb07ca71c5fb5cfb69/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b6cdf19bf84128d5e7c37e8a73a0c5c10d51103a650ac585d42dd6ae233f2b7f", size = 2303975 }, 743 + { url = "https://files.pythonhosted.org/packages/6c/35/68a762e0c1e31f35fa0dac733cbd9f5b118042853698de9509c8e5bf128b/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:031bb17f4885a43773c8c763089499f242aee2ea85cf17154168775dccdecf35", size = 2095325 }, 744 + { url = "https://files.pythonhosted.org/packages/77/bf/1bf8c9a8e91836c926eae5e3e51dce009bf495a60ca56060689d3df3f340/pydantic_core-2.46.3-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:bcf2a8b2982a6673693eae7348ef3d8cf3979c1d63b54fca7c397a635cc68687", size = 2133368 }, 745 + { url = "https://files.pythonhosted.org/packages/e5/50/87d818d6bab915984995157ceb2380f5aac4e563dddbed6b56f0ed057aba/pydantic_core-2.46.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28e8cf2f52d72ced402a137145923a762cbb5081e48b34312f7a0c8f55928ec3", size = 2173908 }, 746 + { url = "https://files.pythonhosted.org/packages/91/88/a311fb306d0bd6185db41fa14ae888fb81d0baf648a761ae760d30819d33/pydantic_core-2.46.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:17eaface65d9fc5abb940003020309c1bf7a211f5f608d7870297c367e6f9022", size = 2186422 }, 747 + { url = "https://files.pythonhosted.org/packages/8f/79/28fd0d81508525ab2054fef7c77a638c8b5b0afcbbaeee493cf7c3fef7e1/pydantic_core-2.46.3-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:93fd339f23408a07e98950a89644f92c54d8729719a40b30c0a30bb9ebc55d23", size = 2332709 }, 748 + { url = "https://files.pythonhosted.org/packages/b3/21/795bf5fe5c0f379308b8ef19c50dedab2e7711dbc8d0c2acf08f1c7daa05/pydantic_core-2.46.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:23cbdb3aaa74dfe0837975dbf69b469753bbde8eacace524519ffdb6b6e89eb7", size = 2372428 }, 749 + { url = "https://files.pythonhosted.org/packages/45/b3/ed14c659cbe7605e3ef063077680a64680aec81eb1a04763a05190d49b7f/pydantic_core-2.46.3-cp313-cp313-win32.whl", hash = "sha256:610eda2e3838f401105e6326ca304f5da1e15393ae25dacae5c5c63f2c275b13", size = 1965601 }, 750 + { url = "https://files.pythonhosted.org/packages/ef/bb/adb70d9a762ddd002d723fbf1bd492244d37da41e3af7b74ad212609027e/pydantic_core-2.46.3-cp313-cp313-win_amd64.whl", hash = "sha256:68cc7866ed863db34351294187f9b729964c371ba33e31c26f478471c52e1ed0", size = 2071517 }, 751 + { url = "https://files.pythonhosted.org/packages/52/eb/66faefabebfe68bd7788339c9c9127231e680b11906368c67ce112fdb47f/pydantic_core-2.46.3-cp313-cp313-win_arm64.whl", hash = "sha256:f64b5537ac62b231572879cd08ec05600308636a5d63bcbdb15063a466977bec", size = 2035802 }, 752 + { url = "https://files.pythonhosted.org/packages/7f/db/a7bcb4940183fda36022cd18ba8dd12f2dff40740ec7b58ce7457befa416/pydantic_core-2.46.3-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:afa3aa644f74e290cdede48a7b0bee37d1c35e71b05105f6b340d484af536d9b", size = 2097614 }, 753 + { url = "https://files.pythonhosted.org/packages/24/35/e4066358a22e3e99519db370494c7528f5a2aa1367370e80e27e20283543/pydantic_core-2.46.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ced3310e51aa425f7f77da8bbbb5212616655bedbe82c70944320bc1dbe5e018", size = 1951896 }, 754 + { url = "https://files.pythonhosted.org/packages/87/92/37cf4049d1636996e4b888c05a501f40a43ff218983a551d57f9d5e14f0d/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e29908922ce9da1a30b4da490bd1d3d82c01dcfdf864d2a74aacee674d0bfa34", size = 1979314 }, 755 + { url = "https://files.pythonhosted.org/packages/d8/36/9ff4d676dfbdfb2d591cf43f3d90ded01e15b1404fd101180ed2d62a2fd3/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0c9ff69140423eea8ed2d5477df3ba037f671f5e897d206d921bc9fdc39613e7", size = 2056133 }, 756 + { url = "https://files.pythonhosted.org/packages/bc/f0/405b442a4d7ba855b06eec8b2bf9c617d43b8432d099dfdc7bf999293495/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b675ab0a0d5b1c8fdb81195dc5bcefea3f3c240871cdd7ff9a2de8aa50772eb2", size = 2228726 }, 757 + { url = "https://files.pythonhosted.org/packages/e7/f8/65cd92dd5a0bd89ba277a98ecbfaf6fc36bbd3300973c7a4b826d6ab1391/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0087084960f209a9a4af50ecd1fb063d9ad3658c07bb81a7a53f452dacbfb2ba", size = 2301214 }, 758 + { url = "https://files.pythonhosted.org/packages/fd/86/ef96a4c6e79e7a2d0410826a68fbc0eccc0fd44aa733be199d5fcac3bb87/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed42e6cc8e1b0e2b9b96e2276bad70ae625d10d6d524aed0c93de974ae029f9f", size = 2099927 }, 759 + { url = "https://files.pythonhosted.org/packages/6d/53/269caf30e0096e0a8a8f929d1982a27b3879872cca2d917d17c2f9fdf4fe/pydantic_core-2.46.3-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:f1771ce258afb3e4201e67d154edbbae712a76a6081079fe247c2f53c6322c22", size = 2128789 }, 760 + { url = "https://files.pythonhosted.org/packages/00/b0/1a6d9b6a587e118482910c244a1c5acf4d192604174132efd12bf0ac486f/pydantic_core-2.46.3-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a7610b6a5242a6c736d8ad47fd5fff87fcfe8f833b281b1c409c3d6835d9227f", size = 2173815 }, 761 + { url = "https://files.pythonhosted.org/packages/87/56/e7e00d4041a7e62b5a40815590114db3b535bf3ca0bf4dca9f16cef25246/pydantic_core-2.46.3-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:ff5e7783bcc5476e1db448bf268f11cb257b1c276d3e89f00b5727be86dd0127", size = 2181608 }, 762 + { url = "https://files.pythonhosted.org/packages/e8/22/4bd23c3d41f7c185d60808a1de83c76cf5aeabf792f6c636a55c3b1ec7f9/pydantic_core-2.46.3-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:9d2e32edcc143bc01e95300671915d9ca052d4f745aa0a49c48d4803f8a85f2c", size = 2326968 }, 763 + { url = "https://files.pythonhosted.org/packages/24/ac/66cd45129e3915e5ade3b292cb3bc7fd537f58f8f8dbdaba6170f7cabb74/pydantic_core-2.46.3-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:6e42d83d1c6b87fa56b521479cff237e626a292f3b31b6345c15a99121b454c1", size = 2369842 }, 764 + { url = "https://files.pythonhosted.org/packages/a2/51/dd4248abb84113615473aa20d5545b7c4cd73c8644003b5259686f93996c/pydantic_core-2.46.3-cp314-cp314-win32.whl", hash = "sha256:07bc6d2a28c3adb4f7c6ae46aa4f2d2929af127f587ed44057af50bf1ce0f505", size = 1959661 }, 765 + { url = "https://files.pythonhosted.org/packages/20/eb/59980e5f1ae54a3b86372bd9f0fa373ea2d402e8cdcd3459334430f91e91/pydantic_core-2.46.3-cp314-cp314-win_amd64.whl", hash = "sha256:8940562319bc621da30714617e6a7eaa6b98c84e8c685bcdc02d7ed5e7c7c44e", size = 2071686 }, 766 + { url = "https://files.pythonhosted.org/packages/8c/db/1cf77e5247047dfee34bc01fa9bca134854f528c8eb053e144298893d370/pydantic_core-2.46.3-cp314-cp314-win_arm64.whl", hash = "sha256:5dcbbcf4d22210ced8f837c96db941bdb078f419543472aca5d9a0bb7cddc7df", size = 2026907 }, 767 + { url = "https://files.pythonhosted.org/packages/57/c0/b3df9f6a543276eadba0a48487b082ca1f201745329d97dbfa287034a230/pydantic_core-2.46.3-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:d0fe3dce1e836e418f912c1ad91c73357d03e556a4d286f441bf34fed2dbeecf", size = 2095047 }, 768 + { url = "https://files.pythonhosted.org/packages/66/57/886a938073b97556c168fd99e1a7305bb363cd30a6d2c76086bf0587b32a/pydantic_core-2.46.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9ce92e58abc722dac1bf835a6798a60b294e48eb0e625ec9fd994b932ac5feee", size = 1934329 }, 769 + { url = "https://files.pythonhosted.org/packages/0b/7c/b42eaa5c34b13b07ecb51da21761297a9b8eb43044c864a035999998f328/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a03e6467f0f5ab796a486146d1b887b2dc5e5f9b3288898c1b1c3ad974e53e4a", size = 1974847 }, 770 + { url = "https://files.pythonhosted.org/packages/e6/9b/92b42db6543e7de4f99ae977101a2967b63122d4b6cf7773812da2d7d5b5/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2798b6ba041b9d70acfb9071a2ea13c8456dd1e6a5555798e41ba7b0790e329c", size = 2041742 }, 771 + { url = "https://files.pythonhosted.org/packages/0f/19/46fbe1efabb5aa2834b43b9454e70f9a83ad9c338c1291e48bdc4fecf167/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9be3e221bdc6d69abf294dcf7aff6af19c31a5cdcc8f0aa3b14be29df4bd03b1", size = 2236235 }, 772 + { url = "https://files.pythonhosted.org/packages/77/da/b3f95bc009ad60ec53120f5d16c6faa8cabdbe8a20d83849a1f2b8728148/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f13936129ce841f2a5ddf6f126fea3c43cd128807b5a59588c37cf10178c2e64", size = 2282633 }, 773 + { url = "https://files.pythonhosted.org/packages/cc/6e/401336117722e28f32fb8220df676769d28ebdf08f2f4469646d404c43a3/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28b5f2ef03416facccb1c6ef744c69793175fd27e44ef15669201601cf423acb", size = 2109679 }, 774 + { url = "https://files.pythonhosted.org/packages/fc/53/b289f9bc8756a32fe718c46f55afaeaf8d489ee18d1a1e7be1db73f42cc4/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:830d1247d77ad23852314f069e9d7ddafeec5f684baf9d7e7065ed46a049c4e6", size = 2108342 }, 775 + { url = "https://files.pythonhosted.org/packages/10/5b/8292fc7c1f9111f1b2b7c1b0dcf1179edcd014fc3ea4517499f50b829d71/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0793c90c1a3c74966e7975eaef3ed30ebdff3260a0f815a62a22adc17e4c01c", size = 2157208 }, 776 + { url = "https://files.pythonhosted.org/packages/2b/9e/f80044e9ec07580f057a89fc131f78dda7a58751ddf52bbe05eaf31db50f/pydantic_core-2.46.3-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:d2d0aead851b66f5245ec0c4fb2612ef457f8bbafefdf65a2bf9d6bac6140f47", size = 2167237 }, 777 + { url = "https://files.pythonhosted.org/packages/f8/84/6781a1b037f3b96be9227edbd1101f6d3946746056231bf4ac48cdff1a8d/pydantic_core-2.46.3-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:2f40e4246676beb31c5ce77c38a55ca4e465c6b38d11ea1bd935420568e0b1ab", size = 2312540 }, 778 + { url = "https://files.pythonhosted.org/packages/3e/db/19c0839feeb728e7df03255581f198dfdf1c2aeb1e174a8420b63c5252e5/pydantic_core-2.46.3-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:cf489cf8986c543939aeee17a09c04d6ffb43bfef8ca16fcbcc5cfdcbed24dba", size = 2369556 }, 779 + { url = "https://files.pythonhosted.org/packages/e0/15/3228774cb7cd45f5f721ddf1b2242747f4eb834d0c491f0c02d606f09fed/pydantic_core-2.46.3-cp314-cp314t-win32.whl", hash = "sha256:ffe0883b56cfc05798bf994164d2b2ff03efe2d22022a2bb080f3b626176dd56", size = 1949756 }, 780 + { url = "https://files.pythonhosted.org/packages/b8/2a/c79cf53fd91e5a87e30d481809f52f9a60dd221e39de66455cf04deaad37/pydantic_core-2.46.3-cp314-cp314t-win_amd64.whl", hash = "sha256:706d9d0ce9cf4593d07270d8e9f53b161f90c57d315aeec4fb4fd7a8b10240d8", size = 2051305 }, 781 + { url = "https://files.pythonhosted.org/packages/0b/db/d8182a7f1d9343a032265aae186eb063fe26ca4c40f256b21e8da4498e89/pydantic_core-2.46.3-cp314-cp314t-win_arm64.whl", hash = "sha256:77706aeb41df6a76568434701e0917da10692da28cb69d5fb6919ce5fdb07374", size = 2026310 }, 782 + ] 783 + 784 + [[package]] 785 + name = "pygments" 786 + version = "2.20.0" 787 + source = { registry = "https://pypi.org/simple" } 788 + sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991 } 789 + wheels = [ 790 + { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151 }, 791 + ] 792 + 793 + [[package]] 794 + name = "python-dotenv" 795 + version = "1.2.2" 796 + source = { registry = "https://pypi.org/simple" } 797 + sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135 } 798 + wheels = [ 799 + { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101 }, 800 + ] 801 + 802 + [[package]] 803 + name = "pyyaml" 804 + version = "6.0.3" 805 + source = { registry = "https://pypi.org/simple" } 806 + sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960 } 807 + wheels = [ 808 + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063 }, 809 + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973 }, 810 + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116 }, 811 + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011 }, 812 + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870 }, 813 + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089 }, 814 + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181 }, 815 + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658 }, 816 + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003 }, 817 + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344 }, 818 + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669 }, 819 + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252 }, 820 + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081 }, 821 + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159 }, 822 + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626 }, 823 + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613 }, 824 + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115 }, 825 + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427 }, 826 + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090 }, 827 + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246 }, 828 + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814 }, 829 + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809 }, 830 + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454 }, 831 + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355 }, 832 + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175 }, 833 + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228 }, 834 + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194 }, 835 + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429 }, 836 + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912 }, 837 + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108 }, 838 + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641 }, 839 + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901 }, 840 + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132 }, 841 + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261 }, 842 + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272 }, 843 + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923 }, 844 + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062 }, 845 + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341 }, 846 + ] 847 + 848 + [[package]] 849 + name = "regex" 850 + version = "2026.4.4" 851 + source = { registry = "https://pypi.org/simple" } 852 + sdist = { url = "https://files.pythonhosted.org/packages/cb/0e/3a246dbf05666918bd3664d9d787f84a9108f6f43cc953a077e4a7dfdb7e/regex-2026.4.4.tar.gz", hash = "sha256:e08270659717f6973523ce3afbafa53515c4dc5dcad637dc215b6fd50f689423", size = 416000 } 853 + wheels = [ 854 + { url = "https://files.pythonhosted.org/packages/e5/28/b972a4d3df61e1d7bcf1b59fdb3cddef22f88b6be43f161bb41ebc0e4081/regex-2026.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c07ab8794fa929e58d97a0e1796b8b76f70943fa39df225ac9964615cf1f9d52", size = 490434 }, 855 + { url = "https://files.pythonhosted.org/packages/84/20/30041446cf6dc3e0eab344fc62770e84c23b6b68a3b657821f9f80cb69b4/regex-2026.4.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2c785939dc023a1ce4ec09599c032cc9933d258a998d16ca6f2b596c010940eb", size = 292061 }, 856 + { url = "https://files.pythonhosted.org/packages/62/c8/3baa06d75c98c46d4cc4262b71fd2edb9062b5665e868bca57859dadf93a/regex-2026.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b1ce5c81c9114f1ce2f9288a51a8fd3aeea33a0cc440c415bf02da323aa0a76", size = 289628 }, 857 + { url = "https://files.pythonhosted.org/packages/31/87/3accf55634caad8c0acab23f5135ef7d4a21c39f28c55c816ae012931408/regex-2026.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:760ef21c17d8e6a4fe8cf406a97cf2806a4df93416ccc82fc98d25b1c20425be", size = 796651 }, 858 + { url = "https://files.pythonhosted.org/packages/f6/0c/aaa2c83f34efedbf06f61cb1942c25f6cf1ee3b200f832c4d05f28306c2e/regex-2026.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7088fcdcb604a4417c208e2169715800d28838fefd7455fbe40416231d1d47c1", size = 865916 }, 859 + { url = "https://files.pythonhosted.org/packages/d9/f6/8c6924c865124643e8f37823eca845dc27ac509b2ee58123685e71cd0279/regex-2026.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:07edca1ba687998968f7db5bc355288d0c6505caa7374f013d27356d93976d13", size = 912287 }, 860 + { url = "https://files.pythonhosted.org/packages/11/0e/a9f6f81013e0deaf559b25711623864970fe6a098314e374ccb1540a4152/regex-2026.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:993f657a7c1c6ec51b5e0ba97c9817d06b84ea5fa8d82e43b9405de0defdc2b9", size = 801126 }, 861 + { url = "https://files.pythonhosted.org/packages/71/61/3a0cc8af2dc0c8deb48e644dd2521f173f7e6513c6e195aad9aa8dd77ac5/regex-2026.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2b69102a743e7569ebee67e634a69c4cb7e59d6fa2e1aa7d3bdbf3f61435f62d", size = 776788 }, 862 + { url = "https://files.pythonhosted.org/packages/64/0b/8bb9cbf21ef7dee58e49b0fdb066a7aded146c823202e16494a36777594f/regex-2026.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dac006c8b6dda72d86ea3d1333d45147de79a3a3f26f10c1cf9287ca4ca0ac3", size = 785184 }, 863 + { url = "https://files.pythonhosted.org/packages/99/c2/d3e80e8137b25ee06c92627de4e4d98b94830e02b3e6f81f3d2e3f504cf5/regex-2026.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:50a766ee2010d504554bfb5f578ed2e066898aa26411d57e6296230627cdefa0", size = 859913 }, 864 + { url = "https://files.pythonhosted.org/packages/bc/e6/9d5d876157d969c804622456ef250017ac7a8f83e0e14f903b9e6df5ce95/regex-2026.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:9e2f5217648f68e3028c823df58663587c1507a5ba8419f4fdfc8a461be76043", size = 765732 }, 865 + { url = "https://files.pythonhosted.org/packages/82/80/b568935b4421388561c8ed42aff77247285d3ae3bb2a6ca22af63bae805e/regex-2026.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:39d8de85a08e32632974151ba59c6e9140646dcc36c80423962b1c5c0a92e244", size = 852152 }, 866 + { url = "https://files.pythonhosted.org/packages/39/29/f0f81217e21cd998245da047405366385d5c6072048038a3d33b37a79dc0/regex-2026.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:55d9304e0e7178dfb1e106c33edf834097ddf4a890e2f676f6c5118f84390f73", size = 789076 }, 867 + { url = "https://files.pythonhosted.org/packages/49/1d/1d957a61976ab9d4e767dd4f9d04b66cc0c41c5e36cf40e2d43688b5ae6f/regex-2026.4.4-cp312-cp312-win32.whl", hash = "sha256:04bb679bc0bde8a7bfb71e991493d47314e7b98380b083df2447cda4b6edb60f", size = 266700 }, 868 + { url = "https://files.pythonhosted.org/packages/c5/5c/bf575d396aeb58ea13b06ef2adf624f65b70fafef6950a80fc3da9cae3bc/regex-2026.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:db0ac18435a40a2543dbb3d21e161a6c78e33e8159bd2e009343d224bb03bb1b", size = 277768 }, 869 + { url = "https://files.pythonhosted.org/packages/c9/27/049df16ec6a6828ccd72add3c7f54b4df029669bea8e9817df6fff58be90/regex-2026.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:4ce255cc05c1947a12989c6db801c96461947adb7a59990f1360b5983fab4983", size = 270568 }, 870 + { url = "https://files.pythonhosted.org/packages/9d/83/c4373bc5f31f2cf4b66f9b7c31005bd87fe66f0dce17701f7db4ee79ee29/regex-2026.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:62f5519042c101762509b1d717b45a69c0139d60414b3c604b81328c01bd1943", size = 490273 }, 871 + { url = "https://files.pythonhosted.org/packages/46/f8/fe62afbcc3cf4ad4ac9adeaafd98aa747869ae12d3e8e2ac293d0593c435/regex-2026.4.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3790ba9fb5dd76715a7afe34dbe603ba03f8820764b1dc929dd08106214ed031", size = 291954 }, 872 + { url = "https://files.pythonhosted.org/packages/5a/92/4712b9fe6a33d232eeb1c189484b80c6c4b8422b90e766e1195d6e758207/regex-2026.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8fae3c6e795d7678963f2170152b0d892cf6aee9ee8afc8c45e6be38d5107fe7", size = 289487 }, 873 + { url = "https://files.pythonhosted.org/packages/88/2c/f83b93f85e01168f1070f045a42d4c937b69fdb8dd7ae82d307253f7e36e/regex-2026.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:298c3ec2d53225b3bf91142eb9691025bab610e0c0c51592dde149db679b3d17", size = 796646 }, 874 + { url = "https://files.pythonhosted.org/packages/df/55/61a2e17bf0c4dc57e11caf8dd11771280d8aaa361785f9e3bc40d653f4a7/regex-2026.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e9638791082eaf5b3ac112c587518ee78e083a11c4b28012d8fe2a0f536dfb17", size = 865904 }, 875 + { url = "https://files.pythonhosted.org/packages/45/32/1ac8ed1b5a346b5993a3d256abe0a0f03b0b73c8cc88d928537368ac65b6/regex-2026.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ae3e764bd4c5ff55035dc82a8d49acceb42a5298edf6eb2fc4d328ee5dd7afae", size = 912304 }, 876 + { url = "https://files.pythonhosted.org/packages/26/47/2ee5c613ab546f0eddebf9905d23e07beb933416b1246c2d8791d01979b4/regex-2026.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ffa81f81b80047ba89a3c69ae6a0f78d06f4a42ce5126b0eb2a0a10ad44e0b2e", size = 801126 }, 877 + { url = "https://files.pythonhosted.org/packages/75/cd/41dacd129ca9fd20bd7d02f83e0fad83e034ac8a084ec369c90f55ef37e2/regex-2026.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f56ebf9d70305307a707911b88469213630aba821e77de7d603f9d2f0730687d", size = 776772 }, 878 + { url = "https://files.pythonhosted.org/packages/89/6d/5af0b588174cb5f46041fa7dd64d3fd5cd2fe51f18766703d1edc387f324/regex-2026.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:773d1dfd652bbffb09336abf890bfd64785c7463716bf766d0eb3bc19c8b7f27", size = 785228 }, 879 + { url = "https://files.pythonhosted.org/packages/b7/3b/f5a72b7045bd59575fc33bf1345f156fcfd5a8484aea6ad84b12c5a82114/regex-2026.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d51d20befd5275d092cdffba57ded05f3c436317ee56466c8928ac32d960edaf", size = 860032 }, 880 + { url = "https://files.pythonhosted.org/packages/39/a4/72a317003d6fcd7a573584a85f59f525dfe8f67e355ca74eb6b53d66a5e2/regex-2026.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:0a51cdb3c1e9161154f976cb2bef9894bc063ac82f31b733087ffb8e880137d0", size = 765714 }, 881 + { url = "https://files.pythonhosted.org/packages/25/1e/5672e16f34dbbcb2560cc7e6a2fbb26dfa8b270711e730101da4423d3973/regex-2026.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ae5266a82596114e41fb5302140e9630204c1b5f325c770bec654b95dd54b0aa", size = 852078 }, 882 + { url = "https://files.pythonhosted.org/packages/f7/0d/c813f0af7c6cc7ed7b9558bac2e5120b60ad0fa48f813e4d4bd55446f214/regex-2026.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c882cd92ec68585e9c1cf36c447ec846c0d94edd706fe59e0c198e65822fd23b", size = 789181 }, 883 + { url = "https://files.pythonhosted.org/packages/ea/6d/a344608d1adbd2a95090ddd906cec09a11be0e6517e878d02a5123e0917f/regex-2026.4.4-cp313-cp313-win32.whl", hash = "sha256:05568c4fbf3cb4fa9e28e3af198c40d3237cf6041608a9022285fe567ec3ad62", size = 266690 }, 884 + { url = "https://files.pythonhosted.org/packages/31/07/54049f89b46235ca6f45cd6c88668a7050e77d4a15555e47dd40fde75263/regex-2026.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:3384df51ed52db0bea967e21458ab0a414f67cdddfd94401688274e55147bb81", size = 277733 }, 885 + { url = "https://files.pythonhosted.org/packages/0e/21/61366a8e20f4d43fb597708cac7f0e2baadb491ecc9549b4980b2be27d16/regex-2026.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:acd38177bd2c8e69a411d6521760806042e244d0ef94e2dd03ecdaa8a3c99427", size = 270565 }, 886 + { url = "https://files.pythonhosted.org/packages/f1/1e/3a2b9672433bef02f5d39aa1143ca2c08f311c1d041c464a42be9ae648dc/regex-2026.4.4-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:f94a11a9d05afcfcfa640e096319720a19cc0c9f7768e1a61fceee6a3afc6c7c", size = 494126 }, 887 + { url = "https://files.pythonhosted.org/packages/4e/4b/c132a4f4fe18ad3340d89fcb56235132b69559136036b845be3c073142ed/regex-2026.4.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:36bcb9d6d1307ab629edc553775baada2aefa5c50ccc0215fbfd2afcfff43141", size = 293882 }, 888 + { url = "https://files.pythonhosted.org/packages/f4/5f/eaa38092ce7a023656280f2341dbbd4ad5f05d780a70abba7bb4f4bea54c/regex-2026.4.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:261c015b3e2ed0919157046d768774ecde57f03d8fa4ba78d29793447f70e717", size = 292334 }, 889 + { url = "https://files.pythonhosted.org/packages/5f/f6/dd38146af1392dac33db7074ab331cec23cced3759167735c42c5460a243/regex-2026.4.4-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c228cf65b4a54583763645dcd73819b3b381ca8b4bb1b349dee1c135f4112c07", size = 811691 }, 890 + { url = "https://files.pythonhosted.org/packages/7a/f0/dc54c2e69f5eeec50601054998ec3690d5344277e782bd717e49867c1d29/regex-2026.4.4-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dd2630faeb6876fb0c287f664d93ddce4d50cd46c6e88e60378c05c9047e08ca", size = 871227 }, 891 + { url = "https://files.pythonhosted.org/packages/a1/af/cb16bd5dc61621e27df919a4449bbb7e5a1034c34d307e0a706e9cc0f3e3/regex-2026.4.4-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6a50ab11b7779b849472337191f3a043e27e17f71555f98d0092fa6d73364520", size = 917435 }, 892 + { url = "https://files.pythonhosted.org/packages/5c/71/8b260897f22996b666edd9402861668f45a2ca259f665ac029e6104a2d7d/regex-2026.4.4-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0734f63afe785138549fbe822a8cfeaccd1bae814c5057cc0ed5b9f2de4fc883", size = 816358 }, 893 + { url = "https://files.pythonhosted.org/packages/1c/60/775f7f72a510ef238254906c2f3d737fc80b16ca85f07d20e318d2eea894/regex-2026.4.4-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c4ee50606cb1967db7e523224e05f32089101945f859928e65657a2cbb3d278b", size = 785549 }, 894 + { url = "https://files.pythonhosted.org/packages/58/42/34d289b3627c03cf381e44da534a0021664188fa49ba41513da0b4ec6776/regex-2026.4.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6c1818f37be3ca02dcb76d63f2c7aaba4b0dc171b579796c6fbe00148dfec6b1", size = 801364 }, 895 + { url = "https://files.pythonhosted.org/packages/fc/20/f6ecf319b382a8f1ab529e898b222c3f30600fcede7834733c26279e7465/regex-2026.4.4-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:f5bfc2741d150d0be3e4a0401a5c22b06e60acb9aa4daa46d9e79a6dcd0f135b", size = 866221 }, 896 + { url = "https://files.pythonhosted.org/packages/92/6a/9f16d3609d549bd96d7a0b2aee1625d7512ba6a03efc01652149ef88e74d/regex-2026.4.4-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:504ffa8a03609a087cad81277a629b6ce884b51a24bd388a7980ad61748618ff", size = 772530 }, 897 + { url = "https://files.pythonhosted.org/packages/fa/f6/aa9768bc96a4c361ac96419fbaf2dcdc33970bb813df3ba9b09d5d7b6d96/regex-2026.4.4-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:70aadc6ff12e4b444586e57fc30771f86253f9f0045b29016b9605b4be5f7dfb", size = 856989 }, 898 + { url = "https://files.pythonhosted.org/packages/4d/b4/c671db3556be2473ae3e4bb7a297c518d281452871501221251ea4ecba57/regex-2026.4.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f4f83781191007b6ef43b03debc35435f10cad9b96e16d147efe84a1d48bdde4", size = 803241 }, 899 + { url = "https://files.pythonhosted.org/packages/2a/5c/83e3b1d89fa4f6e5a1bc97b4abd4a9a97b3c1ac7854164f694f5f0ba98a0/regex-2026.4.4-cp313-cp313t-win32.whl", hash = "sha256:e014a797de43d1847df957c0a2a8e861d1c17547ee08467d1db2c370b7568baa", size = 269921 }, 900 + { url = "https://files.pythonhosted.org/packages/28/07/077c387121f42cdb4d92b1301133c0d93b5709d096d1669ab847dda9fe2e/regex-2026.4.4-cp313-cp313t-win_amd64.whl", hash = "sha256:b15b88b0d52b179712632832c1d6e58e5774f93717849a41096880442da41ab0", size = 281240 }, 901 + { url = "https://files.pythonhosted.org/packages/9d/22/ead4a4abc7c59a4d882662aa292ca02c8b617f30b6e163bc1728879e9353/regex-2026.4.4-cp313-cp313t-win_arm64.whl", hash = "sha256:586b89cdadf7d67bf86ae3342a4dcd2b8d70a832d90c18a0ae955105caf34dbe", size = 272440 }, 902 + { url = "https://files.pythonhosted.org/packages/f0/f5/ed97c2dc47b5fbd4b73c0d7d75f9ebc8eca139f2bbef476bba35f28c0a77/regex-2026.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:2da82d643fa698e5e5210e54af90181603d5853cf469f5eedf9bfc8f59b4b8c7", size = 490343 }, 903 + { url = "https://files.pythonhosted.org/packages/80/e9/de4828a7385ec166d673a5790ad06ac48cdaa98bc0960108dd4b9cc1aef7/regex-2026.4.4-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:54a1189ad9d9357760557c91103d5e421f0a2dabe68a5cdf9103d0dcf4e00752", size = 291909 }, 904 + { url = "https://files.pythonhosted.org/packages/b4/d6/5cfbfc97f3201a4d24b596a77957e092030dcc4205894bc035cedcfce62f/regex-2026.4.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:76d67d5afb1fe402d10a6403bae668d000441e2ab115191a804287d53b772951", size = 289692 }, 905 + { url = "https://files.pythonhosted.org/packages/8e/ac/f2212d9fd56fe897e36d0110ba30ba2d247bd6410c5bd98499c7e5a1e1f2/regex-2026.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e7cd3e4ee8d80447a83bbc9ab0c8459781fa77087f856c3e740d7763be0df27f", size = 796979 }, 906 + { url = "https://files.pythonhosted.org/packages/c9/e3/a016c12675fbac988a60c7e1c16e67823ff0bc016beb27bd7a001dbdabc6/regex-2026.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e19e18c568d2866d8b6a6dfad823db86193503f90823a8f66689315ba28fbe8", size = 866744 }, 907 + { url = "https://files.pythonhosted.org/packages/af/a4/0b90ca4cf17adc3cb43de80ec71018c37c88ad64987e8d0d481a95ca60b5/regex-2026.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7698a6f38730fd1385d390d1ed07bb13dce39aa616aca6a6d89bea178464b9a4", size = 911613 }, 908 + { url = "https://files.pythonhosted.org/packages/8e/3b/2b3dac0b82d41ab43aa87c6ecde63d71189d03fe8854b8ca455a315edac3/regex-2026.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:173a66f3651cdb761018078e2d9487f4cf971232c990035ec0eb1cdc6bf929a9", size = 800551 }, 909 + { url = "https://files.pythonhosted.org/packages/25/fe/5365eb7aa0e753c4b5957815c321519ecab033c279c60e1b1ae2367fa810/regex-2026.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fa7922bbb2cc84fa062d37723f199d4c0cd200245ce269c05db82d904db66b83", size = 776911 }, 910 + { url = "https://files.pythonhosted.org/packages/aa/b3/7fb0072156bba065e3b778a7bc7b0a6328212be5dd6a86fd207e0c4f2dab/regex-2026.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:59f67cd0a0acaf0e564c20bbd7f767286f23e91e2572c5703bf3e56ea7557edb", size = 785751 }, 911 + { url = "https://files.pythonhosted.org/packages/02/1a/9f83677eb699273e56e858f7bd95acdbee376d42f59e8bfca2fd80d79df3/regex-2026.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:475e50f3f73f73614f7cba5524d6de49dee269df00272a1b85e3d19f6d498465", size = 860484 }, 912 + { url = "https://files.pythonhosted.org/packages/3b/7a/93937507b61cfcff8b4c5857f1b452852b09f741daa9acae15c971d8554e/regex-2026.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:a1c0c7d67b64d85ac2e1879923bad2f08a08f3004055f2f406ef73c850114bd4", size = 765939 }, 913 + { url = "https://files.pythonhosted.org/packages/86/ea/81a7f968a351c6552b1670ead861e2a385be730ee28402233020c67f9e0f/regex-2026.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:1371c2ccbb744d66ee63631cc9ca12aa233d5749972626b68fe1a649dd98e566", size = 851417 }, 914 + { url = "https://files.pythonhosted.org/packages/4c/7e/323c18ce4b5b8f44517a36342961a0306e931e499febbd876bb149d900f0/regex-2026.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:59968142787042db793348a3f5b918cf24ced1f23247328530e063f89c128a95", size = 789056 }, 915 + { url = "https://files.pythonhosted.org/packages/c0/af/e7510f9b11b1913b0cd44eddb784b2d650b2af6515bfce4cffcc5bfd1d38/regex-2026.4.4-cp314-cp314-win32.whl", hash = "sha256:59efe72d37fd5a91e373e5146f187f921f365f4abc1249a5ab446a60f30dd5f8", size = 272130 }, 916 + { url = "https://files.pythonhosted.org/packages/9a/51/57dae534c915e2d3a21490e88836fa2ae79dde3b66255ecc0c0a155d2c10/regex-2026.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:e0aab3ff447845049d676827d2ff714aab4f73f340e155b7de7458cf53baa5a4", size = 280992 }, 917 + { url = "https://files.pythonhosted.org/packages/0a/5e/abaf9f4c3792e34edb1434f06717fae2b07888d85cb5cec29f9204931bf8/regex-2026.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:a7a5bb6aa0cf62208bb4fa079b0c756734f8ad0e333b425732e8609bd51ee22f", size = 273563 }, 918 + { url = "https://files.pythonhosted.org/packages/ff/06/35da85f9f217b9538b99cbb170738993bcc3b23784322decb77619f11502/regex-2026.4.4-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:97850d0638391bdc7d35dc1c1039974dcb921eaafa8cc935ae4d7f272b1d60b3", size = 494191 }, 919 + { url = "https://files.pythonhosted.org/packages/54/5b/1bc35f479eef8285c4baf88d8c002023efdeebb7b44a8735b36195486ae7/regex-2026.4.4-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:ee7337f88f2a580679f7bbfe69dc86c043954f9f9c541012f49abc554a962f2e", size = 293877 }, 920 + { url = "https://files.pythonhosted.org/packages/39/5b/f53b9ad17480b3ddd14c90da04bfb55ac6894b129e5dea87bcaf7d00e336/regex-2026.4.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7429f4e6192c11d659900c0648ba8776243bf396ab95558b8c51a345afeddde6", size = 292410 }, 921 + { url = "https://files.pythonhosted.org/packages/bb/56/52377f59f60a7c51aa4161eecf0b6032c20b461805aca051250da435ffc9/regex-2026.4.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4f10fbd5dd13dcf4265b4cc07d69ca70280742870c97ae10093e3d66000359", size = 811831 }, 922 + { url = "https://files.pythonhosted.org/packages/dd/63/8026310bf066f702a9c361f83a8c9658f3fe4edb349f9c1e5d5273b7c40c/regex-2026.4.4-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a152560af4f9742b96f3827090f866eeec5becd4765c8e0d3473d9d280e76a5a", size = 871199 }, 923 + { url = "https://files.pythonhosted.org/packages/20/9f/a514bbb00a466dbb506d43f187a04047f7be1505f10a9a15615ead5080ee/regex-2026.4.4-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54170b3e95339f415d54651f97df3bff7434a663912f9358237941bbf9143f55", size = 917649 }, 924 + { url = "https://files.pythonhosted.org/packages/cb/6b/8399f68dd41a2030218839b9b18360d79b86d22b9fab5ef477c7f23ca67c/regex-2026.4.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:07f190d65f5a72dcb9cf7106bfc3d21e7a49dd2879eda2207b683f32165e4d99", size = 816388 }, 925 + { url = "https://files.pythonhosted.org/packages/1e/9c/103963f47c24339a483b05edd568594c2be486188f688c0170fd504b2948/regex-2026.4.4-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9a2741ce5a29d3c84b0b94261ba630ab459a1b847a0d6beca7d62d188175c790", size = 785746 }, 926 + { url = "https://files.pythonhosted.org/packages/fa/ee/7f6054c0dec0cee3463c304405e4ff42e27cff05bf36fcb34be549ab17bd/regex-2026.4.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b26c30df3a28fd9793113dac7385a4deb7294a06c0f760dd2b008bd49a9139bc", size = 801483 }, 927 + { url = "https://files.pythonhosted.org/packages/30/c2/51d3d941cf6070dc00c3338ecf138615fc3cce0421c3df6abe97a08af61a/regex-2026.4.4-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:421439d1bee44b19f4583ccf42670ca464ffb90e9fdc38d37f39d1ddd1e44f1f", size = 866331 }, 928 + { url = "https://files.pythonhosted.org/packages/16/e8/76d50dcc122ac33927d939f350eebcfe3dbcbda96913e03433fc36de5e63/regex-2026.4.4-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:b40379b53ecbc747fd9bdf4a0ea14eb8188ca1bd0f54f78893a39024b28f4863", size = 772673 }, 929 + { url = "https://files.pythonhosted.org/packages/a5/6e/5f6bf75e20ea6873d05ba4ec78378c375cbe08cdec571c83fbb01606e563/regex-2026.4.4-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:08c55c13d2eef54f73eeadc33146fb0baaa49e7335eb1aff6ae1324bf0ddbe4a", size = 857146 }, 930 + { url = "https://files.pythonhosted.org/packages/0b/33/3c76d9962949e487ebba353a18e89399f292287204ac8f2f4cfc3a51c233/regex-2026.4.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9776b85f510062f5a75ef112afe5f494ef1635607bf1cc220c1391e9ac2f5e81", size = 803463 }, 931 + { url = "https://files.pythonhosted.org/packages/19/eb/ef32dcd2cb69b69bc0c3e55205bce94a7def48d495358946bc42186dcccc/regex-2026.4.4-cp314-cp314t-win32.whl", hash = "sha256:385edaebde5db5be103577afc8699fea73a0e36a734ba24870be7ffa61119d74", size = 275709 }, 932 + { url = "https://files.pythonhosted.org/packages/a0/86/c291bf740945acbf35ed7dbebf8e2eea2f3f78041f6bd7cdab80cb274dc0/regex-2026.4.4-cp314-cp314t-win_amd64.whl", hash = "sha256:5d354b18839328927832e2fa5f7c95b7a3ccc39e7a681529e1685898e6436d45", size = 285622 }, 933 + { url = "https://files.pythonhosted.org/packages/d5/e7/ec846d560ae6a597115153c02ca6138a7877a1748b2072d9521c10a93e58/regex-2026.4.4-cp314-cp314t-win_arm64.whl", hash = "sha256:af0384cb01a33600c49505c27c6c57ab0b27bf84a74e28524c92ca897ebdac9d", size = 275773 }, 934 + ] 935 + 936 + [[package]] 937 + name = "rich" 938 + version = "15.0.0" 939 + source = { registry = "https://pypi.org/simple" } 940 + dependencies = [ 941 + { name = "markdown-it-py" }, 942 + { name = "pygments" }, 943 + ] 944 + sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680 } 945 + wheels = [ 946 + { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654 }, 947 + ] 948 + 949 + [[package]] 950 + name = "safetensors" 951 + version = "0.7.0" 952 + source = { registry = "https://pypi.org/simple" } 953 + sdist = { url = "https://files.pythonhosted.org/packages/29/9c/6e74567782559a63bd040a236edca26fd71bc7ba88de2ef35d75df3bca5e/safetensors-0.7.0.tar.gz", hash = "sha256:07663963b67e8bd9f0b8ad15bb9163606cd27cc5a1b96235a50d8369803b96b0", size = 200878 } 954 + wheels = [ 955 + { url = "https://files.pythonhosted.org/packages/fa/47/aef6c06649039accf914afef490268e1067ed82be62bcfa5b7e886ad15e8/safetensors-0.7.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c82f4d474cf725255d9e6acf17252991c3c8aac038d6ef363a4bf8be2f6db517", size = 467781 }, 956 + { url = "https://files.pythonhosted.org/packages/e8/00/374c0c068e30cd31f1e1b46b4b5738168ec79e7689ca82ee93ddfea05109/safetensors-0.7.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:94fd4858284736bb67a897a41608b5b0c2496c9bdb3bf2af1fa3409127f20d57", size = 447058 }, 957 + { url = "https://files.pythonhosted.org/packages/f1/06/578ffed52c2296f93d7fd2d844cabfa92be51a587c38c8afbb8ae449ca89/safetensors-0.7.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e07d91d0c92a31200f25351f4acb2bc6aff7f48094e13ebb1d0fb995b54b6542", size = 491748 }, 958 + { url = "https://files.pythonhosted.org/packages/ae/33/1debbbb70e4791dde185edb9413d1fe01619255abb64b300157d7f15dddd/safetensors-0.7.0-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8469155f4cb518bafb4acf4865e8bb9d6804110d2d9bdcaa78564b9fd841e104", size = 503881 }, 959 + { url = "https://files.pythonhosted.org/packages/8e/1c/40c2ca924d60792c3be509833df711b553c60effbd91da6f5284a83f7122/safetensors-0.7.0-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54bef08bf00a2bff599982f6b08e8770e09cc012d7bba00783fc7ea38f1fb37d", size = 623463 }, 960 + { url = "https://files.pythonhosted.org/packages/9b/3a/13784a9364bd43b0d61eef4bea2845039bc2030458b16594a1bd787ae26e/safetensors-0.7.0-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42cb091236206bb2016d245c377ed383aa7f78691748f3bb6ee1bfa51ae2ce6a", size = 532855 }, 961 + { url = "https://files.pythonhosted.org/packages/a0/60/429e9b1cb3fc651937727befe258ea24122d9663e4d5709a48c9cbfceecb/safetensors-0.7.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac7252938f0696ddea46f5e855dd3138444e82236e3be475f54929f0c510d48", size = 507152 }, 962 + { url = "https://files.pythonhosted.org/packages/3c/a8/4b45e4e059270d17af60359713ffd83f97900d45a6afa73aaa0d737d48b6/safetensors-0.7.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1d060c70284127fa805085d8f10fbd0962792aed71879d00864acda69dbab981", size = 541856 }, 963 + { url = "https://files.pythonhosted.org/packages/06/87/d26d8407c44175d8ae164a95b5a62707fcc445f3c0c56108e37d98070a3d/safetensors-0.7.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cdab83a366799fa730f90a4ebb563e494f28e9e92c4819e556152ad55e43591b", size = 674060 }, 964 + { url = "https://files.pythonhosted.org/packages/11/f5/57644a2ff08dc6325816ba7217e5095f17269dada2554b658442c66aed51/safetensors-0.7.0-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:672132907fcad9f2aedcb705b2d7b3b93354a2aec1b2f706c4db852abe338f85", size = 771715 }, 965 + { url = "https://files.pythonhosted.org/packages/86/31/17883e13a814bd278ae6e266b13282a01049b0c81341da7fd0e3e71a80a3/safetensors-0.7.0-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:5d72abdb8a4d56d4020713724ba81dac065fedb7f3667151c4a637f1d3fb26c0", size = 714377 }, 966 + { url = "https://files.pythonhosted.org/packages/4a/d8/0c8a7dc9b41dcac53c4cbf9df2b9c83e0e0097203de8b37a712b345c0be5/safetensors-0.7.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b0f6d66c1c538d5a94a73aa9ddca8ccc4227e6c9ff555322ea40bdd142391dd4", size = 677368 }, 967 + { url = "https://files.pythonhosted.org/packages/05/e5/cb4b713c8a93469e3c5be7c3f8d77d307e65fe89673e731f5c2bfd0a9237/safetensors-0.7.0-cp38-abi3-win32.whl", hash = "sha256:c74af94bf3ac15ac4d0f2a7c7b4663a15f8c2ab15ed0fc7531ca61d0835eccba", size = 326423 }, 968 + { url = "https://files.pythonhosted.org/packages/5d/e6/ec8471c8072382cb91233ba7267fd931219753bb43814cbc71757bfd4dab/safetensors-0.7.0-cp38-abi3-win_amd64.whl", hash = "sha256:d1239932053f56f3456f32eb9625590cc7582e905021f94636202a864d470755", size = 341380 }, 969 + ] 970 + 971 + [[package]] 972 + name = "scikit-learn" 973 + version = "1.8.0" 974 + source = { registry = "https://pypi.org/simple" } 975 + dependencies = [ 976 + { name = "joblib" }, 977 + { name = "numpy" }, 978 + { name = "scipy" }, 979 + { name = "threadpoolctl" }, 980 + ] 981 + sdist = { url = "https://files.pythonhosted.org/packages/0e/d4/40988bf3b8e34feec1d0e6a051446b1f66225f8529b9309becaeef62b6c4/scikit_learn-1.8.0.tar.gz", hash = "sha256:9bccbb3b40e3de10351f8f5068e105d0f4083b1a65fa07b6634fbc401a6287fd", size = 7335585 } 982 + wheels = [ 983 + { url = "https://files.pythonhosted.org/packages/90/74/e6a7cc4b820e95cc38cf36cd74d5aa2b42e8ffc2d21fe5a9a9c45c1c7630/scikit_learn-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5fb63362b5a7ddab88e52b6dbb47dac3fd7dafeee740dc6c8d8a446ddedade8e", size = 8548242 }, 984 + { url = "https://files.pythonhosted.org/packages/49/d8/9be608c6024d021041c7f0b3928d4749a706f4e2c3832bbede4fb4f58c95/scikit_learn-1.8.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:5025ce924beccb28298246e589c691fe1b8c1c96507e6d27d12c5fadd85bfd76", size = 8079075 }, 985 + { url = "https://files.pythonhosted.org/packages/dd/47/f187b4636ff80cc63f21cd40b7b2d177134acaa10f6bb73746130ee8c2e5/scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4496bb2cf7a43ce1a2d7524a79e40bc5da45cf598dbf9545b7e8316ccba47bb4", size = 8660492 }, 986 + { url = "https://files.pythonhosted.org/packages/97/74/b7a304feb2b49df9fafa9382d4d09061a96ee9a9449a7cbea7988dda0828/scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0bcfe4d0d14aec44921545fd2af2338c7471de9cb701f1da4c9d85906ab847a", size = 8931904 }, 987 + { url = "https://files.pythonhosted.org/packages/9f/c4/0ab22726a04ede56f689476b760f98f8f46607caecff993017ac1b64aa5d/scikit_learn-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:35c007dedb2ffe38fe3ee7d201ebac4a2deccd2408e8621d53067733e3c74809", size = 8019359 }, 988 + { url = "https://files.pythonhosted.org/packages/24/90/344a67811cfd561d7335c1b96ca21455e7e472d281c3c279c4d3f2300236/scikit_learn-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:8c497fff237d7b4e07e9ef1a640887fa4fb765647f86fbe00f969ff6280ce2bb", size = 7641898 }, 989 + { url = "https://files.pythonhosted.org/packages/03/aa/e22e0768512ce9255eba34775be2e85c2048da73da1193e841707f8f039c/scikit_learn-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0d6ae97234d5d7079dc0040990a6f7aeb97cb7fa7e8945f1999a429b23569e0a", size = 8513770 }, 990 + { url = "https://files.pythonhosted.org/packages/58/37/31b83b2594105f61a381fc74ca19e8780ee923be2d496fcd8d2e1147bd99/scikit_learn-1.8.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:edec98c5e7c128328124a029bceb09eda2d526997780fef8d65e9a69eead963e", size = 8044458 }, 991 + { url = "https://files.pythonhosted.org/packages/2d/5a/3f1caed8765f33eabb723596666da4ebbf43d11e96550fb18bdec42b467b/scikit_learn-1.8.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:74b66d8689d52ed04c271e1329f0c61635bcaf5b926db9b12d58914cdc01fe57", size = 8610341 }, 992 + { url = "https://files.pythonhosted.org/packages/38/cf/06896db3f71c75902a8e9943b444a56e727418f6b4b4a90c98c934f51ed4/scikit_learn-1.8.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8fdf95767f989b0cfedb85f7ed8ca215d4be728031f56ff5a519ee1e3276dc2e", size = 8900022 }, 993 + { url = "https://files.pythonhosted.org/packages/1c/f9/9b7563caf3ec8873e17a31401858efab6b39a882daf6c1bfa88879c0aa11/scikit_learn-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:2de443b9373b3b615aec1bb57f9baa6bb3a9bd093f1269ba95c17d870422b271", size = 7989409 }, 994 + { url = "https://files.pythonhosted.org/packages/49/bd/1f4001503650e72c4f6009ac0c4413cb17d2d601cef6f71c0453da2732fc/scikit_learn-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:eddde82a035681427cbedded4e6eff5e57fa59216c2e3e90b10b19ab1d0a65c3", size = 7619760 }, 995 + { url = "https://files.pythonhosted.org/packages/d2/7d/a630359fc9dcc95496588c8d8e3245cc8fd81980251079bc09c70d41d951/scikit_learn-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7cc267b6108f0a1499a734167282c00c4ebf61328566b55ef262d48e9849c735", size = 8826045 }, 996 + { url = "https://files.pythonhosted.org/packages/cc/56/a0c86f6930cfcd1c7054a2bc417e26960bb88d32444fe7f71d5c2cfae891/scikit_learn-1.8.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:fe1c011a640a9f0791146011dfd3c7d9669785f9fed2b2a5f9e207536cf5c2fd", size = 8420324 }, 997 + { url = "https://files.pythonhosted.org/packages/46/1e/05962ea1cebc1cf3876667ecb14c283ef755bf409993c5946ade3b77e303/scikit_learn-1.8.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72358cce49465d140cc4e7792015bb1f0296a9742d5622c67e31399b75468b9e", size = 8680651 }, 998 + { url = "https://files.pythonhosted.org/packages/fe/56/a85473cd75f200c9759e3a5f0bcab2d116c92a8a02ee08ccd73b870f8bb4/scikit_learn-1.8.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:80832434a6cc114f5219211eec13dcbc16c2bac0e31ef64c6d346cde3cf054cb", size = 8925045 }, 999 + { url = "https://files.pythonhosted.org/packages/cc/b7/64d8cfa896c64435ae57f4917a548d7ac7a44762ff9802f75a79b77cb633/scikit_learn-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ee787491dbfe082d9c3013f01f5991658b0f38aa8177e4cd4bf434c58f551702", size = 8507994 }, 1000 + { url = "https://files.pythonhosted.org/packages/5e/37/e192ea709551799379958b4c4771ec507347027bb7c942662c7fbeba31cb/scikit_learn-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf97c10a3f5a7543f9b88cbf488d33d175e9146115a451ae34568597ba33dcde", size = 7869518 }, 1001 + { url = "https://files.pythonhosted.org/packages/24/05/1af2c186174cc92dcab2233f327336058c077d38f6fe2aceb08e6ab4d509/scikit_learn-1.8.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c22a2da7a198c28dd1a6e1136f19c830beab7fdca5b3e5c8bba8394f8a5c45b3", size = 8528667 }, 1002 + { url = "https://files.pythonhosted.org/packages/a8/25/01c0af38fe969473fb292bba9dc2b8f9b451f3112ff242c647fee3d0dfe7/scikit_learn-1.8.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:6b595b07a03069a2b1740dc08c2299993850ea81cce4fe19b2421e0c970de6b7", size = 8066524 }, 1003 + { url = "https://files.pythonhosted.org/packages/be/ce/a0623350aa0b68647333940ee46fe45086c6060ec604874e38e9ab7d8e6c/scikit_learn-1.8.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:29ffc74089f3d5e87dfca4c2c8450f88bdc61b0fc6ed5d267f3988f19a1309f6", size = 8657133 }, 1004 + { url = "https://files.pythonhosted.org/packages/b8/cb/861b41341d6f1245e6ca80b1c1a8c4dfce43255b03df034429089ca2a2c5/scikit_learn-1.8.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fb65db5d7531bccf3a4f6bec3462223bea71384e2cda41da0f10b7c292b9e7c4", size = 8923223 }, 1005 + { url = "https://files.pythonhosted.org/packages/76/18/a8def8f91b18cd1ba6e05dbe02540168cb24d47e8dcf69e8d00b7da42a08/scikit_learn-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:56079a99c20d230e873ea40753102102734c5953366972a71d5cb39a32bc40c6", size = 8096518 }, 1006 + { url = "https://files.pythonhosted.org/packages/d1/77/482076a678458307f0deb44e29891d6022617b2a64c840c725495bee343f/scikit_learn-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:3bad7565bc9cf37ce19a7c0d107742b320c1285df7aab1a6e2d28780df167242", size = 7754546 }, 1007 + { url = "https://files.pythonhosted.org/packages/2d/d1/ef294ca754826daa043b2a104e59960abfab4cf653891037d19dd5b6f3cf/scikit_learn-1.8.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:4511be56637e46c25721e83d1a9cea9614e7badc7040c4d573d75fbe257d6fd7", size = 8848305 }, 1008 + { url = "https://files.pythonhosted.org/packages/5b/e2/b1f8b05138ee813b8e1a4149f2f0d289547e60851fd1bb268886915adbda/scikit_learn-1.8.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:a69525355a641bf8ef136a7fa447672fb54fe8d60cab5538d9eb7c6438543fb9", size = 8432257 }, 1009 + { url = "https://files.pythonhosted.org/packages/26/11/c32b2138a85dcb0c99f6afd13a70a951bfdff8a6ab42d8160522542fb647/scikit_learn-1.8.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c2656924ec73e5939c76ac4c8b026fc203b83d8900362eb2599d8aee80e4880f", size = 8678673 }, 1010 + { url = "https://files.pythonhosted.org/packages/c7/57/51f2384575bdec454f4fe4e7a919d696c9ebce914590abf3e52d47607ab8/scikit_learn-1.8.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15fc3b5d19cc2be65404786857f2e13c70c83dd4782676dd6814e3b89dc8f5b9", size = 8922467 }, 1011 + { url = "https://files.pythonhosted.org/packages/35/4d/748c9e2872637a57981a04adc038dacaa16ba8ca887b23e34953f0b3f742/scikit_learn-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:00d6f1d66fbcf4eba6e356e1420d33cc06c70a45bb1363cd6f6a8e4ebbbdece2", size = 8774395 }, 1012 + { url = "https://files.pythonhosted.org/packages/60/22/d7b2ebe4704a5e50790ba089d5c2ae308ab6bb852719e6c3bd4f04c3a363/scikit_learn-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:f28dd15c6bb0b66ba09728cf09fd8736c304be29409bd8445a080c1280619e8c", size = 8002647 }, 1013 + ] 1014 + 1015 + [[package]] 1016 + name = "scipy" 1017 + version = "1.17.1" 1018 + source = { registry = "https://pypi.org/simple" } 1019 + dependencies = [ 1020 + { name = "numpy" }, 1021 + ] 1022 + sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822 } 1023 + wheels = [ 1024 + { url = "https://files.pythonhosted.org/packages/35/48/b992b488d6f299dbe3f11a20b24d3dda3d46f1a635ede1c46b5b17a7b163/scipy-1.17.1-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:35c3a56d2ef83efc372eaec584314bd0ef2e2f0d2adb21c55e6ad5b344c0dcb8", size = 31610954 }, 1025 + { url = "https://files.pythonhosted.org/packages/b2/02/cf107b01494c19dc100f1d0b7ac3cc08666e96ba2d64db7626066cee895e/scipy-1.17.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:fcb310ddb270a06114bb64bbe53c94926b943f5b7f0842194d585c65eb4edd76", size = 28172662 }, 1026 + { url = "https://files.pythonhosted.org/packages/cf/a9/599c28631bad314d219cf9ffd40e985b24d603fc8a2f4ccc5ae8419a535b/scipy-1.17.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:cc90d2e9c7e5c7f1a482c9875007c095c3194b1cfedca3c2f3291cdc2bc7c086", size = 20344366 }, 1027 + { url = "https://files.pythonhosted.org/packages/35/f5/906eda513271c8deb5af284e5ef0206d17a96239af79f9fa0aebfe0e36b4/scipy-1.17.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:c80be5ede8f3f8eded4eff73cc99a25c388ce98e555b17d31da05287015ffa5b", size = 22704017 }, 1028 + { url = "https://files.pythonhosted.org/packages/da/34/16f10e3042d2f1d6b66e0428308ab52224b6a23049cb2f5c1756f713815f/scipy-1.17.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e19ebea31758fac5893a2ac360fedd00116cbb7628e650842a6691ba7ca28a21", size = 32927842 }, 1029 + { url = "https://files.pythonhosted.org/packages/01/8e/1e35281b8ab6d5d72ebe9911edcdffa3f36b04ed9d51dec6dd140396e220/scipy-1.17.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02ae3b274fde71c5e92ac4d54bc06c42d80e399fec704383dcd99b301df37458", size = 35235890 }, 1030 + { url = "https://files.pythonhosted.org/packages/c5/5c/9d7f4c88bea6e0d5a4f1bc0506a53a00e9fcb198de372bfe4d3652cef482/scipy-1.17.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8a604bae87c6195d8b1045eddece0514d041604b14f2727bbc2b3020172045eb", size = 35003557 }, 1031 + { url = "https://files.pythonhosted.org/packages/65/94/7698add8f276dbab7a9de9fb6b0e02fc13ee61d51c7c3f85ac28b65e1239/scipy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f590cd684941912d10becc07325a3eeb77886fe981415660d9265c4c418d0bea", size = 37625856 }, 1032 + { url = "https://files.pythonhosted.org/packages/a2/84/dc08d77fbf3d87d3ee27f6a0c6dcce1de5829a64f2eae85a0ecc1f0daa73/scipy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:41b71f4a3a4cab9d366cd9065b288efc4d4f3c0b37a91a8e0947fb5bd7f31d87", size = 36549682 }, 1033 + { url = "https://files.pythonhosted.org/packages/bc/98/fe9ae9ffb3b54b62559f52dedaebe204b408db8109a8c66fdd04869e6424/scipy-1.17.1-cp312-cp312-win_arm64.whl", hash = "sha256:f4115102802df98b2b0db3cce5cb9b92572633a1197c77b7553e5203f284a5b3", size = 24547340 }, 1034 + { url = "https://files.pythonhosted.org/packages/76/27/07ee1b57b65e92645f219b37148a7e7928b82e2b5dbeccecb4dff7c64f0b/scipy-1.17.1-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:5e3c5c011904115f88a39308379c17f91546f77c1667cea98739fe0fccea804c", size = 31590199 }, 1035 + { url = "https://files.pythonhosted.org/packages/ec/ae/db19f8ab842e9b724bf5dbb7db29302a91f1e55bc4d04b1025d6d605a2c5/scipy-1.17.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:6fac755ca3d2c3edcb22f479fceaa241704111414831ddd3bc6056e18516892f", size = 28154001 }, 1036 + { url = "https://files.pythonhosted.org/packages/5b/58/3ce96251560107b381cbd6e8413c483bbb1228a6b919fa8652b0d4090e7f/scipy-1.17.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:7ff200bf9d24f2e4d5dc6ee8c3ac64d739d3a89e2326ba68aaf6c4a2b838fd7d", size = 20325719 }, 1037 + { url = "https://files.pythonhosted.org/packages/b2/83/15087d945e0e4d48ce2377498abf5ad171ae013232ae31d06f336e64c999/scipy-1.17.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4b400bdc6f79fa02a4d86640310dde87a21fba0c979efff5248908c6f15fad1b", size = 22683595 }, 1038 + { url = "https://files.pythonhosted.org/packages/b4/e0/e58fbde4a1a594c8be8114eb4aac1a55bcd6587047efc18a61eb1f5c0d30/scipy-1.17.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b64ca7d4aee0102a97f3ba22124052b4bd2152522355073580bf4845e2550b6", size = 32896429 }, 1039 + { url = "https://files.pythonhosted.org/packages/f5/5f/f17563f28ff03c7b6799c50d01d5d856a1d55f2676f537ca8d28c7f627cd/scipy-1.17.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:581b2264fc0aa555f3f435a5944da7504ea3a065d7029ad60e7c3d1ae09c5464", size = 35203952 }, 1040 + { url = "https://files.pythonhosted.org/packages/8d/a5/9afd17de24f657fdfe4df9a3f1ea049b39aef7c06000c13db1530d81ccca/scipy-1.17.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:beeda3d4ae615106d7094f7e7cef6218392e4465cc95d25f900bebabfded0950", size = 34979063 }, 1041 + { url = "https://files.pythonhosted.org/packages/8b/13/88b1d2384b424bf7c924f2038c1c409f8d88bb2a8d49d097861dd64a57b2/scipy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6609bc224e9568f65064cfa72edc0f24ee6655b47575954ec6339534b2798369", size = 37598449 }, 1042 + { url = "https://files.pythonhosted.org/packages/35/e5/d6d0e51fc888f692a35134336866341c08655d92614f492c6860dc45bb2c/scipy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:37425bc9175607b0268f493d79a292c39f9d001a357bebb6b88fdfaff13f6448", size = 36510943 }, 1043 + { url = "https://files.pythonhosted.org/packages/2a/fd/3be73c564e2a01e690e19cc618811540ba5354c67c8680dce3281123fb79/scipy-1.17.1-cp313-cp313-win_arm64.whl", hash = "sha256:5cf36e801231b6a2059bf354720274b7558746f3b1a4efb43fcf557ccd484a87", size = 24545621 }, 1044 + { url = "https://files.pythonhosted.org/packages/6f/6b/17787db8b8114933a66f9dcc479a8272e4b4da75fe03b0c282f7b0ade8cd/scipy-1.17.1-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:d59c30000a16d8edc7e64152e30220bfbd724c9bbb08368c054e24c651314f0a", size = 31936708 }, 1045 + { url = "https://files.pythonhosted.org/packages/38/2e/524405c2b6392765ab1e2b722a41d5da33dc5c7b7278184a8ad29b6cb206/scipy-1.17.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:010f4333c96c9bb1a4516269e33cb5917b08ef2166d5556ca2fd9f082a9e6ea0", size = 28570135 }, 1046 + { url = "https://files.pythonhosted.org/packages/fd/c3/5bd7199f4ea8556c0c8e39f04ccb014ac37d1468e6cfa6a95c6b3562b76e/scipy-1.17.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:2ceb2d3e01c5f1d83c4189737a42d9cb2fc38a6eeed225e7515eef71ad301dce", size = 20741977 }, 1047 + { url = "https://files.pythonhosted.org/packages/d9/b8/8ccd9b766ad14c78386599708eb745f6b44f08400a5fd0ade7cf89b6fc93/scipy-1.17.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:844e165636711ef41f80b4103ed234181646b98a53c8f05da12ca5ca289134f6", size = 23029601 }, 1048 + { url = "https://files.pythonhosted.org/packages/6d/a0/3cb6f4d2fb3e17428ad2880333cac878909ad1a89f678527b5328b93c1d4/scipy-1.17.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:158dd96d2207e21c966063e1635b1063cd7787b627b6f07305315dd73d9c679e", size = 33019667 }, 1049 + { url = "https://files.pythonhosted.org/packages/f3/c3/2d834a5ac7bf3a0c806ad1508efc02dda3c8c61472a56132d7894c312dea/scipy-1.17.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74cbb80d93260fe2ffa334efa24cb8f2f0f622a9b9febf8b483c0b865bfb3475", size = 35264159 }, 1050 + { url = "https://files.pythonhosted.org/packages/4d/77/d3ed4becfdbd217c52062fafe35a72388d1bd82c2d0ba5ca19d6fcc93e11/scipy-1.17.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:dbc12c9f3d185f5c737d801da555fb74b3dcfa1a50b66a1a93e09190f41fab50", size = 35102771 }, 1051 + { url = "https://files.pythonhosted.org/packages/bd/12/d19da97efde68ca1ee5538bb261d5d2c062f0c055575128f11a2730e3ac1/scipy-1.17.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:94055a11dfebe37c656e70317e1996dc197e1a15bbcc351bcdd4610e128fe1ca", size = 37665910 }, 1052 + { url = "https://files.pythonhosted.org/packages/06/1c/1172a88d507a4baaf72c5a09bb6c018fe2ae0ab622e5830b703a46cc9e44/scipy-1.17.1-cp313-cp313t-win_amd64.whl", hash = "sha256:e30bdeaa5deed6bc27b4cc490823cd0347d7dae09119b8803ae576ea0ce52e4c", size = 36562980 }, 1053 + { url = "https://files.pythonhosted.org/packages/70/b0/eb757336e5a76dfa7911f63252e3b7d1de00935d7705cf772db5b45ec238/scipy-1.17.1-cp313-cp313t-win_arm64.whl", hash = "sha256:a720477885a9d2411f94a93d16f9d89bad0f28ca23c3f8daa521e2dcc3f44d49", size = 24856543 }, 1054 + { url = "https://files.pythonhosted.org/packages/cf/83/333afb452af6f0fd70414dc04f898647ee1423979ce02efa75c3b0f2c28e/scipy-1.17.1-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:a48a72c77a310327f6a3a920092fa2b8fd03d7deaa60f093038f22d98e096717", size = 31584510 }, 1055 + { url = "https://files.pythonhosted.org/packages/ed/a6/d05a85fd51daeb2e4ea71d102f15b34fedca8e931af02594193ae4fd25f7/scipy-1.17.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:45abad819184f07240d8a696117a7aacd39787af9e0b719d00285549ed19a1e9", size = 28170131 }, 1056 + { url = "https://files.pythonhosted.org/packages/db/7b/8624a203326675d7746a254083a187398090a179335b2e4a20e2ddc46e83/scipy-1.17.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:3fd1fcdab3ea951b610dc4cef356d416d5802991e7e32b5254828d342f7b7e0b", size = 20342032 }, 1057 + { url = "https://files.pythonhosted.org/packages/c9/35/2c342897c00775d688d8ff3987aced3426858fd89d5a0e26e020b660b301/scipy-1.17.1-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:7bdf2da170b67fdf10bca777614b1c7d96ae3ca5794fd9587dce41eb2966e866", size = 22678766 }, 1058 + { url = "https://files.pythonhosted.org/packages/ef/f2/7cdb8eb308a1a6ae1e19f945913c82c23c0c442a462a46480ce487fdc0ac/scipy-1.17.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:adb2642e060a6549c343603a3851ba76ef0b74cc8c079a9a58121c7ec9fe2350", size = 32957007 }, 1059 + { url = "https://files.pythonhosted.org/packages/0b/2e/7eea398450457ecb54e18e9d10110993fa65561c4f3add5e8eccd2b9cd41/scipy-1.17.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eee2cfda04c00a857206a4330f0c5e3e56535494e30ca445eb19ec624ae75118", size = 35221333 }, 1060 + { url = "https://files.pythonhosted.org/packages/d9/77/5b8509d03b77f093a0d52e606d3c4f79e8b06d1d38c441dacb1e26cacf46/scipy-1.17.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d2650c1fb97e184d12d8ba010493ee7b322864f7d3d00d3f9bb97d9c21de4068", size = 35042066 }, 1061 + { url = "https://files.pythonhosted.org/packages/f9/df/18f80fb99df40b4070328d5ae5c596f2f00fffb50167e31439e932f29e7d/scipy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:08b900519463543aa604a06bec02461558a6e1cef8fdbb8098f77a48a83c8118", size = 37612763 }, 1062 + { url = "https://files.pythonhosted.org/packages/4b/39/f0e8ea762a764a9dc52aa7dabcfad51a354819de1f0d4652b6a1122424d6/scipy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:3877ac408e14da24a6196de0ddcace62092bfc12a83823e92e49e40747e52c19", size = 37290984 }, 1063 + { url = "https://files.pythonhosted.org/packages/7c/56/fe201e3b0f93d1a8bcf75d3379affd228a63d7e2d80ab45467a74b494947/scipy-1.17.1-cp314-cp314-win_arm64.whl", hash = "sha256:f8885db0bc2bffa59d5c1b72fad7a6a92d3e80e7257f967dd81abb553a90d293", size = 25192877 }, 1064 + { url = "https://files.pythonhosted.org/packages/96/ad/f8c414e121f82e02d76f310f16db9899c4fcde36710329502a6b2a3c0392/scipy-1.17.1-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:1cc682cea2ae55524432f3cdff9e9a3be743d52a7443d0cba9017c23c87ae2f6", size = 31949750 }, 1065 + { url = "https://files.pythonhosted.org/packages/7c/b0/c741e8865d61b67c81e255f4f0a832846c064e426636cd7de84e74d209be/scipy-1.17.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:2040ad4d1795a0ae89bfc7e8429677f365d45aa9fd5e4587cf1ea737f927b4a1", size = 28585858 }, 1066 + { url = "https://files.pythonhosted.org/packages/ed/1b/3985219c6177866628fa7c2595bfd23f193ceebbe472c98a08824b9466ff/scipy-1.17.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:131f5aaea57602008f9822e2115029b55d4b5f7c070287699fe45c661d051e39", size = 20757723 }, 1067 + { url = "https://files.pythonhosted.org/packages/c0/19/2a04aa25050d656d6f7b9e7b685cc83d6957fb101665bfd9369ca6534563/scipy-1.17.1-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:9cdc1a2fcfd5c52cfb3045feb399f7b3ce822abdde3a193a6b9a60b3cb5854ca", size = 23043098 }, 1068 + { url = "https://files.pythonhosted.org/packages/86/f1/3383beb9b5d0dbddd030335bf8a8b32d4317185efe495374f134d8be6cce/scipy-1.17.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e3dcd57ab780c741fde8dc68619de988b966db759a3c3152e8e9142c26295ad", size = 33030397 }, 1069 + { url = "https://files.pythonhosted.org/packages/41/68/8f21e8a65a5a03f25a79165ec9d2b28c00e66dc80546cf5eb803aeeff35b/scipy-1.17.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a9956e4d4f4a301ebf6cde39850333a6b6110799d470dbbb1e25326ac447f52a", size = 35281163 }, 1070 + { url = "https://files.pythonhosted.org/packages/84/8d/c8a5e19479554007a5632ed7529e665c315ae7492b4f946b0deb39870e39/scipy-1.17.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:a4328d245944d09fd639771de275701ccadf5f781ba0ff092ad141e017eccda4", size = 35116291 }, 1071 + { url = "https://files.pythonhosted.org/packages/52/52/e57eceff0e342a1f50e274264ed47497b59e6a4e3118808ee58ddda7b74a/scipy-1.17.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a77cbd07b940d326d39a1d1b37817e2ee4d79cb30e7338f3d0cddffae70fcaa2", size = 37682317 }, 1072 + { url = "https://files.pythonhosted.org/packages/11/2f/b29eafe4a3fbc3d6de9662b36e028d5f039e72d345e05c250e121a230dd4/scipy-1.17.1-cp314-cp314t-win_amd64.whl", hash = "sha256:eb092099205ef62cd1782b006658db09e2fed75bffcae7cc0d44052d8aa0f484", size = 37345327 }, 1073 + { url = "https://files.pythonhosted.org/packages/07/39/338d9219c4e87f3e708f18857ecd24d22a0c3094752393319553096b98af/scipy-1.17.1-cp314-cp314t-win_arm64.whl", hash = "sha256:200e1050faffacc162be6a486a984a0497866ec54149a01270adc8a59b7c7d21", size = 25489165 }, 1074 + ] 1075 + 1076 + [[package]] 1077 + name = "sentence-transformers" 1078 + version = "5.4.1" 1079 + source = { registry = "https://pypi.org/simple" } 1080 + dependencies = [ 1081 + { name = "huggingface-hub" }, 1082 + { name = "numpy" }, 1083 + { name = "scikit-learn" }, 1084 + { name = "scipy" }, 1085 + { name = "torch" }, 1086 + { name = "tqdm" }, 1087 + { name = "transformers" }, 1088 + { name = "typing-extensions" }, 1089 + ] 1090 + sdist = { url = "https://files.pythonhosted.org/packages/4d/68/7f98c221940ce783b492ad6140384daf2e2918cd7175009d6a362c22b9ee/sentence_transformers-5.4.1.tar.gz", hash = "sha256:436bcb1182a0ff42a8fb2b1c43498a70d0a75b688d182f2cd0d1dd115af61ddc", size = 428910 } 1091 + wheels = [ 1092 + { url = "https://files.pythonhosted.org/packages/c5/d9/3a9b6f2ccdedc9dc00fe37b2fc58f58f8efbff44565cf4bf39d8568bb13a/sentence_transformers-5.4.1-py3-none-any.whl", hash = "sha256:a6d640fc363849b63affb8e140e9d328feabab86f83d58ac3e16b1c28140b790", size = 571311 }, 1093 + ] 1094 + 1095 + [[package]] 1096 + name = "setuptools" 1097 + version = "81.0.0" 1098 + source = { registry = "https://pypi.org/simple" } 1099 + sdist = { url = "https://files.pythonhosted.org/packages/0d/1c/73e719955c59b8e424d015ab450f51c0af856ae46ea2da83eba51cc88de1/setuptools-81.0.0.tar.gz", hash = "sha256:487b53915f52501f0a79ccfd0c02c165ffe06631443a886740b91af4b7a5845a", size = 1198299 } 1100 + wheels = [ 1101 + { url = "https://files.pythonhosted.org/packages/e1/e3/c164c88b2e5ce7b24d667b9bd83589cf4f3520d97cad01534cd3c4f55fdb/setuptools-81.0.0-py3-none-any.whl", hash = "sha256:fdd925d5c5d9f62e4b74b30d6dd7828ce236fd6ed998a08d81de62ce5a6310d6", size = 1062021 }, 1102 + ] 1103 + 1104 + [[package]] 1105 + name = "shellingham" 1106 + version = "1.5.4" 1107 + source = { registry = "https://pypi.org/simple" } 1108 + sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310 } 1109 + wheels = [ 1110 + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755 }, 1111 + ] 1112 + 1113 + [[package]] 1114 + name = "starlette" 1115 + version = "1.0.0" 1116 + source = { registry = "https://pypi.org/simple" } 1117 + dependencies = [ 1118 + { name = "anyio" }, 1119 + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, 1120 + ] 1121 + sdist = { url = "https://files.pythonhosted.org/packages/81/69/17425771797c36cded50b7fe44e850315d039f28b15901ab44839e70b593/starlette-1.0.0.tar.gz", hash = "sha256:6a4beaf1f81bb472fd19ea9b918b50dc3a77a6f2e190a12954b25e6ed5eea149", size = 2655289 } 1122 + wheels = [ 1123 + { url = "https://files.pythonhosted.org/packages/0b/c9/584bc9651441b4ba60cc4d557d8a547b5aff901af35bda3a4ee30c819b82/starlette-1.0.0-py3-none-any.whl", hash = "sha256:d3ec55e0bb321692d275455ddfd3df75fff145d009685eb40dc91fc66b03d38b", size = 72651 }, 1124 + ] 1125 + 1126 + [[package]] 1127 + name = "sympy" 1128 + version = "1.14.0" 1129 + source = { registry = "https://pypi.org/simple" } 1130 + dependencies = [ 1131 + { name = "mpmath" }, 1132 + ] 1133 + sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921 } 1134 + wheels = [ 1135 + { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353 }, 1136 + ] 1137 + 1138 + [[package]] 1139 + name = "threadpoolctl" 1140 + version = "3.6.0" 1141 + source = { registry = "https://pypi.org/simple" } 1142 + sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274 } 1143 + wheels = [ 1144 + { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638 }, 1145 + ] 1146 + 1147 + [[package]] 1148 + name = "tokenizers" 1149 + version = "0.22.2" 1150 + source = { registry = "https://pypi.org/simple" } 1151 + dependencies = [ 1152 + { name = "huggingface-hub" }, 1153 + ] 1154 + sdist = { url = "https://files.pythonhosted.org/packages/73/6f/f80cfef4a312e1fb34baf7d85c72d4411afde10978d4657f8cdd811d3ccc/tokenizers-0.22.2.tar.gz", hash = "sha256:473b83b915e547aa366d1eee11806deaf419e17be16310ac0a14077f1e28f917", size = 372115 } 1155 + wheels = [ 1156 + { url = "https://files.pythonhosted.org/packages/92/97/5dbfabf04c7e348e655e907ed27913e03db0923abb5dfdd120d7b25630e1/tokenizers-0.22.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:544dd704ae7238755d790de45ba8da072e9af3eea688f698b137915ae959281c", size = 3100275 }, 1157 + { url = "https://files.pythonhosted.org/packages/2e/47/174dca0502ef88b28f1c9e06b73ce33500eedfac7a7692108aec220464e7/tokenizers-0.22.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1e418a55456beedca4621dbab65a318981467a2b188e982a23e117f115ce5001", size = 2981472 }, 1158 + { url = "https://files.pythonhosted.org/packages/d6/84/7990e799f1309a8b87af6b948f31edaa12a3ed22d11b352eaf4f4b2e5753/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2249487018adec45d6e3554c71d46eb39fa8ea67156c640f7513eb26f318cec7", size = 3290736 }, 1159 + { url = "https://files.pythonhosted.org/packages/78/59/09d0d9ba94dcd5f4f1368d4858d24546b4bdc0231c2354aa31d6199f0399/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25b85325d0815e86e0bac263506dd114578953b7b53d7de09a6485e4a160a7dd", size = 3168835 }, 1160 + { url = "https://files.pythonhosted.org/packages/47/50/b3ebb4243e7160bda8d34b731e54dd8ab8b133e50775872e7a434e524c28/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfb88f22a209ff7b40a576d5324bf8286b519d7358663db21d6246fb17eea2d5", size = 3521673 }, 1161 + { url = "https://files.pythonhosted.org/packages/e0/fa/89f4cb9e08df770b57adb96f8cbb7e22695a4cb6c2bd5f0c4f0ebcf33b66/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c774b1276f71e1ef716e5486f21e76333464f47bece56bbd554485982a9e03e", size = 3724818 }, 1162 + { url = "https://files.pythonhosted.org/packages/64/04/ca2363f0bfbe3b3d36e95bf67e56a4c88c8e3362b658e616d1ac185d47f2/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df6c4265b289083bf710dff49bc51ef252f9d5be33a45ee2bed151114a56207b", size = 3379195 }, 1163 + { url = "https://files.pythonhosted.org/packages/2e/76/932be4b50ef6ccedf9d3c6639b056a967a86258c6d9200643f01269211ca/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:369cc9fc8cc10cb24143873a0d95438bb8ee257bb80c71989e3ee290e8d72c67", size = 3274982 }, 1164 + { url = "https://files.pythonhosted.org/packages/1d/28/5f9f5a4cc211b69e89420980e483831bcc29dade307955cc9dc858a40f01/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:29c30b83d8dcd061078b05ae0cb94d3c710555fbb44861139f9f83dcca3dc3e4", size = 9478245 }, 1165 + { url = "https://files.pythonhosted.org/packages/6c/fb/66e2da4704d6aadebf8cb39f1d6d1957df667ab24cff2326b77cda0dcb85/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:37ae80a28c1d3265bb1f22464c856bd23c02a05bb211e56d0c5301a435be6c1a", size = 9560069 }, 1166 + { url = "https://files.pythonhosted.org/packages/16/04/fed398b05caa87ce9b1a1bb5166645e38196081b225059a6edaff6440fac/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:791135ee325f2336f498590eb2f11dc5c295232f288e75c99a36c5dbce63088a", size = 9899263 }, 1167 + { url = "https://files.pythonhosted.org/packages/05/a1/d62dfe7376beaaf1394917e0f8e93ee5f67fea8fcf4107501db35996586b/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38337540fbbddff8e999d59970f3c6f35a82de10053206a7562f1ea02d046fa5", size = 10033429 }, 1168 + { url = "https://files.pythonhosted.org/packages/fd/18/a545c4ea42af3df6effd7d13d250ba77a0a86fb20393143bbb9a92e434d4/tokenizers-0.22.2-cp39-abi3-win32.whl", hash = "sha256:a6bf3f88c554a2b653af81f3204491c818ae2ac6fbc09e76ef4773351292bc92", size = 2502363 }, 1169 + { url = "https://files.pythonhosted.org/packages/65/71/0670843133a43d43070abeb1949abfdef12a86d490bea9cd9e18e37c5ff7/tokenizers-0.22.2-cp39-abi3-win_amd64.whl", hash = "sha256:c9ea31edff2968b44a88f97d784c2f16dc0729b8b143ed004699ebca91f05c48", size = 2747786 }, 1170 + { url = "https://files.pythonhosted.org/packages/72/f4/0de46cfa12cdcbcd464cc59fde36912af405696f687e53a091fb432f694c/tokenizers-0.22.2-cp39-abi3-win_arm64.whl", hash = "sha256:9ce725d22864a1e965217204946f830c37876eee3b2ba6fc6255e8e903d5fcbc", size = 2612133 }, 1171 + ] 1172 + 1173 + [[package]] 1174 + name = "torch" 1175 + version = "2.11.0" 1176 + source = { registry = "https://pypi.org/simple" } 1177 + dependencies = [ 1178 + { name = "cuda-bindings", marker = "sys_platform == 'linux'" }, 1179 + { name = "cuda-toolkit", extra = ["cublas", "cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "sys_platform == 'linux'" }, 1180 + { name = "filelock" }, 1181 + { name = "fsspec" }, 1182 + { name = "jinja2" }, 1183 + { name = "networkx" }, 1184 + { name = "nvidia-cudnn-cu13", marker = "sys_platform == 'linux'" }, 1185 + { name = "nvidia-cusparselt-cu13", marker = "sys_platform == 'linux'" }, 1186 + { name = "nvidia-nccl-cu13", marker = "sys_platform == 'linux'" }, 1187 + { name = "nvidia-nvshmem-cu13", marker = "sys_platform == 'linux'" }, 1188 + { name = "setuptools" }, 1189 + { name = "sympy" }, 1190 + { name = "triton", marker = "sys_platform == 'linux'" }, 1191 + { name = "typing-extensions" }, 1192 + ] 1193 + wheels = [ 1194 + { url = "https://files.pythonhosted.org/packages/6f/8b/69e3008d78e5cee2b30183340cc425081b78afc5eff3d080daab0adda9aa/torch-2.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b5866312ee6e52ea625cd211dcb97d6a2cdc1131a5f15cc0d87eec948f6dd34", size = 80606338 }, 1195 + { url = "https://files.pythonhosted.org/packages/13/16/42e5915ebe4868caa6bac83a8ed59db57f12e9a61b7d749d584776ed53d5/torch-2.11.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f99924682ef0aa6a4ab3b1b76f40dc6e273fca09f367d15a524266db100a723f", size = 419731115 }, 1196 + { url = "https://files.pythonhosted.org/packages/1a/c9/82638ef24d7877510f83baf821f5619a61b45568ce21c0a87a91576510aa/torch-2.11.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:0f68f4ac6d95d12e896c3b7a912b5871619542ec54d3649cf48cc1edd4dd2756", size = 530712279 }, 1197 + { url = "https://files.pythonhosted.org/packages/1c/ff/6756f1c7ee302f6d202120e0f4f05b432b839908f9071157302cedfc5232/torch-2.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:fbf39280699d1b869f55eac536deceaa1b60bd6788ba74f399cc67e60a5fab10", size = 114556047 }, 1198 + { url = "https://files.pythonhosted.org/packages/87/89/5ea6722763acee56b045435fb84258db7375c48165ec8be7880ab2b281c5/torch-2.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1e6debd97ccd3205bbb37eb806a9d8219e1139d15419982c09e23ef7d4369d18", size = 80606801 }, 1199 + { url = "https://files.pythonhosted.org/packages/32/d1/8ed2173589cbfe744ed54e5a73efc107c0085ba5777ee93a5f4c1ab90553/torch-2.11.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:63a68fa59de8f87acc7e85a5478bb2dddbb3392b7593ec3e78827c793c4b73fd", size = 419732382 }, 1200 + { url = "https://files.pythonhosted.org/packages/3d/e1/b73f7c575a4b8f87a5928f50a1e35416b5e27295d8be9397d5293e7e8d4c/torch-2.11.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:cc89b9b173d9adfab59fd227f0ab5e5516d9a52b658ae41d64e59d2e55a418db", size = 530711509 }, 1201 + { url = "https://files.pythonhosted.org/packages/66/82/3e3fcdd388fbe54e29fd3f991f36846ff4ac90b0d0181e9c8f7236565f82/torch-2.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:4dda3b3f52d121063a731ddb835f010dc137b920d7fec2778e52f60d8e4bf0cd", size = 114555842 }, 1202 + { url = "https://files.pythonhosted.org/packages/db/38/8ac78069621b8c2b4979c2f96dc8409ef5e9c4189f6aac629189a78677ca/torch-2.11.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8b394322f49af4362d4f80e424bcaca7efcd049619af03a4cf4501520bdf0fb4", size = 80959574 }, 1203 + { url = "https://files.pythonhosted.org/packages/6d/6c/56bfb37073e7136e6dd86bfc6af7339946dd684e0ecf2155ac0eee687ae1/torch-2.11.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:2658f34ce7e2dabf4ec73b45e2ca68aedad7a5be87ea756ad656eaf32bf1e1ea", size = 419732324 }, 1204 + { url = "https://files.pythonhosted.org/packages/07/f4/1b666b6d61d3394cca306ea543ed03a64aad0a201b6cd159f1d41010aeb1/torch-2.11.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:98bb213c3084cfe176302949bdc360074b18a9da7ab59ef2edc9d9f742504778", size = 530596026 }, 1205 + { url = "https://files.pythonhosted.org/packages/48/6b/30d1459fa7e4b67e9e3fe1685ca1d8bb4ce7c62ef436c3a615963c6c866c/torch-2.11.0-cp313-cp313t-win_amd64.whl", hash = "sha256:a97b94bbf62992949b4730c6cd2cc9aee7b335921ee8dc207d930f2ed09ae2db", size = 114793702 }, 1206 + { url = "https://files.pythonhosted.org/packages/26/0d/8603382f61abd0db35841148ddc1ffd607bf3100b11c6e1dab6d2fc44e72/torch-2.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:01018087326984a33b64e04c8cb5c2795f9120e0d775ada1f6638840227b04d7", size = 80573442 }, 1207 + { url = "https://files.pythonhosted.org/packages/c7/86/7cd7c66cb9cec6be330fff36db5bd0eef386d80c031b581ec81be1d4b26c/torch-2.11.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:2bb3cc54bd0dea126b0060bb1ec9de0f9c7f7342d93d436646516b0330cd5be7", size = 419749385 }, 1208 + { url = "https://files.pythonhosted.org/packages/47/e8/b98ca2d39b2e0e4730c0ee52537e488e7008025bc77ca89552ff91021f7c/torch-2.11.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:4dc8b3809469b6c30b411bb8c4cad3828efd26236153d9beb6a3ec500f211a60", size = 530716756 }, 1209 + { url = "https://files.pythonhosted.org/packages/78/88/d4a4cda8362f8a30d1ed428564878c3cafb0d87971fbd3947d4c84552095/torch-2.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:2b4e811728bd0cc58fb2b0948fe939a1ee2bf1422f6025be2fca4c7bd9d79718", size = 114552300 }, 1210 + { url = "https://files.pythonhosted.org/packages/bf/46/4419098ed6d801750f26567b478fc185c3432e11e2cad712bc6b4c2ab0d0/torch-2.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8245477871c3700d4370352ffec94b103cfcb737229445cf9946cddb7b2ca7cd", size = 80959460 }, 1211 + { url = "https://files.pythonhosted.org/packages/fd/66/54a56a4a6ceaffb567231994a9745821d3af922a854ed33b0b3a278e0a99/torch-2.11.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:ab9a8482f475f9ba20e12db84b0e55e2f58784bdca43a854a6ccd3fd4b9f75e6", size = 419735835 }, 1212 + { url = "https://files.pythonhosted.org/packages/b1/e7/0b6665f533aa9e337662dc190425abc0af1fe3234088f4454c52393ded61/torch-2.11.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:563ed3d25542d7e7bbc5b235ccfacfeb97fb470c7fee257eae599adb8005c8a2", size = 530613405 }, 1213 + { url = "https://files.pythonhosted.org/packages/cf/bf/c8d12a2c86dbfd7f40fb2f56fbf5a505ccf2d9ce131eb559dfc7c51e1a04/torch-2.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b2a43985ff5ef6ddd923bbcf99943e5f58059805787c5c9a2622bf05ca2965b0", size = 114792991 }, 1214 + ] 1215 + 1216 + [[package]] 1217 + name = "tqdm" 1218 + version = "4.67.3" 1219 + source = { registry = "https://pypi.org/simple" } 1220 + dependencies = [ 1221 + { name = "colorama", marker = "sys_platform == 'win32'" }, 1222 + ] 1223 + sdist = { url = "https://files.pythonhosted.org/packages/09/a9/6ba95a270c6f1fbcd8dac228323f2777d886cb206987444e4bce66338dd4/tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb", size = 169598 } 1224 + wheels = [ 1225 + { url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374 }, 1226 + ] 1227 + 1228 + [[package]] 1229 + name = "transformers" 1230 + version = "5.7.0" 1231 + source = { registry = "https://pypi.org/simple" } 1232 + dependencies = [ 1233 + { name = "huggingface-hub" }, 1234 + { name = "numpy" }, 1235 + { name = "packaging" }, 1236 + { name = "pyyaml" }, 1237 + { name = "regex" }, 1238 + { name = "safetensors" }, 1239 + { name = "tokenizers" }, 1240 + { name = "tqdm" }, 1241 + { name = "typer" }, 1242 + ] 1243 + sdist = { url = "https://files.pythonhosted.org/packages/4d/fe/7e84d20ac7d4d5d14bac2eab5976088d86342959fc2c0da54b4c2fc99856/transformers-5.7.0.tar.gz", hash = "sha256:a9d35cf39804e3456c1f9bc1a79ad5ffa878640a61f51f66f71c97f4b4e2ce10", size = 8401287 } 1244 + wheels = [ 1245 + { url = "https://files.pythonhosted.org/packages/60/60/86a9fe3037bec221094e2acb680219ad88b77006edba42fc0407a577ca93/transformers-5.7.0-py3-none-any.whl", hash = "sha256:869660cd8fc92badc041f5551bf755a42f4b9558c93341bf3fa3eeed7065079c", size = 10474236 }, 1246 + ] 1247 + 1248 + [[package]] 1249 + name = "triton" 1250 + version = "3.6.0" 1251 + source = { registry = "https://pypi.org/simple" } 1252 + wheels = [ 1253 + { url = "https://files.pythonhosted.org/packages/17/5d/08201db32823bdf77a0e2b9039540080b2e5c23a20706ddba942924ebcd6/triton-3.6.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:374f52c11a711fd062b4bfbb201fd9ac0a5febd28a96fb41b4a0f51dde3157f4", size = 176128243 }, 1254 + { url = "https://files.pythonhosted.org/packages/ab/a8/cdf8b3e4c98132f965f88c2313a4b493266832ad47fb52f23d14d4f86bb5/triton-3.6.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74caf5e34b66d9f3a429af689c1c7128daba1d8208df60e81106b115c00d6fca", size = 188266850 }, 1255 + { url = "https://files.pythonhosted.org/packages/3c/12/34d71b350e89a204c2c7777a9bba0dcf2f19a5bfdd70b57c4dbc5ffd7154/triton-3.6.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:448e02fe6dc898e9e5aa89cf0ee5c371e99df5aa5e8ad976a80b93334f3494fd", size = 176133521 }, 1256 + { url = "https://files.pythonhosted.org/packages/f9/0b/37d991d8c130ce81a8728ae3c25b6e60935838e9be1b58791f5997b24a54/triton-3.6.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10c7f76c6e72d2ef08df639e3d0d30729112f47a56b0c81672edc05ee5116ac9", size = 188289450 }, 1257 + { url = "https://files.pythonhosted.org/packages/ce/4e/41b0c8033b503fd3cfcd12392cdd256945026a91ff02452bef40ec34bee7/triton-3.6.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1722e172d34e32abc3eb7711d0025bb69d7959ebea84e3b7f7a341cd7ed694d6", size = 176276087 }, 1258 + { url = "https://files.pythonhosted.org/packages/35/f8/9c66bfc55361ec6d0e4040a0337fb5924ceb23de4648b8a81ae9d33b2b38/triton-3.6.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d002e07d7180fd65e622134fbd980c9a3d4211fb85224b56a0a0efbd422ab72f", size = 188400296 }, 1259 + { url = "https://files.pythonhosted.org/packages/49/55/5ecf0dcaa0f2fbbd4420f7ef227ee3cb172e91e5fede9d0ecaddc43363b4/triton-3.6.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef5523241e7d1abca00f1d240949eebdd7c673b005edbbce0aca95b8191f1d43", size = 176138577 }, 1260 + { url = "https://files.pythonhosted.org/packages/df/3d/9e7eee57b37c80cec63322c0231bb6da3cfe535a91d7a4d64896fcb89357/triton-3.6.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a17a5d5985f0ac494ed8a8e54568f092f7057ef60e1b0fa09d3fd1512064e803", size = 188273063 }, 1261 + { url = "https://files.pythonhosted.org/packages/48/db/56ee649cab5eaff4757541325aca81f52d02d4a7cd3506776cad2451e060/triton-3.6.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b3a97e8ed304dfa9bd23bb41ca04cdf6b2e617d5e782a8653d616037a5d537d", size = 176274804 }, 1262 + { url = "https://files.pythonhosted.org/packages/f6/56/6113c23ff46c00aae423333eb58b3e60bdfe9179d542781955a5e1514cb3/triton-3.6.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46bd1c1af4b6704e554cad2eeb3b0a6513a980d470ccfa63189737340c7746a7", size = 188397994 }, 1263 + ] 1264 + 1265 + [[package]] 1266 + name = "typer" 1267 + version = "0.25.0" 1268 + source = { registry = "https://pypi.org/simple" } 1269 + dependencies = [ 1270 + { name = "annotated-doc" }, 1271 + { name = "click" }, 1272 + { name = "rich" }, 1273 + { name = "shellingham" }, 1274 + ] 1275 + sdist = { url = "https://files.pythonhosted.org/packages/7b/27/ede8cec7596e0041ba7e7b80b47d132562f56ff454313a16f6084e555c9f/typer-0.25.0.tar.gz", hash = "sha256:123eaf9f19bb40fd268310e12a542c0c6b4fab9c98d9d23342a01ff95e3ce930", size = 120150 } 1276 + wheels = [ 1277 + { url = "https://files.pythonhosted.org/packages/9a/72/193d4e586ec5a4db834a36bbeb47641a62f951f114ffd0fe5b1b46e8d56f/typer-0.25.0-py3-none-any.whl", hash = "sha256:ac01b48823d3db9a83c9e164338057eadbb1c9957a2a6b4eeb486669c560b5dc", size = 55993 }, 1278 + ] 1279 + 1280 + [[package]] 1281 + name = "typing-extensions" 1282 + version = "4.15.0" 1283 + source = { registry = "https://pypi.org/simple" } 1284 + sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391 } 1285 + wheels = [ 1286 + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614 }, 1287 + ] 1288 + 1289 + [[package]] 1290 + name = "typing-inspection" 1291 + version = "0.4.2" 1292 + source = { registry = "https://pypi.org/simple" } 1293 + dependencies = [ 1294 + { name = "typing-extensions" }, 1295 + ] 1296 + sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949 } 1297 + wheels = [ 1298 + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611 }, 1299 + ] 1300 + 1301 + [[package]] 1302 + name = "uvicorn" 1303 + version = "0.46.0" 1304 + source = { registry = "https://pypi.org/simple" } 1305 + dependencies = [ 1306 + { name = "click" }, 1307 + { name = "h11" }, 1308 + ] 1309 + sdist = { url = "https://files.pythonhosted.org/packages/1f/93/041fca8274050e40e6791f267d82e0e2e27dd165627bd640d3e0e378d877/uvicorn-0.46.0.tar.gz", hash = "sha256:fb9da0926999cc6cb22dc7cd71a94a632f078e6ae47ff683c5c420750fb7413d", size = 88758 } 1310 + wheels = [ 1311 + { url = "https://files.pythonhosted.org/packages/31/a3/5b1562db76a5a488274b2332a97199b32d0442aca0ed193697fd47786316/uvicorn-0.46.0-py3-none-any.whl", hash = "sha256:bbebbcbed972d162afca128605223022bedd345b7bc7855ce66deb31487a9048", size = 70926 }, 1312 + ] 1313 + 1314 + [package.optional-dependencies] 1315 + standard = [ 1316 + { name = "colorama", marker = "sys_platform == 'win32'" }, 1317 + { name = "httptools" }, 1318 + { name = "python-dotenv" }, 1319 + { name = "pyyaml" }, 1320 + { name = "uvloop", marker = "platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32'" }, 1321 + { name = "watchfiles" }, 1322 + { name = "websockets" }, 1323 + ] 1324 + 1325 + [[package]] 1326 + name = "uvloop" 1327 + version = "0.22.1" 1328 + source = { registry = "https://pypi.org/simple" } 1329 + sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250 } 1330 + wheels = [ 1331 + { url = "https://files.pythonhosted.org/packages/3d/ff/7f72e8170be527b4977b033239a83a68d5c881cc4775fca255c677f7ac5d/uvloop-0.22.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fe94b4564e865d968414598eea1a6de60adba0c040ba4ed05ac1300de402cd42", size = 1359936 }, 1332 + { url = "https://files.pythonhosted.org/packages/c3/c6/e5d433f88fd54d81ef4be58b2b7b0cea13c442454a1db703a1eea0db1a59/uvloop-0.22.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:51eb9bd88391483410daad430813d982010f9c9c89512321f5b60e2cddbdddd6", size = 752769 }, 1333 + { url = "https://files.pythonhosted.org/packages/24/68/a6ac446820273e71aa762fa21cdcc09861edd3536ff47c5cd3b7afb10eeb/uvloop-0.22.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:700e674a166ca5778255e0e1dc4e9d79ab2acc57b9171b79e65feba7184b3370", size = 4317413 }, 1334 + { url = "https://files.pythonhosted.org/packages/5f/6f/e62b4dfc7ad6518e7eff2516f680d02a0f6eb62c0c212e152ca708a0085e/uvloop-0.22.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b5b1ac819a3f946d3b2ee07f09149578ae76066d70b44df3fa990add49a82e4", size = 4426307 }, 1335 + { url = "https://files.pythonhosted.org/packages/90/60/97362554ac21e20e81bcef1150cb2a7e4ffdaf8ea1e5b2e8bf7a053caa18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e047cc068570bac9866237739607d1313b9253c3051ad84738cbb095be0537b2", size = 4131970 }, 1336 + { url = "https://files.pythonhosted.org/packages/99/39/6b3f7d234ba3964c428a6e40006340f53ba37993f46ed6e111c6e9141d18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:512fec6815e2dd45161054592441ef76c830eddaad55c8aa30952e6fe1ed07c0", size = 4296343 }, 1337 + { url = "https://files.pythonhosted.org/packages/89/8c/182a2a593195bfd39842ea68ebc084e20c850806117213f5a299dfc513d9/uvloop-0.22.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:561577354eb94200d75aca23fbde86ee11be36b00e52a4eaf8f50fb0c86b7705", size = 1358611 }, 1338 + { url = "https://files.pythonhosted.org/packages/d2/14/e301ee96a6dc95224b6f1162cd3312f6d1217be3907b79173b06785f2fe7/uvloop-0.22.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cdf5192ab3e674ca26da2eada35b288d2fa49fdd0f357a19f0e7c4e7d5077c8", size = 751811 }, 1339 + { url = "https://files.pythonhosted.org/packages/b7/02/654426ce265ac19e2980bfd9ea6590ca96a56f10c76e63801a2df01c0486/uvloop-0.22.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e2ea3d6190a2968f4a14a23019d3b16870dd2190cd69c8180f7c632d21de68d", size = 4288562 }, 1340 + { url = "https://files.pythonhosted.org/packages/15/c0/0be24758891ef825f2065cd5db8741aaddabe3e248ee6acc5e8a80f04005/uvloop-0.22.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0530a5fbad9c9e4ee3f2b33b148c6a64d47bbad8000ea63704fa8260f4cf728e", size = 4366890 }, 1341 + { url = "https://files.pythonhosted.org/packages/d2/53/8369e5219a5855869bcee5f4d317f6da0e2c669aecf0ef7d371e3d084449/uvloop-0.22.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bc5ef13bbc10b5335792360623cc378d52d7e62c2de64660616478c32cd0598e", size = 4119472 }, 1342 + { url = "https://files.pythonhosted.org/packages/f8/ba/d69adbe699b768f6b29a5eec7b47dd610bd17a69de51b251126a801369ea/uvloop-0.22.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1f38ec5e3f18c8a10ded09742f7fb8de0108796eb673f30ce7762ce1b8550cad", size = 4239051 }, 1343 + { url = "https://files.pythonhosted.org/packages/90/cd/b62bdeaa429758aee8de8b00ac0dd26593a9de93d302bff3d21439e9791d/uvloop-0.22.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3879b88423ec7e97cd4eba2a443aa26ed4e59b45e6b76aabf13fe2f27023a142", size = 1362067 }, 1344 + { url = "https://files.pythonhosted.org/packages/0d/f8/a132124dfda0777e489ca86732e85e69afcd1ff7686647000050ba670689/uvloop-0.22.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4baa86acedf1d62115c1dc6ad1e17134476688f08c6efd8a2ab076e815665c74", size = 752423 }, 1345 + { url = "https://files.pythonhosted.org/packages/a3/94/94af78c156f88da4b3a733773ad5ba0b164393e357cc4bd0ab2e2677a7d6/uvloop-0.22.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:297c27d8003520596236bdb2335e6b3f649480bd09e00d1e3a99144b691d2a35", size = 4272437 }, 1346 + { url = "https://files.pythonhosted.org/packages/b5/35/60249e9fd07b32c665192cec7af29e06c7cd96fa1d08b84f012a56a0b38e/uvloop-0.22.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1955d5a1dd43198244d47664a5858082a3239766a839b2102a269aaff7a4e25", size = 4292101 }, 1347 + { url = "https://files.pythonhosted.org/packages/02/62/67d382dfcb25d0a98ce73c11ed1a6fba5037a1a1d533dcbb7cab033a2636/uvloop-0.22.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b31dc2fccbd42adc73bc4e7cdbae4fc5086cf378979e53ca5d0301838c5682c6", size = 4114158 }, 1348 + { url = "https://files.pythonhosted.org/packages/f0/7a/f1171b4a882a5d13c8b7576f348acfe6074d72eaf52cccef752f748d4a9f/uvloop-0.22.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:93f617675b2d03af4e72a5333ef89450dfaa5321303ede6e67ba9c9d26878079", size = 4177360 }, 1349 + { url = "https://files.pythonhosted.org/packages/79/7b/b01414f31546caf0919da80ad57cbfe24c56b151d12af68cee1b04922ca8/uvloop-0.22.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:37554f70528f60cad66945b885eb01f1bb514f132d92b6eeed1c90fd54ed6289", size = 1454790 }, 1350 + { url = "https://files.pythonhosted.org/packages/d4/31/0bb232318dd838cad3fa8fb0c68c8b40e1145b32025581975e18b11fab40/uvloop-0.22.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b76324e2dc033a0b2f435f33eb88ff9913c156ef78e153fb210e03c13da746b3", size = 796783 }, 1351 + { url = "https://files.pythonhosted.org/packages/42/38/c9b09f3271a7a723a5de69f8e237ab8e7803183131bc57c890db0b6bb872/uvloop-0.22.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:badb4d8e58ee08dad957002027830d5c3b06aea446a6a3744483c2b3b745345c", size = 4647548 }, 1352 + { url = "https://files.pythonhosted.org/packages/c1/37/945b4ca0ac27e3dc4952642d4c900edd030b3da6c9634875af6e13ae80e5/uvloop-0.22.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b91328c72635f6f9e0282e4a57da7470c7350ab1c9f48546c0f2866205349d21", size = 4467065 }, 1353 + { url = "https://files.pythonhosted.org/packages/97/cc/48d232f33d60e2e2e0b42f4e73455b146b76ebe216487e862700457fbf3c/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:daf620c2995d193449393d6c62131b3fbd40a63bf7b307a1527856ace637fe88", size = 4328384 }, 1354 + { url = "https://files.pythonhosted.org/packages/e4/16/c1fd27e9549f3c4baf1dc9c20c456cd2f822dbf8de9f463824b0c0357e06/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6cde23eeda1a25c75b2e07d39970f3374105d5eafbaab2a4482be82f272d5a5e", size = 4296730 }, 1355 + ] 1356 + 1357 + [[package]] 1358 + name = "watchfiles" 1359 + version = "1.1.1" 1360 + source = { registry = "https://pypi.org/simple" } 1361 + dependencies = [ 1362 + { name = "anyio" }, 1363 + ] 1364 + sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440 } 1365 + wheels = [ 1366 + { url = "https://files.pythonhosted.org/packages/74/d5/f039e7e3c639d9b1d09b07ea412a6806d38123f0508e5f9b48a87b0a76cc/watchfiles-1.1.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:8c89f9f2f740a6b7dcc753140dd5e1ab9215966f7a3530d0c0705c83b401bd7d", size = 404745 }, 1367 + { url = "https://files.pythonhosted.org/packages/a5/96/a881a13aa1349827490dab2d363c8039527060cfcc2c92cc6d13d1b1049e/watchfiles-1.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bd404be08018c37350f0d6e34676bd1e2889990117a2b90070b3007f172d0610", size = 391769 }, 1368 + { url = "https://files.pythonhosted.org/packages/4b/5b/d3b460364aeb8da471c1989238ea0e56bec24b6042a68046adf3d9ddb01c/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8526e8f916bb5b9a0a777c8317c23ce65de259422bba5b31325a6fa6029d33af", size = 449374 }, 1369 + { url = "https://files.pythonhosted.org/packages/b9/44/5769cb62d4ed055cb17417c0a109a92f007114a4e07f30812a73a4efdb11/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2edc3553362b1c38d9f06242416a5d8e9fe235c204a4072e988ce2e5bb1f69f6", size = 459485 }, 1370 + { url = "https://files.pythonhosted.org/packages/19/0c/286b6301ded2eccd4ffd0041a1b726afda999926cf720aab63adb68a1e36/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30f7da3fb3f2844259cba4720c3fc7138eb0f7b659c38f3bfa65084c7fc7abce", size = 488813 }, 1371 + { url = "https://files.pythonhosted.org/packages/c7/2b/8530ed41112dd4a22f4dcfdb5ccf6a1baad1ff6eed8dc5a5f09e7e8c41c7/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8979280bdafff686ba5e4d8f97840f929a87ed9cdf133cbbd42f7766774d2aa", size = 594816 }, 1372 + { url = "https://files.pythonhosted.org/packages/ce/d2/f5f9fb49489f184f18470d4f99f4e862a4b3e9ac2865688eb2099e3d837a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dcc5c24523771db3a294c77d94771abcfcb82a0e0ee8efd910c37c59ec1b31bb", size = 475186 }, 1373 + { url = "https://files.pythonhosted.org/packages/cf/68/5707da262a119fb06fbe214d82dd1fe4a6f4af32d2d14de368d0349eb52a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db5d7ae38ff20153d542460752ff397fcf5c96090c1230803713cf3147a6803", size = 456812 }, 1374 + { url = "https://files.pythonhosted.org/packages/66/ab/3cbb8756323e8f9b6f9acb9ef4ec26d42b2109bce830cc1f3468df20511d/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:28475ddbde92df1874b6c5c8aaeb24ad5be47a11f87cde5a28ef3835932e3e94", size = 630196 }, 1375 + { url = "https://files.pythonhosted.org/packages/78/46/7152ec29b8335f80167928944a94955015a345440f524d2dfe63fc2f437b/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:36193ed342f5b9842edd3532729a2ad55c4160ffcfa3700e0d54be496b70dd43", size = 622657 }, 1376 + { url = "https://files.pythonhosted.org/packages/0a/bf/95895e78dd75efe9a7f31733607f384b42eb5feb54bd2eb6ed57cc2e94f4/watchfiles-1.1.1-cp312-cp312-win32.whl", hash = "sha256:859e43a1951717cc8de7f4c77674a6d389b106361585951d9e69572823f311d9", size = 272042 }, 1377 + { url = "https://files.pythonhosted.org/packages/87/0a/90eb755f568de2688cb220171c4191df932232c20946966c27a59c400850/watchfiles-1.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:91d4c9a823a8c987cce8fa2690923b069966dabb196dd8d137ea2cede885fde9", size = 288410 }, 1378 + { url = "https://files.pythonhosted.org/packages/36/76/f322701530586922fbd6723c4f91ace21364924822a8772c549483abed13/watchfiles-1.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:a625815d4a2bdca61953dbba5a39d60164451ef34c88d751f6c368c3ea73d404", size = 278209 }, 1379 + { url = "https://files.pythonhosted.org/packages/bb/f4/f750b29225fe77139f7ae5de89d4949f5a99f934c65a1f1c0b248f26f747/watchfiles-1.1.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:130e4876309e8686a5e37dba7d5e9bc77e6ed908266996ca26572437a5271e18", size = 404321 }, 1380 + { url = "https://files.pythonhosted.org/packages/2b/f9/f07a295cde762644aa4c4bb0f88921d2d141af45e735b965fb2e87858328/watchfiles-1.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5f3bde70f157f84ece3765b42b4a52c6ac1a50334903c6eaf765362f6ccca88a", size = 391783 }, 1381 + { url = "https://files.pythonhosted.org/packages/bc/11/fc2502457e0bea39a5c958d86d2cb69e407a4d00b85735ca724bfa6e0d1a/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14e0b1fe858430fc0251737ef3824c54027bedb8c37c38114488b8e131cf8219", size = 449279 }, 1382 + { url = "https://files.pythonhosted.org/packages/e3/1f/d66bc15ea0b728df3ed96a539c777acfcad0eb78555ad9efcaa1274688f0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f27db948078f3823a6bb3b465180db8ebecf26dd5dae6f6180bd87383b6b4428", size = 459405 }, 1383 + { url = "https://files.pythonhosted.org/packages/be/90/9f4a65c0aec3ccf032703e6db02d89a157462fbb2cf20dd415128251cac0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:059098c3a429f62fc98e8ec62b982230ef2c8df68c79e826e37b895bc359a9c0", size = 488976 }, 1384 + { url = "https://files.pythonhosted.org/packages/37/57/ee347af605d867f712be7029bb94c8c071732a4b44792e3176fa3c612d39/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfb5862016acc9b869bb57284e6cb35fdf8e22fe59f7548858e2f971d045f150", size = 595506 }, 1385 + { url = "https://files.pythonhosted.org/packages/a8/78/cc5ab0b86c122047f75e8fc471c67a04dee395daf847d3e59381996c8707/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:319b27255aacd9923b8a276bb14d21a5f7ff82564c744235fc5eae58d95422ae", size = 474936 }, 1386 + { url = "https://files.pythonhosted.org/packages/62/da/def65b170a3815af7bd40a3e7010bf6ab53089ef1b75d05dd5385b87cf08/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c755367e51db90e75b19454b680903631d41f9e3607fbd941d296a020c2d752d", size = 456147 }, 1387 + { url = "https://files.pythonhosted.org/packages/57/99/da6573ba71166e82d288d4df0839128004c67d2778d3b566c138695f5c0b/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c22c776292a23bfc7237a98f791b9ad3144b02116ff10d820829ce62dff46d0b", size = 630007 }, 1388 + { url = "https://files.pythonhosted.org/packages/a8/51/7439c4dd39511368849eb1e53279cd3454b4a4dbace80bab88feeb83c6b5/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3a476189be23c3686bc2f4321dd501cb329c0a0469e77b7b534ee10129ae6374", size = 622280 }, 1389 + { url = "https://files.pythonhosted.org/packages/95/9c/8ed97d4bba5db6fdcdb2b298d3898f2dd5c20f6b73aee04eabe56c59677e/watchfiles-1.1.1-cp313-cp313-win32.whl", hash = "sha256:bf0a91bfb5574a2f7fc223cf95eeea79abfefa404bf1ea5e339c0c1560ae99a0", size = 272056 }, 1390 + { url = "https://files.pythonhosted.org/packages/1f/f3/c14e28429f744a260d8ceae18bf58c1d5fa56b50d006a7a9f80e1882cb0d/watchfiles-1.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:52e06553899e11e8074503c8e716d574adeeb7e68913115c4b3653c53f9bae42", size = 288162 }, 1391 + { url = "https://files.pythonhosted.org/packages/dc/61/fe0e56c40d5cd29523e398d31153218718c5786b5e636d9ae8ae79453d27/watchfiles-1.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:ac3cc5759570cd02662b15fbcd9d917f7ecd47efe0d6b40474eafd246f91ea18", size = 277909 }, 1392 + { url = "https://files.pythonhosted.org/packages/79/42/e0a7d749626f1e28c7108a99fb9bf524b501bbbeb9b261ceecde644d5a07/watchfiles-1.1.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:563b116874a9a7ce6f96f87cd0b94f7faf92d08d0021e837796f0a14318ef8da", size = 403389 }, 1393 + { url = "https://files.pythonhosted.org/packages/15/49/08732f90ce0fbbc13913f9f215c689cfc9ced345fb1bcd8829a50007cc8d/watchfiles-1.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3ad9fe1dae4ab4212d8c91e80b832425e24f421703b5a42ef2e4a1e215aff051", size = 389964 }, 1394 + { url = "https://files.pythonhosted.org/packages/27/0d/7c315d4bd5f2538910491a0393c56bf70d333d51bc5b34bee8e68e8cea19/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce70f96a46b894b36eba678f153f052967a0d06d5b5a19b336ab0dbbd029f73e", size = 448114 }, 1395 + { url = "https://files.pythonhosted.org/packages/c3/24/9e096de47a4d11bc4df41e9d1e61776393eac4cb6eb11b3e23315b78b2cc/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cb467c999c2eff23a6417e58d75e5828716f42ed8289fe6b77a7e5a91036ca70", size = 460264 }, 1396 + { url = "https://files.pythonhosted.org/packages/cc/0f/e8dea6375f1d3ba5fcb0b3583e2b493e77379834c74fd5a22d66d85d6540/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:836398932192dae4146c8f6f737d74baeac8b70ce14831a239bdb1ca882fc261", size = 487877 }, 1397 + { url = "https://files.pythonhosted.org/packages/ac/5b/df24cfc6424a12deb41503b64d42fbea6b8cb357ec62ca84a5a3476f654a/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:743185e7372b7bc7c389e1badcc606931a827112fbbd37f14c537320fca08620", size = 595176 }, 1398 + { url = "https://files.pythonhosted.org/packages/8f/b5/853b6757f7347de4e9b37e8cc3289283fb983cba1ab4d2d7144694871d9c/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afaeff7696e0ad9f02cbb8f56365ff4686ab205fcf9c4c5b6fdfaaa16549dd04", size = 473577 }, 1399 + { url = "https://files.pythonhosted.org/packages/e1/f7/0a4467be0a56e80447c8529c9fce5b38eab4f513cb3d9bf82e7392a5696b/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7eb7da0eb23aa2ba036d4f616d46906013a68caf61b7fdbe42fc8b25132e77", size = 455425 }, 1400 + { url = "https://files.pythonhosted.org/packages/8e/e0/82583485ea00137ddf69bc84a2db88bd92ab4a6e3c405e5fb878ead8d0e7/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:831a62658609f0e5c64178211c942ace999517f5770fe9436be4c2faeba0c0ef", size = 628826 }, 1401 + { url = "https://files.pythonhosted.org/packages/28/9a/a785356fccf9fae84c0cc90570f11702ae9571036fb25932f1242c82191c/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:f9a2ae5c91cecc9edd47e041a930490c31c3afb1f5e6d71de3dc671bfaca02bf", size = 622208 }, 1402 + { url = "https://files.pythonhosted.org/packages/c3/f4/0872229324ef69b2c3edec35e84bd57a1289e7d3fe74588048ed8947a323/watchfiles-1.1.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:d1715143123baeeaeadec0528bb7441103979a1d5f6fd0e1f915383fea7ea6d5", size = 404315 }, 1403 + { url = "https://files.pythonhosted.org/packages/7b/22/16d5331eaed1cb107b873f6ae1b69e9ced582fcf0c59a50cd84f403b1c32/watchfiles-1.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:39574d6370c4579d7f5d0ad940ce5b20db0e4117444e39b6d8f99db5676c52fd", size = 390869 }, 1404 + { url = "https://files.pythonhosted.org/packages/b2/7e/5643bfff5acb6539b18483128fdc0ef2cccc94a5b8fbda130c823e8ed636/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7365b92c2e69ee952902e8f70f3ba6360d0d596d9299d55d7d386df84b6941fb", size = 449919 }, 1405 + { url = "https://files.pythonhosted.org/packages/51/2e/c410993ba5025a9f9357c376f48976ef0e1b1aefb73b97a5ae01a5972755/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bfff9740c69c0e4ed32416f013f3c45e2ae42ccedd1167ef2d805c000b6c71a5", size = 460845 }, 1406 + { url = "https://files.pythonhosted.org/packages/8e/a4/2df3b404469122e8680f0fcd06079317e48db58a2da2950fb45020947734/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b27cf2eb1dda37b2089e3907d8ea92922b673c0c427886d4edc6b94d8dfe5db3", size = 489027 }, 1407 + { url = "https://files.pythonhosted.org/packages/ea/84/4587ba5b1f267167ee715b7f66e6382cca6938e0a4b870adad93e44747e6/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:526e86aced14a65a5b0ec50827c745597c782ff46b571dbfe46192ab9e0b3c33", size = 595615 }, 1408 + { url = "https://files.pythonhosted.org/packages/6a/0f/c6988c91d06e93cd0bb3d4a808bcf32375ca1904609835c3031799e3ecae/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04e78dd0b6352db95507fd8cb46f39d185cf8c74e4cf1e4fbad1d3df96faf510", size = 474836 }, 1409 + { url = "https://files.pythonhosted.org/packages/b4/36/ded8aebea91919485b7bbabbd14f5f359326cb5ec218cd67074d1e426d74/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c85794a4cfa094714fb9c08d4a218375b2b95b8ed1666e8677c349906246c05", size = 455099 }, 1410 + { url = "https://files.pythonhosted.org/packages/98/e0/8c9bdba88af756a2fce230dd365fab2baf927ba42cd47521ee7498fd5211/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:74d5012b7630714b66be7b7b7a78855ef7ad58e8650c73afc4c076a1f480a8d6", size = 630626 }, 1411 + { url = "https://files.pythonhosted.org/packages/2a/84/a95db05354bf2d19e438520d92a8ca475e578c647f78f53197f5a2f17aaf/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:8fbe85cb3201c7d380d3d0b90e63d520f15d6afe217165d7f98c9c649654db81", size = 622519 }, 1412 + { url = "https://files.pythonhosted.org/packages/1d/ce/d8acdc8de545de995c339be67711e474c77d643555a9bb74a9334252bd55/watchfiles-1.1.1-cp314-cp314-win32.whl", hash = "sha256:3fa0b59c92278b5a7800d3ee7733da9d096d4aabcfabb9a928918bd276ef9b9b", size = 272078 }, 1413 + { url = "https://files.pythonhosted.org/packages/c4/c9/a74487f72d0451524be827e8edec251da0cc1fcf111646a511ae752e1a3d/watchfiles-1.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:c2047d0b6cea13b3316bdbafbfa0c4228ae593d995030fda39089d36e64fc03a", size = 287664 }, 1414 + { url = "https://files.pythonhosted.org/packages/df/b8/8ac000702cdd496cdce998c6f4ee0ca1f15977bba51bdf07d872ebdfc34c/watchfiles-1.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:842178b126593addc05acf6fce960d28bc5fae7afbaa2c6c1b3a7b9460e5be02", size = 277154 }, 1415 + { url = "https://files.pythonhosted.org/packages/47/a8/e3af2184707c29f0f14b1963c0aace6529f9d1b8582d5b99f31bbf42f59e/watchfiles-1.1.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:88863fbbc1a7312972f1c511f202eb30866370ebb8493aef2812b9ff28156a21", size = 403820 }, 1416 + { url = "https://files.pythonhosted.org/packages/c0/ec/e47e307c2f4bd75f9f9e8afbe3876679b18e1bcec449beca132a1c5ffb2d/watchfiles-1.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:55c7475190662e202c08c6c0f4d9e345a29367438cf8e8037f3155e10a88d5a5", size = 390510 }, 1417 + { url = "https://files.pythonhosted.org/packages/d5/a0/ad235642118090f66e7b2f18fd5c42082418404a79205cdfca50b6309c13/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f53fa183d53a1d7a8852277c92b967ae99c2d4dcee2bfacff8868e6e30b15f7", size = 448408 }, 1418 + { url = "https://files.pythonhosted.org/packages/df/85/97fa10fd5ff3332ae17e7e40e20784e419e28521549780869f1413742e9d/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6aae418a8b323732fa89721d86f39ec8f092fc2af67f4217a2b07fd3e93c6101", size = 458968 }, 1419 + { url = "https://files.pythonhosted.org/packages/47/c2/9059c2e8966ea5ce678166617a7f75ecba6164375f3b288e50a40dc6d489/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f096076119da54a6080e8920cbdaac3dbee667eb91dcc5e5b78840b87415bd44", size = 488096 }, 1420 + { url = "https://files.pythonhosted.org/packages/94/44/d90a9ec8ac309bc26db808a13e7bfc0e4e78b6fc051078a554e132e80160/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00485f441d183717038ed2e887a7c868154f216877653121068107b227a2f64c", size = 596040 }, 1421 + { url = "https://files.pythonhosted.org/packages/95/68/4e3479b20ca305cfc561db3ed207a8a1c745ee32bf24f2026a129d0ddb6e/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a55f3e9e493158d7bfdb60a1165035f1cf7d320914e7b7ea83fe22c6023b58fc", size = 473847 }, 1422 + { url = "https://files.pythonhosted.org/packages/4f/55/2af26693fd15165c4ff7857e38330e1b61ab8c37d15dc79118cdba115b7a/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c91ed27800188c2ae96d16e3149f199d62f86c7af5f5f4d2c61a3ed8cd3666c", size = 455072 }, 1423 + { url = "https://files.pythonhosted.org/packages/66/1d/d0d200b10c9311ec25d2273f8aad8c3ef7cc7ea11808022501811208a750/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:311ff15a0bae3714ffb603e6ba6dbfba4065ab60865d15a6ec544133bdb21099", size = 629104 }, 1424 + { url = "https://files.pythonhosted.org/packages/e3/bd/fa9bb053192491b3867ba07d2343d9f2252e00811567d30ae8d0f78136fe/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:a916a2932da8f8ab582f242c065f5c81bed3462849ca79ee357dd9551b0e9b01", size = 622112 }, 1425 + ] 1426 + 1427 + [[package]] 1428 + name = "websockets" 1429 + version = "16.0" 1430 + source = { registry = "https://pypi.org/simple" } 1431 + sdist = { url = "https://files.pythonhosted.org/packages/04/24/4b2031d72e840ce4c1ccb255f693b15c334757fc50023e4db9537080b8c4/websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", size = 179346 } 1432 + wheels = [ 1433 + { url = "https://files.pythonhosted.org/packages/84/7b/bac442e6b96c9d25092695578dda82403c77936104b5682307bd4deb1ad4/websockets-16.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:71c989cbf3254fbd5e84d3bff31e4da39c43f884e64f2551d14bb3c186230f00", size = 177365 }, 1434 + { url = "https://files.pythonhosted.org/packages/b0/fe/136ccece61bd690d9c1f715baaeefd953bb2360134de73519d5df19d29ca/websockets-16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8b6e209ffee39ff1b6d0fa7bfef6de950c60dfb91b8fcead17da4ee539121a79", size = 175038 }, 1435 + { url = "https://files.pythonhosted.org/packages/40/1e/9771421ac2286eaab95b8575b0cb701ae3663abf8b5e1f64f1fd90d0a673/websockets-16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:86890e837d61574c92a97496d590968b23c2ef0aeb8a9bc9421d174cd378ae39", size = 175328 }, 1436 + { url = "https://files.pythonhosted.org/packages/18/29/71729b4671f21e1eaa5d6573031ab810ad2936c8175f03f97f3ff164c802/websockets-16.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9b5aca38b67492ef518a8ab76851862488a478602229112c4b0d58d63a7a4d5c", size = 184915 }, 1437 + { url = "https://files.pythonhosted.org/packages/97/bb/21c36b7dbbafc85d2d480cd65df02a1dc93bf76d97147605a8e27ff9409d/websockets-16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e0334872c0a37b606418ac52f6ab9cfd17317ac26365f7f65e203e2d0d0d359f", size = 186152 }, 1438 + { url = "https://files.pythonhosted.org/packages/4a/34/9bf8df0c0cf88fa7bfe36678dc7b02970c9a7d5e065a3099292db87b1be2/websockets-16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a0b31e0b424cc6b5a04b8838bbaec1688834b2383256688cf47eb97412531da1", size = 185583 }, 1439 + { url = "https://files.pythonhosted.org/packages/47/88/4dd516068e1a3d6ab3c7c183288404cd424a9a02d585efbac226cb61ff2d/websockets-16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:485c49116d0af10ac698623c513c1cc01c9446c058a4e61e3bf6c19dff7335a2", size = 184880 }, 1440 + { url = "https://files.pythonhosted.org/packages/91/d6/7d4553ad4bf1c0421e1ebd4b18de5d9098383b5caa1d937b63df8d04b565/websockets-16.0-cp312-cp312-win32.whl", hash = "sha256:eaded469f5e5b7294e2bdca0ab06becb6756ea86894a47806456089298813c89", size = 178261 }, 1441 + { url = "https://files.pythonhosted.org/packages/c3/f0/f3a17365441ed1c27f850a80b2bc680a0fa9505d733fe152fdf5e98c1c0b/websockets-16.0-cp312-cp312-win_amd64.whl", hash = "sha256:5569417dc80977fc8c2d43a86f78e0a5a22fee17565d78621b6bb264a115d4ea", size = 178693 }, 1442 + { url = "https://files.pythonhosted.org/packages/cc/9c/baa8456050d1c1b08dd0ec7346026668cbc6f145ab4e314d707bb845bf0d/websockets-16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:878b336ac47938b474c8f982ac2f7266a540adc3fa4ad74ae96fea9823a02cc9", size = 177364 }, 1443 + { url = "https://files.pythonhosted.org/packages/7e/0c/8811fc53e9bcff68fe7de2bcbe75116a8d959ac699a3200f4847a8925210/websockets-16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52a0fec0e6c8d9a784c2c78276a48a2bdf099e4ccc2a4cad53b27718dbfd0230", size = 175039 }, 1444 + { url = "https://files.pythonhosted.org/packages/aa/82/39a5f910cb99ec0b59e482971238c845af9220d3ab9fa76dd9162cda9d62/websockets-16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e6578ed5b6981005df1860a56e3617f14a6c307e6a71b4fff8c48fdc50f3ed2c", size = 175323 }, 1445 + { url = "https://files.pythonhosted.org/packages/bd/28/0a25ee5342eb5d5f297d992a77e56892ecb65e7854c7898fb7d35e9b33bd/websockets-16.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95724e638f0f9c350bb1c2b0a7ad0e83d9cc0c9259f3ea94e40d7b02a2179ae5", size = 184975 }, 1446 + { url = "https://files.pythonhosted.org/packages/f9/66/27ea52741752f5107c2e41fda05e8395a682a1e11c4e592a809a90c6a506/websockets-16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0204dc62a89dc9d50d682412c10b3542d748260d743500a85c13cd1ee4bde82", size = 186203 }, 1447 + { url = "https://files.pythonhosted.org/packages/37/e5/8e32857371406a757816a2b471939d51c463509be73fa538216ea52b792a/websockets-16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:52ac480f44d32970d66763115edea932f1c5b1312de36df06d6b219f6741eed8", size = 185653 }, 1448 + { url = "https://files.pythonhosted.org/packages/9b/67/f926bac29882894669368dc73f4da900fcdf47955d0a0185d60103df5737/websockets-16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6e5a82b677f8f6f59e8dfc34ec06ca6b5b48bc4fcda346acd093694cc2c24d8f", size = 184920 }, 1449 + { url = "https://files.pythonhosted.org/packages/3c/a1/3d6ccdcd125b0a42a311bcd15a7f705d688f73b2a22d8cf1c0875d35d34a/websockets-16.0-cp313-cp313-win32.whl", hash = "sha256:abf050a199613f64c886ea10f38b47770a65154dc37181bfaff70c160f45315a", size = 178255 }, 1450 + { url = "https://files.pythonhosted.org/packages/6b/ae/90366304d7c2ce80f9b826096a9e9048b4bb760e44d3b873bb272cba696b/websockets-16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3425ac5cf448801335d6fdc7ae1eb22072055417a96cc6b31b3861f455fbc156", size = 178689 }, 1451 + { url = "https://files.pythonhosted.org/packages/f3/1d/e88022630271f5bd349ed82417136281931e558d628dd52c4d8621b4a0b2/websockets-16.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8cc451a50f2aee53042ac52d2d053d08bf89bcb31ae799cb4487587661c038a0", size = 177406 }, 1452 + { url = "https://files.pythonhosted.org/packages/f2/78/e63be1bf0724eeb4616efb1ae1c9044f7c3953b7957799abb5915bffd38e/websockets-16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:daa3b6ff70a9241cf6c7fc9e949d41232d9d7d26fd3522b1ad2b4d62487e9904", size = 175085 }, 1453 + { url = "https://files.pythonhosted.org/packages/bb/f4/d3c9220d818ee955ae390cf319a7c7a467beceb24f05ee7aaaa2414345ba/websockets-16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fd3cb4adb94a2a6e2b7c0d8d05cb94e6f1c81a0cf9dc2694fb65c7e8d94c42e4", size = 175328 }, 1454 + { url = "https://files.pythonhosted.org/packages/63/bc/d3e208028de777087e6fb2b122051a6ff7bbcca0d6df9d9c2bf1dd869ae9/websockets-16.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:781caf5e8eee67f663126490c2f96f40906594cb86b408a703630f95550a8c3e", size = 185044 }, 1455 + { url = "https://files.pythonhosted.org/packages/ad/6e/9a0927ac24bd33a0a9af834d89e0abc7cfd8e13bed17a86407a66773cc0e/websockets-16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:caab51a72c51973ca21fa8a18bd8165e1a0183f1ac7066a182ff27107b71e1a4", size = 186279 }, 1456 + { url = "https://files.pythonhosted.org/packages/b9/ca/bf1c68440d7a868180e11be653c85959502efd3a709323230314fda6e0b3/websockets-16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19c4dc84098e523fd63711e563077d39e90ec6702aff4b5d9e344a60cb3c0cb1", size = 185711 }, 1457 + { url = "https://files.pythonhosted.org/packages/c4/f8/fdc34643a989561f217bb477cbc47a3a07212cbda91c0e4389c43c296ebf/websockets-16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a5e18a238a2b2249c9a9235466b90e96ae4795672598a58772dd806edc7ac6d3", size = 184982 }, 1458 + { url = "https://files.pythonhosted.org/packages/dd/d1/574fa27e233764dbac9c52730d63fcf2823b16f0856b3329fc6268d6ae4f/websockets-16.0-cp314-cp314-win32.whl", hash = "sha256:a069d734c4a043182729edd3e9f247c3b2a4035415a9172fd0f1b71658a320a8", size = 177915 }, 1459 + { url = "https://files.pythonhosted.org/packages/8a/f1/ae6b937bf3126b5134ce1f482365fde31a357c784ac51852978768b5eff4/websockets-16.0-cp314-cp314-win_amd64.whl", hash = "sha256:c0ee0e63f23914732c6d7e0cce24915c48f3f1512ec1d079ed01fc629dab269d", size = 178381 }, 1460 + { url = "https://files.pythonhosted.org/packages/06/9b/f791d1db48403e1f0a27577a6beb37afae94254a8c6f08be4a23e4930bc0/websockets-16.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:a35539cacc3febb22b8f4d4a99cc79b104226a756aa7400adc722e83b0d03244", size = 177737 }, 1461 + { url = "https://files.pythonhosted.org/packages/bd/40/53ad02341fa33b3ce489023f635367a4ac98b73570102ad2cdd770dacc9a/websockets-16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b784ca5de850f4ce93ec85d3269d24d4c82f22b7212023c974c401d4980ebc5e", size = 175268 }, 1462 + { url = "https://files.pythonhosted.org/packages/74/9b/6158d4e459b984f949dcbbb0c5d270154c7618e11c01029b9bbd1bb4c4f9/websockets-16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:569d01a4e7fba956c5ae4fc988f0d4e187900f5497ce46339c996dbf24f17641", size = 175486 }, 1463 + { url = "https://files.pythonhosted.org/packages/e5/2d/7583b30208b639c8090206f95073646c2c9ffd66f44df967981a64f849ad/websockets-16.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:50f23cdd8343b984957e4077839841146f67a3d31ab0d00e6b824e74c5b2f6e8", size = 185331 }, 1464 + { url = "https://files.pythonhosted.org/packages/45/b0/cce3784eb519b7b5ad680d14b9673a31ab8dcb7aad8b64d81709d2430aa8/websockets-16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:152284a83a00c59b759697b7f9e9cddf4e3c7861dd0d964b472b70f78f89e80e", size = 186501 }, 1465 + { url = "https://files.pythonhosted.org/packages/19/60/b8ebe4c7e89fb5f6cdf080623c9d92789a53636950f7abacfc33fe2b3135/websockets-16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc59589ab64b0022385f429b94697348a6a234e8ce22544e3681b2e9331b5944", size = 186062 }, 1466 + { url = "https://files.pythonhosted.org/packages/88/a8/a080593f89b0138b6cba1b28f8df5673b5506f72879322288b031337c0b8/websockets-16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:32da954ffa2814258030e5a57bc73a3635463238e797c7375dc8091327434206", size = 185356 }, 1467 + { url = "https://files.pythonhosted.org/packages/c2/b6/b9afed2afadddaf5ebb2afa801abf4b0868f42f8539bfe4b071b5266c9fe/websockets-16.0-cp314-cp314t-win32.whl", hash = "sha256:5a4b4cc550cb665dd8a47f868c8d04c8230f857363ad3c9caf7a0c3bf8c61ca6", size = 178085 }, 1468 + { url = "https://files.pythonhosted.org/packages/9f/3e/28135a24e384493fa804216b79a6a6759a38cc4ff59118787b9fb693df93/websockets-16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b14dc141ed6d2dde437cddb216004bcac6a1df0935d79656387bd41632ba0bbd", size = 178531 }, 1469 + { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598 }, 1470 + ]
+25
demo/frontend/components.json
··· 1 + { 2 + "$schema": "https://ui.shadcn.com/schema.json", 3 + "style": "base-lyra", 4 + "rsc": false, 5 + "tsx": true, 6 + "tailwind": { 7 + "config": "tailwind.config.js", 8 + "css": "src/index.css", 9 + "baseColor": "neutral", 10 + "cssVariables": true, 11 + "prefix": "" 12 + }, 13 + "iconLibrary": "phosphor", 14 + "rtl": false, 15 + "aliases": { 16 + "components": "@/components", 17 + "utils": "@/lib/utils", 18 + "ui": "@/components/ui", 19 + "lib": "@/lib", 20 + "hooks": "@/hooks" 21 + }, 22 + "menuColor": "default", 23 + "menuAccent": "subtle", 24 + "registries": {} 25 + }
+12
demo/frontend/index.html
··· 1 + <!doctype html> 2 + <html lang="en" class="dark"> 3 + <head> 4 + <meta charset="UTF-8" /> 5 + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 6 + <title>arXiv Explorer — gvecdb</title> 7 + </head> 8 + <body> 9 + <div id="root"></div> 10 + <script type="module" src="/src/main.tsx"></script> 11 + </body> 12 + </html>
+39
demo/frontend/package.json
··· 1 + { 2 + "name": "arxiv-demo-frontend", 3 + "private": true, 4 + "version": "0.1.0", 5 + "type": "module", 6 + "scripts": { 7 + "dev": "vite", 8 + "build": "tsc -b && vite build", 9 + "preview": "vite preview", 10 + "lint": "biome check src/", 11 + "format": "biome format --write src/" 12 + }, 13 + "dependencies": { 14 + "@base-ui/react": "^1.4.1", 15 + "@fontsource-variable/jetbrains-mono": "^5.2.8", 16 + "@phosphor-icons/react": "^2.1.10", 17 + "@tanstack/react-query": "^5.75.5", 18 + "class-variance-authority": "^0.7.1", 19 + "clsx": "^2.1.1", 20 + "react": "^19.1.0", 21 + "react-dom": "^19.1.0", 22 + "react-force-graph-2d": "^1.26.4", 23 + "react-router-dom": "^7.6.0", 24 + "shadcn": "^4.6.0", 25 + "tailwind-merge": "^3.5.0", 26 + "tw-animate-css": "^1.4.0" 27 + }, 28 + "devDependencies": { 29 + "@biomejs/biome": "^1.9.4", 30 + "@types/react": "^19.1.2", 31 + "@types/react-dom": "^19.1.2", 32 + "@vitejs/plugin-react": "^4.4.1", 33 + "autoprefixer": "^10.4.21", 34 + "postcss": "^8.5.3", 35 + "tailwindcss": "^3.4.17", 36 + "typescript": "^5.8.3", 37 + "vite": "^6.3.4" 38 + } 39 + }
+6
demo/frontend/postcss.config.js
··· 1 + export default { 2 + plugins: { 3 + tailwindcss: {}, 4 + autoprefixer: {}, 5 + }, 6 + };
+11
demo/frontend/src/App.tsx
··· 1 + import { Route, Routes } from "react-router-dom"; 2 + import ExplorerPage from "@/pages/ExplorerPage"; 3 + 4 + export default function App() { 5 + return ( 6 + <Routes> 7 + <Route path="/" element={<ExplorerPage />} /> 8 + <Route path="/explorer/:nodeId" element={<ExplorerPage />} /> 9 + </Routes> 10 + ); 11 + }
+83
demo/frontend/src/api/client.ts
··· 1 + import { useQuery } from "@tanstack/react-query"; 2 + import type { 3 + AuthorDetail, 4 + GraphData, 5 + PaperDetail, 6 + PaperSummary, 7 + SearchFilters, 8 + SearchResponse, 9 + } from "@/api/types"; 10 + 11 + const BASE = "/api"; 12 + 13 + async function fetchJson<T>(url: string): Promise<T> { 14 + const res = await fetch(url); 15 + if (!res.ok) { 16 + throw new Error(`API error: ${res.status} ${res.statusText}`); 17 + } 18 + return res.json() as Promise<T>; 19 + } 20 + 21 + export function useSearch( 22 + query: string, 23 + k = 20, 24 + tag = "abstract_embedding", 25 + filters?: SearchFilters, 26 + ) { 27 + return useQuery<SearchResponse>({ 28 + queryKey: ["search", query, k, tag, filters], 29 + queryFn: () => { 30 + const params = new URLSearchParams({ 31 + q: query, 32 + k: String(k), 33 + tag, 34 + }); 35 + if (filters?.yearMin != null) params.set("year_min", String(filters.yearMin)); 36 + if (filters?.yearMax != null) params.set("year_max", String(filters.yearMax)); 37 + if (filters?.category) params.set("category", filters.category); 38 + if (filters?.publishedOnly) params.set("published_only", "true"); 39 + return fetchJson(`${BASE}/search?${params}`); 40 + }, 41 + enabled: query.length > 0, 42 + }); 43 + } 44 + 45 + export function usePaper(nodeId: number | undefined) { 46 + return useQuery<PaperDetail>({ 47 + queryKey: ["paper", nodeId], 48 + queryFn: () => fetchJson(`${BASE}/paper/by-node/${nodeId}`), 49 + enabled: nodeId !== undefined, 50 + }); 51 + } 52 + 53 + export function useAuthor(nodeId: number | undefined) { 54 + return useQuery<AuthorDetail>({ 55 + queryKey: ["author", nodeId], 56 + queryFn: () => fetchJson(`${BASE}/author/${nodeId}`), 57 + enabled: nodeId !== undefined, 58 + }); 59 + } 60 + 61 + export function useNeighborhood(nodeId: number | undefined, depth = 1) { 62 + return useQuery<GraphData>({ 63 + queryKey: ["neighbourhood", nodeId, depth], 64 + queryFn: () => 65 + fetchJson(`${BASE}/graph/neighbourhood/${nodeId}?depth=${depth}`), 66 + enabled: nodeId !== undefined, 67 + }); 68 + } 69 + 70 + export function useSimilar( 71 + nodeId: number | undefined, 72 + k = 10, 73 + tag = "abstract_embedding", 74 + ) { 75 + return useQuery<PaperSummary[]>({ 76 + queryKey: ["similar", nodeId, k, tag], 77 + queryFn: () => 78 + fetchJson( 79 + `${BASE}/similar/${nodeId}?k=${k}&tag=${encodeURIComponent(tag)}`, 80 + ), 81 + enabled: nodeId !== undefined, 82 + }); 83 + }
+86
demo/frontend/src/api/types.ts
··· 1 + export interface PaperSummary { 2 + node_id: number; 3 + arxiv_id: string; 4 + title: string; 5 + authors: string[]; 6 + year: number; 7 + categories: string; 8 + score: number; 9 + doi: string; 10 + journal_ref: string; 11 + submitted_date: string; 12 + page_count: number; 13 + figure_count: number; 14 + version_count: number; 15 + } 16 + 17 + export interface AuthorRef { 18 + node_id: number; 19 + name: string; 20 + position: number; 21 + } 22 + 23 + export interface PaperRef { 24 + node_id: number; 25 + arxiv_id: string; 26 + title: string; 27 + year: number; 28 + } 29 + 30 + export interface PaperDetail { 31 + node_id: number; 32 + arxiv_id: string; 33 + title: string; 34 + abstract: string; 35 + authors: AuthorRef[]; 36 + year: number; 37 + categories: string; 38 + cites: PaperRef[]; 39 + cited_by: PaperRef[]; 40 + doi: string; 41 + journal_ref: string; 42 + submitted_date: string; 43 + page_count: number; 44 + figure_count: number; 45 + version_count: number; 46 + comments: string; 47 + } 48 + 49 + export interface AuthorDetail { 50 + node_id: number; 51 + name: string; 52 + paper_count: number; 53 + papers: PaperRef[]; 54 + } 55 + 56 + export interface GraphNode { 57 + id: number; 58 + type: string; 59 + label: string; 60 + metadata: Record<string, string | number>; 61 + } 62 + 63 + export interface GraphEdge { 64 + id: number; 65 + source: number; 66 + target: number; 67 + type: string; 68 + } 69 + 70 + export interface GraphData { 71 + nodes: GraphNode[]; 72 + edges: GraphEdge[]; 73 + } 74 + 75 + export interface SearchResponse { 76 + query: string; 77 + tag: string; 78 + results: PaperSummary[]; 79 + } 80 + 81 + export interface SearchFilters { 82 + yearMin?: number; 83 + yearMax?: number; 84 + category?: string; 85 + publishedOnly: boolean; 86 + }
+54
demo/frontend/src/components/AuthorDetail.tsx
··· 1 + import { Badge } from "@/components/ui/badge"; 2 + import { Card, CardContent } from "@/components/ui/card"; 3 + import type { AuthorDetail as AuthorDetailType } from "@/api/types"; 4 + 5 + interface AuthorDetailProps { 6 + author: AuthorDetailType; 7 + onNavigate?: (nodeId: number) => void; 8 + } 9 + 10 + export default function AuthorDetailView({ 11 + author, 12 + onNavigate, 13 + }: AuthorDetailProps) { 14 + return ( 15 + <div className="space-y-4"> 16 + <div> 17 + <h1 className="text-sm font-bold">{author.name}</h1> 18 + <p className="mt-0.5 text-xs text-muted-foreground"> 19 + {author.paper_count} paper{author.paper_count !== 1 && "s"} 20 + </p> 21 + </div> 22 + 23 + <div> 24 + <h2 className="mb-1.5 text-[10px] font-semibold uppercase tracking-widest text-muted-foreground"> 25 + Papers 26 + </h2> 27 + <div className="space-y-1.5"> 28 + {author.papers.map((paper) => ( 29 + <button 30 + key={paper.node_id} 31 + onClick={() => onNavigate?.(paper.node_id)} 32 + className="w-full text-left" 33 + > 34 + <Card 35 + className="transition-colors hover:bg-muted/50" 36 + size="sm" 37 + > 38 + <CardContent> 39 + <h3 className="text-xs font-medium">{paper.title}</h3> 40 + <div className="mt-1 flex items-center gap-1.5"> 41 + <Badge variant="outline">{paper.year}</Badge> 42 + <span className="font-mono text-[10px] text-muted-foreground"> 43 + {paper.arxiv_id} 44 + </span> 45 + </div> 46 + </CardContent> 47 + </Card> 48 + </button> 49 + ))} 50 + </div> 51 + </div> 52 + </div> 53 + ); 54 + }
+145
demo/frontend/src/components/FilterPanel.tsx
··· 1 + import { Funnel } from "@phosphor-icons/react"; 2 + import { useState } from "react"; 3 + import { Badge } from "@/components/ui/badge"; 4 + import { Button } from "@/components/ui/button"; 5 + import { Input } from "@/components/ui/input"; 6 + import type { SearchFilters } from "@/api/types"; 7 + 8 + interface FilterPanelProps { 9 + filters: SearchFilters; 10 + onChange: (filters: SearchFilters) => void; 11 + } 12 + 13 + const CATEGORIES = [ 14 + { value: "cs.AI", label: "AI" }, 15 + { value: "cs.LG", label: "ML" }, 16 + { value: "cs.CL", label: "NLP" }, 17 + { value: "cs.CV", label: "Vision" }, 18 + { value: "cs.IR", label: "IR" }, 19 + ] as const; 20 + 21 + export default function FilterPanel({ filters, onChange }: FilterPanelProps) { 22 + const [open, setOpen] = useState(false); 23 + 24 + const activeCount = 25 + (filters.yearMin != null ? 1 : 0) + 26 + (filters.yearMax != null ? 1 : 0) + 27 + (filters.category ? 1 : 0) + 28 + (filters.publishedOnly ? 1 : 0); 29 + 30 + const selectedCats = new Set(filters.category?.split(",").filter(Boolean) ?? []); 31 + 32 + const toggleCategory = (cat: string) => { 33 + const next = new Set(selectedCats); 34 + if (next.has(cat)) { 35 + next.delete(cat); 36 + } else { 37 + next.add(cat); 38 + } 39 + onChange({ 40 + ...filters, 41 + category: next.size > 0 ? [...next].join(",") : undefined, 42 + }); 43 + }; 44 + 45 + return ( 46 + <div> 47 + <Button 48 + variant={activeCount > 0 ? "secondary" : "outline"} 49 + size="sm" 50 + onClick={() => setOpen(!open)} 51 + > 52 + <Funnel data-icon="inline-start" /> 53 + Filters 54 + {activeCount > 0 && ( 55 + <Badge variant="default" className="ml-1 h-4 min-w-4 px-1"> 56 + {activeCount} 57 + </Badge> 58 + )} 59 + </Button> 60 + 61 + {open && ( 62 + <div className="mt-2 border bg-card p-3 space-y-3"> 63 + <div className="flex items-center gap-2"> 64 + <span className="text-[10px] font-semibold uppercase tracking-widest text-muted-foreground w-12 shrink-0"> 65 + Year 66 + </span> 67 + <Input 68 + type="number" 69 + placeholder="From" 70 + value={filters.yearMin ?? ""} 71 + onChange={(e) => 72 + onChange({ 73 + ...filters, 74 + yearMin: e.target.value ? Number(e.target.value) : undefined, 75 + }) 76 + } 77 + className="w-20" 78 + /> 79 + <span className="text-muted-foreground text-xs">—</span> 80 + <Input 81 + type="number" 82 + placeholder="To" 83 + value={filters.yearMax ?? ""} 84 + onChange={(e) => 85 + onChange({ 86 + ...filters, 87 + yearMax: e.target.value ? Number(e.target.value) : undefined, 88 + }) 89 + } 90 + className="w-20" 91 + /> 92 + </div> 93 + 94 + <div className="flex items-center gap-2"> 95 + <span className="text-[10px] font-semibold uppercase tracking-widest text-muted-foreground w-12 shrink-0"> 96 + Area 97 + </span> 98 + <div className="flex flex-wrap gap-1"> 99 + {CATEGORIES.map((cat) => ( 100 + <button 101 + key={cat.value} 102 + onClick={() => toggleCategory(cat.value)} 103 + className={ 104 + selectedCats.has(cat.value) 105 + ? "inline-flex h-5 items-center px-2 text-xs font-medium bg-primary text-primary-foreground" 106 + : "inline-flex h-5 items-center px-2 text-xs font-medium border text-muted-foreground hover:text-foreground" 107 + } 108 + > 109 + {cat.label} 110 + </button> 111 + ))} 112 + </div> 113 + </div> 114 + 115 + <div className="flex items-center gap-2"> 116 + <span className="text-[10px] font-semibold uppercase tracking-widest text-muted-foreground w-12 shrink-0" /> 117 + <button 118 + onClick={() => 119 + onChange({ ...filters, publishedOnly: !filters.publishedOnly }) 120 + } 121 + className={ 122 + filters.publishedOnly 123 + ? "inline-flex h-5 items-center px-2 text-xs font-medium bg-primary text-primary-foreground" 124 + : "inline-flex h-5 items-center px-2 text-xs font-medium border text-muted-foreground hover:text-foreground" 125 + } 126 + > 127 + Published only (has DOI) 128 + </button> 129 + {activeCount > 0 && ( 130 + <Button 131 + variant="ghost" 132 + size="xs" 133 + onClick={() => 134 + onChange({ publishedOnly: false }) 135 + } 136 + > 137 + Clear all 138 + </Button> 139 + )} 140 + </div> 141 + </div> 142 + )} 143 + </div> 144 + ); 145 + }
+167
demo/frontend/src/components/GraphVisualization.tsx
··· 1 + import { useCallback, useEffect, useMemo, useRef } from "react"; 2 + import ForceGraph2D from "react-force-graph-2d"; 3 + import type { GraphData } from "@/api/types"; 4 + 5 + interface GraphVisualisationProps { 6 + data: GraphData; 7 + width?: number; 8 + height?: number; 9 + selectedNodeId?: number; 10 + onNodeClick: (nodeId: number, nodeType: string) => void; 11 + } 12 + 13 + interface FGNode { 14 + id: number; 15 + type: string; 16 + label: string; 17 + metadata: Record<string, string | number>; 18 + x?: number; 19 + y?: number; 20 + } 21 + 22 + interface FGLink { 23 + source: number | FGNode; 24 + target: number | FGNode; 25 + type: string; 26 + } 27 + 28 + const NODE_COLOURS: Record<string, string> = { 29 + paper: "#3b82f6", 30 + author: "#22c55e", 31 + }; 32 + 33 + const EDGE_COLOURS: Record<string, string> = { 34 + cites: "#64748b", 35 + authored: "#f97316", 36 + }; 37 + 38 + export default function GraphVisualisation({ 39 + data, 40 + width, 41 + height, 42 + selectedNodeId, 43 + onNodeClick, 44 + }: GraphVisualisationProps) { 45 + const fgRef = useRef<{ 46 + d3ReheatSimulation?: () => void; 47 + zoomToFit?: (ms?: number, padding?: number) => void; 48 + centerAt?: (x?: number, y?: number, ms?: number) => void; 49 + zoom?: (zoom: number, ms?: number) => void; 50 + }>(undefined); 51 + const initialFitDone = useRef(false); 52 + const pendingCentreRef = useRef<number | undefined>(undefined); 53 + 54 + const graphData = useMemo( 55 + () => ({ 56 + nodes: data.nodes.map((n) => ({ ...n })) as FGNode[], 57 + links: data.edges.map((e) => ({ 58 + source: e.source, 59 + target: e.target, 60 + type: e.type, 61 + })) as FGLink[], 62 + }), 63 + [data], 64 + ); 65 + 66 + useEffect(() => { 67 + if (selectedNodeId != null) { 68 + pendingCentreRef.current = selectedNodeId; 69 + } 70 + }, [selectedNodeId]); 71 + 72 + const handleEngineStop = useCallback(() => { 73 + if (!initialFitDone.current && fgRef.current?.zoomToFit && data.nodes.length > 0) { 74 + fgRef.current.zoomToFit(300, 40); 75 + initialFitDone.current = true; 76 + return; 77 + } 78 + 79 + const targetId = pendingCentreRef.current; 80 + if (targetId != null && fgRef.current?.centerAt) { 81 + const node = graphData.nodes.find((n) => n.id === targetId) as FGNode | undefined; 82 + if (node?.x != null && node?.y != null) { 83 + fgRef.current.centerAt(node.x, node.y, 400); 84 + } 85 + pendingCentreRef.current = undefined; 86 + } 87 + }, [data.nodes.length, graphData.nodes]); 88 + 89 + const handleNodeClick = useCallback( 90 + (node: FGNode) => { 91 + onNodeClick(node.id, node.type); 92 + }, 93 + [onNodeClick], 94 + ); 95 + 96 + const paintNode = useCallback( 97 + (node: FGNode, ctx: CanvasRenderingContext2D) => { 98 + const r = node.type === "paper" ? 5 : 4; 99 + const colour = NODE_COLOURS[node.type] ?? "#94a3b8"; 100 + const selected = node.id === selectedNodeId; 101 + 102 + if (selected) { 103 + ctx.beginPath(); 104 + ctx.arc(node.x ?? 0, node.y ?? 0, r + 3, 0, 2 * Math.PI); 105 + ctx.strokeStyle = "#ffffff"; 106 + ctx.lineWidth = 1.5; 107 + ctx.stroke(); 108 + } 109 + 110 + ctx.beginPath(); 111 + ctx.arc(node.x ?? 0, node.y ?? 0, r, 0, 2 * Math.PI); 112 + ctx.fillStyle = colour; 113 + ctx.fill(); 114 + if (!selected) { 115 + ctx.strokeStyle = "rgba(255,255,255,0.1)"; 116 + ctx.lineWidth = 0.5; 117 + ctx.stroke(); 118 + } 119 + 120 + ctx.font = "3px 'JetBrains Mono Variable', monospace"; 121 + ctx.textAlign = "center"; 122 + ctx.fillStyle = selected ? "#ffffff" : "rgba(255,255,255,0.6)"; 123 + const label = 124 + node.label.length > 30 125 + ? `${node.label.slice(0, 28)}...` 126 + : node.label; 127 + ctx.fillText(label, node.x ?? 0, (node.y ?? 0) + r + 4); 128 + }, 129 + [selectedNodeId], 130 + ); 131 + 132 + const paintLink = useCallback( 133 + (link: FGLink, ctx: CanvasRenderingContext2D) => { 134 + const src = link.source as FGNode; 135 + const tgt = link.target as FGNode; 136 + ctx.beginPath(); 137 + ctx.moveTo(src.x ?? 0, src.y ?? 0); 138 + ctx.lineTo(tgt.x ?? 0, tgt.y ?? 0); 139 + ctx.strokeStyle = EDGE_COLOURS[link.type] ?? "#475569"; 140 + ctx.lineWidth = 0.5; 141 + ctx.stroke(); 142 + }, 143 + [], 144 + ); 145 + 146 + const getNodeLabel = useCallback((node: FGNode) => node.label, []); 147 + 148 + return ( 149 + <ForceGraph2D 150 + ref={fgRef} 151 + graphData={graphData} 152 + width={width} 153 + height={height} 154 + nodeCanvasObject={paintNode} 155 + linkCanvasObject={paintLink} 156 + onNodeClick={handleNodeClick} 157 + onEngineStop={handleEngineStop} 158 + nodeLabel={getNodeLabel} 159 + cooldownTicks={40} 160 + d3AlphaDecay={0.05} 161 + d3VelocityDecay={0.3} 162 + enableZoomInteraction 163 + enablePanInteraction 164 + backgroundColor="transparent" 165 + /> 166 + ); 167 + }
+88
demo/frontend/src/components/NodeSidebar.tsx
··· 1 + import { X } from "@phosphor-icons/react"; 2 + import { useAuthor, usePaper, useSimilar } from "@/api/client"; 3 + import { Button } from "@/components/ui/button"; 4 + import { Separator } from "@/components/ui/separator"; 5 + import AuthorDetailView from "@/components/AuthorDetail"; 6 + import PaperCard from "@/components/PaperCard"; 7 + import PaperDetailView from "@/components/PaperDetail"; 8 + 9 + interface NodeSidebarProps { 10 + nodeId: number; 11 + nodeType: string; 12 + onClose: () => void; 13 + onNavigate: (nodeId: number) => void; 14 + } 15 + 16 + export default function NodeSidebar({ 17 + nodeId, 18 + nodeType, 19 + onClose, 20 + onNavigate, 21 + }: NodeSidebarProps) { 22 + const paperQuery = usePaper(nodeType === "paper" ? nodeId : undefined); 23 + const authorQuery = useAuthor(nodeType === "author" ? nodeId : undefined); 24 + const similarQuery = useSimilar( 25 + nodeType === "paper" ? nodeId : undefined, 26 + 6, 27 + ); 28 + 29 + const isLoading = 30 + (nodeType === "paper" && paperQuery.isLoading) || 31 + (nodeType === "author" && authorQuery.isLoading); 32 + 33 + return ( 34 + <div className="flex h-full flex-col border-l bg-background/95 backdrop-blur-sm"> 35 + <div className="flex items-center justify-between border-b px-3 py-2 shrink-0"> 36 + <span className="text-[10px] font-semibold uppercase tracking-widest text-muted-foreground"> 37 + {nodeType} 38 + </span> 39 + <Button variant="ghost" size="icon-xs" onClick={onClose}> 40 + <X /> 41 + </Button> 42 + </div> 43 + 44 + <div className="flex-1 overflow-y-auto px-3 py-3"> 45 + {isLoading && ( 46 + <div className="flex items-center justify-center py-12"> 47 + <div className="h-4 w-4 animate-spin border-2 border-foreground border-t-transparent" /> 48 + </div> 49 + )} 50 + 51 + {nodeType === "paper" && paperQuery.data && ( 52 + <div className="space-y-4"> 53 + <PaperDetailView 54 + paper={paperQuery.data} 55 + onNavigate={onNavigate} 56 + /> 57 + {similarQuery.data && similarQuery.data.length > 0 && ( 58 + <> 59 + <Separator /> 60 + <div> 61 + <h2 className="mb-1.5 text-[10px] font-semibold uppercase tracking-widest text-muted-foreground"> 62 + Similar Papers 63 + </h2> 64 + <div className="space-y-1.5"> 65 + {similarQuery.data.map((p) => ( 66 + <PaperCard 67 + key={p.node_id} 68 + paper={p} 69 + onNavigate={onNavigate} 70 + /> 71 + ))} 72 + </div> 73 + </div> 74 + </> 75 + )} 76 + </div> 77 + )} 78 + 79 + {nodeType === "author" && authorQuery.data && ( 80 + <AuthorDetailView 81 + author={authorQuery.data} 82 + onNavigate={onNavigate} 83 + /> 84 + )} 85 + </div> 86 + </div> 87 + ); 88 + }
+82
demo/frontend/src/components/PaperCard.tsx
··· 1 + import { BookOpen, FileText, GitBranch } from "@phosphor-icons/react"; 2 + import { Badge } from "@/components/ui/badge"; 3 + import { Card, CardContent } from "@/components/ui/card"; 4 + import type { PaperSummary } from "@/api/types"; 5 + 6 + interface PaperCardProps { 7 + paper: PaperSummary; 8 + onNavigate?: (nodeId: number) => void; 9 + } 10 + 11 + export default function PaperCard({ paper, onNavigate }: PaperCardProps) { 12 + const cats = paper.categories.split(/\s+/).slice(0, 4); 13 + const scorePercent = Math.round(paper.score * 100); 14 + 15 + return ( 16 + <button 17 + className="w-full text-left" 18 + onClick={() => onNavigate?.(paper.node_id)} 19 + > 20 + <Card className="transition-colors hover:bg-muted/50" size="sm"> 21 + <CardContent className="flex items-start justify-between gap-4"> 22 + <div className="min-w-0 flex-1"> 23 + <h3 className="text-xs font-semibold leading-snug"> 24 + {paper.title} 25 + </h3> 26 + <p className="mt-1 text-xs text-muted-foreground"> 27 + {paper.authors.slice(0, 5).join(", ")} 28 + {paper.authors.length > 5 && 29 + ` +${paper.authors.length - 5} more`} 30 + </p> 31 + <div className="mt-2 flex flex-wrap items-center gap-1.5"> 32 + <Badge variant="outline">{paper.year}</Badge> 33 + {cats.map((cat) => ( 34 + <Badge key={cat} variant="secondary"> 35 + {cat} 36 + </Badge> 37 + ))} 38 + {paper.doi && <Badge variant="secondary">DOI</Badge>} 39 + {paper.journal_ref && ( 40 + <Badge variant="outline"> 41 + {paper.journal_ref.split(/[,.(]/)[0]?.trim()} 42 + </Badge> 43 + )} 44 + </div> 45 + <div className="mt-1.5 flex items-center gap-3 text-[10px] text-muted-foreground"> 46 + <span className="font-mono">{paper.arxiv_id}</span> 47 + {paper.page_count > 0 && ( 48 + <span className="flex items-center gap-0.5"> 49 + <FileText className="size-2.5" /> 50 + {paper.page_count}p 51 + </span> 52 + )} 53 + {paper.figure_count > 0 && ( 54 + <span className="flex items-center gap-0.5"> 55 + <BookOpen className="size-2.5" /> 56 + {paper.figure_count} fig 57 + </span> 58 + )} 59 + {paper.version_count > 1 && ( 60 + <span className="flex items-center gap-0.5"> 61 + <GitBranch className="size-2.5" /> 62 + v{paper.version_count} 63 + </span> 64 + )} 65 + {paper.submitted_date && <span>{paper.submitted_date}</span>} 66 + </div> 67 + </div> 68 + {paper.score > 0 && ( 69 + <div className="flex flex-col items-center"> 70 + <div className="flex h-9 w-9 items-center justify-center border text-xs font-bold tabular-nums"> 71 + {scorePercent} 72 + </div> 73 + <span className="mt-0.5 text-[10px] text-muted-foreground"> 74 + match 75 + </span> 76 + </div> 77 + )} 78 + </CardContent> 79 + </Card> 80 + </button> 81 + ); 82 + }
+177
demo/frontend/src/components/PaperDetail.tsx
··· 1 + import { 2 + ArrowSquareOut, 3 + BookOpen, 4 + CalendarDots, 5 + FileText, 6 + GitBranch, 7 + LinkSimple, 8 + } from "@phosphor-icons/react"; 9 + import { Badge } from "@/components/ui/badge"; 10 + import { Separator } from "@/components/ui/separator"; 11 + import type { PaperDetail as PaperDetailType } from "@/api/types"; 12 + 13 + interface PaperDetailProps { 14 + paper: PaperDetailType; 15 + onNavigate?: (nodeId: number) => void; 16 + } 17 + 18 + export default function PaperDetailView({ 19 + paper, 20 + onNavigate, 21 + }: PaperDetailProps) { 22 + const cats = paper.categories.split(/\s+/); 23 + 24 + return ( 25 + <div className="space-y-4"> 26 + <div> 27 + <h1 className="text-sm font-bold leading-tight">{paper.title}</h1> 28 + <div className="mt-2 flex flex-wrap gap-1.5"> 29 + <Badge variant="outline">{paper.year}</Badge> 30 + {cats.map((cat) => ( 31 + <Badge key={cat} variant="secondary"> 32 + {cat} 33 + </Badge> 34 + ))} 35 + <a 36 + href={`https://arxiv.org/abs/${paper.arxiv_id}`} 37 + target="_blank" 38 + rel="noopener noreferrer" 39 + className="inline-flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors" 40 + > 41 + arXiv:{paper.arxiv_id} 42 + <ArrowSquareOut className="size-3" /> 43 + </a> 44 + </div> 45 + </div> 46 + 47 + <div className="grid grid-cols-2 gap-x-4 gap-y-1.5 text-xs"> 48 + {paper.submitted_date && ( 49 + <div className="flex items-center gap-1.5 text-muted-foreground"> 50 + <CalendarDots className="size-3 shrink-0" /> 51 + <span>{paper.submitted_date}</span> 52 + </div> 53 + )} 54 + {paper.page_count > 0 && ( 55 + <div className="flex items-center gap-1.5 text-muted-foreground"> 56 + <FileText className="size-3 shrink-0" /> 57 + <span>{paper.page_count} pages</span> 58 + </div> 59 + )} 60 + {paper.figure_count > 0 && ( 61 + <div className="flex items-center gap-1.5 text-muted-foreground"> 62 + <BookOpen className="size-3 shrink-0" /> 63 + <span>{paper.figure_count} figures</span> 64 + </div> 65 + )} 66 + {paper.version_count > 0 && ( 67 + <div className="flex items-center gap-1.5 text-muted-foreground"> 68 + <GitBranch className="size-3 shrink-0" /> 69 + <span> 70 + {paper.version_count} version 71 + {paper.version_count !== 1 && "s"} 72 + </span> 73 + </div> 74 + )} 75 + {paper.doi && ( 76 + <div className="flex items-center gap-1.5 text-muted-foreground col-span-2"> 77 + <LinkSimple className="size-3 shrink-0" /> 78 + <a 79 + href={`https://doi.org/${paper.doi}`} 80 + target="_blank" 81 + rel="noopener noreferrer" 82 + className="truncate hover:text-foreground transition-colors" 83 + > 84 + {paper.doi} 85 + </a> 86 + </div> 87 + )} 88 + {paper.journal_ref && ( 89 + <div className="flex items-center gap-1.5 text-muted-foreground col-span-2"> 90 + <BookOpen className="size-3 shrink-0" /> 91 + <span className="truncate">{paper.journal_ref}</span> 92 + </div> 93 + )} 94 + </div> 95 + 96 + {paper.comments && ( 97 + <p className="text-xs text-muted-foreground italic"> 98 + {paper.comments} 99 + </p> 100 + )} 101 + 102 + <div> 103 + <h2 className="mb-1.5 text-[10px] font-semibold uppercase tracking-widest text-muted-foreground"> 104 + Authors 105 + </h2> 106 + <div className="flex flex-wrap gap-1"> 107 + {paper.authors.map((a) => ( 108 + <button 109 + key={a.node_id} 110 + onClick={() => onNavigate?.(a.node_id)} 111 + className="inline-flex h-5 items-center border px-1.5 text-[10px] font-medium text-muted-foreground hover:text-foreground hover:bg-muted transition-colors" 112 + > 113 + {a.name} 114 + </button> 115 + ))} 116 + </div> 117 + </div> 118 + 119 + <Separator /> 120 + 121 + <div> 122 + <h2 className="mb-1.5 text-[10px] font-semibold uppercase tracking-widest text-muted-foreground"> 123 + Abstract 124 + </h2> 125 + <p className="text-xs leading-relaxed text-muted-foreground"> 126 + {paper.abstract} 127 + </p> 128 + </div> 129 + 130 + {paper.cites.length > 0 && ( 131 + <> 132 + <Separator /> 133 + <div> 134 + <h2 className="mb-1.5 text-[10px] font-semibold uppercase tracking-widest text-muted-foreground"> 135 + References ({paper.cites.length}) 136 + </h2> 137 + <div className="space-y-0.5"> 138 + {paper.cites.map((ref) => ( 139 + <button 140 + key={ref.node_id} 141 + onClick={() => onNavigate?.(ref.node_id)} 142 + className="block w-full text-left px-2 py-1.5 text-xs transition-colors hover:bg-muted" 143 + > 144 + {ref.title}{" "} 145 + <span className="text-muted-foreground">({ref.year})</span> 146 + </button> 147 + ))} 148 + </div> 149 + </div> 150 + </> 151 + )} 152 + 153 + {paper.cited_by.length > 0 && ( 154 + <> 155 + <Separator /> 156 + <div> 157 + <h2 className="mb-1.5 text-[10px] font-semibold uppercase tracking-widest text-muted-foreground"> 158 + Cited By ({paper.cited_by.length}) 159 + </h2> 160 + <div className="space-y-0.5"> 161 + {paper.cited_by.map((ref) => ( 162 + <button 163 + key={ref.node_id} 164 + onClick={() => onNavigate?.(ref.node_id)} 165 + className="block w-full text-left px-2 py-1.5 text-xs transition-colors hover:bg-muted" 166 + > 167 + {ref.title}{" "} 168 + <span className="text-muted-foreground">({ref.year})</span> 169 + </button> 170 + ))} 171 + </div> 172 + </div> 173 + </> 174 + )} 175 + </div> 176 + ); 177 + }
+85
demo/frontend/src/components/SearchBar.tsx
··· 1 + import { MagnifyingGlass } from "@phosphor-icons/react"; 2 + import { useCallback, useEffect, useRef, useState } from "react"; 3 + import { Input } from "@/components/ui/input"; 4 + import { cn } from "@/lib/utils"; 5 + 6 + interface SearchBarProps { 7 + onSearch: (query: string, tag: string) => void; 8 + initialQuery?: string; 9 + initialTag?: string; 10 + } 11 + 12 + const TAGS = [ 13 + { value: "abstract_embedding", label: "Abstract" }, 14 + { value: "title_embedding", label: "Title" }, 15 + ] as const; 16 + 17 + export default function SearchBar({ 18 + onSearch, 19 + initialQuery = "", 20 + initialTag = "abstract_embedding", 21 + }: SearchBarProps) { 22 + const [query, setQuery] = useState(initialQuery); 23 + const [tag, setTag] = useState(initialTag); 24 + const timerRef = useRef<ReturnType<typeof setTimeout>>(undefined); 25 + 26 + const triggerSearch = useCallback( 27 + (q: string, t: string) => { 28 + clearTimeout(timerRef.current); 29 + timerRef.current = setTimeout(() => { 30 + if (q.trim()) { 31 + onSearch(q.trim(), t); 32 + } 33 + }, 300); 34 + }, 35 + [onSearch], 36 + ); 37 + 38 + useEffect(() => () => clearTimeout(timerRef.current), []); 39 + 40 + return ( 41 + <div className="flex gap-2"> 42 + <div className="relative flex-1"> 43 + <MagnifyingGlass className="absolute left-2.5 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground" /> 44 + <Input 45 + value={query} 46 + onChange={(e) => { 47 + setQuery(e.target.value); 48 + triggerSearch(e.target.value, tag); 49 + }} 50 + onKeyDown={(e) => { 51 + if (e.key === "Enter" && query.trim()) { 52 + clearTimeout(timerRef.current); 53 + onSearch(query.trim(), tag); 54 + } 55 + }} 56 + placeholder="Search papers by concept, topic, or question..." 57 + className="pl-8" 58 + autoFocus 59 + /> 60 + </div> 61 + <div className="flex overflow-hidden border"> 62 + {TAGS.map((t) => ( 63 + <button 64 + key={t.value} 65 + onClick={() => { 66 + setTag(t.value); 67 + if (query.trim()) { 68 + clearTimeout(timerRef.current); 69 + onSearch(query.trim(), t.value); 70 + } 71 + }} 72 + className={cn( 73 + "px-2.5 py-1.5 text-xs font-medium transition-colors", 74 + tag === t.value 75 + ? "bg-primary text-primary-foreground" 76 + : "bg-background text-muted-foreground hover:bg-muted hover:text-foreground", 77 + )} 78 + > 79 + {t.label} 80 + </button> 81 + ))} 82 + </div> 83 + </div> 84 + ); 85 + }
+52
demo/frontend/src/components/ui/badge.tsx
··· 1 + import { mergeProps } from "@base-ui/react/merge-props" 2 + import { useRender } from "@base-ui/react/use-render" 3 + import { cva, type VariantProps } from "class-variance-authority" 4 + 5 + import { cn } from "@/lib/utils" 6 + 7 + const badgeVariants = cva( 8 + "group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-none border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!", 9 + { 10 + variants: { 11 + variant: { 12 + default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80", 13 + secondary: 14 + "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80", 15 + destructive: 16 + "bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20", 17 + outline: 18 + "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground", 19 + ghost: 20 + "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50", 21 + link: "text-primary underline-offset-4 hover:underline", 22 + }, 23 + }, 24 + defaultVariants: { 25 + variant: "default", 26 + }, 27 + } 28 + ) 29 + 30 + function Badge({ 31 + className, 32 + variant = "default", 33 + render, 34 + ...props 35 + }: useRender.ComponentProps<"span"> & VariantProps<typeof badgeVariants>) { 36 + return useRender({ 37 + defaultTagName: "span", 38 + props: mergeProps<"span">( 39 + { 40 + className: cn(badgeVariants({ variant }), className), 41 + }, 42 + props 43 + ), 44 + render, 45 + state: { 46 + slot: "badge", 47 + variant, 48 + }, 49 + }) 50 + } 51 + 52 + export { Badge, badgeVariants }
+56
demo/frontend/src/components/ui/button.tsx
··· 1 + import { Button as ButtonPrimitive } from "@base-ui/react/button" 2 + import { cva, type VariantProps } from "class-variance-authority" 3 + 4 + import { cn } from "@/lib/utils" 5 + 6 + const buttonVariants = cva( 7 + "group/button inline-flex shrink-0 items-center justify-center rounded-none border border-transparent bg-clip-padding text-xs font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-1 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-1 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", 8 + { 9 + variants: { 10 + variant: { 11 + default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80", 12 + outline: 13 + "border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50", 14 + secondary: 15 + "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground", 16 + ghost: 17 + "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50", 18 + destructive: 19 + "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40", 20 + link: "text-primary underline-offset-4 hover:underline", 21 + }, 22 + size: { 23 + default: 24 + "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2", 25 + xs: "h-6 gap-1 rounded-none px-2 text-xs has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3", 26 + sm: "h-7 gap-1 rounded-none px-2.5 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5", 27 + lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2", 28 + icon: "size-8", 29 + "icon-xs": "size-6 rounded-none [&_svg:not([class*='size-'])]:size-3", 30 + "icon-sm": "size-7 rounded-none", 31 + "icon-lg": "size-9", 32 + }, 33 + }, 34 + defaultVariants: { 35 + variant: "default", 36 + size: "default", 37 + }, 38 + } 39 + ) 40 + 41 + function Button({ 42 + className, 43 + variant = "default", 44 + size = "default", 45 + ...props 46 + }: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>) { 47 + return ( 48 + <ButtonPrimitive 49 + data-slot="button" 50 + className={cn(buttonVariants({ variant, size, className }))} 51 + {...props} 52 + /> 53 + ) 54 + } 55 + 56 + export { Button, buttonVariants }
+103
demo/frontend/src/components/ui/card.tsx
··· 1 + import * as React from "react" 2 + 3 + import { cn } from "@/lib/utils" 4 + 5 + function Card({ 6 + className, 7 + size = "default", 8 + ...props 9 + }: React.ComponentProps<"div"> & { size?: "default" | "sm" }) { 10 + return ( 11 + <div 12 + data-slot="card" 13 + data-size={size} 14 + className={cn( 15 + "group/card flex flex-col gap-4 overflow-hidden rounded-none bg-card py-4 text-xs/relaxed text-card-foreground ring-1 ring-foreground/10 has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-2 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-none *:[img:last-child]:rounded-none", 16 + className 17 + )} 18 + {...props} 19 + /> 20 + ) 21 + } 22 + 23 + function CardHeader({ className, ...props }: React.ComponentProps<"div">) { 24 + return ( 25 + <div 26 + data-slot="card-header" 27 + className={cn( 28 + "group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-none px-4 group-data-[size=sm]/card:px-3 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3", 29 + className 30 + )} 31 + {...props} 32 + /> 33 + ) 34 + } 35 + 36 + function CardTitle({ className, ...props }: React.ComponentProps<"div">) { 37 + return ( 38 + <div 39 + data-slot="card-title" 40 + className={cn( 41 + "font-heading text-sm font-medium group-data-[size=sm]/card:text-sm", 42 + className 43 + )} 44 + {...props} 45 + /> 46 + ) 47 + } 48 + 49 + function CardDescription({ className, ...props }: React.ComponentProps<"div">) { 50 + return ( 51 + <div 52 + data-slot="card-description" 53 + className={cn("text-xs/relaxed text-muted-foreground", className)} 54 + {...props} 55 + /> 56 + ) 57 + } 58 + 59 + function CardAction({ className, ...props }: React.ComponentProps<"div">) { 60 + return ( 61 + <div 62 + data-slot="card-action" 63 + className={cn( 64 + "col-start-2 row-span-2 row-start-1 self-start justify-self-end", 65 + className 66 + )} 67 + {...props} 68 + /> 69 + ) 70 + } 71 + 72 + function CardContent({ className, ...props }: React.ComponentProps<"div">) { 73 + return ( 74 + <div 75 + data-slot="card-content" 76 + className={cn("px-4 group-data-[size=sm]/card:px-3", className)} 77 + {...props} 78 + /> 79 + ) 80 + } 81 + 82 + function CardFooter({ className, ...props }: React.ComponentProps<"div">) { 83 + return ( 84 + <div 85 + data-slot="card-footer" 86 + className={cn( 87 + "flex items-center rounded-none border-t p-4 group-data-[size=sm]/card:p-3", 88 + className 89 + )} 90 + {...props} 91 + /> 92 + ) 93 + } 94 + 95 + export { 96 + Card, 97 + CardHeader, 98 + CardFooter, 99 + CardTitle, 100 + CardAction, 101 + CardDescription, 102 + CardContent, 103 + }
+20
demo/frontend/src/components/ui/input.tsx
··· 1 + import * as React from "react" 2 + import { Input as InputPrimitive } from "@base-ui/react/input" 3 + 4 + import { cn } from "@/lib/utils" 5 + 6 + function Input({ className, type, ...props }: React.ComponentProps<"input">) { 7 + return ( 8 + <InputPrimitive 9 + type={type} 10 + data-slot="input" 11 + className={cn( 12 + "h-8 w-full min-w-0 rounded-none border border-input bg-transparent px-2.5 py-1 text-xs transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-xs file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-1 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-1 aria-invalid:ring-destructive/20 md:text-xs dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40", 13 + className 14 + )} 15 + {...props} 16 + /> 17 + ) 18 + } 19 + 20 + export { Input }
+23
demo/frontend/src/components/ui/separator.tsx
··· 1 + import { Separator as SeparatorPrimitive } from "@base-ui/react/separator" 2 + 3 + import { cn } from "@/lib/utils" 4 + 5 + function Separator({ 6 + className, 7 + orientation = "horizontal", 8 + ...props 9 + }: SeparatorPrimitive.Props) { 10 + return ( 11 + <SeparatorPrimitive 12 + data-slot="separator" 13 + orientation={orientation} 14 + className={cn( 15 + "shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch", 16 + className 17 + )} 18 + {...props} 19 + /> 20 + ) 21 + } 22 + 23 + export { Separator }
+66
demo/frontend/src/components/ui/tooltip.tsx
··· 1 + "use client" 2 + 3 + import { Tooltip as TooltipPrimitive } from "@base-ui/react/tooltip" 4 + 5 + import { cn } from "@/lib/utils" 6 + 7 + function TooltipProvider({ 8 + delay = 0, 9 + ...props 10 + }: TooltipPrimitive.Provider.Props) { 11 + return ( 12 + <TooltipPrimitive.Provider 13 + data-slot="tooltip-provider" 14 + delay={delay} 15 + {...props} 16 + /> 17 + ) 18 + } 19 + 20 + function Tooltip({ ...props }: TooltipPrimitive.Root.Props) { 21 + return <TooltipPrimitive.Root data-slot="tooltip" {...props} /> 22 + } 23 + 24 + function TooltipTrigger({ ...props }: TooltipPrimitive.Trigger.Props) { 25 + return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} /> 26 + } 27 + 28 + function TooltipContent({ 29 + className, 30 + side = "top", 31 + sideOffset = 4, 32 + align = "center", 33 + alignOffset = 0, 34 + children, 35 + ...props 36 + }: TooltipPrimitive.Popup.Props & 37 + Pick< 38 + TooltipPrimitive.Positioner.Props, 39 + "align" | "alignOffset" | "side" | "sideOffset" 40 + >) { 41 + return ( 42 + <TooltipPrimitive.Portal> 43 + <TooltipPrimitive.Positioner 44 + align={align} 45 + alignOffset={alignOffset} 46 + side={side} 47 + sideOffset={sideOffset} 48 + className="isolate z-50" 49 + > 50 + <TooltipPrimitive.Popup 51 + data-slot="tooltip-content" 52 + className={cn( 53 + "z-50 inline-flex w-fit max-w-xs origin-(--transform-origin) items-center gap-1.5 rounded-none bg-foreground px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pr-1.5 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-none data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", 54 + className 55 + )} 56 + {...props} 57 + > 58 + {children} 59 + <TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-none bg-foreground fill-foreground data-[side=bottom]:top-1 data-[side=inline-end]:top-1/2! data-[side=inline-end]:-left-1 data-[side=inline-end]:-translate-y-1/2 data-[side=inline-start]:top-1/2! data-[side=inline-start]:-right-1 data-[side=inline-start]:-translate-y-1/2 data-[side=left]:top-1/2! data-[side=left]:-right-1 data-[side=left]:-translate-y-1/2 data-[side=right]:top-1/2! data-[side=right]:-left-1 data-[side=right]:-translate-y-1/2 data-[side=top]:-bottom-2.5" /> 60 + </TooltipPrimitive.Popup> 61 + </TooltipPrimitive.Positioner> 62 + </TooltipPrimitive.Portal> 63 + ) 64 + } 65 + 66 + export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
+94
demo/frontend/src/index.css
··· 1 + @import "tw-animate-css"; 2 + @import "shadcn/tailwind.css"; 3 + @import "@fontsource-variable/jetbrains-mono"; 4 + @tailwind base; 5 + @tailwind components; 6 + @tailwind utilities; 7 + 8 + @layer base { 9 + .theme { 10 + --font-heading: var(--font-mono); 11 + --font-mono: 'JetBrains Mono Variable', monospace; 12 + } 13 + :root { 14 + --background: oklch(1 0 0); 15 + --foreground: oklch(0.145 0 0); 16 + --card: oklch(1 0 0); 17 + --card-foreground: oklch(0.145 0 0); 18 + --popover: oklch(1 0 0); 19 + --popover-foreground: oklch(0.145 0 0); 20 + --primary: oklch(0.205 0 0); 21 + --primary-foreground: oklch(0.985 0 0); 22 + --secondary: oklch(0.97 0 0); 23 + --secondary-foreground: oklch(0.205 0 0); 24 + --muted: oklch(0.97 0 0); 25 + --muted-foreground: oklch(0.556 0 0); 26 + --accent: oklch(0.97 0 0); 27 + --accent-foreground: oklch(0.205 0 0); 28 + --destructive: oklch(0.577 0.245 27.325); 29 + --border: oklch(0.922 0 0); 30 + --input: oklch(0.922 0 0); 31 + --ring: oklch(0.708 0 0); 32 + --chart-1: oklch(0.87 0 0); 33 + --chart-2: oklch(0.556 0 0); 34 + --chart-3: oklch(0.439 0 0); 35 + --chart-4: oklch(0.371 0 0); 36 + --chart-5: oklch(0.269 0 0); 37 + --radius: 0.625rem; 38 + --sidebar: oklch(0.985 0 0); 39 + --sidebar-foreground: oklch(0.145 0 0); 40 + --sidebar-primary: oklch(0.205 0 0); 41 + --sidebar-primary-foreground: oklch(0.985 0 0); 42 + --sidebar-accent: oklch(0.97 0 0); 43 + --sidebar-accent-foreground: oklch(0.205 0 0); 44 + --sidebar-border: oklch(0.922 0 0); 45 + --sidebar-ring: oklch(0.708 0 0); 46 + } 47 + .dark { 48 + --background: oklch(0.145 0 0); 49 + --foreground: oklch(0.985 0 0); 50 + --card: oklch(0.205 0 0); 51 + --card-foreground: oklch(0.985 0 0); 52 + --popover: oklch(0.205 0 0); 53 + --popover-foreground: oklch(0.985 0 0); 54 + --primary: oklch(0.922 0 0); 55 + --primary-foreground: oklch(0.205 0 0); 56 + --secondary: oklch(0.269 0 0); 57 + --secondary-foreground: oklch(0.985 0 0); 58 + --muted: oklch(0.269 0 0); 59 + --muted-foreground: oklch(0.708 0 0); 60 + --accent: oklch(0.269 0 0); 61 + --accent-foreground: oklch(0.985 0 0); 62 + --destructive: oklch(0.704 0.191 22.216); 63 + --border: oklch(1 0 0 / 10%); 64 + --input: oklch(1 0 0 / 15%); 65 + --ring: oklch(0.556 0 0); 66 + --chart-1: oklch(0.87 0 0); 67 + --chart-2: oklch(0.556 0 0); 68 + --chart-3: oklch(0.439 0 0); 69 + --chart-4: oklch(0.371 0 0); 70 + --chart-5: oklch(0.269 0 0); 71 + --sidebar: oklch(0.205 0 0); 72 + --sidebar-foreground: oklch(0.985 0 0); 73 + --sidebar-primary: oklch(0.488 0.243 264.376); 74 + --sidebar-primary-foreground: oklch(0.985 0 0); 75 + --sidebar-accent: oklch(0.269 0 0); 76 + --sidebar-accent-foreground: oklch(0.985 0 0); 77 + --sidebar-border: oklch(1 0 0 / 10%); 78 + --sidebar-ring: oklch(0.556 0 0); 79 + } 80 + * { 81 + border-color: var(--border); 82 + outline-color: var(--ring); 83 + } 84 + body { 85 + background-color: var(--background); 86 + color: var(--foreground); 87 + } 88 + button:not(:disabled), [role="button"]:not(:disabled) { 89 + cursor: pointer; 90 + } 91 + html { 92 + font-family: 'JetBrains Mono Variable', monospace; 93 + } 94 + }
+6
demo/frontend/src/lib/utils.ts
··· 1 + import { clsx, type ClassValue } from "clsx" 2 + import { twMerge } from "tailwind-merge" 3 + 4 + export function cn(...inputs: ClassValue[]) { 5 + return twMerge(clsx(inputs)) 6 + }
+25
demo/frontend/src/main.tsx
··· 1 + import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; 2 + import { StrictMode } from "react"; 3 + import { createRoot } from "react-dom/client"; 4 + import { BrowserRouter } from "react-router-dom"; 5 + import App from "@/App"; 6 + import "@/index.css"; 7 + 8 + const queryClient = new QueryClient({ 9 + defaultOptions: { 10 + queries: { 11 + staleTime: 60_000, 12 + retry: 1, 13 + }, 14 + }, 15 + }); 16 + 17 + createRoot(document.getElementById("root")!).render( 18 + <StrictMode> 19 + <QueryClientProvider client={queryClient}> 20 + <BrowserRouter> 21 + <App /> 22 + </BrowserRouter> 23 + </QueryClientProvider> 24 + </StrictMode>, 25 + );
+236
demo/frontend/src/pages/ExplorerPage.tsx
··· 1 + import { MagnifyingGlass } from "@phosphor-icons/react"; 2 + import { useCallback, useEffect, useRef, useState } from "react"; 3 + import { useNavigate, useParams } from "react-router-dom"; 4 + import { useNeighborhood, useSearch } from "@/api/client"; 5 + import type { GraphData, SearchFilters } from "@/api/types"; 6 + import { Button } from "@/components/ui/button"; 7 + import FilterPanel from "@/components/FilterPanel"; 8 + import GraphVisualization from "@/components/GraphVisualization"; 9 + import NodeSidebar from "@/components/NodeSidebar"; 10 + import PaperCard from "@/components/PaperCard"; 11 + import SearchBar from "@/components/SearchBar"; 12 + 13 + const MAX_GRAPH_NODES = 500; 14 + 15 + export default function ExplorerPage() { 16 + const { nodeId: paramNodeId } = useParams<{ nodeId: string }>(); 17 + const navigate = useNavigate(); 18 + 19 + const [focusNodeId, setFocusNodeId] = useState<number | undefined>( 20 + paramNodeId ? Number(paramNodeId) : undefined, 21 + ); 22 + const [selectedNode, setSelectedNode] = useState<{ 23 + id: number; 24 + type: string; 25 + } | null>(null); 26 + const [searchQuery, setSearchQuery] = useState(""); 27 + const [searchActive, setSearchActive] = useState(!paramNodeId); 28 + const [filters, setFilters] = useState<SearchFilters>({ 29 + publishedOnly: false, 30 + }); 31 + const [mergedGraph, setMergedGraph] = useState<GraphData>({ 32 + nodes: [], 33 + edges: [], 34 + }); 35 + const containerRef = useRef<HTMLDivElement>(null); 36 + const [dimensions, setDimensions] = useState({ width: 800, height: 600 }); 37 + 38 + const { data: neighbourhood } = useNeighborhood(focusNodeId, 2); 39 + const { data: searchResults } = useSearch(searchQuery, 20, "abstract_embedding", filters); 40 + 41 + useEffect(() => { 42 + if (!neighbourhood || !focusNodeId) return; 43 + const node = neighbourhood.nodes.find((n) => n.id === focusNodeId); 44 + if (node && (!selectedNode || selectedNode.id !== focusNodeId)) { 45 + setSelectedNode({ id: node.id, type: node.type }); 46 + } 47 + }, [neighbourhood, focusNodeId, selectedNode]); 48 + 49 + useEffect(() => { 50 + if (!neighbourhood) return; 51 + setMergedGraph((prev) => { 52 + const existingNodeIds = new Set(prev.nodes.map((n) => n.id)); 53 + const existingEdgeIds = new Set(prev.edges.map((e) => e.id)); 54 + const newNodes = neighbourhood.nodes.filter( 55 + (n) => !existingNodeIds.has(n.id), 56 + ); 57 + const newEdges = neighbourhood.edges.filter( 58 + (e) => !existingEdgeIds.has(e.id), 59 + ); 60 + if (newNodes.length === 0 && newEdges.length === 0) return prev; 61 + 62 + let allNodes = [...prev.nodes, ...newNodes]; 63 + let allEdges = [...prev.edges, ...newEdges]; 64 + 65 + if (allNodes.length > MAX_GRAPH_NODES) { 66 + allNodes = allNodes.slice(allNodes.length - MAX_GRAPH_NODES); 67 + const nodeIds = new Set(allNodes.map((n) => n.id)); 68 + allEdges = allEdges.filter( 69 + (e) => nodeIds.has(e.source) && nodeIds.has(e.target), 70 + ); 71 + } 72 + 73 + return { nodes: allNodes, edges: allEdges }; 74 + }); 75 + }, [neighbourhood]); 76 + 77 + useEffect(() => { 78 + const el = containerRef.current; 79 + if (!el) return; 80 + const ro = new ResizeObserver((entries) => { 81 + const entry = entries[0]; 82 + if (entry) { 83 + setDimensions({ 84 + width: entry.contentRect.width, 85 + height: entry.contentRect.height, 86 + }); 87 + } 88 + }); 89 + ro.observe(el); 90 + return () => ro.disconnect(); 91 + }, []); 92 + 93 + const selectNode = useCallback( 94 + (nodeId: number) => { 95 + setFocusNodeId(nodeId); 96 + setSearchActive(false); 97 + setSearchQuery(""); 98 + navigate(`/explorer/${nodeId}`, { replace: true }); 99 + 100 + const existing = mergedGraph.nodes.find((n) => n.id === nodeId); 101 + if (existing) { 102 + setSelectedNode({ id: existing.id, type: existing.type }); 103 + } 104 + }, 105 + [navigate, mergedGraph.nodes], 106 + ); 107 + 108 + const handleNodeClick = useCallback( 109 + (nodeId: number, nodeType: string) => { 110 + setFocusNodeId(nodeId); 111 + setSelectedNode({ id: nodeId, type: nodeType }); 112 + setSearchActive(false); 113 + setSearchQuery(""); 114 + navigate(`/explorer/${nodeId}`, { replace: true }); 115 + }, 116 + [navigate], 117 + ); 118 + 119 + const handleSearch = useCallback((_q: string, _t: string) => { 120 + setSearchQuery(_q); 121 + setSearchActive(true); 122 + }, []); 123 + 124 + const handleReset = useCallback(() => { 125 + setMergedGraph({ nodes: [], edges: [] }); 126 + setFocusNodeId(undefined); 127 + setSelectedNode(null); 128 + setSearchActive(true); 129 + navigate("/explorer", { replace: true }); 130 + }, [navigate]); 131 + 132 + const handleCloseSidebar = useCallback(() => { 133 + setSelectedNode(null); 134 + }, []); 135 + 136 + const sidebarWidth = selectedNode ? 380 : 0; 137 + const graphWidth = dimensions.width - sidebarWidth; 138 + 139 + return ( 140 + <div className="flex h-screen flex-col"> 141 + <div ref={containerRef} className="relative flex-1 flex overflow-hidden"> 142 + <div className="flex-1 relative"> 143 + {mergedGraph.nodes.length > 0 ? ( 144 + <GraphVisualization 145 + data={mergedGraph} 146 + width={graphWidth > 0 ? graphWidth : dimensions.width} 147 + height={dimensions.height} 148 + selectedNodeId={selectedNode?.id} 149 + onNodeClick={handleNodeClick} 150 + /> 151 + ) : ( 152 + <div className="flex h-full items-center justify-center"> 153 + <div className="text-center"> 154 + <MagnifyingGlass className="mx-auto mb-2 size-6 text-muted-foreground/30" /> 155 + <p className="text-xs text-muted-foreground"> 156 + Search for a paper to start exploring 157 + </p> 158 + </div> 159 + </div> 160 + )} 161 + 162 + <div className="absolute top-3 left-3 right-3 z-10 pointer-events-none"> 163 + <div className="mx-auto max-w-xl pointer-events-auto"> 164 + <div className="flex gap-2"> 165 + <div className="flex-1"> 166 + <SearchBar onSearch={handleSearch} /> 167 + </div> 168 + {mergedGraph.nodes.length > 0 && ( 169 + <Button variant="outline" size="sm" onClick={handleReset}> 170 + Reset 171 + </Button> 172 + )} 173 + </div> 174 + <div className="mt-1.5"> 175 + <FilterPanel filters={filters} onChange={setFilters} /> 176 + </div> 177 + 178 + {searchActive && 179 + searchResults && 180 + searchResults.results.length > 0 && ( 181 + <div className="mt-2 max-h-[60vh] overflow-y-auto border bg-background/90 backdrop-blur-md p-2 space-y-1.5"> 182 + <p className="text-[10px] text-muted-foreground px-1"> 183 + {searchResults.results.length} results 184 + </p> 185 + {searchResults.results.map((p) => ( 186 + <PaperCard 187 + key={p.node_id} 188 + paper={p} 189 + onNavigate={selectNode} 190 + /> 191 + ))} 192 + </div> 193 + )} 194 + </div> 195 + </div> 196 + </div> 197 + 198 + {selectedNode && ( 199 + <div className="w-[380px] shrink-0 h-full overflow-hidden"> 200 + <NodeSidebar 201 + key={selectedNode.id} 202 + nodeId={selectedNode.id} 203 + nodeType={selectedNode.type} 204 + onClose={handleCloseSidebar} 205 + onNavigate={selectNode} 206 + /> 207 + </div> 208 + )} 209 + </div> 210 + 211 + <div className="border-t bg-card/50 px-4 py-1.5 shrink-0"> 212 + <div className="flex items-center gap-3 text-[10px] text-muted-foreground"> 213 + <span className="tabular-nums"> 214 + {mergedGraph.nodes.length} nodes, {mergedGraph.edges.length} edges 215 + </span> 216 + <span className="flex items-center gap-1"> 217 + <span className="inline-block h-2 w-2 rounded-full bg-[#3b82f6]" /> 218 + Paper 219 + </span> 220 + <span className="flex items-center gap-1"> 221 + <span className="inline-block h-2 w-2 rounded-full bg-[#22c55e]" /> 222 + Author 223 + </span> 224 + <span className="flex items-center gap-1"> 225 + <span className="inline-block h-1 w-3 rounded bg-[#64748b]" /> 226 + Cites 227 + </span> 228 + <span className="flex items-center gap-1"> 229 + <span className="inline-block h-1 w-3 rounded bg-[#f97316]" /> 230 + Authored 231 + </span> 232 + </div> 233 + </div> 234 + </div> 235 + ); 236 + }
+22
demo/frontend/src/vite-env.d.ts
··· 1 + /// <reference types="vite/client" /> 2 + 3 + declare module "react-force-graph-2d" { 4 + interface ForceGraphProps { 5 + graphData?: { nodes: object[]; links: object[] }; 6 + width?: number; 7 + height?: number; 8 + backgroundColor?: string; 9 + nodeCanvasObject?: (node: never, ctx: CanvasRenderingContext2D, globalScale: number) => void; 10 + linkCanvasObject?: (link: never, ctx: CanvasRenderingContext2D, globalScale: number) => void; 11 + onNodeClick?: (node: never, event: MouseEvent) => void; 12 + onEngineStop?: () => void; 13 + nodeLabel?: string | ((node: never) => string); 14 + cooldownTicks?: number; 15 + enableZoomInteraction?: boolean; 16 + enablePanInteraction?: boolean; 17 + [key: string]: unknown; 18 + } 19 + 20 + const ForceGraph2D: React.ForwardRefExoticComponent<ForceGraphProps & React.RefAttributes<unknown>>; 21 + export default ForceGraph2D; 22 + }
+70
demo/frontend/tailwind.config.js
··· 1 + /** @type {import('tailwindcss').Config} */ 2 + export default { 3 + content: ["./index.html", "./src/**/*.{ts,tsx}"], 4 + darkMode: "class", 5 + theme: { 6 + extend: { 7 + colors: { 8 + background: "var(--background)", 9 + foreground: "var(--foreground)", 10 + card: { 11 + DEFAULT: "var(--card)", 12 + foreground: "var(--card-foreground)", 13 + }, 14 + popover: { 15 + DEFAULT: "var(--popover)", 16 + foreground: "var(--popover-foreground)", 17 + }, 18 + primary: { 19 + DEFAULT: "var(--primary)", 20 + foreground: "var(--primary-foreground)", 21 + }, 22 + secondary: { 23 + DEFAULT: "var(--secondary)", 24 + foreground: "var(--secondary-foreground)", 25 + }, 26 + muted: { 27 + DEFAULT: "var(--muted)", 28 + foreground: "var(--muted-foreground)", 29 + }, 30 + accent: { 31 + DEFAULT: "var(--accent)", 32 + foreground: "var(--accent-foreground)", 33 + }, 34 + destructive: { 35 + DEFAULT: "var(--destructive)", 36 + foreground: "var(--destructive-foreground)", 37 + }, 38 + border: "var(--border)", 39 + input: "var(--input)", 40 + ring: "var(--ring)", 41 + chart: { 42 + 1: "var(--chart-1)", 43 + 2: "var(--chart-2)", 44 + 3: "var(--chart-3)", 45 + 4: "var(--chart-4)", 46 + 5: "var(--chart-5)", 47 + }, 48 + sidebar: { 49 + DEFAULT: "var(--sidebar)", 50 + foreground: "var(--sidebar-foreground)", 51 + primary: "var(--sidebar-primary)", 52 + "primary-foreground": "var(--sidebar-primary-foreground)", 53 + accent: "var(--sidebar-accent)", 54 + "accent-foreground": "var(--sidebar-accent-foreground)", 55 + border: "var(--sidebar-border)", 56 + ring: "var(--sidebar-ring)", 57 + }, 58 + }, 59 + borderRadius: { 60 + lg: "var(--radius)", 61 + md: "calc(var(--radius) - 2px)", 62 + sm: "calc(var(--radius) - 4px)", 63 + }, 64 + fontFamily: { 65 + mono: ["'JetBrains Mono Variable'", "monospace"], 66 + }, 67 + }, 68 + }, 69 + plugins: [], 70 + };
+24
demo/frontend/tsconfig.json
··· 1 + { 2 + "compilerOptions": { 3 + "target": "ES2022", 4 + "lib": ["ES2022", "DOM", "DOM.Iterable"], 5 + "module": "ESNext", 6 + "skipLibCheck": true, 7 + "moduleResolution": "bundler", 8 + "allowImportingTsExtensions": true, 9 + "isolatedModules": true, 10 + "moduleDetection": "force", 11 + "noEmit": true, 12 + "jsx": "react-jsx", 13 + "strict": true, 14 + "noUnusedLocals": true, 15 + "noUnusedParameters": true, 16 + "noFallthroughCasesInSwitch": true, 17 + "noUncheckedIndexedAccess": true, 18 + "baseUrl": ".", 19 + "paths": { 20 + "@/*": ["./src/*"] 21 + } 22 + }, 23 + "include": ["src"] 24 + }
+17
demo/frontend/vite.config.ts
··· 1 + import path from "path"; 2 + import { defineConfig } from "vite"; 3 + import react from "@vitejs/plugin-react"; 4 + 5 + export default defineConfig({ 6 + plugins: [react()], 7 + resolve: { 8 + alias: { 9 + "@": path.resolve(__dirname, "./src"), 10 + }, 11 + }, 12 + server: { 13 + proxy: { 14 + "/api": "http://localhost:8000", 15 + }, 16 + }, 17 + });
+2 -19
lib/gvecdb.ml
··· 153 153 (fun () -> 154 154 let node_count = Hnsw_mvcc.table_node_count table in 155 155 if node_count > 0 then begin 156 - (* build slot_id set from LMDB hnsw_slots for this tag *) 157 156 let lmdb_slots = 158 157 match tag_id_opt with 159 158 | None -> Hashtbl.create 0 ··· 167 166 tbl) 168 167 (Hashtbl.create (node_count * 2)) 169 168 in 170 - (* scan HNSW nodes and fix discrepancies *) 171 169 let hnsw_txn = Hnsw_mvcc.begin_write mvcc in 172 170 let changed = ref false in 173 171 for slot_id = 0 to node_count - 1 do ··· 190 188 in 191 189 Hnsw_mvcc.set_entry_point hnsw_txn ~entry_point:ep 192 190 ~max_level:level; 193 - (* commit result matters for crash consistency *) 194 191 match Hnsw_mvcc.commit mvcc hnsw_txn with 195 192 | Ok () -> () 196 193 | Error e -> ··· 211 208 let hnsw_epoch = Hnsw_mvcc.get_epoch mvcc in 212 209 let lmdb_epoch = get_lmdb_hnsw_epoch t.db tag_name in 213 210 if hnsw_epoch <> lmdb_epoch then begin 214 - (* epoch mismatch crash between HNSW commit and LMDB commit. 215 - Reconcile HNSW to match LMDB (source of truth), then sync epochs *) 211 + (* LMDB is source of truth; mismatch means crash between HNSW commit and LMDB commit *) 216 212 reconcile_hnsw t tag_name; 217 213 let current_epoch = Hnsw_mvcc.get_epoch mvcc in 218 214 ignore ··· 669 665 (Lmdb.Map.get t.db.hnsw_slots ?txn slot_key)) 670 666 with Not_found | Lmdb.Not_found | Invalid_argument _ -> None 671 667 in 672 - (* mark deleted in MVCC file *) 673 668 (match (slot_id_opt, Hashtbl.find_opt t.hnsw_mvcc vector_tag) with 674 669 | Some slot_id, Some mvcc -> 675 670 let table = Hnsw_mvcc.begin_read mvcc in ··· 697 692 | Error _ -> Hnsw_mvcc.rollback mvcc hnsw_txn) 698 693 | None -> ()) 699 694 | _ -> ()); 700 - (* remove slot mapping from LMDB *) 701 695 (try Lmdb.Map.remove t.db.hnsw_slots ?txn slot_key 702 696 with Not_found | Lmdb.Not_found -> ()); 703 - (* remove from LMDB *) 704 697 Lmdb.Map.remove t.db.vector_owners ?txn key; 705 698 let index_key = 706 699 Keys.encode_vector_index_bs ~owner_kind ~owner_id ~vector_tag_id ··· 718 711 let delete_vector (t : t) ~txn (vector_id : vector_id) : (unit, error) result = 719 712 delete_vector_internal t ~txn vector_id 720 713 721 - (* delete all vectors attached to an owner (node or edge). Used for cascade 722 - deletes. silently ignores already-deleted vectors *) 723 714 let delete_vectors_for_owner (t : t) ?txn (owner_kind : owner_kind) 724 715 (owner_id : id) : (unit, error) result = 725 716 let prefix = Keys.encode_vector_index_prefix_bs ~owner_kind ~owner_id () in ··· 751 742 752 743 let delete_node (t : t) ?txn (node_id : node_id) : (unit, error) result = 753 744 let key = Keys.encode_id_bs node_id in 754 - (* check node exists first *) 755 745 let node_meta_exists = 756 746 try 757 747 let _ = Lmdb.Map.get t.db.node_meta ?txn key in ··· 761 751 if not node_meta_exists then Error (Node_not_found node_id) 762 752 else 763 753 try 764 - (* 1. delete all vectors directly attached to this node *) 765 754 let* () = delete_vectors_for_owner t ?txn Node node_id in 766 - (* 2. get all outbound edges and delete them (with their vectors) *) 767 755 let* outbound_edges = get_outbound_edges t ?txn node_id () in 768 756 let rec delete_edges = function 769 757 | [] -> Ok () ··· 773 761 delete_edges rest 774 762 in 775 763 let* () = delete_edges outbound_edges in 776 - (* 3. get all inbound edges and delete them (with their vectors) *) 777 764 let* inbound_edges = get_inbound_edges t ?txn node_id () in 778 765 let* () = delete_edges inbound_edges in 779 - (* 4. delete the node itself *) 780 766 Lmdb.Map.remove t.db.nodes ?txn key; 781 767 Lmdb.Map.remove t.db.node_meta ?txn key; 782 768 Ok () ··· 852 838 (metric_to_string metric) 853 839 (metric_to_string index_metric))) 854 840 else 855 - (* capture MVCC snapshot *) 856 841 let table = Hnsw_mvcc.begin_read mvcc in 857 842 Fun.protect 858 843 ~finally:(fun () -> Hnsw_mvcc.end_read mvcc table) ··· 885 870 in 886 871 let results = Hnsw_mvcc.search_mvcc ctx ~k ~ef in 887 872 888 - (* convert to knn_result *) 889 873 let results_with_info = 890 874 List.filter_map 891 875 (fun (slot_id, dist) -> ··· 925 909 if vtag_id = target_tag_id then (vid, offset) :: acc else acc) 926 910 [] 927 911 in 912 + (* Rw cursor for in-place deletion; can't use fold_prefix *) 928 913 let clear_slot_mappings tag_id = 929 914 let prefix = Keys.encode_id_bs tag_id in 930 915 let prefix_len = Bigstring.length prefix in ··· 966 951 (match tag_id_opt with 967 952 | Some tag_id -> clear_slot_mappings tag_id 968 953 | None -> ()); 969 - (* create fresh MVCC file *) 970 954 match Hnsw_mvcc.create file_path ~metric ~params:hnsw_params () with 971 955 | Error e -> Error (Storage_error (Hnsw_mvcc.error_to_string e)) 972 956 | Ok mvcc -> ··· 976 960 Ok () 977 961 end 978 962 else begin 979 - (* get dimension from first vector *) 980 963 let dim = 981 964 match List.hd vectors with 982 965 | _, offset -> (
+2 -20
lib/hnsw_mvcc.ml
··· 203 203 let fd = Unix.openfile path Unix.[ O_RDWR; O_CREAT; O_TRUNC ] 0o644 in 204 204 Unix.ftruncate fd initial_file_size; 205 205 let mmap = create_mmap fd initial_file_size in 206 - (* superblock *) 207 206 for i = 0 to 7 do 208 207 Bigstringaf.set mmap (sb_magic_off + i) (String.get magic i) 209 208 done; ··· 218 217 Bigstringaf.set_int64_le mmap sb_ml_off (Int64.bits_of_float params.ml); 219 218 Bigstringaf.set_int32_le mmap sb_checksum_off 220 219 (Hnsw_page.crc32 mmap 0 sb_checksum_off); 221 - (* empty page tables *) 222 220 let empty = 223 221 { 224 222 epoch = 1L; ··· 446 444 try 447 445 update_layout t txn.new_dimension; 448 446 let layout = t.layout in 449 - (* Flush overlay to dirty_pages *) 450 447 (match txn.overlay with 451 448 | None -> () 452 449 | Some ov -> ··· 468 465 Hashtbl.fold (fun pid page acc -> (pid, page) :: acc) txn.dirty_pages [] 469 466 |> List.sort (fun (a, _) (b, _) -> compare a b) 470 467 in 471 - (* allocate space: append-only, old pages are abandoned *) 468 + (* append-only: old pages are abandoned, not reclaimed *) 472 469 let write_offset = ref txn.base_table.max_data_offset in 473 470 let new_offsets = 474 471 Array.init new_page_count (fun pid -> ··· 485 482 dirty_list 486 483 in 487 484 let new_max_offset = !write_offset in 488 - (* grow file if needed *) 489 485 let needed = Int64.to_int new_max_offset in 490 486 let initial_file_size = initial_file_size_for_layout layout in 491 487 if needed > t.file_size then 492 488 grow_file t (max (t.file_size * 2) (needed + initial_file_size)); 493 - (* write pages *) 494 489 List.iter 495 490 (fun (off, page) -> 496 491 Hnsw_page.blit_page_to_mmap page t.mmap 497 492 ~dst_off:(Int64.to_int off) ~len:layout.page_size) 498 493 allocations; 499 - (* build new page table *) 500 494 let new_table = 501 495 { 502 496 epoch = Int64.add txn.base_epoch 1L; ··· 513 507 let shadow = 1 - t.active_which in 514 508 write_page_table t.mmap shadow new_table; 515 509 Msync.msync t.mmap; 516 - (* atomic swap *) 510 + (* atomic swap: linearization point of the commit *) 517 511 Bigstringaf.set_int64_le t.mmap sb_active_root_off (Int64.of_int shadow); 518 512 Bigstringaf.set_int32_le t.mmap sb_checksum_off 519 513 (Hnsw_page.crc32 t.mmap 0 sb_checksum_off); ··· 550 544 let table_max_level table = table.max_level 551 545 let table_node_count table = table.node_count 552 546 553 - (* Search context for MVCC-based search *) 554 547 type search_context = { 555 548 mvcc : t; 556 549 table : page_table; ··· 567 560 | _ when n <= 0 -> List.rev acc 568 561 | x :: rest -> take_n (n - 1) (x :: acc) rest 569 562 570 - (* Beam search on MVCC snapshot - single layer *) 571 563 let search_layer_mvcc ?(visited_size = 0) ctx ~entry_points ~ef ~layer = 572 564 let n_slots = max visited_size ctx.table.node_count in 573 565 let visited = Bitset.create (n_slots + 1) in ··· 614 606 else ctx.dist_from_offset (fo + Hnsw_page.node_vec_header_off)) 615 607 in 616 608 617 - (* Initialize with entry points *) 618 609 List.iter 619 610 (fun ep -> 620 611 if not (Bitset.test_and_set visited ep) then begin ··· 626 617 end) 627 618 entry_points; 628 619 629 - (* Expand candidates *) 630 620 let rec expand () = 631 621 match Int_heap.pop candidates with 632 622 | None -> () ··· 646 636 end 647 637 end 648 638 in 649 - (* Check overlay first, then fall back to mmap accessors *) 650 639 let from_overlay = 651 640 match overlay with 652 641 | Some ov -> Hashtbl.find_opt ov c_slot ··· 674 663 expand (); 675 664 Int_topk.to_sorted_list results |> List.map (fun (dist, slot) -> (slot, dist)) 676 665 677 - (* Full HNSW search on MVCC snapshot *) 678 666 let search_mvcc ctx ~k ~ef = 679 667 let table = ctx.table in 680 668 if table.entry_point < 0 then [] ··· 712 700 if remaining > 0 then selected @ take_n remaining [] discarded 713 701 else selected 714 702 715 - (* Insert into MVCC with inline vector data. 716 - inline_vec: the raw vector bytes (16-byte header + dim*4 float32 data) 717 - compute_distance: takes a byte offset into the HNSW mmap (at vec header) 718 - and returns distance to the query vector 719 - compute_pairwise_distance: takes two vector_ids (int64) and returns the 720 - distance between them (used for neighbor pruning, reads from vector_file) *) 721 703 let insert_mvcc t txn ~vector_id ~inline_vec ~compute_distance 722 704 ~dist_from_inline ~compute_pairwise_distance ~dimension = 723 705 let params = t.params in
-1
lib/schema_registry.ml
··· 310 310 { type_name; kind; data_word_count = dwc; pointer_count = pc; 311 311 fields; field_by_name } 312 312 313 - (* In-memory cache -- keyed by type_name *) 314 313 let schema_cache : (string, registered_schema) Hashtbl.t = Hashtbl.create 16 315 314 316 315 let register_schema_from_capnp (db : t)
-2
lib/store.ml
··· 4 4 let default_map_size = 10 * 1024 * 1024 * 1024 5 5 6 6 let vector_file_path (lmdb_path : string) : string = 7 - (* LMDB path is the .db file, vector file is alongside it *) 8 7 let base = Filename.remove_extension lmdb_path in 9 8 base ^ ".vectors" 10 9 ··· 17 16 Filename.concat dir (tag ^ ".hnsw") 18 17 19 18 let create ?(map_size = default_map_size) (path : string) : (t, error) result = 20 - (* First try to create the vector file *) 21 19 let vec_path = vector_file_path path in 22 20 match Vector_file.create vec_path with 23 21 | Error e -> Error (Storage_error (Vector_file.error_to_string e))
+3 -3
server/main.ml
··· 9 9 in 10 10 Gvecdb.load_all_schemas db; 11 11 Switch.run @@ fun sw -> 12 - let service_id = Capnp_rpc_unix.Vat_config.derived_id config "main" in 13 12 let service = Gvecdb_rpc.Gvecdb_service.local db in 13 + let service_id = 14 + Capnp_rpc_net.Restorer.Id.public "" in 14 15 let restore = Capnp_rpc_net.Restorer.single service_id service in 15 16 let vat = Capnp_rpc_unix.serve ~sw ~restore config in 16 17 (match Capnp_rpc_unix.Cap_file.save_service vat service_id "gvecdb.cap" with 17 18 | Error (`Msg m) -> traceln "Warning: could not save cap file: %s" m 18 19 | Ok () -> traceln "Saved capability to gvecdb.cap"); 19 - let uri = Vat.sturdy_uri vat service_id in 20 - traceln "gvecdb server running at:@.%a" Uri.pp_hum uri; 20 + traceln "gvecdb server running"; 21 21 traceln "Database: %s" db_path; 22 22 Fiber.await_cancel () 23 23