From b25a9c90a214fee8e2936ec47e88ac78424f92aa Mon Sep 17 00:00:00 2001 From: Alex Merose Date: Thu, 28 May 2026 14:29:35 -0700 Subject: [PATCH 1/3] Contact Doc v2 Generation with Zephyr & Iris. --- .gitignore | 2 +- .../README.md | 239 ++ .../build_summary.py | 258 ++ .../cli.py | 433 ++++ .../generate.py | 307 +++ .../parse.py | 302 +++ .../pyproject.toml | 49 + .../summary_narrative.md | 17 + .../tests/__init__.py | 0 .../tests/conftest.py | 87 + .../tests/test_byte_identity.py | 104 + .../tests/test_cli.py | 162 ++ .../tests/test_parse.py | 79 + .../uv.lock | 2140 +++++++++++++++++ .../vocab.py | 68 + 15 files changed, 4246 insertions(+), 1 deletion(-) create mode 100644 experiments/exp5_data_contacts_and_distances_v2_zephyr/README.md create mode 100644 experiments/exp5_data_contacts_and_distances_v2_zephyr/build_summary.py create mode 100644 experiments/exp5_data_contacts_and_distances_v2_zephyr/cli.py create mode 100644 experiments/exp5_data_contacts_and_distances_v2_zephyr/generate.py create mode 100644 experiments/exp5_data_contacts_and_distances_v2_zephyr/parse.py create mode 100644 experiments/exp5_data_contacts_and_distances_v2_zephyr/pyproject.toml create mode 100644 experiments/exp5_data_contacts_and_distances_v2_zephyr/summary_narrative.md create mode 100644 experiments/exp5_data_contacts_and_distances_v2_zephyr/tests/__init__.py create mode 100644 experiments/exp5_data_contacts_and_distances_v2_zephyr/tests/conftest.py create mode 100644 experiments/exp5_data_contacts_and_distances_v2_zephyr/tests/test_byte_identity.py create mode 100644 experiments/exp5_data_contacts_and_distances_v2_zephyr/tests/test_cli.py create mode 100644 experiments/exp5_data_contacts_and_distances_v2_zephyr/tests/test_parse.py create mode 100644 experiments/exp5_data_contacts_and_distances_v2_zephyr/uv.lock create mode 100644 experiments/exp5_data_contacts_and_distances_v2_zephyr/vocab.py diff --git a/.gitignore b/.gitignore index 68cfadb..ae5cef9 100644 --- a/.gitignore +++ b/.gitignore @@ -190,7 +190,7 @@ cython_debug/ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. -# .idea/ +.idea/ # Abstra # Abstra is an AI-powered process automation framework. diff --git a/experiments/exp5_data_contacts_and_distances_v2_zephyr/README.md b/experiments/exp5_data_contacts_and_distances_v2_zephyr/README.md new file mode 100644 index 0000000..5c26838 --- /dev/null +++ b/experiments/exp5_data_contacts_and_distances_v2_zephyr/README.md @@ -0,0 +1,239 @@ +--- +marinfold_experiment: + issue: 5 + title: "implement data generation on zephyr" + kind: data + branch: u/alxmrs/exp5 +--- + +# implement data generation on zephyr + +**Issue:** [#5](https://github.com/Open-Athena/MarinFold/issues/5) · **Kind:** `data` · **Branch:** `u/alxmrs/exp5` + +## Question + +Can we run the v2 training-doc generator (`contacts-and-distances-v2`) +at scale on the marin Iris cluster via Zephyr, ingesting the +[`timodonnell/afdb-1.6M`](https://huggingface.co/datasets/timodonnell/afdb-1.6M) +manifest and producing per-shard parquet outputs to GCS? + +## Hypothesis + +Yes — with the right tuning. Prior runs in exp34 surfaced three +specific levers that, together, should land 1.6M structures in +single-digit minutes: + +1. Don't ship the bulky `cif_content` cross-cloud. The manifest has a + `gcs_uri` column pointing at the public AFDB GCS bucket — parquet + is columnar, so reading just `[entry_id, gcs_uri]` is ~160 KB/shard + instead of ~70 MB. +2. Per-shard I/O is dominated by ~2,000 small in-region GCS GETs. + Concurrent fetches inside a shard (a thread pool around the per-row + parse) overlap the GETs with gemmi's parse, which releases the GIL + during the C++ tokenize. +3. The per-shard CPU is *single-threaded* in Python — gemmi's + per-attribute access dominates. Honor that by sizing + `--worker-cpu 1` and scaling out via `--max-workers` rather than + over-allocating per-worker cores. + +## Background + +- Issue [#5](https://github.com/Open-Athena/MarinFold/issues/5) is the + Zephyr-runner request. The dataset target was originally v1, but the + v2 endorsement is in [this comment](https://github.com/Open-Athena/MarinFold/issues/5#issuecomment-4557947163): + *"We have a new document type, `contacts-and-distances-v2` that we + can use as a test for running document generation on zephyr."* +- Document spec + the canonical v2 generation algorithm: + [`exp34_document_structures_contacts_and_distances_v2`](../exp34_document_structures_contacts_and_distances_v2/). + exp34 stays untouched (per the PR #19 review); exp5 is the at-scale + Zephyr runner that re-implements the spec from scratch for + performance, with a byte-identity test pinning the contract. +- The "no bulk-numpy extraction in gemmi's Python binding" finding — + and the maintainer's `pos.tolist()` + `Model.all()` workaround — + comes from [project-gemmi/gemmi#314](https://github.com/project-gemmi/gemmi/issues/314). + Both optimizations are folded into `parse.py`. + +## Approach + +This is a fresh implementation. It calls out to nothing in exp34 at +runtime; the byte-identity contract is enforced by a test that +imports exp34's reference via `sys.path` and SHA-1-compares the +generated docs. + +### Design choices + +- **Columnar in-memory structure** (`parse.ParsedStructure`). + Per-residue arrays at the *structure* level: `plddt_per_residue: + float64[N]`, `cb_or_ca_xyz: float64[N, 3]`, plus a flat CSR atom + table (`atom_offsets: int32[N+1]`, `atom_name_id: uint8[T]`, + `atom_xyz: float64[T, 3]`). No backward compat with marinfold's + tuple-of-tuples `Residue.atoms` — designed to round-trip zero-copy + to `pyarrow.RecordBatch` if a precomputed-store experiment ever + needs it. +- **Single-pass parser** with the gemmi fast path: `Model.all()` flat + CRA iteration + `atom.pos.tolist()` (one nanobind call vs three + attribute accesses). Polymer-iteration fallback for non-AFDB + inputs. See `parse.py` for the determinism contract that keeps docs + byte-identical to exp34. +- **Vectorized contact eligibility.** `np.triu_indices(k=1)` over the + `cb_or_ca_xyz` array replaces a ~1.7 M-call Python `euclidean` + loop. Row-major order is preserved so `rng.sample` over each + contacts-by-mode list sees identical inputs to exp34. +- **URI-mode input is the default.** `--cif-uri-column` defaults to + `gcs_uri`. The bulky `cif_content` path is still available via + `--cif-text-column` for testing or datasets without URI columns. +- **In-shard concurrent fetches** via `Dataset.map_shard` + + `ThreadPoolExecutor` (`--fetch-concurrency`, default 32). This is + the new lever — the prior production run landed at ~128 s/shard + because the 2,000 per-row GCS GETs ran sequentially. The fetch + backend is `fsspec` + `gcsfs`; see "On obstore" below for why we + didn't switch. +- **Resource defaults bake in the lesson:** `--worker-cpu 1`, + `--worker-memory 4g`, `--max-workers` left to Zephyr's default of + 128 on a cluster (raise via the flag or `ZEPHYR_MAX_WORKERS`). +- **`--num-docs N` is a global cap on emitted documents**, collapses + to a single output file. For sharded output use a `{shard}` + placeholder in `--out` and drop `--num-docs`. + +### Output schema + +Every output row carries provenance so a generated doc can be traced +back to its source structure and audited against the published +reference dataset. Always present: + +| column | type | source | +|---|---|---| +| `entry_id` | string | passthrough from input manifest | +| `structure` | string | constant — the doc-structure name `"contacts-and-distances-v2"` | +| `document` | string | the v2 doc text | +| `sha1` | string | `sha1(document.encode())` — matches the `sha1` column in `timodonnell/protein-docs/contacts-and-distances-v1-5x`, so byte-equality with a published reference is one column compare | +| `seq_len` | int | number of residues the doc actually serialized (parsed-structure value, not the manifest's) | +| `global_plddt` | float | mean pLDDT used to pick the doc's pLDDT-bin token (parsed-structure value) | +| `contacts_emitted` | int | count of `` + `` + `` tokens in the doc | + +Opportunistic passthrough — any of the following manifest columns +that exist in the input parquet schema are copied verbatim onto +every row (afdb-1.6M carries them all; minimal test manifests +typically don't): + +`split`, `seq_cluster_id`, `struct_cluster_id`, `uniprot_accession`, +`tax_id`, `organism_name`, `gcs_uri`. + +`gcs_uri` is special: when `--cif-uri-column='gcs_uri'` (the default) +it's *both* the data source and a passthrough column, so the manifest +read deduplicates it before calling `load_parquet(columns=...)`. + +Schema detection happens once at submission time (peek the first +matching parquet file), so the output schema is stable across shards. + +### On obstore (and why we didn't switch) + +We evaluated [obstore](https://developmentseed.org/obstore/) (Rust +`object_store`-backed, advertised ~9× throughput vs `fsspec` on +parallel small-object GETs) as the per-row fetch backend. It works on +single-region GCS buckets like our output bucket +`gs://marin-us-central1/...`, but it **cannot read AFDB** as shipped +(obstore 0.9.5). The root cause is upstream: `object_store`'s HTTP +handler hard-requires a `Content-Length` header on every GCS GET +response, and AFDB cifs are stored with `Content-Encoding: gzip` so +GCS transparently decompresses on the wire (visible in the response +as `x-guploader-response-body-transformations: gunzipped`) and omits +`Content-Length`. The documented client-side workaround +(`Accept-Encoding: gzip`) can only be set via +`client_options.default_headers` today, and those headers leak into +the OAuth2 token fetch and break it. + +Variants we tried (all fail identically on AFDB): + +* `GCSStore.from_url` / `GCSStore(bucket=…)` / `.get_range` / + `.stream`, +* `client_options={"http1_only"/"http2_only"/"allow_http"}`, +* `client_options={"default_headers": {"Accept-Encoding": "gzip"}}` + — breaks the token fetch, +* static `credential_provider` + `default_headers` — Accept-Encoding + doesn't reach the GCS GET, +* `obstore.fsspec.register("gs")` + `fsspec.open(...)`, +* `FsspecStore("gs", ...)`. + +Until either `object_store` relaxes the `Content-Length` invariant or +obstore exposes per-request / per-bucket header overrides that don't +touch the auth flow, we stay on `fsspec/gcsfs` for the fetch. +Worth filing upstream the next time someone touches obstore. + +### Layout + +``` +experiments/exp5_data_contacts_and_distances_v2_zephyr/ +├── vocab.py # re-exports marinfold v1 vocab + appends V2_NEW_TOKENS +├── parse.py # columnar ParsedStructure + gemmi fast-path extractor +├── generate.py # vectorized v2 generation; byte-identical RNG stream vs exp34 +├── cli.py # `generate` (Zephyr pipeline) + `tokenizer` subcommands +├── pyproject.toml # marinfold + gemmi/numpy/pyarrow + marin-zephyr/fsspec/gcsfs/hf +└── tests/ + ├── test_parse.py # columnar shape + per-residue invariants + ├── test_byte_identity.py # SHA-1 match against exp34 reference + └── test_cli.py # end-to-end cli.cmd_generate smoke +``` + +### Running it locally + +```bash +uv sync --extra test + +# Build / save / push the v2 tokenizer. +uv run python cli.py tokenizer --save-local /tmp/tok-v2/ + +# Run the full test suite (incl. byte-identity vs exp34). +uv run pytest tests/ -v +``` + +### Generating on Iris + +In a dedicated terminal, connect to the cluster: + +```bash +uv run iris --cluster=marin cluster dashboard +``` + +**Smoke test** — a single parquet shard from the manifest, capped to 100 +docs, single output file: + +```bash +uv run iris --cluster=marin job run --cpu 1 --memory 2GB -- python cli.py generate --input "hf://datasets/timodonnell/afdb-1.6M/shard_000-999/shard_000000.parquet" --num-docs 100 --out "gs://marin-tmp-us-central1/marin-fold-tests/exp5-smoke.parquet" --worker-cpu 1 --worker-memory 4g --fetch-concurrency 32 +``` + +**Full run** — all 1.6M structures, one output parquet per input shard: + +```bash +uv run iris --cluster=marin job run --cpu 1 --memory 2GB -- python cli.py generate --input "hf://datasets/timodonnell/afdb-1.6M/**/*.parquet" --out "gs://marin-us-central1/protein-structure/MarinFold/exp5/corpus_v2-{shard:05d}-of-{total:05d}.parquet" --worker-cpu 1 --worker-memory 4g --worker-disk 64g --max-workers 512 --fetch-concurrency 32 +``` + +Each command must stay on **one line** — a trailing space after a +backslash silently truncates the command and the rest leaks to your +shell. Cancel a running job with +`uv run iris --cluster=marin job stop ` (find IDs via +`iris --cluster=marin job list`). + +## Success criteria + +1. **Byte-identity vs exp34.** Generated v2 docs are SHA-1-equal to + exp34's reference implementation for the same structure + entry_id, + across multiple seeds — covered by + `tests/test_byte_identity.py::test_doc_byte_identical_to_exp34`. +2. **Full 1.6M-structure generation completes** on the marin Iris + cluster, writing one parquet per input shard to GCS, with no shard + left unwritten and no `ZephyrWorkerError` propagating. +3. **Wall-clock improves over the prior runs** — the v2 production + runs from the exp34 session landed at ~14 min (sequential + `cif_content`) and ~8 min (`gcs_uri` mode without intra-shard + concurrency). With the thread-pool fetches the per-shard time + should drop below the prior ~128 s, and end-to-end wall should + come in well under ~5 min compute. + +## Results + +_(Fill in after the at-scale run completes.)_ + +## Conclusion + +_(Fill in once results are in.)_ diff --git a/experiments/exp5_data_contacts_and_distances_v2_zephyr/build_summary.py b/experiments/exp5_data_contacts_and_distances_v2_zephyr/build_summary.py new file mode 100644 index 0000000..d1d3bbd --- /dev/null +++ b/experiments/exp5_data_contacts_and_distances_v2_zephyr/build_summary.py @@ -0,0 +1,258 @@ +# Copyright The MarinFold Authors +# SPDX-License-Identifier: Apache-2.0 + +"""Build ``plots/summary.pdf`` — this experiment's living presentation. + +Run from the experiment dir:: + + uv run python build_summary.py # if the experiment has a pyproject + python build_summary.py # otherwise (pure-analysis dirs) + +Contract (see ``experiments/AGENTS.md`` for the full story): + +- Narrative section first: sourced from ``summary_narrative.md`` — + one ``## `` heading per slide, body text below it. Edit this as + the experiment progresses; it reflects what we're doing, why, and + the current state of the results. +- Plot appendix last: auto-discovered from ``plots/*.{png,jpg,jpeg}``. + Each plot's caption + generating script + args come from a + sidecar ``plots/..meta.json`` written by the plotting + script via :func:`save_plot_with_meta` below. +- Plots without a sidecar still appear, with a placeholder caption + nudging you to wire ``save_plot_with_meta`` in. + +Regeneration is meant to be fast: no analysis is rerun, only PNGs +and text are assembled. Build time scales linearly with slide count. +""" + +import json +import re +import sys +import textwrap +from dataclasses import dataclass, field +from pathlib import Path +from typing import Sequence + + +# --------------------------------------------------------------------------- +# Helper for plotting scripts to call when they save a plot. +# Importable without pulling in matplotlib (those imports are lazy in main()). +# --------------------------------------------------------------------------- + +def save_plot_with_meta( + fig, + path: str | Path, + *, + caption: str, + script: str | None = None, + args: Sequence[str] | None = None, + **savefig_kwargs, +) -> Path: + """Save ``fig`` to ``path`` plus a ``.meta.json`` sidecar. + + The sidecar carries the generating script's name, its argv, and a + human-readable caption. ``build_summary.py`` reads these when + assembling the slide appendix so the PDF can print, in small text + on each plot page, exactly how to rerun the script. + + Defaults: ``script`` = ``sys.argv[0]``, ``args`` = ``sys.argv[1:]``. + Any extra kwargs (``dpi``, ``transparent``, ...) pass through to + ``fig.savefig``. ``bbox_inches="tight"`` is the default unless you + override it. + """ + path = Path(path) + path.parent.mkdir(parents=True, exist_ok=True) + savefig_kwargs.setdefault("bbox_inches", "tight") + fig.savefig(path, **savefig_kwargs) + + script = script if script is not None else Path(sys.argv[0]).name + args = list(args) if args is not None else list(sys.argv[1:]) + + sidecar = path.with_suffix(path.suffix + ".meta.json") + sidecar.write_text(json.dumps( + {"script": script, "args": args, "caption": caption}, + indent=2, + )) + return path + + +# --------------------------------------------------------------------------- +# Module-level config. +# --------------------------------------------------------------------------- + +HERE = Path(__file__).resolve().parent +PLOTS_DIR = HERE / "plots" +NARRATIVE_PATH = HERE / "summary_narrative.md" +OUTPUT_PATH = PLOTS_DIR / "summary.pdf" + +# Letter landscape, in inches. +SLIDE_W, SLIDE_H = 13.33, 7.5 + +PLOT_EXTENSIONS = (".png", ".jpg", ".jpeg") + + +# --------------------------------------------------------------------------- +# Narrative parsing. +# --------------------------------------------------------------------------- + +@dataclass +class Slide: + title: str + body: str + + +def parse_narrative(path: Path) -> list[Slide]: + if not path.exists(): + return [Slide( + title="(narrative missing)", + body=( + f"Create `{path.name}` in the experiment dir.\n\n" + "One `## ` heading per slide. Keep it updated as the experiment progresses." + ), + )] + text = path.read_text() + slides: list[Slide] = [] + current_title: str | None = None + buf: list[str] = [] + for line in text.splitlines(): + m = re.match(r"^##\s+(.*)$", line) + if m: + if current_title is not None: + slides.append(Slide(title=current_title, body="\n".join(buf).strip())) + current_title = m.group(1).strip() + buf = [] + elif current_title is not None: + buf.append(line) + if current_title is not None: + slides.append(Slide(title=current_title, body="\n".join(buf).strip())) + if not slides: + return [Slide( + title="(empty narrative)", + body=f"Add `## ` headings to `{path.name}`.", + )] + return slides + + +# --------------------------------------------------------------------------- +# Plot discovery. +# --------------------------------------------------------------------------- + +@dataclass +class PlotEntry: + path: Path + caption: str + script: str + args: list[str] = field(default_factory=list) + + @property + def invocation(self) -> str: + if not self.args: + return self.script + return self.script + " " + " ".join(self.args) + + +def load_plots(plots_dir: Path) -> list[PlotEntry]: + if not plots_dir.exists(): + return [] + paths: list[Path] = [ + p for p in plots_dir.iterdir() + if p.suffix.lower() in PLOT_EXTENSIONS + ] + paths.sort(key=lambda p: p.name) + + entries: list[PlotEntry] = [] + for img in paths: + meta_path = img.with_suffix(img.suffix + ".meta.json") + if meta_path.exists(): + meta = json.loads(meta_path.read_text()) + caption = str(meta.get("caption", "")) + script = str(meta.get("script", "?")) + args = [str(a) for a in meta.get("args", [])] + else: + caption = ( + "(metadata missing — call `save_plot_with_meta(...)` in " + "the generating script; see build_summary.py)" + ) + script = "?" + args = [] + entries.append(PlotEntry(path=img, caption=caption, script=script, args=args)) + return entries + + +# --------------------------------------------------------------------------- +# PDF rendering. matplotlib is imported lazily inside main() so that +# importers of `save_plot_with_meta` don't pay the matplotlib startup cost. +# --------------------------------------------------------------------------- + +def _new_slide(plt): + fig = plt.figure(figsize=(SLIDE_W, SLIDE_H)) + fig.patch.set_facecolor("white") + return fig + + +def render_text_slide(plt, slide: Slide): + fig = _new_slide(plt) + fig.text(0.05, 0.92, slide.title, fontsize=26, fontweight="bold", va="top") + + paragraphs = [p.strip() for p in (slide.body or "").split("\n\n") if p.strip()] + wrapped = "\n\n".join(textwrap.fill(p, width=90) for p in paragraphs) + fig.text(0.05, 0.82, wrapped, fontsize=14, va="top") + return fig + + +def render_plot_slide(plt, mpimg, entry: PlotEntry): + fig = _new_slide(plt) + fig.text(0.05, 0.94, entry.path.name, fontsize=18, fontweight="bold", va="top") + if entry.caption: + fig.text( + 0.05, 0.88, + textwrap.fill(entry.caption, width=130), + fontsize=11, va="top", + ) + + ax = fig.add_axes([0.06, 0.10, 0.88, 0.74]) + ax.set_axis_off() + img = mpimg.imread(entry.path) + ax.imshow(img) + + fig.text( + 0.05, 0.04, + f"$ {entry.invocation}", + fontsize=8, family="monospace", color="#555", + ) + return fig + + +def main() -> int: + # Lazy imports so `from build_summary import save_plot_with_meta` + # stays cheap for plotting scripts. + import matplotlib.image as mpimg + import matplotlib.pyplot as plt + from matplotlib.backends.backend_pdf import PdfPages + + narrative_slides = parse_narrative(NARRATIVE_PATH) + plot_entries = load_plots(PLOTS_DIR) + + PLOTS_DIR.mkdir(exist_ok=True) + with PdfPages(OUTPUT_PATH) as pdf: + for slide in narrative_slides: + fig = render_text_slide(plt, slide) + pdf.savefig(fig) + plt.close(fig) + for entry in plot_entries: + fig = render_plot_slide(plt, mpimg, entry) + pdf.savefig(fig) + plt.close(fig) + + try: + rel = OUTPUT_PATH.relative_to(HERE.parent) + except ValueError: + rel = OUTPUT_PATH + print(f"Wrote {rel}") + print(f" Narrative slides: {len(narrative_slides)}") + print(f" Plot slides: {len(plot_entries)}") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/experiments/exp5_data_contacts_and_distances_v2_zephyr/cli.py b/experiments/exp5_data_contacts_and_distances_v2_zephyr/cli.py new file mode 100644 index 0000000..584098b --- /dev/null +++ b/experiments/exp5_data_contacts_and_distances_v2_zephyr/cli.py @@ -0,0 +1,433 @@ +# Copyright The MarinFold Authors +# SPDX-License-Identifier: Apache-2.0 + +"""contacts-and-distances-v2-on-zephyr driver. + +Two subcommands: + +* ``generate`` — runs the v2 generator at scale on the marin Iris + cluster. Input is the timodonnell/afdb-1.6M parquet manifest on + HuggingFace; per-row mmCIFs are fetched concurrently from the + ``gcs_uri`` column inside each Zephyr shard (the perf finding + from the prior production run: sequential per-row GCS fetches + were the residual bottleneck once the URI mode killed the + cross-cloud HF egress). + +* ``tokenizer`` — build / save / push the v2 tokenizer (same as + exp34's; included so a downstream training job can pin a single + experiment for both the dataset and the tokenizer build). + +Run examples are in the experiment README. +""" + +from __future__ import annotations + +import argparse +import hashlib +import os +import sys +import typing +from concurrent.futures import ThreadPoolExecutor +from functools import partial +from pathlib import Path + +from fray import ResourceConfig +from marinfold import build_tokenizer +from zephyr import Dataset, ZephyrContext + +import generate +import parse +from vocab import CONTEXT_LENGTH, NAME, all_domain_tokens + + +# Manifest columns we'll copy verbatim to the output *if* the input parquet +# schema has them. afdb-1.6M ships every one of these; minimal test +# manifests typically have only a subset, which the schema-peek in +# ``_resolve_input_columns`` filters down at submission time. +_OPTIONAL_PASSTHROUGH: tuple[str, ...] = ( + "split", + "seq_cluster_id", + "struct_cluster_id", + "uniprot_accession", + "tax_id", + "organism_name", + # The cif URI itself is provenance — surfaces the gs:// path each doc was + # generated from. When --cif-uri-column='gcs_uri' (the default) this column + # is also the data source, so ``_resolve_input_columns`` dedups before + # passing the column list to ``load_parquet``. + "gcs_uri", +) + +# Contact-statement opener tokens — counted in the emitted doc to populate +# the ``contacts_emitted`` provenance column (matches the published +# reference dataset's column of the same name). +_CONTACT_MARKERS: frozenset[str] = frozenset(( + "", + "", + "", +)) + + +def _resolve_input_columns(input_path: str, cif_col: str) -> tuple[list[str], list[str]]: + """Peek the first matching parquet file to decide which columns to load. + + Returns ``(columns_to_load, passthrough_present)`` — the second is the + subset of ``_OPTIONAL_PASSTHROUGH`` actually present in the input schema, + so the worker only emits keys it can fill. We do this once at submission + time so the same column list is used for every shard's + ``load_parquet(columns=...)`` call — keeping the output schema stable. + """ + import fsspec + import pyarrow.parquet as pq + + fs, _ = fsspec.core.url_to_fs(input_path) + # fs.glob handles both a literal path and a glob (with ``**``). Take the + # first match — schemas are uniform across the manifest's shards. + matches = sorted(fs.glob(input_path)) + if not matches: + raise FileNotFoundError(f"No parquet files match {input_path!r}") + first = fs.unstrip_protocol(matches[0]) + with fsspec.open(first, "rb") as f: + present = set(pq.ParquetFile(f).schema_arrow.names) + if "entry_id" not in present: + raise ValueError( + f"{first}: input parquet is missing the required 'entry_id' column " + f"(found {sorted(present)})" + ) + if cif_col not in present: + raise ValueError( + f"{first}: input parquet is missing the cif column {cif_col!r} " + f"(found {sorted(present)})" + ) + passthrough = [c for c in _OPTIONAL_PASSTHROUGH if c in present] + # Dedup — if the cif data-source column ('gcs_uri' by default) is also a + # passthrough column, we only need to read it once. The output row still + # gets it via the passthrough loop in _build_output_row. + base = ["entry_id", cif_col] + columns = base + [c for c in passthrough if c not in base] + return columns, passthrough + + +def _build_output_row(row: dict, doc: str, structure: parse.ParsedStructure, + passthrough_columns: list[str]) -> dict: + """Assemble one output row: doc text + computed provenance + manifest passthrough. + + ``global_plddt`` and ``seq_len`` use the *parsed-structure* values (what + ``generate_one`` actually serialized into the doc's pLDDT bin and + sequence-token block), not the manifest's. Keeps each row internally + consistent — the metadata describes what was emitted. + """ + contacts_emitted = sum(1 for t in doc.split() if t in _CONTACT_MARKERS) + out = { + "entry_id": row.get("entry_id"), + "structure": NAME, + "document": doc, + "sha1": hashlib.sha1(doc.encode()).hexdigest(), + "seq_len": int(structure.num_residues), + "global_plddt": float(structure.global_plddt), + "contacts_emitted": int(contacts_emitted), + } + for col in passthrough_columns: + out[col] = row.get(col) + return out + + +# -------------------------------------------------------------------------- +# Per-row worker function (pickled + shipped to Zephyr workers) +# -------------------------------------------------------------------------- + + +def _generate_doc_for_row( + row: dict, + *, + cif_uri_column: str, + cif_text_column: str | None, + context_length: int, + cfg: generate.GenerationConfig, + passthrough_columns: list[str], +) -> dict | None: + """Fetch + parse + generate one document for one parquet row. + + Returns an output-row dict (with ``document`` + provenance metadata) on + success, or ``None`` when the structure was unfetchable / unparseable / + too small to fit the token budget. Designed to be safe inside a Zephyr + worker — never raises on transient I/O errors. + """ + entry_id = row.get("entry_id") + if cif_text_column is not None: + cif = row.get(cif_text_column) + if cif is None: + return None + structure = parse.try_parse_cif_content(cif, entry_id=entry_id, source=entry_id) + else: + uri = row.get(cif_uri_column) + if uri is None: + return None + structure = parse.try_parse_cif_from_uri(uri, entry_id=entry_id) + if structure is None: + return None + doc = generate.generate_one(structure, context_length=context_length, cfg=cfg) + if doc is None: + return None + return _build_output_row(row, doc, structure, passthrough_columns) + + +def _generate_shard( + items, + shard_info, + *, + cif_uri_column: str, + cif_text_column: str | None, + context_length: int, + cfg: generate.GenerationConfig, + fetch_concurrency: int, + passthrough_columns: list[str], +): + """``map_shard`` body: fetch all rows' cifs concurrently within the shard. + + The CPU work (gemmi parse + doc generation) takes ~6 ms per row; + the GCS GET is ~30-80 ms on a cold connection. Without intra-shard + concurrency, per-shard latency is bounded by the sum of GETs + (sequential I/O), which is what made run 233550 land at ~128 s/shard + despite vectorized parse. A ThreadPoolExecutor of ``fetch_concurrency`` + workers overlaps the I/O — gemmi releases the GIL during the C++ + parse, so the threads make real progress in parallel. + + Order within the output shard mirrors input row order (we use + ``executor.map`` not ``as_completed``). That makes the run + deterministic per-shard, which matters for byte-comparing outputs + across runs. + """ + rows = list(items) + if not rows: + return + worker = partial( + _generate_doc_for_row, + cif_uri_column=cif_uri_column, + cif_text_column=cif_text_column, + context_length=context_length, + cfg=cfg, + passthrough_columns=passthrough_columns, + ) + # Cap concurrency at the actual row count (no point spawning 64 threads + # for a 2-row test). + workers = min(fetch_concurrency, len(rows)) + with ThreadPoolExecutor(max_workers=workers, thread_name_prefix="exp5-fetch") as pool: + for out_row in pool.map(worker, rows): + if out_row is not None: + yield out_row + + +# -------------------------------------------------------------------------- +# Subcommand handlers +# -------------------------------------------------------------------------- + + +def cmd_generate(args: argparse.Namespace) -> None: + cfg = generate.GenerationConfig( + contact_cutoff_angstrom=args.contact_cutoff_angstrom, + contact_f_range=tuple(args.contact_f_range), + contact_rank_mean=args.contact_rank_mean, + distance_rank_mean=args.distance_rank_mean, + rank_std=args.rank_std, + residue_plddt_min=args.residue_plddt_min, + think_initial_prob=args.think_initial_prob, + think_initial_geom_p=args.think_initial_geom_p, + think_additional_count_range=tuple(args.think_additional_count_range), + think_run_length_geom_p=args.think_run_length_geom_p, + ) + + # Column selection on the parquet manifest. The default cif source is + # --cif-uri-column='gcs_uri' (the fast path: ~160 KB/shard instead of + # ~70 MB); --cif-text-column overrides for inline cif_content. Schema + # peek decides which optional passthrough columns to load (afdb-1.6M + # has them all; test fixtures often don't). + cif_col = args.cif_text_column or args.cif_uri_column + columns, passthrough_columns = _resolve_input_columns(args.input, cif_col) + + rows = Dataset.from_files(args.input).load_parquet(columns=columns) + if args.num_docs is not None: + # Global cap; collapses to a single shard so the take is deterministic + # across runs. Same semantics as exp34's --num-docs. + rows = rows.reshard(1).take_per_shard(args.num_docs) + + # ``_generate_shard`` yields output-row dicts (document + provenance); + # no extra .map wrapping needed before write_parquet. + out_rows = rows.map_shard(partial( + _generate_shard, + cif_uri_column=args.cif_uri_column, + cif_text_column=args.cif_text_column, + context_length=args.context_length, + cfg=cfg, + fetch_concurrency=args.fetch_concurrency, + passthrough_columns=passthrough_columns, + )) + + # Output sharding: a {shard} placeholder writes one file per input + # shard (good for the full 1.6M-structure run; ~1000 output files); + # otherwise collapse to a single file. + if "{shard" not in args.out: + out_rows = out_rows.reshard(1) + + suffix = os.path.splitext(args.out)[1] + match suffix: + case ".parquet": + ds = out_rows.write_parquet(args.out) + case ".jsonl" | ".json": + ds = out_rows.write_jsonl(args.out) + case _: + typing.assert_never(suffix) + + ctx = ZephyrContext( + max_workers=args.max_workers, + resources=ResourceConfig( + cpu=args.worker_cpu, + ram=args.worker_memory, + disk=args.worker_disk, + ), + ) + ctx.execute(ds) + print(f"[{NAME}] wrote {args.out}", file=sys.stderr) + + +def cmd_tokenizer(args: argparse.Namespace) -> None: + tokenizer = build_tokenizer(all_domain_tokens()) + print(f"[{NAME}] built tokenizer with {len(tokenizer)} tokens", file=sys.stderr) + did_anything = False + if args.save_local is not None: + out_dir = Path(args.save_local) + out_dir.mkdir(parents=True, exist_ok=True) + tokenizer.save_pretrained(str(out_dir)) + print(f"[{NAME}] saved tokenizer to {out_dir}", file=sys.stderr) + did_anything = True + if args.push is not None: + tokenizer.push_to_hub(args.push, private=args.private) + print(f"[{NAME}] pushed tokenizer to https://huggingface.co/{args.push}", + file=sys.stderr) + did_anything = True + if not did_anything: + sample = " ".join(all_domain_tokens()[:8]) + encoded = tokenizer.encode(sample, add_special_tokens=False) + print(f"sample: {sample!r}") + print(f" ids: {encoded}") + print(f" back: {tokenizer.decode(encoded)!r}") + print("Use --save-local DIR or --push REPO to persist the tokenizer.") + + +# -------------------------------------------------------------------------- +# Argparse +# -------------------------------------------------------------------------- + + +def build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser( + prog="python cli.py", + description=f"{NAME} on zephyr — generate / tokenizer.", + ) + sub = parser.add_subparsers(dest="cmd", required=True) + + # ---- generate ---------------------------------------------------------- + p_gen = sub.add_parser("generate", help="Generate v2 training documents at scale.") + p_gen.add_argument( + "--input", type=str, required=True, + help="Parquet file/glob for the input manifest (e.g. " + "hf://datasets/timodonnell/afdb-1.6M/**/*.parquet). Rows need " + "an 'entry_id' column plus either --cif-uri-column (default, " + "fast path) or --cif-text-column (fallback, streams inline cif).", + ) + p_gen.add_argument( + "--cif-uri-column", type=str, default="gcs_uri", + help="Column holding a URI per row. Workers fetch the structure " + "from that URI directly — much faster than streaming a bulky " + "cif column cross-cloud. Default: 'gcs_uri' (the column in " + "afdb-1.6M pointing at the public AFDB bucket).", + ) + p_gen.add_argument( + "--cif-text-column", type=str, default=None, + help="Optional fallback: column holding the mmCIF text inline. " + "When set, overrides --cif-uri-column and reads the bulk " + "cif column from the manifest (slow path; for testing or " + "datasets without URI columns).", + ) + p_gen.add_argument( + "--num-docs", type=int, default=None, + help="Global cap on total documents produced (writes a single " + "merged file). Caps documents written, not parquet shards " + "scanned — pair with a bounded --input for a cheap sample.", + ) + p_gen.add_argument( + "--context-length", type=int, default=CONTEXT_LENGTH, + help="Token budget per document (default 8192).", + ) + p_gen.add_argument( + "--out", type=str, required=True, + help="Output path (.parquet or .jsonl), local or cloud URL. " + "Include a {shard} placeholder (e.g. corpus-{shard:05d}-of-" + "{total:05d}.parquet) to write one file per input shard; " + "omit it for a single merged file.", + ) + + # ---- Zephyr worker resources ------------------------------------------ + # Defaults bake in the perf lesson from the prior production runs: 1 CPU + # per worker (per-shard CPU work is single-threaded once the in-shard + # ThreadPool releases the GIL on I/O), scale out via --max-workers. + p_gen.add_argument("--worker-cpu", type=float, default=1) + p_gen.add_argument("--worker-memory", type=str, default="4g") + p_gen.add_argument("--worker-disk", type=str, default="32g") + p_gen.add_argument( + "--max-workers", type=int, default=None, + help="Cap concurrent Zephyr workers (default: 128 for distributed, " + "or ZEPHYR_MAX_WORKERS).", + ) + p_gen.add_argument( + "--fetch-concurrency", type=int, default=32, + help="Concurrent URI fetches per shard (ThreadPoolExecutor inside " + "map_shard). Overlaps the per-row gs:// GETs with gemmi parse. " + "Default 32 — the right ballpark for ~30-80 ms GCS GETs and a " + "~6 ms CPU step per row.", + ) + + # ---- v2 generation knobs (same defaults as exp34) --------------------- + cfg_defaults = generate.GenerationConfig() + p_gen.add_argument("--contact-cutoff-angstrom", type=float, + default=cfg_defaults.contact_cutoff_angstrom) + p_gen.add_argument("--residue-plddt-min", type=float, + default=cfg_defaults.residue_plddt_min) + p_gen.add_argument("--contact-f-range", type=float, nargs=2, + metavar=("LOW", "HIGH"), + default=list(cfg_defaults.contact_f_range)) + p_gen.add_argument("--contact-rank-mean", type=float, + default=cfg_defaults.contact_rank_mean) + p_gen.add_argument("--distance-rank-mean", type=float, + default=cfg_defaults.distance_rank_mean) + p_gen.add_argument("--rank-std", type=float, default=cfg_defaults.rank_std) + p_gen.add_argument("--think-initial-prob", type=float, + default=cfg_defaults.think_initial_prob) + p_gen.add_argument("--think-initial-geom-p", type=float, + default=cfg_defaults.think_initial_geom_p) + p_gen.add_argument("--think-additional-count-range", type=float, nargs=2, + metavar=("LOW", "HIGH"), + default=list(cfg_defaults.think_additional_count_range)) + p_gen.add_argument("--think-run-length-geom-p", type=float, + default=cfg_defaults.think_run_length_geom_p) + p_gen.set_defaults(func=cmd_generate) + + # ---- tokenizer --------------------------------------------------------- + p_tok = sub.add_parser("tokenizer", help="Build / save / push the v2 tokenizer.") + p_tok.add_argument("--save-local", type=Path, default=None) + p_tok.add_argument("--push", type=str, default=None) + p_tok.add_argument("--private", action="store_true") + p_tok.set_defaults(func=cmd_tokenizer) + + return parser + + +def main(argv: list[str] | None = None) -> int: + parser = build_parser() + args = parser.parse_args(argv) + args.func(args) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/experiments/exp5_data_contacts_and_distances_v2_zephyr/generate.py b/experiments/exp5_data_contacts_and_distances_v2_zephyr/generate.py new file mode 100644 index 0000000..62fb7bb --- /dev/null +++ b/experiments/exp5_data_contacts_and_distances_v2_zephyr/generate.py @@ -0,0 +1,307 @@ +# Copyright The MarinFold Authors +# SPDX-License-Identifier: Apache-2.0 + +"""contacts-and-distances-v2 doc generation, vectorized. + +Faithful port of the exp34 v2 algorithm — same RNG stream order, same +statement build order, same float arithmetic. Two differences from +exp34's implementation: + +* Consumes the columnar :class:`parse.ParsedStructure` (numpy arrays + for cb_or_ca_xyz / plddt_per_residue + CSR-style atom table). All + per-atom Python loops in the hot path are gone. +* The CB-CB eligibility loop is one ``np.triu_indices`` + a vectorized + distance computation instead of an O(N²) Python nested loop. The + resulting ``contacts_long``/``contacts_medium``/``contacts_short`` + lists are in the same row-major order as the legacy nested loop, so + ``rng.sample`` sees identical inputs. + +Byte-identity vs exp34's reference is enforced by +``tests/test_byte_identity.py``. Any change here that perturbs the RNG +stream order — adding or removing a draw, swapping ``rng.choice`` for +``rng.randrange`` on a different-sized sequence, etc. — will break that +test and is forbidden without an explicit version bump. +""" + +from __future__ import annotations + +import hashlib +import math +import random +from dataclasses import dataclass + +import numpy as np + +from parse import ParsedStructure, _ATOM_NAMES_TUPLE +from vocab import NAME, THINK_TOKEN + + +CONTACT_TOKENS_PER_STATEMENT = 3 # +DISTANCE_TOKENS_PER_STATEMENT = 6 # + + +@dataclass(frozen=True) +class GenerationConfig: + """Hyperparameters for :func:`generate_one`. + + The non-``think_*`` fields reproduce v1's defaults exactly. + The ``think_*`` fields are the v2 think-token knobs, pinned to the + values from issue #34 (0.75 / Geom(0.13) / Uniform(-4, 4) / Geom(0.25)). + """ + + # ---- inherited from v1 ------------------------------------------------- + contact_cutoff_angstrom: float = 8.0 + long_range_sep: int = 24 + medium_range_sep: int = 12 + short_range_sep: int = 6 + contact_f_range: tuple[float, float] = (-0.1, 0.2) + contact_rank_mean: float = 2.0 + distance_rank_mean: float = 0.0 + rank_std: float = 1.0 + residue_plddt_min: float = 70.0 + plddt_bin_edges: tuple[float, ...] = (70.0, 75.0, 80.0, 85.0, 90.0, 95.0) + # ---- v2 think-token knobs ---------------------------------------------- + think_initial_prob: float = 0.75 + think_initial_geom_p: float = 0.13 + think_additional_count_range: tuple[float, float] = (-4.0, 4.0) + think_run_length_geom_p: float = 0.25 + + +def _plddt_bin_token(global_plddt: float, bin_edges: tuple[float, ...]) -> str: + for i, edge in enumerate(bin_edges): + if global_plddt < edge: + if i == 0: + return f"plddt_lt{int(edge)}" + return f"plddt_{int(bin_edges[i - 1])}_{int(edge)}" + return f"plddt_{int(bin_edges[-1])}_100" + + +def _distance_token(dist_angstrom: float) -> str: + bin_idx = int(math.ceil(dist_angstrom / 0.5)) + if bin_idx < 1: + bin_idx = 1 + if bin_idx > 64: + bin_idx = 64 + return f"d{bin_idx * 0.5:.1f}" + + +def _geometric(rng: random.Random, p: float) -> int: + """Sample Geometric(p), support {1, 2, ...}, via inverse-CDF.""" + if not (0.0 < p <= 1.0): + raise ValueError(f"p must be in (0, 1]; got {p!r}") + if p == 1.0: + return 1 + u = 1.0 - rng.random() + return int(math.ceil(math.log(u) / math.log(1.0 - p))) + + +def _sample_think_overhead(rng: random.Random, cfg: GenerationConfig + ) -> tuple[int, list[int]]: + if rng.random() < cfg.think_initial_prob: + k1 = _geometric(rng, cfg.think_initial_geom_p) + else: + k1 = 0 + lo, hi = cfg.think_additional_count_range + k2 = rng.uniform(lo, hi) + n_additional = max(int(k2), 0) + additional_lengths = [ + _geometric(rng, cfg.think_run_length_geom_p) for _ in range(n_additional) + ] + return k1, additional_lengths + + +@dataclass(frozen=True) +class _Statement: + rank: float + tokens: tuple[str, ...] + + +def generate_one( + structure: ParsedStructure, + *, + context_length: int, + cfg: GenerationConfig, +) -> str | None: + """Build one v2 document string for ``structure``, or ``None`` if no fit. + + Byte-identical to exp34's ``_generate_one`` on the same ``entry_id`` + + structure (enforced by ``tests/test_byte_identity.py``). + """ + num_residues = structure.num_residues + if num_residues < 2: + return None + + seed = int(hashlib.sha1(structure.entry_id.encode()).hexdigest()[:8], 16) + rng = random.Random(seed) + + # Draw #1: think-token overhead, MUST come before any contact/distance + # sampling so the RNG stream layout matches exp34 + future generators + # that share the v2 entry_id seeding contract. + k1, additional_run_lengths = _sample_think_overhead(rng, cfg) + total_think_tokens = k1 + sum(additional_run_lengths) + + # ---- vectorized CB-CB contact eligibility ------------------------------ + # Legacy did a per-residue Python loop building cb_positions, then a + # nested loop over all (i < j) computing euclidean. Here we keep the + # exact same row-major (i, j) emission order via np.triu_indices(k=1) + # and the same FP via np.sqrt((diffs**2).sum(axis=1)) on float64. + cb_xyz = structure.cb_or_ca_xyz + plddt_arr = structure.plddt_per_residue + valid_mask = (plddt_arr >= cfg.residue_plddt_min) & np.isfinite(cb_xyz[:, 0]) + valid_indices = np.flatnonzero(valid_mask) + 1 # 1-based residue indices + valid_xyz = cb_xyz[valid_mask] + + contacts_long: list[tuple[int, int]] = [] + contacts_medium: list[tuple[int, int]] = [] + contacts_short: list[tuple[int, int]] = [] + if valid_indices.size >= 2: + ii_arr, jj_arr = np.triu_indices(valid_indices.size, k=1) + diffs = valid_xyz[ii_arr] - valid_xyz[jj_arr] + dists = np.sqrt((diffs * diffs).sum(axis=1)) + i_arr = valid_indices[ii_arr] + j_arr = valid_indices[jj_arr] + sep_arr = j_arr - i_arr + elig = ( + (sep_arr > 1) + & (sep_arr >= cfg.short_range_sep) + & (dists <= cfg.contact_cutoff_angstrom) + ) + long_mask = elig & (sep_arr >= cfg.long_range_sep) + medium_mask = elig & (sep_arr >= cfg.medium_range_sep) & (sep_arr < cfg.long_range_sep) + short_mask = elig & (sep_arr < cfg.medium_range_sep) + contacts_long = list(zip(i_arr[long_mask].tolist(), j_arr[long_mask].tolist())) + contacts_medium = list(zip(i_arr[medium_mask].tolist(), j_arr[medium_mask].tolist())) + contacts_short = list(zip(i_arr[short_mask].tolist(), j_arr[short_mask].tolist())) + + # 5 overhead = + + + + + fixed_overhead = 5 + num_residues + total_think_tokens + available_tokens = context_length - fixed_overhead + if available_tokens <= 0: + return None + + # Draws #2-4: per-mode fractions. + f_long = max(0.0, rng.uniform(*cfg.contact_f_range)) + f_medium = max(0.0, rng.uniform(*cfg.contact_f_range)) + f_short = max(0.0, rng.uniform(*cfg.contact_f_range)) + + n_long = min(int(available_tokens * f_long) // CONTACT_TOKENS_PER_STATEMENT, len(contacts_long)) + n_medium = min(int(available_tokens * f_medium) // CONTACT_TOKENS_PER_STATEMENT, len(contacts_medium)) + n_short = min(int(available_tokens * f_short) // CONTACT_TOKENS_PER_STATEMENT, len(contacts_short)) + + contact_tokens_used = (n_long + n_medium + n_short) * CONTACT_TOKENS_PER_STATEMENT + n_distance = (available_tokens - contact_tokens_used) // DISTANCE_TOKENS_PER_STATEMENT + + # Draws #5-7: contact sampling. + selected_long = rng.sample(contacts_long, n_long) if n_long > 0 else [] + selected_medium = rng.sample(contacts_medium, n_medium) if n_medium > 0 else [] + selected_short = rng.sample(contacts_short, n_short) if n_short > 0 else [] + + # ---- distance statements ---------------------------------------------- + # ``distance_indices`` = 1-based residue indices with at least one + # in-vocab atom — preserves legacy iteration order (ascending). + atoms_per_residue = np.diff(structure.atom_offsets) + distance_indices = (np.flatnonzero(atoms_per_residue > 0) + 1).tolist() + atom_offsets = structure.atom_offsets + atom_name_id = structure.atom_name_id + atom_xyz = structure.atom_xyz + + distance_statements: list[tuple[int, int, str, str, str]] = [] + if len(distance_indices) >= 2: + for _ in range(n_distance): + # Draw: residue pair (with up to 10 retries when sep <= 1, exactly + # matching the legacy retry loop). + a, b = rng.sample(distance_indices, 2) + i, j = (a, b) if a < b else (b, a) + if j - i <= 1: + ok = False + for _retry in range(10): + a, b = rng.sample(distance_indices, 2) + i, j = (a, b) if a < b else (b, a) + if j - i > 1: + ok = True + break + if not ok: + continue + # Draw: per-residue atom pick. ``rng.choice(tuple)`` and + # ``rng.randrange(len)`` both call ``self._randbelow(len)``, so + # the RNG state evolves identically as long as the lengths match. + ki = int(atom_offsets[i] - atom_offsets[i - 1]) # i is 1-based + kj = int(atom_offsets[j] - atom_offsets[j - 1]) + idx_a = rng.randrange(ki) + idx_b = rng.randrange(kj) + i0_start = int(atom_offsets[i - 1]) + j0_start = int(atom_offsets[j - 1]) + ai_name = _ATOM_NAMES_TUPLE[int(atom_name_id[i0_start + idx_a])] + aj_name = _ATOM_NAMES_TUPLE[int(atom_name_id[j0_start + idx_b])] + # Pull as Python floats (.tolist on a (3,) float64 row) so the + # subsequent (ax - bx)**2 + … + math.sqrt sequence is identical + # to exp34's tuple-based computation. + ax, ay, az = atom_xyz[i0_start + idx_a].tolist() + bx, by, bz = atom_xyz[j0_start + idx_b].tolist() + d = math.sqrt((ax - bx) ** 2 + (ay - by) ** 2 + (az - bz) ** 2) + distance_statements.append((i, j, ai_name, aj_name, _distance_token(d))) + + # ---- assemble statements + ranks -------------------------------------- + statements: list[_Statement] = [] + for i, j in selected_long: + statements.append(_Statement( + rank=rng.gauss(cfg.contact_rank_mean, cfg.rank_std), + tokens=("", f"", f""), + )) + for i, j in selected_medium: + statements.append(_Statement( + rank=rng.gauss(cfg.contact_rank_mean, cfg.rank_std), + tokens=("", f"", f""), + )) + for i, j in selected_short: + statements.append(_Statement( + rank=rng.gauss(cfg.contact_rank_mean, cfg.rank_std), + tokens=("", f"", f""), + )) + for i, j, ai, aj, dist_tok in distance_statements: + statements.append(_Statement( + rank=rng.gauss(cfg.distance_rank_mean, cfg.rank_std), + tokens=("", f"", f"", f"<{ai}>", f"<{aj}>", f"<{dist_tok}>"), + )) + statements.sort(key=lambda s: -s.rank) + + plddt_token = _plddt_bin_token(structure.global_plddt, cfg.plddt_bin_edges) + plddt_at_end = rng.random() < 0.5 + plddt_insert_idx = None if plddt_at_end else rng.randint(0, len(statements)) + + # Additional-run slot assignment — identical to exp34. When statements + # is empty, additional runs are dropped (no slot anchor). + think_at_slot: dict[int, int] = {} + if k1 > 0: + think_at_slot[0] = k1 + if statements and additional_run_lengths: + n_stmts = len(statements) + for length in additional_run_lengths: + slot = rng.randint(0, n_stmts - 1) + think_at_slot[slot] = think_at_slot.get(slot, 0) + length + + out: list[str] = [] + out.append(f"<{NAME}>") + out.append("") + for name in structure.sequence: + out.append(f"<{name}>") + out.append("") + for idx, stmt in enumerate(statements): + # Think tokens always come *before* any pLDDT in the same slot so + # the "immediately after " wording holds at slot 0. + n_think = think_at_slot.get(idx, 0) + if n_think: + out.extend([THINK_TOKEN] * n_think) + if plddt_insert_idx is not None and idx == plddt_insert_idx: + out.append(f"<{plddt_token}>") + out.extend(stmt.tokens) + if not statements and k1 > 0: + # No statements but the initial run still landed — emit it anyway + # so the document captures the sampled overhead. + out.extend([THINK_TOKEN] * k1) + if plddt_insert_idx is not None and plddt_insert_idx >= len(statements): + out.append(f"<{plddt_token}>") + if plddt_at_end: + out.append(f"<{plddt_token}>") + out.append("") + return " ".join(out) diff --git a/experiments/exp5_data_contacts_and_distances_v2_zephyr/parse.py b/experiments/exp5_data_contacts_and_distances_v2_zephyr/parse.py new file mode 100644 index 0000000..1dc9296 --- /dev/null +++ b/experiments/exp5_data_contacts_and_distances_v2_zephyr/parse.py @@ -0,0 +1,302 @@ +# Copyright The MarinFold Authors +# SPDX-License-Identifier: Apache-2.0 + +"""Columnar gemmi-backed parser for contacts-and-distances-v2 generation. + +A from-scratch design — no backward compatibility with marinfold's +``ParsedStructure`` shape. Every per-residue / per-atom array lives at +the *structure* level as numpy arrays, so the doc-generation hot paths +in :mod:`generate` can vectorize (``np.triu_indices`` over the CB-CB +distance matrix, etc.) instead of walking gemmi proxy objects from +Python. + +The atom data is stored CSR-style — one flat ``atom_xyz`` array of +shape ``(T, 3)`` plus an ``atom_offsets`` array of length ``N + 1`` so +the atoms of residue ``i`` are ``atom_xyz[atom_offsets[i] : atom_offsets[i + 1]]``. +This is the layout that round-trips zero-copy to ``pyarrow.RecordBatch`` +if we ever want a precomputed parquet store. + +Determinism contract (versus exp34's reference parser): + +* ``plddt_per_residue[i]`` is the left-fold mean of every non-hydrogen + atom's ``b_iso`` for residue ``i`` (``-inf`` if no heavy atoms) — + byte-identical Python FP to the legacy ``_residue_plddt``. +* ``cb_or_ca_xyz[i]`` is the first CB seen in residue ``i``, falling + back to the *last* CA seen for non-GLY (matches the legacy + ``cb_or_ca_position`` scan order where ``fallback_ca`` is + overwritten); for GLY it's the *first* CA. ``NaN`` row when neither + atom is present. +* ``global_plddt`` is computed as ``sum(values_list) / N`` over Python + floats — *not* ``np.mean`` — so the FP reduction order matches + exp34's ``ParsedStructure.global_plddt`` property exactly. + +These three invariants are what keeps v2 docs byte-identical to the +exp34 reference (see ``tests/test_byte_identity.py``). +""" + +from __future__ import annotations + +import warnings +from dataclasses import dataclass +from typing import Self + +import fsspec +import numpy as np + +from vocab import AMINO_ACIDS, ATOM_NAMES + + +# -------------------------------------------------------------------------- +# Vocab lookups, hoisted once at module load +# -------------------------------------------------------------------------- + +_CANONICAL_20 = frozenset(AMINO_ACIDS) + +# ATOM_NAMES is small (~37 entries) and fixed. Encoding each atom as an +# index into this tuple lets us store atom_name_id as uint8 and recover +# the string via O(1) tuple indexing in generate.py. +_ATOM_NAMES_TUPLE: tuple[str, ...] = tuple(ATOM_NAMES) +_ATOM_NAME_TO_ID: dict[str, int] = {n: i for i, n in enumerate(_ATOM_NAMES_TUPLE)} + +_NAN3: tuple[float, float, float] = (float("nan"), float("nan"), float("nan")) + + +# -------------------------------------------------------------------------- +# ParsedStructure +# -------------------------------------------------------------------------- + + +@dataclass(frozen=True) +class ParsedStructure: + """One polymer chain in a vectorization-friendly columnar layout. + + See module docstring for the determinism contract that keeps generated + v2 docs byte-identical to the exp34 reference implementation. + """ + + entry_id: str # canonical id, e.g. "AF-A0A090SHW3-F1" (matches afdb-1.6M manifest) + source: str # URI or path, for provenance + + # Per-residue arrays — all length N. + sequence: tuple[str, ...] # 3-letter canonical AA names, "UNK" for non-canonical + plddt_per_residue: np.ndarray # float64[N] + cb_or_ca_xyz: np.ndarray # float64[N, 3], NaN row when neither CB nor CA present + + # Flat CSR-style atom table — atoms for residue i live at + # atom_xyz[atom_offsets[i] : atom_offsets[i+1]] (same slice for atom_name_id). + atom_offsets: np.ndarray # int32[N+1] + atom_name_id: np.ndarray # uint8[T] — index into ``parse._ATOM_NAMES_TUPLE`` + atom_xyz: np.ndarray # float64[T, 3] + + # Precomputed once at parse time so generate.py doesn't have to choose + # between FP-fidelity (sum/N over Python floats) and vectorization. + global_plddt: float + + @classmethod + def from_gemmi(cls, structure, *, entry_id: str, source: str, require_single_chain: bool) -> Self: + """Single-pass extraction from a parsed gemmi ``Structure``.""" + structure.setup_entities() + if len(structure) == 0: + raise ValueError(f"{source}: no models in structure") + model = structure[0] + polymer_chains = [ch for ch in model if ch.get_polymer()] + if not polymer_chains: + raise ValueError(f"{source}: no polymer chain") + if require_single_chain and len(polymer_chains) != 1: + raise ValueError( + f"{source}: expected single polymer chain, found {len(polymer_chains)}." + ) + chain = polymer_chains[0] + polymer = chain.get_polymer() + + # Fast-path detection (AFDB-shape): one chain in the model AND every + # residue in that chain is a polymer residue (no waters / ligands). When + # both hold, ``model.all()`` yields exactly the polymer atoms in order + # — see the gemmi maintainer's advice in + # https://github.com/project-gemmi/gemmi/issues/314 — and we can avoid + # the nested-loop overhead. Otherwise fall back to polymer iteration so + # we don't pick up solvent/ligand atoms. + n_polymer = sum(1 for _ in polymer) + fast_path = len(model) == 1 and sum(1 for _ in chain) == n_polymer + + sequence: list[str] = [] + plddt_values: list[float] = [] + cb_or_ca_rows: list[tuple[float, float, float]] = [] + atom_name_id_list: list[int] = [] + atom_xyz_list: list[tuple[float, float, float]] = [] + atom_offsets: list[int] = [0] + + def _flush_residue(name: str, b_values: list[float], + cb: tuple[float, float, float] | None, + ca_first: tuple[float, float, float] | None, + ca_last: tuple[float, float, float] | None) -> None: + # FP-identical to exp34's _residue_plddt (left-fold sum over Python + # floats divided by count; -inf if no heavy atoms). + plddt = sum(b_values) / len(b_values) if b_values else float("-inf") + if name == "GLY": + chosen = ca_first + else: + chosen = cb if cb is not None else ca_last + sequence.append(name) + plddt_values.append(plddt) + cb_or_ca_rows.append(chosen if chosen is not None else _NAN3) + atom_offsets.append(len(atom_name_id_list)) + + if fast_path: + prev_res = None + name = "" + b_values: list[float] = [] + cb: tuple[float, float, float] | None = None + ca_first: tuple[float, float, float] | None = None + ca_last: tuple[float, float, float] | None = None + for cra in model.all(): + if cra.residue is not prev_res: + if prev_res is not None: + _flush_residue(name, b_values, cb, ca_first, ca_last) + prev_res = cra.residue + raw = cra.residue.name.strip() + name = raw if raw in _CANONICAL_20 else "UNK" + b_values = [] + cb = None + ca_first = None + ca_last = None + atom = cra.atom + if atom.is_hydrogen(): + continue + b_values.append(atom.b_iso) + atom_name = atom.name.strip() + name_id = _ATOM_NAME_TO_ID.get(atom_name) + if name_id is None: + continue + x, y, z = atom.pos.tolist() # one nanobind call vs three attr lookups + atom_name_id_list.append(name_id) + atom_xyz_list.append((x, y, z)) + if atom_name == "CB": + if cb is None: + cb = (x, y, z) + elif atom_name == "CA": + if ca_first is None: + ca_first = (x, y, z) + ca_last = (x, y, z) + if prev_res is not None: + _flush_residue(name, b_values, cb, ca_first, ca_last) + else: + for res in polymer: + raw = res.name.strip() + name = raw if raw in _CANONICAL_20 else "UNK" + b_values = [] + cb = None + ca_first = None + ca_last = None + for atom in res: + if atom.is_hydrogen(): + continue + b_values.append(atom.b_iso) + atom_name = atom.name.strip() + name_id = _ATOM_NAME_TO_ID.get(atom_name) + if name_id is None: + continue + x, y, z = atom.pos.tolist() + atom_name_id_list.append(name_id) + atom_xyz_list.append((x, y, z)) + if atom_name == "CB": + if cb is None: + cb = (x, y, z) + elif atom_name == "CA": + if ca_first is None: + ca_first = (x, y, z) + ca_last = (x, y, z) + _flush_residue(name, b_values, cb, ca_first, ca_last) + + if not sequence: + raise ValueError(f"{source}: no residues parsed") + + # global_plddt: sum/N over the Python float list (NOT np.mean) so the + # FP reduction order matches exp34's ParsedStructure.global_plddt + # property — keeps RNG-seeded doc content byte-identical. + global_plddt = sum(plddt_values) / len(plddt_values) + + return cls( + entry_id=entry_id, + source=source, + sequence=tuple(sequence), + plddt_per_residue=np.asarray(plddt_values, dtype=np.float64), + cb_or_ca_xyz=np.asarray(cb_or_ca_rows, dtype=np.float64), + atom_offsets=np.asarray(atom_offsets, dtype=np.int32), + atom_name_id=( + np.asarray(atom_name_id_list, dtype=np.uint8) + if atom_name_id_list else np.empty(0, dtype=np.uint8) + ), + atom_xyz=( + np.asarray(atom_xyz_list, dtype=np.float64).reshape(-1, 3) + if atom_xyz_list else np.empty((0, 3), dtype=np.float64) + ), + global_plddt=global_plddt, + ) + + @property + def num_residues(self) -> int: + return len(self.sequence) + + def atoms_for(self, i: int) -> tuple[np.ndarray, np.ndarray]: + """Atoms of residue ``i`` (0-based): ``(name_ids[K], xyz[K, 3])``.""" + start = int(self.atom_offsets[i]) + end = int(self.atom_offsets[i + 1]) + return self.atom_name_id[start:end], self.atom_xyz[start:end] + + +# -------------------------------------------------------------------------- +# Public parsers +# -------------------------------------------------------------------------- + + +def parse_cif_content(data, entry_id: str, *, source: str | None = None, + require_single_chain: bool = True) -> ParsedStructure: + """Parse an mmCIF document held in memory (``str`` or ``bytes``).""" + import gemmi + + structure = gemmi.read_structure_string(data, format=gemmi.CoorFormat.Mmcif) + return ParsedStructure.from_gemmi( + structure, + entry_id=entry_id, + source=source or f"", + require_single_chain=require_single_chain, + ) + + +def parse_cif_from_uri(uri: str, entry_id: str, *, + require_single_chain: bool = True) -> ParsedStructure: + """Fetch ``uri`` (any fsspec URL) and parse the bytes as an mmCIF document. + + Transparent gzip via ``compression='infer'``. The ``entry_id`` arrives + from the manifest (e.g. afdb-1.6M's ``entry_id`` column) and is the + canonical id used to seed generation — it does NOT need to match the + URI basename. + """ + with fsspec.open(uri, "rb", compression="infer") as f: + data = f.read() + return parse_cif_content(data, entry_id=entry_id, source=uri, + require_single_chain=require_single_chain) + + +def try_parse_cif_content(data, entry_id: str, *, source: str | None = None + ) -> ParsedStructure | None: + """``.map``-friendly :func:`parse_cif_content`: ``None`` on failure.""" + try: + return parse_cif_content(data, entry_id=entry_id, source=source) + except ValueError as exc: + warnings.warn(f"skipping {source or entry_id}: {exc}", stacklevel=2) + return None + + +def try_parse_cif_from_uri(uri: str, entry_id: str) -> ParsedStructure | None: + """``.map``-friendly :func:`parse_cif_from_uri`: ``None`` on failure. + + Catches ``OSError`` (gs:// 404 / transient HTTP / DNS) in addition to + ``ValueError`` so a single missing object can't kill a Zephyr worker. + """ + try: + return parse_cif_from_uri(uri, entry_id=entry_id) + except (ValueError, OSError) as exc: + warnings.warn(f"skipping {uri}: {exc}", stacklevel=2) + return None diff --git a/experiments/exp5_data_contacts_and_distances_v2_zephyr/pyproject.toml b/experiments/exp5_data_contacts_and_distances_v2_zephyr/pyproject.toml new file mode 100644 index 0000000..105dc3a --- /dev/null +++ b/experiments/exp5_data_contacts_and_distances_v2_zephyr/pyproject.toml @@ -0,0 +1,49 @@ +[project] +name = "exp5-data-contacts-and-distances-v2-zephyr" +version = "0.1.0" +description = "Generate contacts-and-distances-v2 training documents at scale on the marin Iris cluster via Zephyr." +requires-python = ">=3.11,<3.13" +dependencies = [ + "marinfold", # vocab (AMINO_ACIDS/ATOM_NAMES) + build_tokenizer for the v2 tokenizer + "gemmi>=0.7", # mmCIF parsing — read_structure_string requires >=0.7.0 + "numpy", # columnar in-memory structure representation + "pyarrow", # parquet read/write + Zephyr-native row dicts + "marin-zephyr", # distributed generation on Iris + "fsspec", # uniform local + cloud file IO + "gcsfs", # gs:// for the AFDB public bucket + marin output buckets + "huggingface_hub", # hf:// for the timodonnell/afdb-1.6M manifest +] +# NOTE on obstore: we tried it (object_store-backed, ~9x throughput vs gcsfs +# for parallel small-object GETs). It works for our marin-us-central1 output +# bucket but fails on the AFDB public bucket with +# "GenericError: Content-Length Header missing from response". Root cause is +# upstream: object_store's HTTP handler strictly requires Content-Length, but +# AFDB cifs are stored with Content-Encoding: gzip and GCS transparently +# gunzips them on the wire, omitting Content-Length. The documented fix +# (Accept-Encoding: gzip) can only be set via client_options.default_headers +# in obstore today, and those headers leak into the OAuth2 token fetch and +# break it. Diagnostic + variants tried are documented in the README. + +[project.optional-dependencies] +test = [ + "pytest>=8", + # exp34's generate is the byte-identity oracle for v2 doc content. We import + # it through a sys.path shim in tests/ (it's an experiment dir, not a package), + # but its own deps (gemmi, marinfold, numpy) are already covered above. +] + +[dependency-groups] +dev = [] + +[tool.uv] +package = false +fork-strategy = "fewest" +prerelease = "allow" +environments = [ + "sys_platform == 'darwin'", + "sys_platform == 'linux'", +] + +[tool.uv.sources] +marinfold = { git = "https://github.com/Open-Athena/MarinFold.git", subdirectory = "marinfold" } +marin-zephyr = { git = "https://github.com/marin-community/marin.git", subdirectory = "lib/zephyr", branch = "alxmrs/stamp-iris-build-date" } diff --git a/experiments/exp5_data_contacts_and_distances_v2_zephyr/summary_narrative.md b/experiments/exp5_data_contacts_and_distances_v2_zephyr/summary_narrative.md new file mode 100644 index 0000000..6cd1e78 --- /dev/null +++ b/experiments/exp5_data_contacts_and_distances_v2_zephyr/summary_narrative.md @@ -0,0 +1,17 @@ +# Summary slides — implement data generation on zephyr + + + +## What we're doing + +_(Copy from the issue.)_ + +## Why + +_(Copy from the issue.)_ + +## Results so far + +_(Fill in as results come in.)_ diff --git a/experiments/exp5_data_contacts_and_distances_v2_zephyr/tests/__init__.py b/experiments/exp5_data_contacts_and_distances_v2_zephyr/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/experiments/exp5_data_contacts_and_distances_v2_zephyr/tests/conftest.py b/experiments/exp5_data_contacts_and_distances_v2_zephyr/tests/conftest.py new file mode 100644 index 0000000..60ece5b --- /dev/null +++ b/experiments/exp5_data_contacts_and_distances_v2_zephyr/tests/conftest.py @@ -0,0 +1,87 @@ +# Copyright The MarinFold Authors +# SPDX-License-Identifier: Apache-2.0 + +"""Test fixtures shared across exp5's test suite. + +Two path adjustments: + +* Add the experiment dir itself to ``sys.path`` so ``import parse`` / + ``import generate`` / ``import vocab`` work — experiments aren't + importable packages. +* Add exp34's directory so the byte-identity test can import its + parse + generate as the reference oracle. + +Provides a ``synthetic_cif`` fixture: a realistic-ish polymer mmCIF +text (~50 residues) built by converting a synthetic PDB via gemmi — +identical generation logic to exp34's ``_make_long_pdb`` test helper, +so cross-experiment byte-identity checks are reproducible. +""" + +from __future__ import annotations + +import sys +from pathlib import Path + +import pytest + +_EXP_DIR = Path(__file__).resolve().parents[1] +_EXP34_DIR = _EXP_DIR.parent / "exp34_document_structures_contacts_and_distances_v2" + +# Order matters: exp5's own modules first so ``import parse`` resolves to +# exp5's parse, not exp34's. The exp34 oracle imports under the +# ``exp34_*`` aliases in test_byte_identity.py via a sys.path swap. +for p in (_EXP_DIR, _EXP34_DIR): + p_str = str(p) + if p_str not in sys.path: + sys.path.insert(0, p_str) + + +def _make_long_pdb(num_residues: int) -> str: + """Build a synthetic poly-ALA PDB string with realistic backbone+CB atoms. + + Helical-ish coordinates with monotonically increasing z so CB-CB + distances span the eligibility cutoff and we exercise all three + contact modes (short / medium / long range). + """ + lines = [f"HEADER SYNTHETIC 01-JAN-26 SYN"] + serial = 1 + for i in range(1, num_residues + 1): + z = i * 3.0 + atoms = [ + ("N", 6.0, 0.0, z + 0.0), + ("CA", 6.0, 1.5, z + 0.5), + ("C", 6.0, 3.0, z + 0.0), + ("O", 6.0, 3.0, z - 1.2), + ("CB", 4.5, 1.5, z + 0.5), + ] + for name, x, y, zc in atoms: + element = name[0] + lines.append( + f"ATOM {serial:5d} {name:<3s} ALA A{i:4d} " + f"{x:8.3f}{y:8.3f}{zc:8.3f} 1.00 90.00 {element:>2s}" + ) + serial += 1 + lines.append("END") + return "\n".join(lines) + "\n" + + +@pytest.fixture(scope="session") +def synthetic_cif() -> str: + """Realistic-ish ~50-residue polymer mmCIF text (built once per session).""" + import gemmi + + pdb = _make_long_pdb(50) + st = gemmi.read_pdb_string(pdb) + st.setup_entities() + return st.make_mmcif_document().as_string() + + +@pytest.fixture +def small_cif() -> str: + """A 5-residue cif for parse-shape assertions.""" + import gemmi + + pdb = _make_long_pdb(5) + st = gemmi.read_pdb_string(pdb) + st.setup_entities() + return st.make_mmcif_document().as_string() diff --git a/experiments/exp5_data_contacts_and_distances_v2_zephyr/tests/test_byte_identity.py b/experiments/exp5_data_contacts_and_distances_v2_zephyr/tests/test_byte_identity.py new file mode 100644 index 0000000..65c4471 --- /dev/null +++ b/experiments/exp5_data_contacts_and_distances_v2_zephyr/tests/test_byte_identity.py @@ -0,0 +1,104 @@ +# Copyright The MarinFold Authors +# SPDX-License-Identifier: Apache-2.0 + +"""Byte-identity test: exp5 generates the same docs as exp34's reference. + +exp5 re-implements parse + generate for performance, with a fresh +columnar ParsedStructure that's not interchangeable with exp34's +tuple-based one. The contract that keeps the docs aligned is that the +RNG stream order is unchanged and the FP arithmetic is reproduced +exactly (see ``parse.py`` and ``generate.py`` docstrings for the +specifics: left-fold pLDDT sum, np.sqrt over float64 = math.sqrt on +the same floats, np.triu_indices preserves the legacy row-major (i, j) +iteration order, etc.). + +This test pins that contract: a synthetic structure through several +distinct entry_ids must produce the same doc bytes whether parsed + +generated by exp34 or by exp5. Any drift will surface here. + +The exp34 reference is imported by temporarily swapping +``sys.path`` — it lives in a sibling experiment dir, not as a +package. +""" + +from __future__ import annotations + +import dataclasses +import hashlib +import importlib +import sys +from pathlib import Path + +import pytest + +import generate as exp5_generate +import parse as exp5_parse + +_EXP34_DIR = Path(__file__).resolve().parents[2] / "exp34_document_structures_contacts_and_distances_v2" + + +def _import_exp34(): + """Import exp34's parse + generate under aliases, isolated from exp5's modules.""" + # Save exp5's modules under their original names, swap exp34 in, import, + # then restore. This way both versions coexist for the duration of the test. + saved = {name: sys.modules[name] for name in ("parse", "generate", "vocab") + if name in sys.modules} + # exp34 is already on sys.path (added in conftest), but its imports + # ("from parse import …" in generate.py) need to resolve to exp34's + # parse, not exp5's. Force re-import in the right path order. + for name in ("parse", "generate", "vocab"): + sys.modules.pop(name, None) + sys.path.insert(0, str(_EXP34_DIR)) + try: + exp34_parse = importlib.import_module("parse") + exp34_generate = importlib.import_module("generate") + return exp34_parse, exp34_generate + finally: + sys.path.remove(str(_EXP34_DIR)) + # Restore exp5's modules so subsequent tests see the right ones. + for name in ("parse", "generate", "vocab"): + sys.modules.pop(name, None) + for name, mod in saved.items(): + sys.modules[name] = mod + + +@pytest.mark.parametrize("entry_id", [ + "AF-A0A090SHW3-F1", + "AF-H0GCG4-F1", + "AF-K7EXM5-F1", + "AF-XYZ123-F1", + "AF-TESTSEED-F2", +]) +def test_doc_byte_identical_to_exp34(synthetic_cif, entry_id, tmp_path): + """Same cif + same entry_id → exp34 and exp5 produce the same doc string. + + The structure is fixed; ``entry_id`` is what seeds the RNG, so varying + it across the parametrize sweep exercises a few different statement + orderings + think-overhead samples + pLDDT placements. + + exp34's ``parse_structure`` reads from a ``Path`` and derives entry_id + from the file stem; we write the synthetic cif to a tmpfile, parse it, + then ``dataclasses.replace`` to swap in our parametrized entry_id — + keeps the cif content identical across both sides so any diff is + parse/generate logic, not source data. + """ + cif_path = tmp_path / f"{entry_id}.cif" + cif_path.write_text(synthetic_cif) + + cfg5 = exp5_generate.GenerationConfig() + ps5 = exp5_parse.parse_cif_content(synthetic_cif, entry_id=entry_id) + doc5 = exp5_generate.generate_one(ps5, context_length=8192, cfg=cfg5) + + exp34_parse, exp34_generate = _import_exp34() + cfg34 = exp34_generate.GenerationConfig() + ps34_raw = exp34_parse.parse_structure(cif_path) + ps34 = dataclasses.replace(ps34_raw, entry_id=entry_id) + doc34 = exp34_generate._generate_one(ps34, context_length=8192, cfg=cfg34) + + assert doc5 is not None, "exp5 returned None" + assert doc34 is not None, "exp34 returned None" + assert doc5 == doc34, ( + f"docs diverge for entry_id={entry_id}\n" + f" exp5 sha1: {hashlib.sha1(doc5.encode()).hexdigest()[:16]} len={len(doc5)}\n" + f" exp34 sha1: {hashlib.sha1(doc34.encode()).hexdigest()[:16]} len={len(doc34)}\n" + ) diff --git a/experiments/exp5_data_contacts_and_distances_v2_zephyr/tests/test_cli.py b/experiments/exp5_data_contacts_and_distances_v2_zephyr/tests/test_cli.py new file mode 100644 index 0000000..75a18ce --- /dev/null +++ b/experiments/exp5_data_contacts_and_distances_v2_zephyr/tests/test_cli.py @@ -0,0 +1,162 @@ +# Copyright The MarinFold Authors +# SPDX-License-Identifier: Apache-2.0 + +"""End-to-end CLI smoke: the full Zephyr pipeline composes off-cluster. + +Uses ``file://`` URIs in a local parquet manifest so the URI-mode path +(``Dataset.from_files`` → ``load_parquet`` → ``map_shard`` w/ thread +pool fetch → write_parquet) exercises against a real fsspec backend +without needing GCS auth or network access. +""" + +from __future__ import annotations + +from pathlib import Path + +import gemmi +import pyarrow as pa +import pyarrow.parquet as pq + +import cli + + +def _make_local_manifest(tmp_path: Path, synthetic_cif: str, n_rows: int) -> Path: + """Write `n_rows` copies of the synthetic cif as individual files + + a parquet manifest pointing at them via ``file://`` URIs.""" + cifs_dir = tmp_path / "cifs" + cifs_dir.mkdir() + entry_ids, uris = [], [] + for i in range(n_rows): + cif_path = cifs_dir / f"s{i}.cif" + cif_path.write_text(synthetic_cif) + entry_ids.append(f"AF-SMOKE{i:03d}-F1") + uris.append(f"file://{cif_path}") + manifest = tmp_path / "manifest.parquet" + pq.write_table( + pa.table({"entry_id": entry_ids, "gcs_uri": uris}), + str(manifest), + ) + return manifest + + +_REQUIRED_OUTPUT_COLUMNS = { + "entry_id", "structure", "document", + "sha1", "seq_len", "global_plddt", "contacts_emitted", +} + + +def test_cmd_generate_uri_mode_e2e(tmp_path, synthetic_cif): + """URI mode: parquet manifest → fetch + parse + gen → single output parquet.""" + manifest = _make_local_manifest(tmp_path, synthetic_cif, n_rows=3) + out = tmp_path / "docs.parquet" + + args = cli.build_parser().parse_args([ + "generate", + "--input", str(manifest), + "--out", str(out), + # Default cif_uri_column='gcs_uri' picks our column. + # ThreadPool of 2 keeps the test deterministic + fast. + "--fetch-concurrency", "2", + ]) + cli.cmd_generate(args) + + tbl = pq.read_table(str(out)) + assert tbl.num_rows == 3 + cols = set(tbl.column_names) + assert _REQUIRED_OUTPUT_COLUMNS.issubset(cols) + # gcs_uri is in the manifest (it's our cif source) and listed in + # _OPTIONAL_PASSTHROUGH, so it should also appear in the output. + assert "gcs_uri" in cols, f"expected gcs_uri passthrough; got {sorted(cols)}" + structures = set(tbl.column("structure").to_pylist()) + assert structures == {"contacts-and-distances-v2"} + # entry_id + gcs_uri are threaded through verbatim from the manifest. + assert tbl.column("entry_id").to_pylist() == ["AF-SMOKE000-F1", "AF-SMOKE001-F1", "AF-SMOKE002-F1"] + for u in tbl.column("gcs_uri").to_pylist(): + assert u.startswith("file://") and u.endswith(".cif") + # Each doc starts with the v2 marker + the synthetic poly-ALA sequence, + # ends with , and its sha1 is consistent. + import hashlib + for d, sha in zip(tbl.column("document").to_pylist(), tbl.column("sha1").to_pylist()): + assert d.startswith(" ") + assert d.endswith("") + assert sha == hashlib.sha1(d.encode()).hexdigest() + + +def test_cmd_generate_inline_cif_text_fallback(tmp_path, synthetic_cif): + """--cif-text-column path: bulk inline-cif read, no URI fetching.""" + manifest = tmp_path / "inline.parquet" + pq.write_table( + pa.table({ + "entry_id": ["AF-INL1-F1", "AF-INL2-F1"], + "cif_content": [synthetic_cif, synthetic_cif], + }), + str(manifest), + ) + out = tmp_path / "inline_docs.parquet" + args = cli.build_parser().parse_args([ + "generate", + "--input", str(manifest), + "--cif-text-column", "cif_content", + "--out", str(out), + ]) + cli.cmd_generate(args) + tbl = pq.read_table(str(out)) + assert tbl.num_rows == 2 + + +def test_cmd_generate_passthrough_columns_propagated(tmp_path, synthetic_cif): + """Manifest columns matching the passthrough wishlist are copied to output. + + Mirrors the afdb-1.6M shape (a 'split' + cluster ids alongside entry_id + + gcs_uri). The schema peek in ``_resolve_input_columns`` should detect them + and pass them through verbatim to every output row. + """ + cifs_dir = tmp_path / "cifs" + cifs_dir.mkdir() + rows = [] + for i, split in enumerate(["train", "train", "val"]): + cif_path = cifs_dir / f"r{i}.cif" + cif_path.write_text(synthetic_cif) + rows.append({ + "entry_id": f"AF-PT{i:02d}-F1", + "gcs_uri": f"file://{cif_path}", + "split": split, + "seq_cluster_id": f"clust{i // 2}", + "struct_cluster_id": f"sclust{i}", + }) + manifest = tmp_path / "rich.parquet" + pq.write_table( + pa.table({k: [r[k] for r in rows] for k in rows[0]}), + str(manifest), + ) + out = tmp_path / "rich_out.parquet" + args = cli.build_parser().parse_args([ + "generate", "--input", str(manifest), "--out", str(out), + "--fetch-concurrency", "2", + ]) + cli.cmd_generate(args) + + tbl = pq.read_table(str(out)) + assert tbl.num_rows == 3 + cols = set(tbl.column_names) + assert _REQUIRED_OUTPUT_COLUMNS.issubset(cols) + assert {"split", "seq_cluster_id", "struct_cluster_id"}.issubset(cols), ( + f"expected passthrough columns present in output, got {sorted(cols)}" + ) + # Per-row values copied unchanged (and aligned to entry_id, since + # ``executor.map`` preserves row order within a shard). + assert tbl.column("split").to_pylist() == ["train", "train", "val"] + assert tbl.column("seq_cluster_id").to_pylist() == ["clust0", "clust0", "clust1"] + + +def test_cmd_generate_num_docs_caps_total(tmp_path, synthetic_cif): + """--num-docs N collapses to a single output file with up to N rows.""" + manifest = _make_local_manifest(tmp_path, synthetic_cif, n_rows=5) + out = tmp_path / "capped.parquet" + args = cli.build_parser().parse_args([ + "generate", "--input", str(manifest), "--out", str(out), + "--num-docs", "2", + "--fetch-concurrency", "2", + ]) + cli.cmd_generate(args) + assert pq.read_table(str(out)).num_rows == 2 diff --git a/experiments/exp5_data_contacts_and_distances_v2_zephyr/tests/test_parse.py b/experiments/exp5_data_contacts_and_distances_v2_zephyr/tests/test_parse.py new file mode 100644 index 0000000..92f05e9 --- /dev/null +++ b/experiments/exp5_data_contacts_and_distances_v2_zephyr/tests/test_parse.py @@ -0,0 +1,79 @@ +# Copyright The MarinFold Authors +# SPDX-License-Identifier: Apache-2.0 + +"""Sanity tests for exp5's columnar ParsedStructure. + +Not exhaustive — the byte-identity test against exp34 is what catches +correctness regressions on the generation side. These tests just pin +the *shape* of the new representation so a future change to the +columnar layout doesn't silently break downstream code. +""" + +from __future__ import annotations + +import math + +import numpy as np + +import parse + + +def test_parsed_structure_shapes(small_cif): + """Per-residue arrays line up with sequence length; atom CSR offsets are monotonic.""" + ps = parse.parse_cif_content(small_cif, entry_id="AF-TEST-F1") + n = ps.num_residues + assert n == 5 + assert ps.sequence == ("ALA",) * 5 + assert ps.plddt_per_residue.shape == (n,) + assert ps.plddt_per_residue.dtype == np.float64 + assert ps.cb_or_ca_xyz.shape == (n, 3) + assert ps.cb_or_ca_xyz.dtype == np.float64 + assert ps.atom_offsets.shape == (n + 1,) + assert ps.atom_offsets[0] == 0 + assert np.all(np.diff(ps.atom_offsets) >= 0) # monotonic non-decreasing + assert ps.atom_name_id.shape == (int(ps.atom_offsets[-1]),) + assert ps.atom_xyz.shape == (int(ps.atom_offsets[-1]), 3) + + +def test_atoms_for_recovers_per_residue_data(small_cif): + """``atoms_for(i)`` returns the right per-residue atom slice.""" + ps = parse.parse_cif_content(small_cif, entry_id="AF-TEST-F1") + # The synthetic fixture emits N/CA/C/O/CB per residue (5 atoms, all in vocab). + for i in range(ps.num_residues): + name_ids, xyz = ps.atoms_for(i) + assert len(name_ids) == 5, f"residue {i}: expected 5 in-vocab atoms" + assert xyz.shape == (5, 3) + # Convert ids back to names — the order matches the PDB record order + # (N, CA, C, O, CB), which the fast-path single-pass extractor + # preserves. + names = [parse._ATOM_NAMES_TUPLE[int(k)] for k in name_ids] + assert names == ["N", "CA", "C", "O", "CB"] + + +def test_cb_or_ca_falls_back_to_ca_for_gly(small_cif): + """For non-GLY residues with CB present, cb_or_ca_xyz is the CB position.""" + ps = parse.parse_cif_content(small_cif, entry_id="AF-TEST-F1") + # Synthetic fixture is poly-ALA so cb_or_ca_xyz row should be the CB + # coordinate (4.5, 1.5, z + 0.5) for residue i (1-based in PDB, 0-based here). + for i in range(ps.num_residues): + x, y, z = ps.cb_or_ca_xyz[i].tolist() + expected_z = (i + 1) * 3.0 + 0.5 + assert math.isclose(x, 4.5) + assert math.isclose(y, 1.5) + assert math.isclose(z, expected_z) + + +def test_plddt_per_residue_equals_mean_b_factor(small_cif): + """All heavy atoms have b_iso=90 in the fixture → plddt = 90.0 per residue.""" + ps = parse.parse_cif_content(small_cif, entry_id="AF-TEST-F1") + assert np.allclose(ps.plddt_per_residue, 90.0) + assert math.isclose(ps.global_plddt, 90.0) + + +def test_try_parse_returns_none_on_bad_input(): + """``try_parse_cif_content`` warns and returns None on garbage.""" + import warnings + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + out = parse.try_parse_cif_content("not a cif", entry_id="X") + assert out is None diff --git a/experiments/exp5_data_contacts_and_distances_v2_zephyr/uv.lock b/experiments/exp5_data_contacts_and_distances_v2_zephyr/uv.lock new file mode 100644 index 0000000..ada572b --- /dev/null +++ b/experiments/exp5_data_contacts_and_distances_v2_zephyr/uv.lock @@ -0,0 +1,2140 @@ +version = 1 +revision = 3 +requires-python = ">=3.11, <3.13" +resolution-markers = [ + "sys_platform == 'darwin'", + "sys_platform == 'linux'", +] +supported-markers = [ + "sys_platform == 'darwin'", + "sys_platform == 'linux'", +] + +[options] +prerelease-mode = "allow" +fork-strategy = "fewest" + +[[package]] +name = "aiobotocore" +version = "3.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "aioitertools", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "botocore", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "jmespath", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "multidict", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "python-dateutil", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "wrapt", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e7/75/42cce839c2ec263ff74b10b650fe36b066fbb124cbee6f247eac0983e1ab/aiobotocore-3.7.0.tar.gz", hash = "sha256:c64d871ed5491a6571948dd48eabd185b46c6c23b64e3afd0c059fc7593ada30", size = 127054, upload-time = "2026-05-09T10:02:52.332Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/5f/85535dfb3cfd6442d66d1df1694062c5d6df02f895329e7e120b2a3d2b8b/aiobotocore-3.7.0-py3-none-any.whl", hash = "sha256:680bde7c64679a821a9312641b759d9497f790ba8b2e88c6959e6273ee765b8e", size = 89539, upload-time = "2026-05-09T10:02:50.389Z" }, +] + +[[package]] +name = "aiohappyeyeballs" +version = "2.6.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/33/c6/61a2d7b7572279226bb2e7f61d7a19ca7c90da0329c93fa0d560cbf288d8/aiohappyeyeballs-2.6.2.tar.gz", hash = "sha256:e202810ee718bd01fc6ef49e8ea53d023d5cb6b581076d7925aa499fa55dbe64", size = 22591, upload-time = "2026-05-20T15:12:24.631Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/fc/a7bf5b6e4e617b45f90f2d9d2a68519c249c81dd4fc2658c7a2a61c4f4b7/aiohappyeyeballs-2.6.2-py3-none-any.whl", hash = "sha256:4708045e2d7a6c6bdf8aafa8ed39649eaf926a4543b54560659129e3365953c4", size = 15062, upload-time = "2026-05-20T15:12:23.328Z" }, +] + +[[package]] +name = "aiohttp" +version = "3.13.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohappyeyeballs", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "aiosignal", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "attrs", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "frozenlist", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "multidict", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "propcache", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "yarl", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/77/9a/152096d4808df8e4268befa55fba462f440f14beab85e8ad9bf990516918/aiohttp-3.13.5.tar.gz", hash = "sha256:9d98cc980ecc96be6eb4c1994ce35d28d8b1f5e5208a23b421187d1209dbb7d1", size = 7858271, upload-time = "2026-03-31T22:01:03.343Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d6/f5/a20c4ac64aeaef1679e25c9983573618ff765d7aa829fa2b84ae7573169e/aiohttp-3.13.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ab7229b6f9b5c1ba4910d6c41a9eb11f543eadb3f384df1b4c293f4e73d44d6", size = 757513, upload-time = "2026-03-31T21:57:02.146Z" }, + { url = "https://files.pythonhosted.org/packages/75/0a/39fa6c6b179b53fcb3e4b3d2b6d6cad0180854eda17060c7218540102bef/aiohttp-3.13.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8f14c50708bb156b3a3ca7230b3d820199d56a48e3af76fa21c2d6087190fe3d", size = 506748, upload-time = "2026-03-31T21:57:04.275Z" }, + { url = "https://files.pythonhosted.org/packages/87/ec/e38ce072e724fd7add6243613f8d1810da084f54175353d25ccf9f9c7e5a/aiohttp-3.13.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e7d2f8616f0ff60bd332022279011776c3ac0faa0f1b463f7bb12326fbc97a1c", size = 501673, upload-time = "2026-03-31T21:57:06.208Z" }, + { url = "https://files.pythonhosted.org/packages/ba/ba/3bc7525d7e2beaa11b309a70d48b0d3cfc3c2089ec6a7d0820d59c657053/aiohttp-3.13.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2567b72e1ffc3ab25510db43f355b29eeada56c0a622e58dcdb19530eb0a3cb", size = 1763757, upload-time = "2026-03-31T21:57:07.882Z" }, + { url = "https://files.pythonhosted.org/packages/5e/ab/e87744cf18f1bd78263aba24924d4953b41086bd3a31d22452378e9028a0/aiohttp-3.13.5-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fb0540c854ac9c0c5ad495908fdfd3e332d553ec731698c0e29b1877ba0d2ec6", size = 1720152, upload-time = "2026-03-31T21:57:09.946Z" }, + { url = "https://files.pythonhosted.org/packages/6b/f3/ed17a6f2d742af17b50bae2d152315ed1b164b07a5fd5cc1754d99e4dfa5/aiohttp-3.13.5-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c9883051c6972f58bfc4ebb2116345ee2aa151178e99c3f2b2bbe2af712abd13", size = 1818010, upload-time = "2026-03-31T21:57:12.157Z" }, + { url = "https://files.pythonhosted.org/packages/53/06/ecbc63dc937192e2a5cb46df4d3edb21deb8225535818802f210a6ea5816/aiohttp-3.13.5-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2294172ce08a82fb7c7273485895de1fa1186cc8294cfeb6aef4af42ad261174", size = 1907251, upload-time = "2026-03-31T21:57:14.023Z" }, + { url = "https://files.pythonhosted.org/packages/7e/a5/0521aa32c1ddf3aa1e71dcc466be0b7db2771907a13f18cddaa45967d97b/aiohttp-3.13.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3a807cabd5115fb55af198b98178997a5e0e57dead43eb74a93d9c07d6d4a7dc", size = 1759969, upload-time = "2026-03-31T21:57:16.146Z" }, + { url = "https://files.pythonhosted.org/packages/f6/78/a38f8c9105199dd3b9706745865a8a59d0041b6be0ca0cc4b2ccf1bab374/aiohttp-3.13.5-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:aa6d0d932e0f39c02b80744273cd5c388a2d9bc07760a03164f229c8e02662f6", size = 1616871, upload-time = "2026-03-31T21:57:17.856Z" }, + { url = "https://files.pythonhosted.org/packages/6f/41/27392a61ead8ab38072105c71aa44ff891e71653fe53d576a7067da2b4e8/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:60869c7ac4aaabe7110f26499f3e6e5696eae98144735b12a9c3d9eae2b51a49", size = 1739844, upload-time = "2026-03-31T21:57:19.679Z" }, + { url = "https://files.pythonhosted.org/packages/6e/55/5564e7ae26d94f3214250009a0b1c65a0c6af4bf88924ccb6fdab901de28/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:26d2f8546f1dfa75efa50c3488215a903c0168d253b75fba4210f57ab77a0fb8", size = 1731969, upload-time = "2026-03-31T21:57:22.006Z" }, + { url = "https://files.pythonhosted.org/packages/6d/c5/705a3929149865fc941bcbdd1047b238e4a72bcb215a9b16b9d7a2e8d992/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1162a1492032c82f14271e831c8f4b49f2b6078f4f5fc74de2c912fa225d51d", size = 1795193, upload-time = "2026-03-31T21:57:24.256Z" }, + { url = "https://files.pythonhosted.org/packages/a6/19/edabed62f718d02cff7231ca0db4ef1c72504235bc467f7b67adb1679f48/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:8b14eb3262fad0dc2f89c1a43b13727e709504972186ff6a99a3ecaa77102b6c", size = 1606477, upload-time = "2026-03-31T21:57:26.364Z" }, + { url = "https://files.pythonhosted.org/packages/de/fc/76f80ef008675637d88d0b21584596dc27410a990b0918cb1e5776545b5b/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ca9ac61ac6db4eb6c2a0cd1d0f7e1357647b638ccc92f7e9d8d133e71ed3c6ac", size = 1813198, upload-time = "2026-03-31T21:57:28.316Z" }, + { url = "https://files.pythonhosted.org/packages/e5/67/5b3ac26b80adb20ea541c487f73730dc8fa107d632c998f25bbbab98fcda/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7996023b2ed59489ae4762256c8516df9820f751cf2c5da8ed2fb20ee50abab3", size = 1752321, upload-time = "2026-03-31T21:57:30.549Z" }, + { url = "https://files.pythonhosted.org/packages/be/6f/353954c29e7dcce7cf00280a02c75f30e133c00793c7a2ed3776d7b2f426/aiohttp-3.13.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:023ecba036ddd840b0b19bf195bfae970083fd7024ce1ac22e9bba90464620e9", size = 748876, upload-time = "2026-03-31T21:57:36.319Z" }, + { url = "https://files.pythonhosted.org/packages/f5/1b/428a7c64687b3b2e9cd293186695affc0e1e54a445d0361743b231f11066/aiohttp-3.13.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:15c933ad7920b7d9a20de151efcd05a6e38302cbf0e10c9b2acb9a42210a2416", size = 499557, upload-time = "2026-03-31T21:57:38.236Z" }, + { url = "https://files.pythonhosted.org/packages/29/47/7be41556bfbb6917069d6a6634bb7dd5e163ba445b783a90d40f5ac7e3a7/aiohttp-3.13.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ab2899f9fa2f9f741896ebb6fa07c4c883bfa5c7f2ddd8cf2aafa86fa981b2d2", size = 500258, upload-time = "2026-03-31T21:57:39.923Z" }, + { url = "https://files.pythonhosted.org/packages/67/84/c9ecc5828cb0b3695856c07c0a6817a99d51e2473400f705275a2b3d9239/aiohttp-3.13.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60eaa2d440cd4707696b52e40ed3e2b0f73f65be07fd0ef23b6b539c9c0b0b4", size = 1749199, upload-time = "2026-03-31T21:57:41.938Z" }, + { url = "https://files.pythonhosted.org/packages/f0/d3/3c6d610e66b495657622edb6ae7c7fd31b2e9086b4ec50b47897ad6042a9/aiohttp-3.13.5-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:55b3bdd3292283295774ab585160c4004f4f2f203946997f49aac032c84649e9", size = 1721013, upload-time = "2026-03-31T21:57:43.904Z" }, + { url = "https://files.pythonhosted.org/packages/49/a0/24409c12217456df0bae7babe3b014e460b0b38a8e60753d6cb339f6556d/aiohttp-3.13.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2b2355dc094e5f7d45a7bb262fe7207aa0460b37a0d87027dcf21b5d890e7d5", size = 1781501, upload-time = "2026-03-31T21:57:46.285Z" }, + { url = "https://files.pythonhosted.org/packages/98/9d/b65ec649adc5bccc008b0957a9a9c691070aeac4e41cea18559fef49958b/aiohttp-3.13.5-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b38765950832f7d728297689ad78f5f2cf79ff82487131c4d26fe6ceecdc5f8e", size = 1878981, upload-time = "2026-03-31T21:57:48.734Z" }, + { url = "https://files.pythonhosted.org/packages/57/d8/8d44036d7eb7b6a8ec4c5494ea0c8c8b94fbc0ed3991c1a7adf230df03bf/aiohttp-3.13.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b18f31b80d5a33661e08c89e202edabf1986e9b49c42b4504371daeaa11b47c1", size = 1767934, upload-time = "2026-03-31T21:57:51.171Z" }, + { url = "https://files.pythonhosted.org/packages/31/04/d3f8211f273356f158e3464e9e45484d3fb8c4ce5eb2f6fe9405c3273983/aiohttp-3.13.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:33add2463dde55c4f2d9635c6ab33ce154e5ecf322bd26d09af95c5f81cfa286", size = 1566671, upload-time = "2026-03-31T21:57:53.326Z" }, + { url = "https://files.pythonhosted.org/packages/41/db/073e4ebe00b78e2dfcacff734291651729a62953b48933d765dc513bf798/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:327cc432fdf1356fb4fbc6fe833ad4e9f6aacb71a8acaa5f1855e4b25910e4a9", size = 1705219, upload-time = "2026-03-31T21:57:55.385Z" }, + { url = "https://files.pythonhosted.org/packages/48/45/7dfba71a2f9fd97b15c95c06819de7eb38113d2cdb6319669195a7d64270/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7c35b0bf0b48a70b4cb4fc5d7bed9b932532728e124874355de1a0af8ec4bc88", size = 1743049, upload-time = "2026-03-31T21:57:57.341Z" }, + { url = "https://files.pythonhosted.org/packages/18/71/901db0061e0f717d226386a7f471bb59b19566f2cae5f0d93874b017271f/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:df23d57718f24badef8656c49743e11a89fd6f5358fa8a7b96e728fda2abf7d3", size = 1749557, upload-time = "2026-03-31T21:57:59.626Z" }, + { url = "https://files.pythonhosted.org/packages/08/d5/41eebd16066e59cd43728fe74bce953d7402f2b4ddfdfef2c0e9f17ca274/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:02e048037a6501a5ec1f6fc9736135aec6eb8a004ce48838cb951c515f32c80b", size = 1558931, upload-time = "2026-03-31T21:58:01.972Z" }, + { url = "https://files.pythonhosted.org/packages/30/e6/4a799798bf05740e66c3a1161079bda7a3dd8e22ca392481d7a7f9af82a6/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31cebae8b26f8a615d2b546fee45d5ffb76852ae6450e2a03f42c9102260d6fe", size = 1774125, upload-time = "2026-03-31T21:58:04.007Z" }, + { url = "https://files.pythonhosted.org/packages/84/63/7749337c90f92bc2cb18f9560d67aa6258c7060d1397d21529b8004fcf6f/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:888e78eb5ca55a615d285c3c09a7a91b42e9dd6fc699b166ebd5dee87c9ccf14", size = 1732427, upload-time = "2026-03-31T21:58:06.337Z" }, +] + +[[package]] +name = "aioitertools" +version = "0.13.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/3c/53c4a17a05fb9ea2313ee1777ff53f5e001aefd5cc85aa2f4c2d982e1e38/aioitertools-0.13.0.tar.gz", hash = "sha256:620bd241acc0bbb9ec819f1ab215866871b4bbd1f73836a55f799200ee86950c", size = 19322, upload-time = "2025-11-06T22:17:07.609Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl", hash = "sha256:0be0292b856f08dfac90e31f4739432f4cb6d7520ab9eb73e143f4f2fa5259be", size = 24182, upload-time = "2025-11-06T22:17:06.502Z" }, +] + +[[package]] +name = "aiosignal" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "frozenlist", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, +] + +[[package]] +name = "annotated-doc" +version = "0.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" }, +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, +] + +[[package]] +name = "anyio" +version = "4.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" }, +] + +[[package]] +name = "attrs" +version = "26.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, +] + +[[package]] +name = "blinker" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460, upload-time = "2024-11-08T17:25:47.436Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458, upload-time = "2024-11-08T17:25:46.184Z" }, +] + +[[package]] +name = "botocore" +version = "1.43.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jmespath", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "python-dateutil", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "urllib3", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/79/2f4be1896db3db7ccf44504253a175d56b6bd6b669619edc5147d1aa21ea/botocore-1.43.0.tar.gz", hash = "sha256:e933b31a2d644253e1d029d7d39e99ba41b87e29300534f189744cc438cdf928", size = 15286817, upload-time = "2026-04-29T22:07:31.723Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bf/4b/afc1fef8a43bafb139f57f73bbd70df82807af5934321e8112ae50668827/botocore-1.43.0-py3-none-any.whl", hash = "sha256:cc5b15eaec3c6eac05d8012cb5ef17ebe891beb88a16ca13c374bfaece1241e6", size = 14970102, upload-time = "2026-04-29T22:07:27Z" }, +] + +[[package]] +name = "braceexpand" +version = "0.1.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/93/badd4f5ccf25209f3fef2573073da9fe4a45a3da99fca2f800f942130c0f/braceexpand-0.1.7.tar.gz", hash = "sha256:e6e539bd20eaea53547472ff94f4fb5c3d3bf9d0a89388c4b56663aba765f705", size = 7777, upload-time = "2021-05-07T13:49:07.323Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/93/e8c04e80e82391a6e51f218ca49720f64236bc824e92152a2633b74cf7ab/braceexpand-0.1.7-py2.py3-none-any.whl", hash = "sha256:91332d53de7828103dcae5773fb43bc34950b0c8160e35e0f44c4427a3b85014", size = 5923, upload-time = "2021-05-07T13:49:05.146Z" }, +] + +[[package]] +name = "certifi" +version = "2026.5.20" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/ce/ee2ecad540810a79593028e88299baeae54d346cc7a0d94b6199988b89b1/certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d", size = 135422, upload-time = "2026-05-20T11:46:50.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/59/8c/57e832b7af6d7c5abe66eb3fbe3a3a32f4d11ea23a1aa7131371035be991/certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897", size = 134134, upload-time = "2026-05-20T11:46:48.578Z" }, +] + +[[package]] +name = "cffi" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser", marker = "(implementation_name != 'PyPy' and sys_platform == 'darwin') or (implementation_name != 'PyPy' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" }, + { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" }, + { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" }, + { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" }, + { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" }, + { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" }, + { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" }, + { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" }, + { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" }, + { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" }, + { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, + { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, + { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, + { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, + { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, + { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, + { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, + { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, + { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/d7/b5b7020a0565c2e9fa8c09f4b5fa6232feb326b8c20081ccded47ea368fd/charset_normalizer-3.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7", size = 309705, upload-time = "2026-04-02T09:26:02.191Z" }, + { url = "https://files.pythonhosted.org/packages/5a/53/58c29116c340e5456724ecd2fff4196d236b98f3da97b404bc5e51ac3493/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7", size = 206419, upload-time = "2026-04-02T09:26:03.583Z" }, + { url = "https://files.pythonhosted.org/packages/b2/02/e8146dc6591a37a00e5144c63f29fb7c97a734ea8a111190783c0e60ab63/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e", size = 227901, upload-time = "2026-04-02T09:26:04.738Z" }, + { url = "https://files.pythonhosted.org/packages/fb/73/77486c4cd58f1267bf17db420e930c9afa1b3be3fe8c8b8ebbebc9624359/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c", size = 222742, upload-time = "2026-04-02T09:26:06.36Z" }, + { url = "https://files.pythonhosted.org/packages/a1/fa/f74eb381a7d94ded44739e9d94de18dc5edc9c17fb8c11f0a6890696c0a9/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df", size = 214061, upload-time = "2026-04-02T09:26:08.347Z" }, + { url = "https://files.pythonhosted.org/packages/dc/92/42bd3cefcf7687253fb86694b45f37b733c97f59af3724f356fa92b8c344/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265", size = 199239, upload-time = "2026-04-02T09:26:09.823Z" }, + { url = "https://files.pythonhosted.org/packages/4c/3d/069e7184e2aa3b3cddc700e3dd267413dc259854adc3380421c805c6a17d/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4", size = 210173, upload-time = "2026-04-02T09:26:10.953Z" }, + { url = "https://files.pythonhosted.org/packages/62/51/9d56feb5f2e7074c46f93e0ebdbe61f0848ee246e2f0d89f8e20b89ebb8f/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e", size = 209841, upload-time = "2026-04-02T09:26:12.142Z" }, + { url = "https://files.pythonhosted.org/packages/d2/59/893d8f99cc4c837dda1fe2f1139079703deb9f321aabcb032355de13b6c7/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38", size = 200304, upload-time = "2026-04-02T09:26:13.711Z" }, + { url = "https://files.pythonhosted.org/packages/7d/1d/ee6f3be3464247578d1ed5c46de545ccc3d3ff933695395c402c21fa6b77/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c", size = 229455, upload-time = "2026-04-02T09:26:14.941Z" }, + { url = "https://files.pythonhosted.org/packages/54/bb/8fb0a946296ea96a488928bdce8ef99023998c48e4713af533e9bb98ef07/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b", size = 210036, upload-time = "2026-04-02T09:26:16.478Z" }, + { url = "https://files.pythonhosted.org/packages/9a/bc/015b2387f913749f82afd4fcba07846d05b6d784dd16123cb66860e0237d/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c", size = 224739, upload-time = "2026-04-02T09:26:17.751Z" }, + { url = "https://files.pythonhosted.org/packages/17/ab/63133691f56baae417493cba6b7c641571a2130eb7bceba6773367ab9ec5/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d", size = 216277, upload-time = "2026-04-02T09:26:18.981Z" }, + { url = "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", size = 311328, upload-time = "2026-04-02T09:26:24.331Z" }, + { url = "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", size = 208061, upload-time = "2026-04-02T09:26:25.568Z" }, + { url = "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", size = 229031, upload-time = "2026-04-02T09:26:26.865Z" }, + { url = "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", size = 225239, upload-time = "2026-04-02T09:26:28.044Z" }, + { url = "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", size = 216589, upload-time = "2026-04-02T09:26:29.239Z" }, + { url = "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", size = 202733, upload-time = "2026-04-02T09:26:30.5Z" }, + { url = "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", size = 212652, upload-time = "2026-04-02T09:26:31.709Z" }, + { url = "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", size = 211229, upload-time = "2026-04-02T09:26:33.282Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", size = 203552, upload-time = "2026-04-02T09:26:34.845Z" }, + { url = "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", size = 230806, upload-time = "2026-04-02T09:26:36.152Z" }, + { url = "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", size = 212316, upload-time = "2026-04-02T09:26:37.672Z" }, + { url = "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464", size = 227274, upload-time = "2026-04-02T09:26:38.93Z" }, + { url = "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", size = 218468, upload-time = "2026-04-02T09:26:40.17Z" }, + { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, +] + +[[package]] +name = "click" +version = "8.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9b/98/518d8e5081007684232226f475082b30087d0f585e8457db087298259f49/click-8.4.1.tar.gz", hash = "sha256:918b5633eddf6b41c32d4f454bf0de810065c74e3f7dbf8ee5452f8be88d3e96", size = 353007, upload-time = "2026-05-22T04:08:37.769Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/0d/67e5b4109ea4a837e80daa87c2c696711955e40449a97e8926672534def2/click-8.4.1-py3-none-any.whl", hash = "sha256:482be17c6991b8c19c5429a1e995d9b0efdbb63172824c41f99965dc0ade8ec2", size = 116639, upload-time = "2026-05-22T04:08:35.26Z" }, +] + +[[package]] +name = "cloudpickle" +version = "3.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330, upload-time = "2025-11-03T09:25:26.604Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228, upload-time = "2025-11-03T09:25:25.534Z" }, +] + +[[package]] +name = "connect-python" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "pyqwest", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/74/fc/0e4798c53e2754f5de36ecf4d198706cb23711d603df6c008f6e7b5b21ae/connect_python-0.9.0.tar.gz", hash = "sha256:a188ec843b0f5953b7e1b88061af50ad91c9aaa2e982d7a89a63ae5c1fff932e", size = 46094, upload-time = "2026-03-19T02:40:42.279Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4c/15/5b42df2d9d34e5103f2b69e4f6a4aeb47c52589eaac8d53eb5b0a40eabaa/connect_python-0.9.0-py3-none-any.whl", hash = "sha256:896171fa7236d4e1557e3f7eee76daa8c9dd762f2c21662515f2060f1b542574", size = 63381, upload-time = "2026-03-19T02:40:40.743Z" }, +] + +[[package]] +name = "contourpy" +version = "1.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/2e/c4390a31919d8a78b90e8ecf87cd4b4c4f05a5b48d05ec17db8e5404c6f4/contourpy-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:709a48ef9a690e1343202916450bc48b9e51c049b089c7f79a267b46cffcdaa1", size = 288773, upload-time = "2025-07-26T12:01:02.277Z" }, + { url = "https://files.pythonhosted.org/packages/0d/44/c4b0b6095fef4dc9c420e041799591e3b63e9619e3044f7f4f6c21c0ab24/contourpy-1.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:23416f38bfd74d5d28ab8429cc4d63fa67d5068bd711a85edb1c3fb0c3e2f381", size = 270149, upload-time = "2025-07-26T12:01:04.072Z" }, + { url = "https://files.pythonhosted.org/packages/30/2e/dd4ced42fefac8470661d7cb7e264808425e6c5d56d175291e93890cce09/contourpy-1.3.3-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:929ddf8c4c7f348e4c0a5a3a714b5c8542ffaa8c22954862a46ca1813b667ee7", size = 329222, upload-time = "2025-07-26T12:01:05.688Z" }, + { url = "https://files.pythonhosted.org/packages/f2/74/cc6ec2548e3d276c71389ea4802a774b7aa3558223b7bade3f25787fafc2/contourpy-1.3.3-cp311-cp311-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9e999574eddae35f1312c2b4b717b7885d4edd6cb46700e04f7f02db454e67c1", size = 377234, upload-time = "2025-07-26T12:01:07.054Z" }, + { url = "https://files.pythonhosted.org/packages/03/b3/64ef723029f917410f75c09da54254c5f9ea90ef89b143ccadb09df14c15/contourpy-1.3.3-cp311-cp311-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf67e0e3f482cb69779dd3061b534eb35ac9b17f163d851e2a547d56dba0a3a", size = 380555, upload-time = "2025-07-26T12:01:08.801Z" }, + { url = "https://files.pythonhosted.org/packages/5f/4b/6157f24ca425b89fe2eb7e7be642375711ab671135be21e6faa100f7448c/contourpy-1.3.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51e79c1f7470158e838808d4a996fa9bac72c498e93d8ebe5119bc1e6becb0db", size = 355238, upload-time = "2025-07-26T12:01:10.319Z" }, + { url = "https://files.pythonhosted.org/packages/98/56/f914f0dd678480708a04cfd2206e7c382533249bc5001eb9f58aa693e200/contourpy-1.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:598c3aaece21c503615fd59c92a3598b428b2f01bfb4b8ca9c4edeecc2438620", size = 1326218, upload-time = "2025-07-26T12:01:12.659Z" }, + { url = "https://files.pythonhosted.org/packages/fb/d7/4a972334a0c971acd5172389671113ae82aa7527073980c38d5868ff1161/contourpy-1.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:322ab1c99b008dad206d406bb61d014cf0174df491ae9d9d0fac6a6fda4f977f", size = 1392867, upload-time = "2025-07-26T12:01:15.533Z" }, + { url = "https://files.pythonhosted.org/packages/be/45/adfee365d9ea3d853550b2e735f9d66366701c65db7855cd07621732ccfc/contourpy-1.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b08a32ea2f8e42cf1d4be3169a98dd4be32bafe4f22b6c4cb4ba810fa9e5d2cb", size = 293419, upload-time = "2025-07-26T12:01:21.16Z" }, + { url = "https://files.pythonhosted.org/packages/53/3e/405b59cfa13021a56bba395a6b3aca8cec012b45bf177b0eaf7a202cde2c/contourpy-1.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:556dba8fb6f5d8742f2923fe9457dbdd51e1049c4a43fd3986a0b14a1d815fc6", size = 273979, upload-time = "2025-07-26T12:01:22.448Z" }, + { url = "https://files.pythonhosted.org/packages/d4/1c/a12359b9b2ca3a845e8f7f9ac08bdf776114eb931392fcad91743e2ea17b/contourpy-1.3.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92d9abc807cf7d0e047b95ca5d957cf4792fcd04e920ca70d48add15c1a90ea7", size = 332653, upload-time = "2025-07-26T12:01:24.155Z" }, + { url = "https://files.pythonhosted.org/packages/63/12/897aeebfb475b7748ea67b61e045accdfcf0d971f8a588b67108ed7f5512/contourpy-1.3.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2e8faa0ed68cb29af51edd8e24798bb661eac3bd9f65420c1887b6ca89987c8", size = 379536, upload-time = "2025-07-26T12:01:25.91Z" }, + { url = "https://files.pythonhosted.org/packages/43/8a/a8c584b82deb248930ce069e71576fc09bd7174bbd35183b7943fb1064fd/contourpy-1.3.3-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:626d60935cf668e70a5ce6ff184fd713e9683fb458898e4249b63be9e28286ea", size = 384397, upload-time = "2025-07-26T12:01:27.152Z" }, + { url = "https://files.pythonhosted.org/packages/cc/8f/ec6289987824b29529d0dfda0d74a07cec60e54b9c92f3c9da4c0ac732de/contourpy-1.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d00e655fcef08aba35ec9610536bfe90267d7ab5ba944f7032549c55a146da1", size = 362601, upload-time = "2025-07-26T12:01:28.808Z" }, + { url = "https://files.pythonhosted.org/packages/05/0a/a3fe3be3ee2dceb3e615ebb4df97ae6f3828aa915d3e10549ce016302bd1/contourpy-1.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:451e71b5a7d597379ef572de31eeb909a87246974d960049a9848c3bc6c41bf7", size = 1331288, upload-time = "2025-07-26T12:01:31.198Z" }, + { url = "https://files.pythonhosted.org/packages/33/1d/acad9bd4e97f13f3e2b18a3977fe1b4a37ecf3d38d815333980c6c72e963/contourpy-1.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:459c1f020cd59fcfe6650180678a9993932d80d44ccde1fa1868977438f0b411", size = 1403386, upload-time = "2025-07-26T12:01:33.947Z" }, + { url = "https://files.pythonhosted.org/packages/a5/29/8dcfe16f0107943fa92388c23f6e05cff0ba58058c4c95b00280d4c75a14/contourpy-1.3.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cd5dfcaeb10f7b7f9dc8941717c6c2ade08f587be2226222c12b25f0483ed497", size = 278809, upload-time = "2025-07-26T12:02:52.74Z" }, + { url = "https://files.pythonhosted.org/packages/85/a9/8b37ef4f7dafeb335daee3c8254645ef5725be4d9c6aa70b50ec46ef2f7e/contourpy-1.3.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:0c1fc238306b35f246d61a1d416a627348b5cf0648648a031e14bb8705fcdfe8", size = 261593, upload-time = "2025-07-26T12:02:54.037Z" }, + { url = "https://files.pythonhosted.org/packages/0a/59/ebfb8c677c75605cc27f7122c90313fd2f375ff3c8d19a1694bda74aaa63/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70f9aad7de812d6541d29d2bbf8feb22ff7e1c299523db288004e3157ff4674e", size = 302202, upload-time = "2025-07-26T12:02:55.947Z" }, + { url = "https://files.pythonhosted.org/packages/3c/37/21972a15834d90bfbfb009b9d004779bd5a07a0ec0234e5ba8f64d5736f4/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ed3657edf08512fc3fe81b510e35c2012fbd3081d2e26160f27ca28affec989", size = 329207, upload-time = "2025-07-26T12:02:57.468Z" }, +] + +[[package]] +name = "cryptography" +version = "48.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "(platform_python_implementation != 'PyPy' and sys_platform == 'darwin') or (platform_python_implementation != 'PyPy' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9f/a9/db8f313fdcd85d767d4973515e1db101f9c71f95fced83233de224673757/cryptography-48.0.0.tar.gz", hash = "sha256:5c3932f4436d1cccb036cb0eaef46e6e2db91035166f1ad6505c3c9d5a635920", size = 832984, upload-time = "2026-05-04T22:59:38.133Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/3d/01f6dd9190170a5a241e0e98c2d04be3664a9e6f5b9b872cde63aff1c3dd/cryptography-48.0.0-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:0c558d2cdffd8f4bbb30fc7134c74d2ca9a476f830bb053074498fbc86f41ed6", size = 8001587, upload-time = "2026-05-04T22:57:36.803Z" }, + { url = "https://files.pythonhosted.org/packages/b2/6e/e90527eef33f309beb811cf7c982c3aeffcce8e3edb178baa4ca3ae4a6fa/cryptography-48.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f5333311663ea94f75dd408665686aaf426563556bb5283554a3539177e03b8c", size = 4690433, upload-time = "2026-05-04T22:57:40.373Z" }, + { url = "https://files.pythonhosted.org/packages/90/04/673510ed51ddff56575f306cf1617d80411ee76831ccd3097599140efdfe/cryptography-48.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7995ef305d7165c3f11ae07f2517e5a4f1d5c18da1376a0a9ed496336b69e5f3", size = 4710620, upload-time = "2026-05-04T22:57:42.935Z" }, + { url = "https://files.pythonhosted.org/packages/14/d5/e9c4ef932c8d800490c34d8bd589d64a31d5890e27ec9e9ad532be893294/cryptography-48.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:40ba1f85eaa6959837b1d51c9767e230e14612eea4ef110ee8854ada22da1bf5", size = 4696283, upload-time = "2026-05-04T22:57:45.294Z" }, + { url = "https://files.pythonhosted.org/packages/0c/29/174b9dfb60b12d59ecfc6cfa04bc88c21b42a54f01b8aae09bb6e51e4c7f/cryptography-48.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:369a6348999f94bbd53435c894377b20ab95f25a9065c283570e70150d8abc3c", size = 5296573, upload-time = "2026-05-04T22:57:47.933Z" }, + { url = "https://files.pythonhosted.org/packages/95/38/0d29a6fd7d0d1373f0c0c88a04ba20e359b257753ac497564cd660fc1d55/cryptography-48.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a0e692c683f4df67815a2d258b324e66f4738bd7a96a218c826dce4f4bd05d8f", size = 4743677, upload-time = "2026-05-04T22:57:50.067Z" }, + { url = "https://files.pythonhosted.org/packages/30/be/eef653013d5c63b6a490529e0316f9ac14a37602965d4903efed1399f32b/cryptography-48.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:18349bbc56f4743c8b12dc32e2bccb2cf83ee8b69a3bba74ef8ae857e26b3d25", size = 4330808, upload-time = "2026-05-04T22:57:52.301Z" }, + { url = "https://files.pythonhosted.org/packages/84/9e/500463e87abb7a0a0f9f256ec21123ecde0a7b5541a15e840ea54551fd81/cryptography-48.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e8eac43dfca5c4cccc6dad9a80504436fca53bb9bc3100a2386d730fbe6b602", size = 4695941, upload-time = "2026-05-04T22:57:54.603Z" }, + { url = "https://files.pythonhosted.org/packages/e3/dc/7303087450c2ec9e7fbb750e17c2abfbc658f23cbd0e54009509b7cc4091/cryptography-48.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9ccdac7d40688ecb5a3b4a604b8a88c8002e3442d6c60aead1db2a89a041560c", size = 5252579, upload-time = "2026-05-04T22:57:57.207Z" }, + { url = "https://files.pythonhosted.org/packages/d0/c0/7101d3b7215edcdc90c45da544961fd8ed2d6448f77577460fa75a8443f7/cryptography-48.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:bd72e68b06bb1e96913f97dd4901119bc17f39d4586a5adf2d3e47bc2b9d58b5", size = 4743326, upload-time = "2026-05-04T22:57:59.535Z" }, + { url = "https://files.pythonhosted.org/packages/ac/d8/5b833bad13016f562ab9d063d68199a4bd121d18458e439515601d3357ec/cryptography-48.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:59baa2cb386c4f0b9905bd6eb4c2a79a69a128408fd31d32ca4d7102d4156321", size = 4826672, upload-time = "2026-05-04T22:58:01.996Z" }, + { url = "https://files.pythonhosted.org/packages/98/e1/7074eb8bf3c135558c73fc2bcf0f5633f912e6fb87e868a55c454080ef09/cryptography-48.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9249e3cd978541d665967ac2cb2787fd6a62bddf1e75b3e347a594d7dacf4f74", size = 4972574, upload-time = "2026-05-04T22:58:03.968Z" }, + { url = "https://files.pythonhosted.org/packages/f2/63/61d4a4e1c6b6bab6ce1e213cd36a24c415d90e76d78c5eb8577c5541d2e8/cryptography-48.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:58d00498e8933e4a194f3076aee1b4a97dfec1a6da444535755822fe5d8b0b86", size = 7983482, upload-time = "2026-05-04T22:58:43.769Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ac/f5b5995b87770c693e2596559ffafe195b4033a57f14a82268a2842953f3/cryptography-48.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:614d0949f4790582d2cc25553abd09dd723025f0c0e7c67376a1d77196743d6e", size = 4683266, upload-time = "2026-05-04T22:58:46.064Z" }, + { url = "https://files.pythonhosted.org/packages/ec/c6/8b14f67e18338fbc4adb76f66c001f5c3610b3e2d1837f268f47a347dbbb/cryptography-48.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7ce4bfae76319a532a2dc68f82cc32f5676ee792a983187dac07183690e5c66f", size = 4696228, upload-time = "2026-05-04T22:58:48.22Z" }, + { url = "https://files.pythonhosted.org/packages/ea/73/f808fbae9514bd91b47875b003f13e284c8c6bdfd904b7944e803937eec1/cryptography-48.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:2eb992bbd4661238c5a397594c83f5b4dc2bc5b848c365c8f991b6780efcc5c7", size = 4689097, upload-time = "2026-05-04T22:58:50.9Z" }, + { url = "https://files.pythonhosted.org/packages/93/01/d86632d7d28db8ae83221995752eeb6639ffb374c2d22955648cf8d52797/cryptography-48.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:22a5cb272895dce158b2cacdfdc3debd299019659f42947dbdac6f32d68fe832", size = 5283582, upload-time = "2026-05-04T22:58:53.017Z" }, + { url = "https://files.pythonhosted.org/packages/02/e1/50edc7a50334807cc4791fc4a0ce7468b4a1416d9138eab358bfc9a3d70b/cryptography-48.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2b4d59804e8408e2fea7d1fbaf218e5ec984325221db76e6a241a9abd6cdd95c", size = 4730479, upload-time = "2026-05-04T22:58:55.611Z" }, + { url = "https://files.pythonhosted.org/packages/6f/af/99a582b1b1641ff5911ac559beb45097cf79efd4ead4657f578ef1af2d47/cryptography-48.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:984a20b0f62a26f48a3396c72e4bc34c66e356d356bf370053066b3b6d54634a", size = 4326481, upload-time = "2026-05-04T22:58:57.607Z" }, + { url = "https://files.pythonhosted.org/packages/90/ee/89aa26a06ef0a7d7611788ffd571a7c50e368cc6a4d5eef8b4884e866edb/cryptography-48.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:5a5ed8fde7a1d09376ca0b40e68cd59c69fe23b1f9768bd5824f54681626032a", size = 4688713, upload-time = "2026-05-04T22:59:00.077Z" }, + { url = "https://files.pythonhosted.org/packages/70/ba/bcb1b0bb7a33d4c7c0c4d4c7874b4a62ae4f56113a5f4baefa362dfb1f0f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:8cd666227ef7af430aa5914a9910e0ddd703e75f039cef0825cd0da71b6b711a", size = 5238165, upload-time = "2026-05-04T22:59:02.317Z" }, + { url = "https://files.pythonhosted.org/packages/c9/70/ca4003b1ce5ca3dc3186ada51908c8a9b9ff7d5cab83cc0d43ee14ec144f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9071196d81abc88b3516ac8cdfad32e2b66dd4a5393a8e68a961e9161ddc6239", size = 4729947, upload-time = "2026-05-04T22:59:05.255Z" }, + { url = "https://files.pythonhosted.org/packages/44/a0/4ec7cf774207905aef1a8d11c3750d5a1db805eb380ee4e16df317870128/cryptography-48.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1e2d54c8be6152856a36f0882ab231e70f8ec7f14e93cf87db8a2ed056bf160c", size = 4822059, upload-time = "2026-05-04T22:59:07.802Z" }, + { url = "https://files.pythonhosted.org/packages/1e/75/a2e55f99c16fcac7b5d6c1eb19ad8e00799854d6be5ca845f9259eae1681/cryptography-48.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a5da777e32ffed6f85a7b2b3f7c5cbc88c146bfcd0a1d7baf5fcc6c52ee35dd4", size = 4960575, upload-time = "2026-05-04T22:59:09.851Z" }, + { url = "https://files.pythonhosted.org/packages/be/d2/024b5e06be9d44cb021fb0e1a03d34d63989cf56a0fe62f3dfbab695b9b4/cryptography-48.0.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:84cf79f0dc8b36ac5da873481716e87aef31fcfa0444f9e1d8b4b2cece142855", size = 3950391, upload-time = "2026-05-04T22:59:17.415Z" }, + { url = "https://files.pythonhosted.org/packages/bc/17/3861e17c56fa0fd37491a14a8673fdb77c57fc5693cafe745ea8b06dba75/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:fdfef35d751d510fcef5252703621574364fec16418c4a1e5e1055248401054b", size = 4637126, upload-time = "2026-05-04T22:59:20.197Z" }, + { url = "https://files.pythonhosted.org/packages/f0/0a/7e226dbff530f21480727eb764973a7bff2b912f8e15cd4f129e71b56d1d/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:0890f502ddf7d9c6426129c3f49f5c0a39278ed7cd6322c8755ffca6ee675a13", size = 4667270, upload-time = "2026-05-04T22:59:22.647Z" }, + { url = "https://files.pythonhosted.org/packages/3b/f2/5a72274ca9f1b2a8b44a662ee0bf1b435909deb473d6f97bcd035bcdbc71/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:ecde28a596bead48b0cfd2a1b4416c3d43074c2d785e3a398d7ec1fc4d0f7fbb", size = 4636797, upload-time = "2026-05-04T22:59:24.912Z" }, + { url = "https://files.pythonhosted.org/packages/b4/e1/48cedb2fe63626e91ded1edad159e2a4fb8b6906c4425eb7749673077ce7/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:4defde8685ae324a9eb9d818717e93b4638ef67070ac9bc15b8ca85f63048355", size = 4666800, upload-time = "2026-05-04T22:59:27.474Z" }, +] + +[[package]] +name = "cycler" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" }, +] + +[[package]] +name = "decorator" +version = "5.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/60/8b/32f9823da46cde7df2087faa08cd98d01b908f8dcab982cdba9c84e85355/decorator-5.3.1.tar.gz", hash = "sha256:4cbcdd55a6efadb9dbea26b858f4fb3264567b52d69ca0d25b721b553f60ea82", size = 58084, upload-time = "2026-05-18T06:03:28.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/7f/798705f5296a58ca505d600456748d1be48078eac8a7050d8a98bc9edb89/decorator-5.3.1-py3-none-any.whl", hash = "sha256:f47fe6fdbd2edd623ecfe36875d37aba411624e2670dd395dddae1358689bb3c", size = 10365, upload-time = "2026-05-18T06:03:26.517Z" }, +] + +[[package]] +name = "duckdb" +version = "1.6.0.dev12" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/84/03ae58a2454c9a5d9e5ce77463b1e644727c0ce1036e58ee80ec6126b96d/duckdb-1.6.0.dev12.tar.gz", hash = "sha256:8f89ca33f5cd5821108b174eb58692c36252614478644033be112944bea40a15", size = 17979744, upload-time = "2026-03-20T12:06:35.581Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/88/84e4f868ffa52100ef3774f054804f8802c997f6b68a381f45ec97943d63/duckdb-1.6.0.dev12-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8cc5b224427141943177de67134d7a88273d393bfdc5111453784688bf5c0af7", size = 29050739, upload-time = "2026-03-20T12:05:09.76Z" }, + { url = "https://files.pythonhosted.org/packages/fb/b2/30688fcd69e4f64068255297509cc3dc954d4234e379166b3f5a8becb719/duckdb-1.6.0.dev12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:41c2ac29caf69053ea9b17a11c87c9a96a0a715be36bd6570b8a9b93bdeb4da1", size = 15448638, upload-time = "2026-03-20T12:05:12.519Z" }, + { url = "https://files.pythonhosted.org/packages/24/3c/c3a1fc3842db38e7521977aced84c58e58f6b950b82efed864d3b0a04f85/duckdb-1.6.0.dev12-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:172b76ed0c15fcf7a38fda5c9537716a4e0c0b7210422a0f20eb59263a5b9bda", size = 13725567, upload-time = "2026-03-20T12:05:15Z" }, + { url = "https://files.pythonhosted.org/packages/27/dc/670d0f55efa6d2147baefdc73851fdf4cb31e83c5e9c7669ecf4a4793e36/duckdb-1.6.0.dev12-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc3ca7825de1d3c1be7b5ef97b4e1012ce8154474af3c9b4478a3a5ed1c9a42d", size = 18927911, upload-time = "2026-03-20T12:05:17.927Z" }, + { url = "https://files.pythonhosted.org/packages/1f/55/16b5b299d920340bd48588660f931febced67f56270f937cc506f0f4a8e5/duckdb-1.6.0.dev12-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ac05b330ea96c7cfc3f48f22fc17ffad3039854035e669cea5a9a89926ce1d29", size = 20976812, upload-time = "2026-03-20T12:05:21.445Z" }, + { url = "https://files.pythonhosted.org/packages/4e/aa/7b7f8728499e52d7ca7edad44af21364b5128b7595247fe5ce759be1579f/duckdb-1.6.0.dev12-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fa89fc52d8248fadfe210ce8452d0d842d789c8821fb49c8f3e3643bbf479ab2", size = 29097346, upload-time = "2026-03-20T12:05:30.381Z" }, + { url = "https://files.pythonhosted.org/packages/aa/31/f08e40c9d84324773698899a6ed8b5f3159a7100c1d977fcab40e214cac4/duckdb-1.6.0.dev12-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e1f4cbf17c432752e7cd571667f5eadb8fe74cc8e6d699bd0070da5e5b598add", size = 15473280, upload-time = "2026-03-20T12:05:33.511Z" }, + { url = "https://files.pythonhosted.org/packages/58/c4/410761a85ba7d1e5867863e153c626480bf85d68b630171b6f9a0dab1832/duckdb-1.6.0.dev12-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1f4fb584c00da67528baccd4ea6811bd123afc5666cfa90779b9ab83f8efaf27", size = 13750349, upload-time = "2026-03-20T12:05:36.21Z" }, + { url = "https://files.pythonhosted.org/packages/96/8c/59226ee21f7d788d4a734458f0cd3f8da723c92a6fc4cb55028886eb2f2d/duckdb-1.6.0.dev12-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:20725806e6fb2282304570489aa05a238b2d99c68d47442e208502e7b6a33130", size = 18945769, upload-time = "2026-03-20T12:05:39.137Z" }, + { url = "https://files.pythonhosted.org/packages/de/ae/94004809aae5a2637581e5515b3045b7bf251bef3956f5daa293768cf454/duckdb-1.6.0.dev12-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7ce571230208f479b508b5a1bdf93948c519a782e283336688db75bb37a851f", size = 21006239, upload-time = "2026-03-20T12:05:41.989Z" }, +] + +[[package]] +name = "exp5-data-contacts-and-distances-v2-zephyr" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "fsspec", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "gcsfs", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "gemmi", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "huggingface-hub", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "marin-zephyr", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "marinfold", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "numpy", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "pyarrow", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] + +[package.optional-dependencies] +test = [ + { name = "pytest", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] + +[package.metadata] +requires-dist = [ + { name = "fsspec" }, + { name = "gcsfs" }, + { name = "gemmi", specifier = ">=0.7" }, + { name = "huggingface-hub" }, + { name = "marin-zephyr", git = "https://github.com/marin-community/marin.git?subdirectory=lib%2Fzephyr&branch=alxmrs%2Fstamp-iris-build-date" }, + { name = "marinfold", git = "https://github.com/Open-Athena/MarinFold.git?subdirectory=marinfold" }, + { name = "numpy" }, + { name = "pyarrow" }, + { name = "pytest", marker = "extra == 'test'", specifier = ">=8" }, +] +provides-extras = ["test"] + +[package.metadata.requires-dev] +dev = [] + +[[package]] +name = "fastapi" +version = "0.136.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-doc", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "starlette", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "typing-inspection", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/81/2d/ff8d91d7b564d464629a0fd50a4489c97fcb836ac230bf3a7269232a9b1f/fastapi-0.136.3.tar.gz", hash = "sha256:e487fae93ad408e6f47641ee4dfe389864fd7bec92e547ea8498fc13f43e83ab", size = 396410, upload-time = "2026-05-23T18:53:15.192Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/82/45359b62a067409bd929ae8a56b8ed13e5a8c8a61194b3c236920999ab83/fastapi-0.136.3-py3-none-any.whl", hash = "sha256:3d2a69bdf04b7e9f3afa292c3bc7a98816bbfafa10bc9b45f3f3700d2f761620", size = 117481, upload-time = "2026-05-23T18:53:16.924Z" }, +] + +[[package]] +name = "filelock" +version = "3.29.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/fe/997687a931ab51049acce6fa1f23e8f01216374ea81374ddee763c493db5/filelock-3.29.0.tar.gz", hash = "sha256:69974355e960702e789734cb4871f884ea6fe50bd8404051a3530bc07809cf90", size = 57571, upload-time = "2026-04-19T15:39:10.068Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/47/dd9a212ef6e343a6857485ffe25bba537304f1913bdbed446a23f7f592e1/filelock-3.29.0-py3-none-any.whl", hash = "sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258", size = 39812, upload-time = "2026-04-19T15:39:08.752Z" }, +] + +[[package]] +name = "flask" +version = "3.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "blinker", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "click", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "itsdangerous", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "jinja2", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "markupsafe", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "werkzeug", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/26/00/35d85dcce6c57fdc871f3867d465d780f302a175ea360f62533f12b27e2b/flask-3.1.3.tar.gz", hash = "sha256:0ef0e52b8a9cd932855379197dd8f94047b359ca0a78695144304cb45f87c9eb", size = 759004, upload-time = "2026-02-19T05:00:57.678Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/9c/34f6962f9b9e9c71f6e5ed806e0d0ff03c9d1b0b2340088a0cf4bce09b18/flask-3.1.3-py3-none-any.whl", hash = "sha256:f4bcbefc124291925f1a26446da31a5178f9483862233b23c0c96a20701f670c", size = 103424, upload-time = "2026-02-19T05:00:56.027Z" }, +] + +[[package]] +name = "fonttools" +version = "4.63.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/84/69/c97f2c18e0db87d2c7b15da1974dace76ae938f1cfa22e2727a648b7ed43/fonttools-4.63.0.tar.gz", hash = "sha256:caeb583deeb5168e694b65cda8b4ee62abedfa66cf88488734466f2366b9c4e0", size = 3597189, upload-time = "2026-05-14T12:04:30.958Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/75/2b/a7f1545bdf5da69c4bda0cea2a5781f0ad2a6623e0277267672db43c5fe6/fonttools-4.63.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2b8ae05d9eacf6081414d759c0a352769ac28ce31280d6bb8e77b03f9e3c449f", size = 2881793, upload-time = "2026-05-14T12:02:56.645Z" }, + { url = "https://files.pythonhosted.org/packages/49/50/965308c703f085f225db2886813b27e015b8b3438c350b22dd65b52c2a2c/fonttools-4.63.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79cdc9f567aec74a72918fd060283911406750cbc9fd28c1316023deb6ce31a9", size = 2428130, upload-time = "2026-05-14T12:02:58.891Z" }, + { url = "https://files.pythonhosted.org/packages/d8/38/6937fbd7f2dc3a6b48725851bc2c15ec949b9af14d9bbcb5fe83cdf9bdf9/fonttools-4.63.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c14b4fd138c4bafcca294765c547914e1aa431ae1ca94ab99d8db08c958bd3b", size = 5111952, upload-time = "2026-05-14T12:03:01.263Z" }, + { url = "https://files.pythonhosted.org/packages/0b/43/a81f20050a3115b57d62c8e781446949512eac36690dc384ccea65ff4cc1/fonttools-4.63.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d76ac49f929aecaf82d83250b8347e099d7aecba0f4726c1d9b6df3b8bb5fe18", size = 5082308, upload-time = "2026-05-14T12:03:03.211Z" }, + { url = "https://files.pythonhosted.org/packages/67/00/cdd9d4944ca6ae280d01e69cc37bde3bf663630b837a6fc6d2cd65d80e0e/fonttools-4.63.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dcf076a4474fe0d7367e5bbf5b052c7284fa1feca729c04176ce513521afd8a0", size = 5087932, upload-time = "2026-05-14T12:03:05.147Z" }, + { url = "https://files.pythonhosted.org/packages/f5/f1/0aa0dbea778c75adbef223c42019fd47d22262b905974d62d829545d485f/fonttools-4.63.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7dd683fef0663e9f0f45cf541d788d24caa3ec9db50796b588e1757d8b3bc007", size = 5213271, upload-time = "2026-05-14T12:03:07.238Z" }, + { url = "https://files.pythonhosted.org/packages/08/ef/b3c6b9b5be2f82416d73fe2ed2e96e2793cd80e7510bd6a17ca79cdd88ec/fonttools-4.63.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:37dd23e621e3b0aef1baa70a303b80aaf38449632cfc8fd2a55fb285bbccfc02", size = 2881131, upload-time = "2026-05-14T12:03:13.386Z" }, + { url = "https://files.pythonhosted.org/packages/44/a0/c815bea63117fa63e4e1c01f8a1110d2112fa003f838e6467094ec2432ce/fonttools-4.63.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a9faff9e0c1f76f9fd55899d2ce785832efebab37eb8ae13995853aef178bef0", size = 2426704, upload-time = "2026-05-14T12:03:15.801Z" }, + { url = "https://files.pythonhosted.org/packages/44/04/0b91d8e916e92ad1fac9e4624760baf0fd5ff2ead614c2f68fb21373f03f/fonttools-4.63.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef3048ef05dbb552b89817713d9cac912e00d0fde4a3105c00d29e52e10c89af", size = 5044298, upload-time = "2026-05-14T12:03:18.085Z" }, + { url = "https://files.pythonhosted.org/packages/77/c7/2342da9830e3e9d4870305ca5d2091d2a83284f2953079b7bdd3b5e029d8/fonttools-4.63.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:58dc6bb86a78d782f00f9190ca02c119cf5bbe2807536e361e18d42019f877d8", size = 4999800, upload-time = "2026-05-14T12:03:20.161Z" }, + { url = "https://files.pythonhosted.org/packages/e6/6d/67fe16c48d7ce050979b33f47e0d28a318f02da030602e944c34f7a16ef3/fonttools-4.63.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ee08ebfa58f6e1aeff5697ab9582105bb620008c1caafb681e4c557e7483027b", size = 4982666, upload-time = "2026-05-14T12:03:22.87Z" }, + { url = "https://files.pythonhosted.org/packages/f2/00/3bbab338c07c71fa56269953845e92c951a61457bbbb0f1022551ea266d9/fonttools-4.63.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:27fdc65af8da6f88b9c6121c47a464cbe359fcfff7ff6fc2d37a1f395d755b78", size = 5133598, upload-time = "2026-05-14T12:03:25.168Z" }, + { url = "https://files.pythonhosted.org/packages/2c/47/c99d5268f354002ce80f8d029cd9d7d872969da1de8b93d32de4dc56d6f4/fonttools-4.63.0-py3-none-any.whl", hash = "sha256:445af2eab030a16b9171ea8bdda7ebf7d96bda2df88ee182a464252f6e05e20d", size = 1164562, upload-time = "2026-05-14T12:04:29.092Z" }, +] + +[[package]] +name = "frozenlist" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/03/077f869d540370db12165c0aa51640a873fb661d8b315d1d4d67b284d7ac/frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84", size = 86912, upload-time = "2025-10-06T05:35:45.98Z" }, + { url = "https://files.pythonhosted.org/packages/df/b5/7610b6bd13e4ae77b96ba85abea1c8cb249683217ef09ac9e0ae93f25a91/frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9", size = 50046, upload-time = "2025-10-06T05:35:47.009Z" }, + { url = "https://files.pythonhosted.org/packages/6e/ef/0e8f1fe32f8a53dd26bdd1f9347efe0778b0fddf62789ea683f4cc7d787d/frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93", size = 50119, upload-time = "2025-10-06T05:35:48.38Z" }, + { url = "https://files.pythonhosted.org/packages/11/b1/71a477adc7c36e5fb628245dfbdea2166feae310757dea848d02bd0689fd/frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f", size = 231067, upload-time = "2025-10-06T05:35:49.97Z" }, + { url = "https://files.pythonhosted.org/packages/45/7e/afe40eca3a2dc19b9904c0f5d7edfe82b5304cb831391edec0ac04af94c2/frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695", size = 233160, upload-time = "2025-10-06T05:35:51.729Z" }, + { url = "https://files.pythonhosted.org/packages/a6/aa/7416eac95603ce428679d273255ffc7c998d4132cfae200103f164b108aa/frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52", size = 228544, upload-time = "2025-10-06T05:35:53.246Z" }, + { url = "https://files.pythonhosted.org/packages/8b/3d/2a2d1f683d55ac7e3875e4263d28410063e738384d3adc294f5ff3d7105e/frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581", size = 243797, upload-time = "2025-10-06T05:35:54.497Z" }, + { url = "https://files.pythonhosted.org/packages/78/1e/2d5565b589e580c296d3bb54da08d206e797d941a83a6fdea42af23be79c/frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567", size = 247923, upload-time = "2025-10-06T05:35:55.861Z" }, + { url = "https://files.pythonhosted.org/packages/aa/c3/65872fcf1d326a7f101ad4d86285c403c87be7d832b7470b77f6d2ed5ddc/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b", size = 230886, upload-time = "2025-10-06T05:35:57.399Z" }, + { url = "https://files.pythonhosted.org/packages/a0/76/ac9ced601d62f6956f03cc794f9e04c81719509f85255abf96e2510f4265/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92", size = 245731, upload-time = "2025-10-06T05:35:58.563Z" }, + { url = "https://files.pythonhosted.org/packages/b9/49/ecccb5f2598daf0b4a1415497eba4c33c1e8ce07495eb07d2860c731b8d5/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d", size = 241544, upload-time = "2025-10-06T05:35:59.719Z" }, + { url = "https://files.pythonhosted.org/packages/53/4b/ddf24113323c0bbcc54cb38c8b8916f1da7165e07b8e24a717b4a12cbf10/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd", size = 241806, upload-time = "2025-10-06T05:36:00.959Z" }, + { url = "https://files.pythonhosted.org/packages/a7/fb/9b9a084d73c67175484ba2789a59f8eebebd0827d186a8102005ce41e1ba/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967", size = 229382, upload-time = "2025-10-06T05:36:02.22Z" }, + { url = "https://files.pythonhosted.org/packages/69/29/948b9aa87e75820a38650af445d2ef2b6b8a6fab1a23b6bb9e4ef0be2d59/frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1", size = 87782, upload-time = "2025-10-06T05:36:06.649Z" }, + { url = "https://files.pythonhosted.org/packages/64/80/4f6e318ee2a7c0750ed724fa33a4bdf1eacdc5a39a7a24e818a773cd91af/frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b", size = 50594, upload-time = "2025-10-06T05:36:07.69Z" }, + { url = "https://files.pythonhosted.org/packages/2b/94/5c8a2b50a496b11dd519f4a24cb5496cf125681dd99e94c604ccdea9419a/frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4", size = 50448, upload-time = "2025-10-06T05:36:08.78Z" }, + { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411, upload-time = "2025-10-06T05:36:09.801Z" }, + { url = "https://files.pythonhosted.org/packages/8f/83/f61505a05109ef3293dfb1ff594d13d64a2324ac3482be2cedc2be818256/frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", size = 243014, upload-time = "2025-10-06T05:36:11.394Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cb/cb6c7b0f7d4023ddda30cf56b8b17494eb3a79e3fda666bf735f63118b35/frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", size = 234909, upload-time = "2025-10-06T05:36:12.598Z" }, + { url = "https://files.pythonhosted.org/packages/31/c5/cd7a1f3b8b34af009fb17d4123c5a778b44ae2804e3ad6b86204255f9ec5/frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", size = 250049, upload-time = "2025-10-06T05:36:14.065Z" }, + { url = "https://files.pythonhosted.org/packages/c0/01/2f95d3b416c584a1e7f0e1d6d31998c4a795f7544069ee2e0962a4b60740/frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", size = 256485, upload-time = "2025-10-06T05:36:15.39Z" }, + { url = "https://files.pythonhosted.org/packages/ce/03/024bf7720b3abaebcff6d0793d73c154237b85bdf67b7ed55e5e9596dc9a/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", size = 237619, upload-time = "2025-10-06T05:36:16.558Z" }, + { url = "https://files.pythonhosted.org/packages/69/fa/f8abdfe7d76b731f5d8bd217827cf6764d4f1d9763407e42717b4bed50a0/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", size = 250320, upload-time = "2025-10-06T05:36:17.821Z" }, + { url = "https://files.pythonhosted.org/packages/f5/3c/b051329f718b463b22613e269ad72138cc256c540f78a6de89452803a47d/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", size = 246820, upload-time = "2025-10-06T05:36:19.046Z" }, + { url = "https://files.pythonhosted.org/packages/0f/ae/58282e8f98e444b3f4dd42448ff36fa38bef29e40d40f330b22e7108f565/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", size = 250518, upload-time = "2025-10-06T05:36:20.763Z" }, + { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096, upload-time = "2025-10-06T05:36:22.129Z" }, + { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, +] + +[[package]] +name = "fsspec" +version = "2026.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d5/8d/1c51c094345df128ca4a990d633fe1a0ff28726c9e6b3c41ba65087bba1d/fsspec-2026.4.0.tar.gz", hash = "sha256:301d8ac70ae90ef3ad05dcf94d6c3754a097f9b5fe4667d2787aa359ec7df7e4", size = 312760, upload-time = "2026-04-29T20:42:38.635Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/0c/043d5e551459da400957a1395e0febbf771446ff34291afcbe3d8be2a279/fsspec-2026.4.0-py3-none-any.whl", hash = "sha256:11ef7bb35dab8a394fde6e608221d5cf3e8499401c249bebaeaad760a1a8dec2", size = 203402, upload-time = "2026-04-29T20:42:36.842Z" }, +] + +[[package]] +name = "gcsfs" +version = "2026.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "decorator", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "fsspec", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "google-auth", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "google-auth-oauthlib", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "google-cloud-storage", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "google-cloud-storage-control", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "requests", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1c/03/0bd3f6796422cfed6be1949c44668b386400f231d63ab30aefa6165baf0f/gcsfs-2026.4.0.tar.gz", hash = "sha256:f3d80dd1c98737798bc84472e6ff9c59873bd17a26e170d46ce90b3118db4390", size = 1139035, upload-time = "2026-04-29T21:04:11.64Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/37/3922951a55a3d0f0340e884929087ce08e333cbb16a86002535c095960fc/gcsfs-2026.4.0-py3-none-any.whl", hash = "sha256:d9e838834d8cce6cb623c6a6a5fad66a4d122dc5c609d4b1c1977b55f759dcc5", size = 72190, upload-time = "2026-04-29T21:04:09.997Z" }, +] + +[[package]] +name = "gemmi" +version = "0.7.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/60/38/a79d9e672b837ceefa7da1921c33b10479362603c4c6370bc004d69558d6/gemmi-0.7.5.tar.gz", hash = "sha256:3328f26c8a8a0ef6a7fc8bb28e167818e324e4239dd4197d6b6066ae2b6315fe", size = 1523171, upload-time = "2026-03-02T08:27:31.157Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/79/b13830a65bf9fc85474a984604f094cc18817dc93a784f4c567a2dc05169/gemmi-0.7.5-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:e134fd33f34bf9f2ffacd9e0207aeac6329dde818f62340e7390217a25ee8e2d", size = 2834430, upload-time = "2026-03-02T08:31:16.14Z" }, + { url = "https://files.pythonhosted.org/packages/42/15/26cac702cdf6281ddeb185d5912ce14e555e277c6e4caeb1d36966e43822/gemmi-0.7.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4db34eaa3d3fc102afea7a156330862cbeb82f557444c079403d4412e326c527", size = 2716532, upload-time = "2026-03-02T08:31:18.321Z" }, + { url = "https://files.pythonhosted.org/packages/2a/42/a60fd259b99785f04fe5c7fdbda1ec9c10aa9641d4efe9186c019a1689d9/gemmi-0.7.5-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fcd6b82ce6b33049aa43d2aaed167090a77eaa1370f51f5422a683edfe2eec97", size = 2638468, upload-time = "2026-03-02T08:31:20.26Z" }, + { url = "https://files.pythonhosted.org/packages/48/eb/46e443fc70b4aabe6e775521ff476aefb051db9acabb16a5cb51f04e3e2b/gemmi-0.7.5-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:895c63c7bcf30cffba97cf12c89dc3905f4645f838c17009b4534459a6c53a1e", size = 2991765, upload-time = "2026-03-02T08:31:22.617Z" }, + { url = "https://files.pythonhosted.org/packages/32/7e/27b2313a644b42e02ed875ebeff73a1e88d7f564f15c1bf88c9557bbda0b/gemmi-0.7.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7f9524061282ceb114d5316af333667fae850896141ddbadfd2d275d9d6ac5ad", size = 3161503, upload-time = "2026-03-02T08:31:24.757Z" }, + { url = "https://files.pythonhosted.org/packages/8f/05/ee808eb8ece89c612d1bf6dd071ee870e129a69331383b95e482bbd4b692/gemmi-0.7.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:264726ed818aee8907dd8e6007f4a14c28fbf1f1b8ded3761e793e5a5f3284c6", size = 3514467, upload-time = "2026-03-02T08:31:26.812Z" }, + { url = "https://files.pythonhosted.org/packages/0b/72/7e33f0c1871d648088e3dd67ce47366ed942d625300f6d966730da92a7d7/gemmi-0.7.5-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:2da5d5c1d31fc8c3bffe7530c697c97f8389edff57e8b12898218c588c4f0dac", size = 2845071, upload-time = "2026-03-02T08:31:31.413Z" }, + { url = "https://files.pythonhosted.org/packages/b0/3e/a6497e1c2c9bc6ed2b79e0f2d31a4ce509fd2a9eed4e4f7ac63eda8113cb/gemmi-0.7.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5682920985109c6a08616ae9aae080f8b46a9714534dc864b535e3e6d203d5b8", size = 2720607, upload-time = "2026-03-02T08:31:33.778Z" }, + { url = "https://files.pythonhosted.org/packages/df/31/704e6c7ffc251d1dc1a19a8cd2e30881a83978c6df8668ba052523fa1720/gemmi-0.7.5-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:255ca0b0a7f6fb0bf4322f2d69c5f94edf6e95fb801bc1d120ca8dd93b646065", size = 2624986, upload-time = "2026-03-02T08:31:35.902Z" }, + { url = "https://files.pythonhosted.org/packages/e8/88/5a431cd1ea7587408a66947384b39beb2ab2bcc1c87b7c4082f05036719f/gemmi-0.7.5-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:217bb9ac9da7c90704026dacfc0a0652a38f4df1e318225d8f35c75f1f8c7ebf", size = 2982717, upload-time = "2026-03-02T08:31:37.781Z" }, + { url = "https://files.pythonhosted.org/packages/36/e0/ca646b4e22b3d6129ce56a087a9031f7a7843d47425f0adc38a7ab789b24/gemmi-0.7.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:74e1b5177b626aadb819fd8168f5d6064c04a2a1e45c87f357a96d30ddafc749", size = 3146031, upload-time = "2026-03-02T08:31:39.744Z" }, + { url = "https://files.pythonhosted.org/packages/d5/cc/47e6039859393175a9b38f9a72732c018a3052d838fecf1ff635d8b84d95/gemmi-0.7.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6d30fa7ae889149c22dbb58899e77117e6548edc6e8ccfae3b4b2a259464d2ee", size = 3505196, upload-time = "2026-03-02T08:31:41.651Z" }, +] + +[[package]] +name = "google-api-core" +version = "2.30.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-auth", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "googleapis-common-protos", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "proto-plus", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "protobuf", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "requests", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/16/ce/502a57fb0ec752026d24df1280b162294b22a0afb98a326084f9a979138b/google_api_core-2.30.3.tar.gz", hash = "sha256:e601a37f148585319b26db36e219df68c5d07b6382cff2d580e83404e44d641b", size = 177001, upload-time = "2026-04-10T00:41:28.035Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/03/15/e56f351cf6ef1cfea58e6ac226a7318ed1deb2218c4b3cc9bd9e4b786c5a/google_api_core-2.30.3-py3-none-any.whl", hash = "sha256:a85761ba72c444dad5d611c2220633480b2b6be2521eca69cca2dbb3ffd6bfe8", size = 173274, upload-time = "2026-04-09T22:57:16.198Z" }, +] + +[package.optional-dependencies] +grpc = [ + { name = "grpcio", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "grpcio-status", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] + +[[package]] +name = "google-auth" +version = "2.53.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "pyasn1-modules", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c6/ad/ff781329bbbdc0974a098d996e89c9e1f7024262f9e3eec442fbb9ad1ac6/google_auth-2.53.0.tar.gz", hash = "sha256:e7e6aa16f6bee7b2b264830fd04f08087a1d5a836df516251a5d15327b246c9c", size = 335844, upload-time = "2026-05-15T20:53:07.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/c9/db44165ba7c581268c6d46017ef63339110378305062830104fc7fa144cb/google_auth-2.53.0-py3-none-any.whl", hash = "sha256:6e7449917c599b35126a99ec268ec6880301f2fea41dce198fe8fd83ff642b68", size = 246071, upload-time = "2026-05-15T20:53:05.609Z" }, +] + +[[package]] +name = "google-auth-oauthlib" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-auth", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "requests-oauthlib", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/70/18/90c7fac516e63cf2058166fce0c88c353647c677b51cc036c09c49bb5cbb/google_auth_oauthlib-1.4.0.tar.gz", hash = "sha256:18b5e28880eb8eba9065c436becdc0ee8e4b59117a73a510679c82f70cd363d2", size = 21675, upload-time = "2026-05-07T08:03:47.816Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/37/d3/d7dff0d58a9e9244b48044bfb6a898bfcc8ecc42e0031d1bebc695344725/google_auth_oauthlib-1.4.0-py3-none-any.whl", hash = "sha256:251314f213a9ee46a5ae73988e84fd7cca8bb68e7ecf4bfd45940f9e7f51d070", size = 19261, upload-time = "2026-05-07T08:02:13.798Z" }, +] + +[[package]] +name = "google-cloud-core" +version = "2.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-api-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "google-auth", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a8/dd/1eef226e470369b26824a505c34482c0b493bc35fe8e0c6b003b5feca21a/google_cloud_core-2.6.0.tar.gz", hash = "sha256:e76149739f90fac1fc6757c09f47eaccb3145b54adbd7759b0f7c4b235f46c83", size = 36001, upload-time = "2026-05-07T08:04:04.124Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/4a/98da8930ab109c73d9a5d13782a9ebb81ea8c111f6d534a567b71d23e52b/google_cloud_core-2.6.0-py3-none-any.whl", hash = "sha256:6d63ac8e5eca6d9e4319d0a1e2265fadcd7f1049904378caecfa01cf52dd869e", size = 29390, upload-time = "2026-05-07T08:02:34.672Z" }, +] + +[[package]] +name = "google-cloud-storage" +version = "3.10.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-api-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "google-auth", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "google-cloud-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "google-crc32c", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "google-resumable-media", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "requests", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4c/47/205eb8e9a1739b5345843e5a425775cbdc472cc38e7eda082ba5b8d02450/google_cloud_storage-3.10.1.tar.gz", hash = "sha256:97db9aa4460727982040edd2bd13ff3d5e2260b5331ad22895802da1fc2a5286", size = 17309950, upload-time = "2026-03-23T09:35:23.409Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ad/ff/ca9ab2417fa913d75aae38bf40bf856bb2749a604b2e0f701b37cfcd23cc/google_cloud_storage-3.10.1-py3-none-any.whl", hash = "sha256:a72f656759b7b99bda700f901adcb3425a828d4a29f911bc26b3ea79c5b1217f", size = 324453, upload-time = "2026-03-23T09:35:21.368Z" }, +] + +[[package]] +name = "google-cloud-storage-control" +version = "1.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-api-core", extra = ["grpc"], marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "google-auth", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "grpc-google-iam-v1", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "grpcio", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "proto-plus", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "protobuf", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/da/4d/f36795a1cb37562caaf7fa9367ed34af29a09fd28ccda36f2f62c686de8f/google_cloud_storage_control-1.11.0.tar.gz", hash = "sha256:e97a0f3c99bc5ec5a2a770298ca9e8d5feeaa41bb55f2f2d7b36df2b817ee950", size = 117255, upload-time = "2026-03-26T22:18:32.645Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ab/aa/4df4066f4883afdae530ad95362f769cd9379fd2fb2d91f819aa7c9bdcc0/google_cloud_storage_control-1.11.0-py3-none-any.whl", hash = "sha256:98d2a3be8ab124ae09c33c1b8e4aadc68597c171c60c33e62666bcc74493f8ee", size = 89728, upload-time = "2026-03-26T22:16:17.2Z" }, +] + +[[package]] +name = "google-cloud-tpu" +version = "1.26.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-api-core", extra = ["grpc"], marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "google-auth", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "grpcio", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "proto-plus", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "protobuf", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7c/8a/8d510bda239e3ca0796b11a5e52a33f57c8cfc2ed894e2b9c74e5d496009/google_cloud_tpu-1.26.0.tar.gz", hash = "sha256:3f28e47c79e9075434be2dca206f4d9c879fd434d994162a09a4d43fff37f7bb", size = 255052, upload-time = "2026-03-26T22:17:21.744Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/c1/2bf6995ccc2e9fb37f0fac7f0bd2de6d7b3adbb1eea16302c784d9f49dc6/google_cloud_tpu-1.26.0-py3-none-any.whl", hash = "sha256:3ab17595302c2cd5ce8c1193fdf5d60023f6eb285e36f767ee79679f3e6e68e2", size = 200265, upload-time = "2026-03-26T22:15:53.762Z" }, +] + +[[package]] +name = "google-crc32c" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/03/41/4b9c02f99e4c5fb477122cd5437403b552873f014616ac1d19ac8221a58d/google_crc32c-1.8.0.tar.gz", hash = "sha256:a428e25fb7691024de47fecfbff7ff957214da51eddded0da0ae0e0f03a2cf79", size = 14192, upload-time = "2025-12-16T00:35:25.142Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/ef/21ccfaab3d5078d41efe8612e0ed0bfc9ce22475de074162a91a25f7980d/google_crc32c-1.8.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:014a7e68d623e9a4222d663931febc3033c5c7c9730785727de2a81f87d5bab8", size = 31298, upload-time = "2025-12-16T00:20:32.241Z" }, + { url = "https://files.pythonhosted.org/packages/c5/b8/f8413d3f4b676136e965e764ceedec904fe38ae8de0cdc52a12d8eb1096e/google_crc32c-1.8.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:86cfc00fe45a0ac7359e5214a1704e51a99e757d0272554874f419f79838c5f7", size = 30872, upload-time = "2025-12-16T00:33:58.785Z" }, + { url = "https://files.pythonhosted.org/packages/f6/fd/33aa4ec62b290477181c55bb1c9302c9698c58c0ce9a6ab4874abc8b0d60/google_crc32c-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:19b40d637a54cb71e0829179f6cb41835f0fbd9e8eb60552152a8b52c36cbe15", size = 33243, upload-time = "2025-12-16T00:40:21.46Z" }, + { url = "https://files.pythonhosted.org/packages/71/03/4820b3bd99c9653d1a5210cb32f9ba4da9681619b4d35b6a052432df4773/google_crc32c-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:17446feb05abddc187e5441a45971b8394ea4c1b6efd88ab0af393fd9e0a156a", size = 33608, upload-time = "2025-12-16T00:40:22.204Z" }, + { url = "https://files.pythonhosted.org/packages/e9/5f/7307325b1198b59324c0fa9807cafb551afb65e831699f2ce211ad5c8240/google_crc32c-1.8.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:4b8286b659c1335172e39563ab0a768b8015e88e08329fa5321f774275fc3113", size = 31300, upload-time = "2025-12-16T00:21:56.723Z" }, + { url = "https://files.pythonhosted.org/packages/21/8e/58c0d5d86e2220e6a37befe7e6a94dd2f6006044b1a33edf1ff6d9f7e319/google_crc32c-1.8.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:2a3dc3318507de089c5384cc74d54318401410f82aa65b2d9cdde9d297aca7cb", size = 30867, upload-time = "2025-12-16T00:38:31.302Z" }, + { url = "https://files.pythonhosted.org/packages/ce/a9/a780cc66f86335a6019f557a8aaca8fbb970728f0efd2430d15ff1beae0e/google_crc32c-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:14f87e04d613dfa218d6135e81b78272c3b904e2a7053b841481b38a7d901411", size = 33364, upload-time = "2025-12-16T00:40:22.96Z" }, + { url = "https://files.pythonhosted.org/packages/21/3f/3457ea803db0198c9aaca2dd373750972ce28a26f00544b6b85088811939/google_crc32c-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cb5c869c2923d56cb0c8e6bcdd73c009c36ae39b652dbe46a05eb4ef0ad01454", size = 33740, upload-time = "2025-12-16T00:40:23.96Z" }, + { url = "https://files.pythonhosted.org/packages/52/c5/c171e4d8c44fec1422d801a6d2e5d7ddabd733eeda505c79730ee9607f07/google_crc32c-1.8.0-pp311-pypy311_pp73-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:87fa445064e7db928226b2e6f0d5304ab4cd0339e664a4e9a25029f384d9bb93", size = 28615, upload-time = "2025-12-16T00:40:29.298Z" }, + { url = "https://files.pythonhosted.org/packages/9c/97/7d75fe37a7a6ed171a2cf17117177e7aab7e6e0d115858741b41e9dd4254/google_crc32c-1.8.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f639065ea2042d5c034bf258a9f085eaa7af0cd250667c0635a3118e8f92c69c", size = 28800, upload-time = "2025-12-16T00:40:30.322Z" }, +] + +[[package]] +name = "google-resumable-media" +version = "2.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-crc32c", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/00/4b/0b235beccc310d0a48adbc7246b719d173cca6c88c572dfa4b090e39143c/google_resumable_media-2.9.0.tar.gz", hash = "sha256:f7cfb224846a9dd444d125115dfbe8ef02a2b893e78f087762fe716a255a734b", size = 2164534, upload-time = "2026-05-07T08:04:44.236Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/73/3518e63deb1667c5409a4579e28daf5e84479a87a72c547e0487f7883dcd/google_resumable_media-2.9.0-py3-none-any.whl", hash = "sha256:c8901e88e389af8bed64d9696c74d8bad961865eb2236e13e0bfca9bb0a65ca3", size = 81507, upload-time = "2026-05-07T08:03:23.809Z" }, +] + +[[package]] +name = "googleapis-common-protos" +version = "1.75.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b5/c8/f439cffde755cffa462bfbb156278fa6f9d09119719af9814b858fd4f81f/googleapis_common_protos-1.75.0.tar.gz", hash = "sha256:53a062ff3c32552fbd62c11fe23768b78e4ddf0494d5e5fd97d3f4689c75fbbd", size = 151035, upload-time = "2026-05-07T08:04:49.423Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/c8/e2645aa8ed02fd4c7a2f59d68783b65b1f3cbdfe39a6308e156509d1fee8/googleapis_common_protos-1.75.0-py3-none-any.whl", hash = "sha256:961ed60399c457ceb0ee8f285a84c870aabc9c6a832b9d37bb281b5bebde43ed", size = 300631, upload-time = "2026-05-07T08:03:30.345Z" }, +] + +[package.optional-dependencies] +grpc = [ + { name = "grpcio", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] + +[[package]] +name = "grpc-google-iam-v1" +version = "0.14.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "googleapis-common-protos", extra = ["grpc"], marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "grpcio", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "protobuf", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/44/4f/d098419ad0bfc06c9ce440575f05aa22d8973b6c276e86ac7890093d3c37/grpc_google_iam_v1-0.14.4.tar.gz", hash = "sha256:392b3796947ed6334e61171d9ab06bf7eb357f554e5fc7556ad7aab6d0e17038", size = 23706, upload-time = "2026-04-01T01:57:49.813Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/89/22/c2dd50c09bf679bd38173656cd4402d2511e563b33bc88f90009cf50613c/grpc_google_iam_v1-0.14.4-py3-none-any.whl", hash = "sha256:412facc320fcbd94034b4df3d557662051d4d8adfa86e0ddb4dca70a3f739964", size = 32675, upload-time = "2026-04-01T01:57:47.69Z" }, +] + +[[package]] +name = "grpcio" +version = "1.81.0rc1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2c/59/979626adf461397501a2d14558b83e92007254786bde5dd2c1957b52fa22/grpcio-1.81.0rc1.tar.gz", hash = "sha256:80ead909bb0e368a965c42bcdbf3288bfe6441cce86b1ee5fb517c1e7dcc9d21", size = 13032995, upload-time = "2026-05-18T08:00:21.224Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/9c/4a64ffbcc6b7d092530c7600186ff1d1a5fc29b0c3d4c253be3688cffe59/grpcio-1.81.0rc1-cp311-cp311-linux_armv7l.whl", hash = "sha256:7e435e22e3fec6ca93c3c3ee28823da5e574f7e78738d8fbf1aa54e227eed4f1", size = 6093549, upload-time = "2026-05-18T07:58:27.919Z" }, + { url = "https://files.pythonhosted.org/packages/ff/d4/edc62bdb68850692d96e519076bc2f780824e75a1571b87306d8b5c62baa/grpcio-1.81.0rc1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:2f8289046bef2a573aaa08be149bc4edca42863771c25657418e4da377a5e071", size = 12075830, upload-time = "2026-05-18T07:58:30.441Z" }, + { url = "https://files.pythonhosted.org/packages/96/53/9f5d86497809303f8b87653391627cba6edaaefd213cb2e94104cf4da4c3/grpcio-1.81.0rc1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:93594272aebab343a3041dc14390da48a2599c52aa87003a054c26e4ffd11b69", size = 6640812, upload-time = "2026-05-18T07:58:33.511Z" }, + { url = "https://files.pythonhosted.org/packages/ec/c2/39c457275c5bb4493b252fd157889bcbbd9d06d2459e7b56c753b0ad3264/grpcio-1.81.0rc1-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:2ba7343ae99c9be320b0c5af61ea79160fb23de3e7d4847ff8f78860d7956836", size = 7332597, upload-time = "2026-05-18T07:58:36.097Z" }, + { url = "https://files.pythonhosted.org/packages/46/26/fe14b687dbae66c046c8551e6fb665cdbab6f4c13f0becccc1df3e541062/grpcio-1.81.0rc1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4793b4636b2b610fc247552c6883798cd0958eae0cf02355a9dd77c970edcd77", size = 6842621, upload-time = "2026-05-18T07:58:39.493Z" }, + { url = "https://files.pythonhosted.org/packages/6a/74/82acd0d376082795b32cb762535b63a10b8de02e2a7aab009c0fd45c5440/grpcio-1.81.0rc1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dd890d3f8185a847d229d99c5a02f9ef4f9228ba05cd9e6168b614d7ea6a7c83", size = 7446432, upload-time = "2026-05-18T07:58:42.751Z" }, + { url = "https://files.pythonhosted.org/packages/43/ab/9086bbb7b5f29dda62116195dcdc22369ded3e4ce2b12c9997629d2de243/grpcio-1.81.0rc1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e608e746022ced956946dc1a7f6b6455aa269b951502db73d8e41b31cceb044b", size = 8445643, upload-time = "2026-05-18T07:58:46.012Z" }, + { url = "https://files.pythonhosted.org/packages/ef/63/315275d3c6f99c8baba06f60e3985b3ae4a48deadd3c0a2b8119e7a0c36a/grpcio-1.81.0rc1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:aed495677521b9baa07626264241d54f56ef741b1b415e2b0648acf1731c1bbb", size = 7874069, upload-time = "2026-05-18T07:58:49.893Z" }, + { url = "https://files.pythonhosted.org/packages/da/68/0c8932c3323c6a534122702d2e7e70295c645f5997055f43cba7eb5fc3a7/grpcio-1.81.0rc1-cp312-cp312-linux_armv7l.whl", hash = "sha256:ad1254a9d0924fbf6ec00510f02564c6964ceafd9780ad6191c16b1c260b594d", size = 6053524, upload-time = "2026-05-18T07:58:56.582Z" }, + { url = "https://files.pythonhosted.org/packages/4e/7c/fba50951bd75872b88fe2d0c1d5bfadcd2ad875b3b258a14a3bdccb67c0a/grpcio-1.81.0rc1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:732469dc6c4749e4bf73eaafb1787d03cbe12c3d86f787797d1e756e35240782", size = 12054084, upload-time = "2026-05-18T07:58:59.113Z" }, + { url = "https://files.pythonhosted.org/packages/21/02/58e262523fd5fb261c8bdf2718aa794a78b96d804617981c0a449679ff6e/grpcio-1.81.0rc1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:935002f850e47b31495a081036004be8b6a8e5c22eec51212f02fc61cea73d95", size = 6596041, upload-time = "2026-05-18T07:59:02.139Z" }, + { url = "https://files.pythonhosted.org/packages/cf/20/a20f0d1d242c426cd48f02556d4ffea78db8fc50f6c9d8987ddeb25de6ef/grpcio-1.81.0rc1-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:223165634c5925cc6d3e594fc46e0fb31c443583dddb309fb3c0165c65360de6", size = 7303918, upload-time = "2026-05-18T07:59:04.736Z" }, + { url = "https://files.pythonhosted.org/packages/18/91/4df3366d17ad04bf30a0980971f5d75e2b250a3426236da8dfc34d783bf6/grpcio-1.81.0rc1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:476583c19a7b90dc7ab68f63bd0b53c55eeccd125b54feb74ce43eb3b03289a3", size = 6809742, upload-time = "2026-05-18T07:59:07.017Z" }, + { url = "https://files.pythonhosted.org/packages/d7/1f/a3b0813008495dcbd97ce8a155b5ee3491108caaa55d3ff12ee8373d8cc9/grpcio-1.81.0rc1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5155a7370935313edc2b2497e269916e7f637e530918daf933337a223aa2fc2a", size = 7412748, upload-time = "2026-05-18T07:59:10.221Z" }, + { url = "https://files.pythonhosted.org/packages/8f/7f/7eec37133d70e69544e19a1dcdb8d5cb1cff9f6842f05bff284a3bbc75f4/grpcio-1.81.0rc1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:786adc03e3676d8a2e5d5a909e3108123f8112eed153daa80feb7a4b8b36dd10", size = 8408095, upload-time = "2026-05-18T07:59:12.48Z" }, + { url = "https://files.pythonhosted.org/packages/13/6d/914d6587759f2a7efb5bd47bbd7f9719fddc8ee669189f8df0ad03489721/grpcio-1.81.0rc1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e66b42e9b4f9a8c86b39ebdba7a5bd66566a8f02ad0081f675ad54fd6dba4a01", size = 7849575, upload-time = "2026-05-18T07:59:15.334Z" }, +] + +[[package]] +name = "grpcio-status" +version = "1.81.0rc1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "googleapis-common-protos", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "grpcio", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "protobuf", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/db/f3/6533add1ab04064f07d878817159d3ca47dcb1405ef47042b10073b13b37/grpcio_status-1.81.0rc1.tar.gz", hash = "sha256:17fa5518440d77d26cb30091d45fc2b02559da48055f9c3f1efd58c2accbd9d4", size = 13912, upload-time = "2026-05-18T08:09:35.28Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/0d/aa170e19d5f17e0d08eef60ae6ffe1c47ed1a84b2cb287d926b9ff5a1dfa/grpcio_status-1.81.0rc1-py3-none-any.whl", hash = "sha256:ce6837f9208f9ec747bedb5144643495492c729b10934908850441d25471d902", size = 14671, upload-time = "2026-05-18T08:09:13.011Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "hf-xet" +version = "1.5.1.dev0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/87/48/ff1de104fd07bf8da611ca7fdc5620539bb099210f698f2bea19014b6132/hf_xet-1.5.1.dev0.tar.gz", hash = "sha256:ca95731d04b8cf3a5e9be5ec07c1d385436622574bcdfe883cf42d8e4ae53728", size = 870898, upload-time = "2026-05-28T10:53:40.14Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/24/7694b6fe31f5d20dee12116b062de319d333c63e3828265aedda0d6816dd/hf_xet-1.5.1.dev0-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:dca95f81782c02fa9ab877e9216169bab3f7f7e61184048b70f8925b04673b6c", size = 7012347, upload-time = "2026-05-28T10:53:14.686Z" }, + { url = "https://files.pythonhosted.org/packages/99/79/5c0f02890bac3ebabd09d4905b5e756445bc94302fe5e5e4690a37c5042b/hf_xet-1.5.1.dev0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:1f9119fc4457d00e7f66561bf24fa0f2dd0f4b698d6b14987a2fe06367cbc13d", size = 6630124, upload-time = "2026-05-28T10:53:13.135Z" }, + { url = "https://files.pythonhosted.org/packages/85/f6/c9e5b5997aaeb9b6312539706fae1f02821284194601690485e52ad61bda/hf_xet-1.5.1.dev0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d85fa3f4bb5f5266de939f49de930b7296a15cb502eae84ac72f8c5150a211f3", size = 63298433, upload-time = "2026-05-28T10:53:00.552Z" }, + { url = "https://files.pythonhosted.org/packages/c3/c2/22537edb2aa20e2f03b6108e9f1ae8f4df7bde7c03607fe51094f0849d19/hf_xet-1.5.1.dev0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7ce6201f377eeb589e0f6afb815989d3a320eb833eca2965ed00f464e9105060", size = 58386424, upload-time = "2026-05-28T10:52:57.388Z" }, + { url = "https://files.pythonhosted.org/packages/8c/65/12f45b090dd92607b6c28fed7f68ea0ef0682a35500eafe1d149ee469bc8/hf_xet-1.5.1.dev0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:029a4bbe38b3653af5f99375fc174693eb3f5eecf57f2dc5d9008c8a6eb2cdb8", size = 58899816, upload-time = "2026-05-28T10:53:27.792Z" }, + { url = "https://files.pythonhosted.org/packages/94/d1/396ad99a14a14224d3587e143a38ad0fa1386cc42285aef806a29dad5543/hf_xet-1.5.1.dev0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7532996077b0b09724c85cfa7ae6c7272187ca777e9faa496feb14883e37dafa", size = 60289771, upload-time = "2026-05-28T10:53:30.983Z" }, +] + +[[package]] +name = "httptools" +version = "0.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/e5/d471fcb0e14523fe1c3f4ba58ca52480e7bd70ad7109a3846bc75892f7fb/httptools-0.8.0.tar.gz", hash = "sha256:6b2a32f18d97e16e90827d7a819ffa8dbd8cc245fc4e1fa9d1095b54ef4bd999", size = 271342, upload-time = "2026-05-25T22:17:48.841Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/d2/c3eedaef57de65c3cc5f8dc244cf12d09c84ad258a479055aad6db23206c/httptools-0.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ed377e64805bdba4943c82717333f8f8603a13b09aff9cead2717c6c817fb168", size = 208428, upload-time = "2026-05-25T22:16:59.717Z" }, + { url = "https://files.pythonhosted.org/packages/f1/94/dfe435d90d0ef61ec0f2cc3d480eef78c59727c6c2ce039f433882f6131a/httptools-0.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9518c406d7b310f05adb1a37f80acabac40504a575d7c0da6d3e365c695ac20d", size = 113366, upload-time = "2026-05-25T22:17:00.795Z" }, + { url = "https://files.pythonhosted.org/packages/cc/d4/13025f1a56e615dcb331e0bbe2d9a1143212b58c263385fc5d2e558f5bac/httptools-0.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:57278e6fa0424c42a8a3e454828ab4f0aff27b40cddf9679579b98c6dce6a376", size = 464676, upload-time = "2026-05-25T22:17:02.014Z" }, + { url = "https://files.pythonhosted.org/packages/bf/95/4c1c26c0b985f8a3331682d802598f14e32dc41bf7509266eb2c04ad4801/httptools-0.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bbb8caadb2b742d293169d2b458b5c001ef70e3158704aa3d3ef9597624c5d1d", size = 464235, upload-time = "2026-05-25T22:17:03.109Z" }, + { url = "https://files.pythonhosted.org/packages/a2/82/6735be2b0ca527718c431cdb8e5f70c3862c0844a687df0f572c51e11497/httptools-0.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:52dd695b865fe96d9d2b16b64a895f3f57bf3cb064e8383cd3b5713a069e8085", size = 449809, upload-time = "2026-05-25T22:17:04.443Z" }, + { url = "https://files.pythonhosted.org/packages/b5/f9/5811c74f37a758c8a4aa3dc430375119d335947e883efc4664d8f3559a41/httptools-0.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:20b4aac66ff65f7db06a375808b78f42a94970aa22e826b3cb2b43eb09174124", size = 452174, upload-time = "2026-05-25T22:17:05.476Z" }, + { url = "https://files.pythonhosted.org/packages/14/88/1d21a36da8f5cb0fa49eafd4b169eba5608d57e75bbcf61845cbc6243216/httptools-0.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:880490234c10f70a9830743097e8958d6e4b9f5a0ffc24515023afeef984054d", size = 208247, upload-time = "2026-05-25T22:17:07.843Z" }, + { url = "https://files.pythonhosted.org/packages/a5/42/cc4feea2945cb3051038f090c9b36bd5b8a9d7f5a894a506a8983e33fd1c/httptools-0.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5931891fb7b441b8a3853cf1b85c82c903defce084dd5f6771ca46e31bf862c5", size = 113064, upload-time = "2026-05-25T22:17:09.136Z" }, + { url = "https://files.pythonhosted.org/packages/e3/a6/febbb8b8db0f58b38e44ad6cb946e6a255ae49b55f2e8543408fb7501ccd/httptools-0.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b15fc622b0f869d19207c4089a501d9bcc63ca5e071ffdd2f03f922df882dcb2", size = 523851, upload-time = "2026-05-25T22:17:10.106Z" }, + { url = "https://files.pythonhosted.org/packages/b7/e4/f90a0df0b83beff265b7e3b65f2a4cefd95792d4be0ac3e16049f2acd3c2/httptools-0.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:425f83884fd6343828d8c565f046cb72b6d19063f6924093e11bcd8e1548cd09", size = 518842, upload-time = "2026-05-25T22:17:11.218Z" }, + { url = "https://files.pythonhosted.org/packages/9e/2d/0c9ac76dd2c893841fbf6498d6acec4f2442e1b7067f6e3e316a80e494e8/httptools-0.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ef7c3c97f4311c7be57e2986629df89d49cb434dbff78eafcd48c2bff986b15a", size = 501238, upload-time = "2026-05-25T22:17:12.728Z" }, + { url = "https://files.pythonhosted.org/packages/ca/42/906adc91ae3a5fa9c59c0a2f21c139725bd7e5b41ae6acd485cd14123ebf/httptools-0.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a1afd7c9fbff0d9f5d489c4ce2768bd09c84a46ddefc7161e6aa82ae35c85745", size = 509567, upload-time = "2026-05-25T22:17:13.842Z" }, +] + +[[package]] +name = "httpx" +version = "1.0.dev3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/25/40/b6f25020eeafd822fc473394a5db45a5964a64c88788eb5dc49ffabb64e7/httpx-1.0.dev3.tar.gz", hash = "sha256:e95700e4f9cf6430295f4c195f9cb0ca0549bab4294927f8002bf196851d40db", size = 761377, upload-time = "2025-09-15T16:15:12.087Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/97/8d/ff4f018c9a813994e28fabb998f8d76542fc2b46c1210d4c1b2c615eea7f/httpx-1.0.dev3-py3-none-any.whl", hash = "sha256:80b33db1bc8e1fac2a15f419839e324d472d528822608ea6b7a93fed2011722d", size = 34319, upload-time = "2025-09-15T16:15:10.458Z" }, +] + +[[package]] +name = "huggingface-hub" +version = "0.36.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "fsspec", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "hf-xet", marker = "(platform_machine == 'aarch64' and sys_platform == 'darwin') or (platform_machine == 'amd64' and sys_platform == 'darwin') or (platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'amd64' and sys_platform == 'linux') or (platform_machine == 'arm64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "packaging", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "pyyaml", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "requests", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "tqdm", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7c/b7/8cb61d2eece5fb05a83271da168186721c450eb74e3c31f7ef3169fa475b/huggingface_hub-0.36.2.tar.gz", hash = "sha256:1934304d2fb224f8afa3b87007d58501acfda9215b334eed53072dd5e815ff7a", size = 649782, upload-time = "2026-02-06T09:24:13.098Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/af/48ac8483240de756d2438c380746e7130d1c6f75802ef22f3c6d49982787/huggingface_hub-0.36.2-py3-none-any.whl", hash = "sha256:48f0c8eac16145dfce371e9d2d7772854a4f591bcb56c9cf548accf531d54270", size = 566395, upload-time = "2026-02-06T09:24:11.133Z" }, +] + +[[package]] +name = "humanfriendly" +version = "10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/3f/2c29224acb2e2df4d2046e4c73ee2662023c58ff5b113c4c1adac0886c43/humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc", size = 360702, upload-time = "2021-09-17T21:40:43.31Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", size = 86794, upload-time = "2021-09-17T21:40:39.897Z" }, +] + +[[package]] +name = "idna" +version = "3.17" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/28/99c51f664567218d824af024c0251650fb27e4ca066df188dab0769c5b91/idna-3.17.tar.gz", hash = "sha256:5eb0cb53bc467c12eadcf6de83163ad8527cec9416f44b9b61b19caedad2b87f", size = 196048, upload-time = "2026-05-28T14:32:38.55Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/a7/f76514cc40ad6234098ecdebda08732d75964776c51a42845b7da10649e2/idna-3.17-py3-none-any.whl", hash = "sha256:466e48829084efe2548012b855df21540b96f2e20e51bd124c851536556a592c", size = 65316, upload-time = "2026-05-28T14:32:37.035Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[package]] +name = "itsdangerous" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/cb/8ac0172223afbccb63986cc25049b154ecfb5e85932587206f42317be31d/itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173", size = 54410, upload-time = "2024-04-16T21:28:15.614Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234, upload-time = "2024-04-16T21:28:14.499Z" }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + +[[package]] +name = "jmespath" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d3/59/322338183ecda247fb5d1763a6cbe46eff7222eaeebafd9fa65d4bf5cb11/jmespath-1.1.0.tar.gz", hash = "sha256:472c87d80f36026ae83c6ddd0f1d05d4e510134ed462851fd5f754c8c3cbb88d", size = 27377, upload-time = "2026-01-22T16:35:26.279Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl", hash = "sha256:a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64", size = 20419, upload-time = "2026-01-22T16:35:24.919Z" }, +] + +[[package]] +name = "kiwisolver" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/67/9c61eccb13f0bdca9307614e782fec49ffdde0f7a2314935d489fa93cd9c/kiwisolver-1.5.0.tar.gz", hash = "sha256:d4193f3d9dc3f6f79aaed0e5637f45d98850ebf01f7ca20e69457f3e8946b66a", size = 103482, upload-time = "2026-03-09T13:15:53.382Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/dd/a495a9c104be1c476f0386e714252caf2b7eca883915422a64c50b88c6f5/kiwisolver-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9eed0f7edbb274413b6ee781cca50541c8c0facd3d6fd289779e494340a2b85c", size = 122798, upload-time = "2026-03-09T13:12:58.963Z" }, + { url = "https://files.pythonhosted.org/packages/11/60/37b4047a2af0cf5ef6d8b4b26e91829ae6fc6a2d1f74524bcb0e7cd28a32/kiwisolver-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c4923e404d6bcd91b6779c009542e5647fef32e4a5d75e115e3bbac6f2335eb", size = 66216, upload-time = "2026-03-09T13:13:00.155Z" }, + { url = "https://files.pythonhosted.org/packages/0a/aa/510dc933d87767584abfe03efa445889996c70c2990f6f87c3ebaa0a18c5/kiwisolver-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0df54df7e686afa55e6f21fb86195224a6d9beb71d637e8d7920c95cf0f89aac", size = 63911, upload-time = "2026-03-09T13:13:01.671Z" }, + { url = "https://files.pythonhosted.org/packages/80/46/bddc13df6c2a40741e0cc7865bb1c9ed4796b6760bd04ce5fae3928ef917/kiwisolver-1.5.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2517e24d7315eb51c10664cdb865195df38ab74456c677df67bb47f12d088a27", size = 1438209, upload-time = "2026-03-09T13:13:03.385Z" }, + { url = "https://files.pythonhosted.org/packages/fd/d6/76621246f5165e5372f02f5e6f3f48ea336a8f9e96e43997d45b240ed8cd/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff710414307fefa903e0d9bdf300972f892c23477829f49504e59834f4195398", size = 1248888, upload-time = "2026-03-09T13:13:05.231Z" }, + { url = "https://files.pythonhosted.org/packages/b2/c1/31559ec6fb39a5b48035ce29bb63ade628f321785f38c384dee3e2c08bc1/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6176c1811d9d5a04fa391c490cc44f451e240697a16977f11c6f722efb9041db", size = 1266304, upload-time = "2026-03-09T13:13:06.743Z" }, + { url = "https://files.pythonhosted.org/packages/5e/ef/1cb8276f2d29cc6a41e0a042f27946ca347d3a4a75acf85d0a16aa6dcc82/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50847dca5d197fcbd389c805aa1a1cf32f25d2e7273dc47ab181a517666b68cc", size = 1319650, upload-time = "2026-03-09T13:13:08.607Z" }, + { url = "https://files.pythonhosted.org/packages/4c/e4/5ba3cecd7ce6236ae4a80f67e5d5531287337d0e1f076ca87a5abe4cd5d0/kiwisolver-1.5.0-cp311-cp311-manylinux_2_39_riscv64.whl", hash = "sha256:01808c6d15f4c3e8559595d6d1fe6411c68e4a3822b4b9972b44473b24f4e679", size = 970949, upload-time = "2026-03-09T13:13:10.299Z" }, + { url = "https://files.pythonhosted.org/packages/5a/69/dc61f7ae9a2f071f26004ced87f078235b5507ab6e5acd78f40365655034/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f1f9f4121ec58628c96baa3de1a55a4e3a333c5102c8e94b64e23bf7b2083309", size = 2199125, upload-time = "2026-03-09T13:13:11.841Z" }, + { url = "https://files.pythonhosted.org/packages/e5/7b/abbe0f1b5afa85f8d084b73e90e5f801c0939eba16ac2e49af7c61a6c28d/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b7d335370ae48a780c6e6a6bbfa97342f563744c39c35562f3f367665f5c1de2", size = 2293783, upload-time = "2026-03-09T13:13:14.399Z" }, + { url = "https://files.pythonhosted.org/packages/8a/80/5908ae149d96d81580d604c7f8aefd0e98f4fd728cf172f477e9f2a81744/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:800ee55980c18545af444d93fdd60c56b580db5cc54867d8cbf8a1dc0829938c", size = 1960726, upload-time = "2026-03-09T13:13:16.047Z" }, + { url = "https://files.pythonhosted.org/packages/84/08/a78cb776f8c085b7143142ce479859cfec086bd09ee638a317040b6ef420/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c438f6ca858697c9ab67eb28246c92508af972e114cac34e57a6d4ba17a3ac08", size = 2464738, upload-time = "2026-03-09T13:13:17.897Z" }, + { url = "https://files.pythonhosted.org/packages/b1/e1/65584da5356ed6cb12c63791a10b208860ac40a83de165cb6a6751a686e3/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8c63c91f95173f9c2a67c7c526b2cea976828a0e7fced9cdcead2802dc10f8a4", size = 2270718, upload-time = "2026-03-09T13:13:19.421Z" }, + { url = "https://files.pythonhosted.org/packages/4d/b2/818b74ebea34dabe6d0c51cb1c572e046730e64844da6ed646d5298c40ce/kiwisolver-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4e9750bc21b886308024f8a54ccb9a2cc38ac9fa813bf4348434e3d54f337ff9", size = 123158, upload-time = "2026-03-09T13:13:23.127Z" }, + { url = "https://files.pythonhosted.org/packages/bf/d9/405320f8077e8e1c5c4bd6adc45e1e6edf6d727b6da7f2e2533cf58bff71/kiwisolver-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:72ec46b7eba5b395e0a7b63025490d3214c11013f4aacb4f5e8d6c3041829588", size = 66388, upload-time = "2026-03-09T13:13:24.765Z" }, + { url = "https://files.pythonhosted.org/packages/99/9f/795fedf35634f746151ca8839d05681ceb6287fbed6cc1c9bf235f7887c2/kiwisolver-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ed3a984b31da7481b103f68776f7128a89ef26ed40f4dc41a2223cda7fb24819", size = 64068, upload-time = "2026-03-09T13:13:25.878Z" }, + { url = "https://files.pythonhosted.org/packages/c4/13/680c54afe3e65767bed7ec1a15571e1a2f1257128733851ade24abcefbcc/kiwisolver-1.5.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bb5136fb5352d3f422df33f0c879a1b0c204004324150cc3b5e3c4f310c9049f", size = 1477934, upload-time = "2026-03-09T13:13:27.166Z" }, + { url = "https://files.pythonhosted.org/packages/c8/2f/cebfcdb60fd6a9b0f6b47a9337198bcbad6fbe15e68189b7011fd914911f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b2af221f268f5af85e776a73d62b0845fc8baf8ef0abfae79d29c77d0e776aaf", size = 1278537, upload-time = "2026-03-09T13:13:28.707Z" }, + { url = "https://files.pythonhosted.org/packages/f2/0d/9b782923aada3fafb1d6b84e13121954515c669b18af0c26e7d21f579855/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b0f172dc8ffaccb8522d7c5d899de00133f2f1ca7b0a49b7da98e901de87bf2d", size = 1296685, upload-time = "2026-03-09T13:13:30.528Z" }, + { url = "https://files.pythonhosted.org/packages/27/70/83241b6634b04fe44e892688d5208332bde130f38e610c0418f9ede47ded/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6ab8ba9152203feec73758dad83af9a0bbe05001eb4639e547207c40cfb52083", size = 1346024, upload-time = "2026-03-09T13:13:32.818Z" }, + { url = "https://files.pythonhosted.org/packages/e4/db/30ed226fb271ae1a6431fc0fe0edffb2efe23cadb01e798caeb9f2ceae8f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:cdee07c4d7f6d72008d3f73b9bf027f4e11550224c7c50d8df1ae4a37c1402a6", size = 987241, upload-time = "2026-03-09T13:13:34.435Z" }, + { url = "https://files.pythonhosted.org/packages/ec/bd/c314595208e4c9587652d50959ead9e461995389664e490f4dce7ff0f782/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7c60d3c9b06fb23bd9c6139281ccbdc384297579ae037f08ae90c69f6845c0b1", size = 2227742, upload-time = "2026-03-09T13:13:36.4Z" }, + { url = "https://files.pythonhosted.org/packages/c1/43/0499cec932d935229b5543d073c2b87c9c22846aab48881e9d8d6e742a2d/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e315e5ec90d88e140f57696ff85b484ff68bb311e36f2c414aa4286293e6dee0", size = 2323966, upload-time = "2026-03-09T13:13:38.204Z" }, + { url = "https://files.pythonhosted.org/packages/3d/6f/79b0d760907965acfd9d61826a3d41f8f093c538f55cd2633d3f0db269f6/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:1465387ac63576c3e125e5337a6892b9e99e0627d52317f3ca79e6930d889d15", size = 1977417, upload-time = "2026-03-09T13:13:39.966Z" }, + { url = "https://files.pythonhosted.org/packages/ab/31/01d0537c41cb75a551a438c3c7a80d0c60d60b81f694dac83dd436aec0d0/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:530a3fd64c87cffa844d4b6b9768774763d9caa299e9b75d8eca6a4423b31314", size = 2491238, upload-time = "2026-03-09T13:13:41.698Z" }, + { url = "https://files.pythonhosted.org/packages/e4/34/8aefdd0be9cfd00a44509251ba864f5caf2991e36772e61c408007e7f417/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1d9daea4ea6b9be74fe2f01f7fbade8d6ffab263e781274cffca0dba9be9eec9", size = 2294947, upload-time = "2026-03-09T13:13:43.343Z" }, + { url = "https://files.pythonhosted.org/packages/1c/fa/2910df836372d8761bb6eff7d8bdcb1613b5c2e03f260efe7abe34d388a7/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-macosx_10_13_x86_64.whl", hash = "sha256:5ae8e62c147495b01a0f4765c878e9bfdf843412446a247e28df59936e99e797", size = 130262, upload-time = "2026-03-09T13:15:35.629Z" }, + { url = "https://files.pythonhosted.org/packages/0f/41/c5f71f9f00aabcc71fee8b7475e3f64747282580c2fe748961ba29b18385/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:f6764a4ccab3078db14a632420930f6186058750df066b8ea2a7106df91d3203", size = 138036, upload-time = "2026-03-09T13:15:36.894Z" }, + { url = "https://files.pythonhosted.org/packages/fa/06/7399a607f434119c6e1fdc8ec89a8d51ccccadf3341dee4ead6bd14caaf5/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c31c13da98624f957b0fb1b5bae5383b2333c2c3f6793d9825dd5ce79b525cb7", size = 194295, upload-time = "2026-03-09T13:15:38.22Z" }, + { url = "https://files.pythonhosted.org/packages/e9/eb/5fcbbbf9a0e2c3a35effb88831a483345326bbc3a030a3b5b69aee647f84/kiwisolver-1.5.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ec4c85dc4b687c7f7f15f553ff26a98bfe8c58f5f7f0ac8905f0ba4c7be60232", size = 59532, upload-time = "2026-03-09T13:15:47.047Z" }, + { url = "https://files.pythonhosted.org/packages/c3/9b/e17104555bb4db148fd52327feea1e96be4b88e8e008b029002c281a21ab/kiwisolver-1.5.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:12e91c215a96e39f57989c8912ae761286ac5a9584d04030ceb3368a357f017a", size = 57420, upload-time = "2026-03-09T13:15:48.199Z" }, + { url = "https://files.pythonhosted.org/packages/48/44/2b5b95b7aa39fb2d8d9d956e0f3d5d45aef2ae1d942d4c3ffac2f9cfed1a/kiwisolver-1.5.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:be4a51a55833dc29ab5d7503e7bcb3b3af3402d266018137127450005cdfe737", size = 79892, upload-time = "2026-03-09T13:15:49.694Z" }, + { url = "https://files.pythonhosted.org/packages/52/7d/7157f9bba6b455cfb4632ed411e199fc8b8977642c2b12082e1bd9e6d173/kiwisolver-1.5.0-pp311-pypy311_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:daae526907e262de627d8f70058a0f64acc9e2641c164c99c8f594b34a799a16", size = 77603, upload-time = "2026-03-09T13:15:50.945Z" }, +] + +[[package]] +name = "marin-finelog" +version = "0.99" +source = { git = "https://github.com/marin-community/marin.git?subdirectory=lib%2Ffinelog&branch=alxmrs%2Fstamp-iris-build-date#ce9cb46a76f2246d3385be842cde644ee363b743" } +dependencies = [ + { name = "click", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "connect-python", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "duckdb", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "fsspec", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "gcsfs", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "marin-rigging", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "numpy", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "protobuf", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "pyarrow", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "pyyaml", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "starlette", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "uvicorn", extra = ["standard"], marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "zstandard", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] + +[[package]] +name = "marin-fray" +version = "0.99" +source = { git = "https://github.com/marin-community/marin.git?subdirectory=lib%2Ffray&branch=alxmrs%2Fstamp-iris-build-date#ce9cb46a76f2246d3385be842cde644ee363b743" } +dependencies = [ + { name = "click", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "cloudpickle", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "fastapi", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "flask", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "fsspec", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "httpx", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "humanfriendly", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "marin-iris", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "marin-rigging", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "mergedeep", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "pyyaml", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "tabulate", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "uvicorn", extra = ["standard"], marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "zstandard", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] + +[[package]] +name = "marin-iris" +version = "0.99" +source = { git = "https://github.com/marin-community/marin.git?subdirectory=lib%2Firis&branch=alxmrs%2Fstamp-iris-build-date#ce9cb46a76f2246d3385be842cde644ee363b743" } +dependencies = [ + { name = "click", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "cloudpickle", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "connect-python", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "fsspec", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "gcsfs", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "google-auth", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "google-cloud-tpu", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "grpcio", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "httpx", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "humanfriendly", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "marin-finelog", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "marin-rigging", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "pyjwt", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "pyyaml", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "s3fs", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "sqlalchemy", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "starlette", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "tabulate", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "uvicorn", extra = ["standard"], marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "zstandard", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] + +[[package]] +name = "marin-rigging" +version = "0.99" +source = { git = "https://github.com/marin-community/marin.git?subdirectory=lib%2Frigging&branch=alxmrs%2Fstamp-iris-build-date#ce9cb46a76f2246d3385be842cde644ee363b743" } +dependencies = [ + { name = "connect-python", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "fsspec", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "gcsfs", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "s3fs", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] + +[[package]] +name = "marin-zephyr" +version = "0.99" +source = { git = "https://github.com/marin-community/marin.git?subdirectory=lib%2Fzephyr&branch=alxmrs%2Fstamp-iris-build-date#ce9cb46a76f2246d3385be842cde644ee363b743" } +dependencies = [ + { name = "braceexpand", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "click", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "cloudpickle", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "fsspec", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "humanfriendly", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "marin-fray", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "marin-iris", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "marin-rigging", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "msgspec", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "numpy", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "tqdm-loggable", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "vortex-data", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "xxhash", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "zstandard", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] + +[[package]] +name = "marinfold" +version = "0.1.0" +source = { git = "https://github.com/Open-Athena/MarinFold.git?subdirectory=marinfold#ba4bb6b5aa558086f8210e77e7b526a6abe466d0" } +dependencies = [ + { name = "gemmi", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "huggingface-hub", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "matplotlib", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "numpy", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "pyarrow", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "pyyaml", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "tokenizers", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "transformers", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631, upload-time = "2025-09-27T18:36:18.185Z" }, + { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058, upload-time = "2025-09-27T18:36:19.444Z" }, + { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287, upload-time = "2025-09-27T18:36:20.768Z" }, + { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940, upload-time = "2025-09-27T18:36:22.249Z" }, + { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887, upload-time = "2025-09-27T18:36:23.535Z" }, + { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692, upload-time = "2025-09-27T18:36:24.823Z" }, + { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471, upload-time = "2025-09-27T18:36:25.95Z" }, + { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923, upload-time = "2025-09-27T18:36:27.109Z" }, + { 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, upload-time = "2025-09-27T18:36:30.854Z" }, + { 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, upload-time = "2025-09-27T18:36:31.971Z" }, + { 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, upload-time = "2025-09-27T18:36:32.813Z" }, + { 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, upload-time = "2025-09-27T18:36:33.86Z" }, + { 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, upload-time = "2025-09-27T18:36:35.099Z" }, + { 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, upload-time = "2025-09-27T18:36:36.001Z" }, + { 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, upload-time = "2025-09-27T18:36:36.906Z" }, + { 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, upload-time = "2025-09-27T18:36:37.868Z" }, +] + +[[package]] +name = "matplotlib" +version = "3.11.0rc2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "contourpy", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "cycler", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "fonttools", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "kiwisolver", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "numpy", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "packaging", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "pillow", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "pyparsing", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "python-dateutil", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/b4/41b4c812df4c89230465b71cc86217923f904349d803abf67119a471e0ad/matplotlib-3.11.0rc2.tar.gz", hash = "sha256:cba0e90ae7bade3cec236c1082ef1c622ddb46f0efb060149bc2f25566ce6e5d", size = 33206182, upload-time = "2026-05-13T00:32:15.03Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/84/39184792502c51f1443877fad5f3ce3ea272ef67a0f11575b7b963c8d80b/matplotlib-3.11.0rc2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:01112632f910144490d0bffe535e824ef559197765acb3395bd8df24dcf1126f", size = 9428950, upload-time = "2026-05-13T00:30:08.093Z" }, + { url = "https://files.pythonhosted.org/packages/34/27/3cbecc4589417dee9397840103ca090255c868babf7be8e24dad7e4a4b62/matplotlib-3.11.0rc2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b1b57d0aeb4c724141edc63c5c532ecfe5974a973e326063744e4bd207c1ff49", size = 9257512, upload-time = "2026-05-13T00:30:11.959Z" }, + { url = "https://files.pythonhosted.org/packages/10/b5/79bbccf16d13560df5f8f2864e0ffa1520bdad4f1a698325e4e32af1a723/matplotlib-3.11.0rc2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0410755d6490a7180a3cb3b5929884fb40bdf27e772f2784dbcb03eec31d1ca2", size = 10017059, upload-time = "2026-05-13T00:30:14.282Z" }, + { url = "https://files.pythonhosted.org/packages/be/b1/9e7d16f408150e3e5c97865f0fece8e484995b343325cdd6557038fe4b3a/matplotlib-3.11.0rc2-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4ac738c7c83043c97c36dd6c554b65a3fb63a7876359d48d9f849245cf71920", size = 10825960, upload-time = "2026-05-13T00:30:17.045Z" }, + { url = "https://files.pythonhosted.org/packages/ac/1c/59d1b688008a1f5fcc6872a32b07f6d4b3a69892b98d2e2013f9353a793c/matplotlib-3.11.0rc2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:454d1fb35cc5cb0c5ac8c64fc9117a409208f60d08b4ca4dc1c7161d310ed640", size = 10908363, upload-time = "2026-05-13T00:30:19.913Z" }, + { url = "https://files.pythonhosted.org/packages/f2/00/d95a0ed2ccfb992b40556e1cd8b36566f184126692947890b5d929616ace/matplotlib-3.11.0rc2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:890805345e006505b8ad0287ab93b52de6386d0aa23cb0731d19a45c25f40054", size = 9442512, upload-time = "2026-05-13T00:30:27.925Z" }, + { url = "https://files.pythonhosted.org/packages/a4/3d/97ba1929d1cf2ac77a274e7deda7d3f9412a0d4ac242fad7ad6674ea8881/matplotlib-3.11.0rc2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c72242126b16f1b952241a37e035e97669d48376112dcf86983b2d5fff353327", size = 9272154, upload-time = "2026-05-13T00:30:30.701Z" }, + { url = "https://files.pythonhosted.org/packages/ee/81/2d5edc84d40ba6b7b9b944cfa47c57ceed0b54bf73413ad8baab05b2a84f/matplotlib-3.11.0rc2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4bc28579e1ec8e78a025c44996adcd21236dd95e3543407c699c1fa2b0e9a4fc", size = 10027704, upload-time = "2026-05-13T00:30:33.525Z" }, + { url = "https://files.pythonhosted.org/packages/bb/24/22b2d940fe69cf3fefb3eab282356b4b93694b606c8c0a14a5472e190bc7/matplotlib-3.11.0rc2-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c19506b29c40c315560afd0d2aa6fb0560e2003e45224504973b10bc19e53961", size = 10835543, upload-time = "2026-05-13T00:30:36.639Z" }, + { url = "https://files.pythonhosted.org/packages/51/39/91f77bf1b8c78f31ad8983075a331b3768f3f593cadf648a61a915b234e8/matplotlib-3.11.0rc2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b44b93d313bdb9532299ce1ab2265519a4bb9ad7c7c4c9475f6e0cf9ffcfcd15", size = 10919334, upload-time = "2026-05-13T00:30:39.557Z" }, + { url = "https://files.pythonhosted.org/packages/eb/3d/345a63bffc3411bd0082bf8273324eb78bdd80e87514137791271412a34f/matplotlib-3.11.0rc2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:014e8a328beb6d42147b8d377022b895c881ebddac512c30e64005e399c1d76b", size = 9426667, upload-time = "2026-05-13T00:32:06.727Z" }, + { url = "https://files.pythonhosted.org/packages/c6/82/2cd569c1a83dbecb814c67095df69d96aebae7a6ad75516b9f7cfb803ee1/matplotlib-3.11.0rc2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:8e212ed755cdebafc1c6058070fc5f343affd9bbe2ce6259581b2117dfc8296c", size = 9255018, upload-time = "2026-05-13T00:32:09.322Z" }, + { url = "https://files.pythonhosted.org/packages/4a/66/bee684008483e5e5cc53cb55567d074c9488356a9880cca5844e314f2b4b/matplotlib-3.11.0rc2-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:15adeecd2b98a10cd71d78ecbd067dcb9ebbf7c6448333c88de6034165f5a7c8", size = 10011587, upload-time = "2026-05-13T00:32:11.87Z" }, +] + +[[package]] +name = "mergedeep" +version = "1.3.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/41/580bb4006e3ed0361b8151a01d324fb03f420815446c7def45d02f74c270/mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", size = 4661, upload-time = "2021-02-05T18:55:30.623Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354, upload-time = "2021-02-05T18:55:29.583Z" }, +] + +[[package]] +name = "msgspec" +version = "0.21.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/60/f79b9b013a16fa3a58350c9295ddc6789f2e335f36ea61ed10a21b215364/msgspec-0.21.1.tar.gz", hash = "sha256:2313508e394b0d208f8f56892ca9b2799e2561329de9763b19619595a6c0f72c", size = 319193, upload-time = "2026-04-12T21:44:50.394Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/7f/bbc4e74cd33d316b75541149e4d35b163b63bce066530ae185a2ec3b5bfc/msgspec-0.21.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b504b6e7f7a22a24b27232b73034421692147865162daaec9f3bf62439007c87", size = 193131, upload-time = "2026-04-12T21:43:56.094Z" }, + { url = "https://files.pythonhosted.org/packages/c1/60/504886af1aaf854112663b842d5eea9a15d9588f9bf7d0d2df736424b84d/msgspec-0.21.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4692b7c1609155708c4418f88e92f63c13fdf08aa095c84bae82bad75b53389b", size = 186597, upload-time = "2026-04-12T21:43:57.242Z" }, + { url = "https://files.pythonhosted.org/packages/fa/54/d24ddeaa65b5278c9e67f48ce3c17a9831e8f3722f3c8322ee120aca22ef/msgspec-0.21.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d3124010b3815451494c85ff345e693cb9fe5889cfcbbef39ed8622e0e72319c", size = 215158, upload-time = "2026-04-12T21:43:58.442Z" }, + { url = "https://files.pythonhosted.org/packages/9f/75/bb79c8b89a93ae23cd33c0d802373f16feaf9633f05d8af77091350dda0a/msgspec-0.21.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6badc03b9725352219cca017bfe71c61f2fbd0fb5982b410ac17c97c213deb30", size = 219856, upload-time = "2026-04-12T21:44:00.015Z" }, + { url = "https://files.pythonhosted.org/packages/b4/9c/c5ca26b46f0ebbd3a6683695ef89396712cb9e4199fd1f0bc1dd968216b1/msgspec-0.21.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5d2d4116ebe3035a78d9ec76e99a9d64e5fa6d44fe61a9c5de7fd1acf54bcc69", size = 220314, upload-time = "2026-04-12T21:44:01.548Z" }, + { url = "https://files.pythonhosted.org/packages/c8/31/645a351c4285dce40ed6755c3dcc0aa648e26dacb20a98018fe2cce5e87b/msgspec-0.21.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0d1009f6715f5bff3b54d4ff5c7428ad96197e0534e1645b8e9b955890c84664", size = 223215, upload-time = "2026-04-12T21:44:02.884Z" }, + { url = "https://files.pythonhosted.org/packages/6e/cf/317224852c00248c620a9bcf4b26e2e4ab8afd752f18d2a6ef73ebd423b6/msgspec-0.21.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d4248cf0b6129b7d230eacd493c17cc2d4f3989f3bb7f633a928a85b7dcfa251", size = 196188, upload-time = "2026-04-12T21:44:07.181Z" }, + { url = "https://files.pythonhosted.org/packages/6d/81/074612945c0666078f7366f40000013de9f6ba687491d450df699bceebc9/msgspec-0.21.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5102c7e9b3acff82178449b85006d96310e690291bb1ea0142f1b24bcb8aabcb", size = 188473, upload-time = "2026-04-12T21:44:08.736Z" }, + { url = "https://files.pythonhosted.org/packages/8a/37/655101799590bcc5fddb2bd3fe0e6194e816c2d1da7c361725f5eb89a910/msgspec-0.21.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:846758412e9518252b2ac9bffd6f0e54d9ff614f5f9488df7749f81ff5c80920", size = 218871, upload-time = "2026-04-12T21:44:09.917Z" }, + { url = "https://files.pythonhosted.org/packages/b5/d1/d4cd9fe89c7d400d7a18f86ccc94daa3f0927f53558846fcb60791dce5d6/msgspec-0.21.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:21995e74b5c598c2e004110ad66ec7f1b8c20bf2bcf3b2de8fd9a3094422d3ff", size = 225025, upload-time = "2026-04-12T21:44:11.191Z" }, + { url = "https://files.pythonhosted.org/packages/24/bf/e20549e602b9edccadeeff98760345a416f9cce846a657e8b18e3396b212/msgspec-0.21.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6129f0cca52992e898fd5344187f7c8127b63d810b2fd73e36fca73b4c6475ee", size = 222672, upload-time = "2026-04-12T21:44:12.481Z" }, + { url = "https://files.pythonhosted.org/packages/b4/68/04d7a8f0f786545cf9b8c280c57aa6befb5977af6e884b8b54191cbe44b3/msgspec-0.21.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ef3ec2296248d1f8b9231acb051b6d471dfde8f21819e86c9adaaa9f42918521", size = 227303, upload-time = "2026-04-12T21:44:13.709Z" }, +] + +[[package]] +name = "multidict" +version = "6.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010, upload-time = "2026-01-26T02:46:45.979Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/f1/a90635c4f88fb913fbf4ce660b83b7445b7a02615bda034b2f8eb38fd597/multidict-6.7.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ff981b266af91d7b4b3793ca3382e53229088d193a85dfad6f5f4c27fc73e5d", size = 76626, upload-time = "2026-01-26T02:43:26.485Z" }, + { url = "https://files.pythonhosted.org/packages/a6/9b/267e64eaf6fc637a15b35f5de31a566634a2740f97d8d094a69d34f524a4/multidict-6.7.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:844c5bca0b5444adb44a623fb0a1310c2f4cd41f402126bb269cd44c9b3f3e1e", size = 44706, upload-time = "2026-01-26T02:43:27.607Z" }, + { url = "https://files.pythonhosted.org/packages/dd/a4/d45caf2b97b035c57267791ecfaafbd59c68212004b3842830954bb4b02e/multidict-6.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f2a0a924d4c2e9afcd7ec64f9de35fcd96915149b2216e1cb2c10a56df483855", size = 44356, upload-time = "2026-01-26T02:43:28.661Z" }, + { url = "https://files.pythonhosted.org/packages/fd/d2/0a36c8473f0cbaeadd5db6c8b72d15bbceeec275807772bfcd059bef487d/multidict-6.7.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8be1802715a8e892c784c0197c2ace276ea52702a0ede98b6310c8f255a5afb3", size = 244355, upload-time = "2026-01-26T02:43:31.165Z" }, + { url = "https://files.pythonhosted.org/packages/5d/16/8c65be997fd7dd311b7d39c7b6e71a0cb449bad093761481eccbbe4b42a2/multidict-6.7.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2e2d2ed645ea29f31c4c7ea1552fcfd7cb7ba656e1eafd4134a6620c9f5fdd9e", size = 246433, upload-time = "2026-01-26T02:43:32.581Z" }, + { url = "https://files.pythonhosted.org/packages/01/fb/4dbd7e848d2799c6a026ec88ad39cf2b8416aa167fcc903baa55ecaa045c/multidict-6.7.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:95922cee9a778659e91db6497596435777bd25ed116701a4c034f8e46544955a", size = 225376, upload-time = "2026-01-26T02:43:34.417Z" }, + { url = "https://files.pythonhosted.org/packages/b6/8a/4a3a6341eac3830f6053062f8fbc9a9e54407c80755b3f05bc427295c2d0/multidict-6.7.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6b83cabdc375ffaaa15edd97eb7c0c672ad788e2687004990074d7d6c9b140c8", size = 257365, upload-time = "2026-01-26T02:43:35.741Z" }, + { url = "https://files.pythonhosted.org/packages/f7/a2/dd575a69c1aa206e12d27d0770cdf9b92434b48a9ef0cd0d1afdecaa93c4/multidict-6.7.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:38fb49540705369bab8484db0689d86c0a33a0a9f2c1b197f506b71b4b6c19b0", size = 254747, upload-time = "2026-01-26T02:43:36.976Z" }, + { url = "https://files.pythonhosted.org/packages/5a/56/21b27c560c13822ed93133f08aa6372c53a8e067f11fbed37b4adcdac922/multidict-6.7.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:439cbebd499f92e9aa6793016a8acaa161dfa749ae86d20960189f5398a19144", size = 246293, upload-time = "2026-01-26T02:43:38.258Z" }, + { url = "https://files.pythonhosted.org/packages/5a/a4/23466059dc3854763423d0ad6c0f3683a379d97673b1b89ec33826e46728/multidict-6.7.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6d3bc717b6fe763b8be3f2bee2701d3c8eb1b2a8ae9f60910f1b2860c82b6c49", size = 242962, upload-time = "2026-01-26T02:43:40.034Z" }, + { url = "https://files.pythonhosted.org/packages/1f/67/51dd754a3524d685958001e8fa20a0f5f90a6a856e0a9dcabff69be3dbb7/multidict-6.7.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:619e5a1ac57986dbfec9f0b301d865dddf763696435e2962f6d9cf2fdff2bb71", size = 237360, upload-time = "2026-01-26T02:43:41.752Z" }, + { url = "https://files.pythonhosted.org/packages/64/3f/036dfc8c174934d4b55d86ff4f978e558b0e585cef70cfc1ad01adc6bf18/multidict-6.7.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0b38ebffd9be37c1170d33bc0f36f4f262e0a09bc1aac1c34c7aa51a7293f0b3", size = 245940, upload-time = "2026-01-26T02:43:43.042Z" }, + { url = "https://files.pythonhosted.org/packages/3d/20/6214d3c105928ebc353a1c644a6ef1408bc5794fcb4f170bb524a3c16311/multidict-6.7.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:10ae39c9cfe6adedcdb764f5e8411d4a92b055e35573a2eaa88d3323289ef93c", size = 253502, upload-time = "2026-01-26T02:43:44.371Z" }, + { url = "https://files.pythonhosted.org/packages/b1/e2/c653bc4ae1be70a0f836b82172d643fcf1dade042ba2676ab08ec08bff0f/multidict-6.7.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:25167cc263257660290fba06b9318d2026e3c910be240a146e1f66dd114af2b0", size = 247065, upload-time = "2026-01-26T02:43:45.745Z" }, + { url = "https://files.pythonhosted.org/packages/c8/11/a854b4154cd3bd8b1fd375e8a8ca9d73be37610c361543d56f764109509b/multidict-6.7.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:128441d052254f42989ef98b7b6a6ecb1e6f708aa962c7984235316db59f50fa", size = 241870, upload-time = "2026-01-26T02:43:47.054Z" }, + { url = "https://files.pythonhosted.org/packages/8d/9c/f20e0e2cf80e4b2e4b1c365bf5fe104ee633c751a724246262db8f1a0b13/multidict-6.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a90f75c956e32891a4eda3639ce6dd86e87105271f43d43442a3aedf3cddf172", size = 76893, upload-time = "2026-01-26T02:43:52.754Z" }, + { url = "https://files.pythonhosted.org/packages/fe/cf/18ef143a81610136d3da8193da9d80bfe1cb548a1e2d1c775f26b23d024a/multidict-6.7.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fccb473e87eaa1382689053e4a4618e7ba7b9b9b8d6adf2027ee474597128cd", size = 45456, upload-time = "2026-01-26T02:43:53.893Z" }, + { url = "https://files.pythonhosted.org/packages/a9/65/1caac9d4cd32e8433908683446eebc953e82d22b03d10d41a5f0fefe991b/multidict-6.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0fa96985700739c4c7853a43c0b3e169360d6855780021bfc6d0f1ce7c123e7", size = 43872, upload-time = "2026-01-26T02:43:55.041Z" }, + { url = "https://files.pythonhosted.org/packages/cf/3b/d6bd75dc4f3ff7c73766e04e705b00ed6dbbaccf670d9e05a12b006f5a21/multidict-6.7.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cb2a55f408c3043e42b40cc8eecd575afa27b7e0b956dfb190de0f8499a57a53", size = 251018, upload-time = "2026-01-26T02:43:56.198Z" }, + { url = "https://files.pythonhosted.org/packages/fd/80/c959c5933adedb9ac15152e4067c702a808ea183a8b64cf8f31af8ad3155/multidict-6.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb0ce7b2a32d09892b3dd6cc44877a0d02a33241fafca5f25c8b6b62374f8b75", size = 258883, upload-time = "2026-01-26T02:43:57.499Z" }, + { url = "https://files.pythonhosted.org/packages/86/85/7ed40adafea3d4f1c8b916e3b5cc3a8e07dfcdcb9cd72800f4ed3ca1b387/multidict-6.7.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c3a32d23520ee37bf327d1e1a656fec76a2edd5c038bf43eddfa0572ec49c60b", size = 242413, upload-time = "2026-01-26T02:43:58.755Z" }, + { url = "https://files.pythonhosted.org/packages/d2/57/b8565ff533e48595503c785f8361ff9a4fde4d67de25c207cd0ba3befd03/multidict-6.7.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9c90fed18bffc0189ba814749fdcc102b536e83a9f738a9003e569acd540a733", size = 268404, upload-time = "2026-01-26T02:44:00.216Z" }, + { url = "https://files.pythonhosted.org/packages/e0/50/9810c5c29350f7258180dfdcb2e52783a0632862eb334c4896ac717cebcb/multidict-6.7.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:da62917e6076f512daccfbbde27f46fed1c98fee202f0559adec8ee0de67f71a", size = 269456, upload-time = "2026-01-26T02:44:02.202Z" }, + { url = "https://files.pythonhosted.org/packages/f3/8d/5e5be3ced1d12966fefb5c4ea3b2a5b480afcea36406559442c6e31d4a48/multidict-6.7.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfde23ef6ed9db7eaee6c37dcec08524cb43903c60b285b172b6c094711b3961", size = 256322, upload-time = "2026-01-26T02:44:03.56Z" }, + { url = "https://files.pythonhosted.org/packages/31/6e/d8a26d81ac166a5592782d208dd90dfdc0a7a218adaa52b45a672b46c122/multidict-6.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3758692429e4e32f1ba0df23219cd0b4fc0a52f476726fff9337d1a57676a582", size = 253955, upload-time = "2026-01-26T02:44:04.845Z" }, + { url = "https://files.pythonhosted.org/packages/59/4c/7c672c8aad41534ba619bcd4ade7a0dc87ed6b8b5c06149b85d3dd03f0cd/multidict-6.7.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:398c1478926eca669f2fd6a5856b6de9c0acf23a2cb59a14c0ba5844fa38077e", size = 251254, upload-time = "2026-01-26T02:44:06.133Z" }, + { url = "https://files.pythonhosted.org/packages/7b/bd/84c24de512cbafbdbc39439f74e967f19570ce7924e3007174a29c348916/multidict-6.7.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c102791b1c4f3ab36ce4101154549105a53dc828f016356b3e3bcae2e3a039d3", size = 252059, upload-time = "2026-01-26T02:44:07.518Z" }, + { url = "https://files.pythonhosted.org/packages/fa/ba/f5449385510825b73d01c2d4087bf6d2fccc20a2d42ac34df93191d3dd03/multidict-6.7.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a088b62bd733e2ad12c50dad01b7d0166c30287c166e137433d3b410add807a6", size = 263588, upload-time = "2026-01-26T02:44:09.382Z" }, + { url = "https://files.pythonhosted.org/packages/d7/11/afc7c677f68f75c84a69fe37184f0f82fce13ce4b92f49f3db280b7e92b3/multidict-6.7.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3d51ff4785d58d3f6c91bdbffcb5e1f7ddfda557727043aa20d20ec4f65e324a", size = 259642, upload-time = "2026-01-26T02:44:10.73Z" }, + { url = "https://files.pythonhosted.org/packages/2b/17/ebb9644da78c4ab36403739e0e6e0e30ebb135b9caf3440825001a0bddcb/multidict-6.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc5907494fccf3e7d3f94f95c91d6336b092b5fc83811720fae5e2765890dfba", size = 251377, upload-time = "2026-01-26T02:44:12.042Z" }, + { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" }, +] + +[[package]] +name = "numpy" +version = "2.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/ad/fed0499ce6a338d2a03ebae59cd15093910c8875328855781952abf6c2fe/numpy-2.4.6.tar.gz", hash = "sha256:f3a3570c4a2a16746ac2c31a7c7c7b0c186b95ce902e33db6f28094ed7387dda", size = 20735807, upload-time = "2026-05-18T23:37:14.07Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/49/ec46835a70be8fa6446c495126ac84fdb28cb2558e1620ffb87a10c8b64c/numpy-2.4.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0280e0356c0829a18d9de1cb7eee50ec22ca639878d7240307ca0943d73cd2c4", size = 16969194, upload-time = "2026-05-18T23:33:13.503Z" }, + { url = "https://files.pythonhosted.org/packages/0e/0d/f5957185c0ee2f3e12f78715aa9e3b353fd83633316c8532b38faa37e3f6/numpy-2.4.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:110f8b71aacb688ec69062bb7f6938a0f8acb01b7c1c4beb453c65b6d234584d", size = 14964111, upload-time = "2026-05-18T23:33:17.795Z" }, + { url = "https://files.pythonhosted.org/packages/ad/40/40a40ee0ddf7ceb782c49af278894b686e586d65d8c1889c8b5da01a3d7d/numpy-2.4.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:4cfe66903cc32a9921a6733d96b19bb6abf310397581bbad89c228f5abaf0ee8", size = 5469159, upload-time = "2026-05-18T23:33:20.654Z" }, + { url = "https://files.pythonhosted.org/packages/63/13/f9a8046535cb21deae82f8d03de9617e08882d274fad2539630761888228/numpy-2.4.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8155154c7c691289fe18f510b5d4657c68c67989f293f0535a91360392ff6538", size = 6798936, upload-time = "2026-05-18T23:33:22.987Z" }, + { url = "https://files.pythonhosted.org/packages/33/a8/6fa8c1a345a8c85dbb21932c447bee07c30a2c2a3f31e369c0a84b300147/numpy-2.4.6-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ab0a9c4ffb1a6d95ef519fe4247dba8eb6b18ad93999f76b7f657039acabd47", size = 15966692, upload-time = "2026-05-18T23:33:26.62Z" }, + { url = "https://files.pythonhosted.org/packages/02/03/74fe2a4cb3817d94d86402f2506554130a2f01414e299b5a843e5a8a957f/numpy-2.4.6-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:89cd468399cfd2504718f0ba50e410dca55a170b61a02ad92bb18c8a65186e93", size = 16918164, upload-time = "2026-05-18T23:33:29.955Z" }, + { url = "https://files.pythonhosted.org/packages/c5/80/3615be3313f7e7696609bc194b9f0101da809df79e859bdb84e0cd043f46/numpy-2.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2d37ab77531417474168eb79d6d80b14f821a966818505d03013d0833edb7a8", size = 17322877, upload-time = "2026-05-18T23:33:34.724Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ac/a691e0fe2675e370d0e08ff905adc49a1c8830e8cae03efe4477e92cd55d/numpy-2.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f407cb6b8e9d6d8c626bc73c945db1706035af8fd632295547bf1c9e46d092d6", size = 18651487, upload-time = "2026-05-18T23:33:38.217Z" }, + { url = "https://files.pythonhosted.org/packages/95/2a/3d7b5ac8aac24feaf9ad7ed58f45b0bbc06d37e4338ae84c9f2298b570f9/numpy-2.4.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:001fbb8e08d942dd57599e781f2472269ee7f2755fae407b4f67b2f0b17da3f1", size = 16689119, upload-time = "2026-05-18T23:33:54.065Z" }, + { url = "https://files.pythonhosted.org/packages/ea/12/92c4c131527599e8288d6918e888d88726f84d805d784b771f32408aeaef/numpy-2.4.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ebfb099f8dcf083deef3ac1ca4c1503f387cf76296fcb3816b66f5ecb5f54fdb", size = 14699246, upload-time = "2026-05-18T23:33:57.621Z" }, + { url = "https://files.pythonhosted.org/packages/ad/fe/c0a6b7b2ca128a8fb228575147073b660656734b8ebe4d76c8fd748dcc79/numpy-2.4.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3213d622a0283a39a93d188f3cf72b26862df52fbb4ca3697f51705016523d41", size = 5204410, upload-time = "2026-05-18T23:34:00.302Z" }, + { url = "https://files.pythonhosted.org/packages/f3/d4/9770d14ba719432bb90a421bfd443872ed0f70f7264b64bec12ea363d5fd/numpy-2.4.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:357cc07a6d7b0b182ff02249616a03742827ebb1277546b5c7cd7f7620a45698", size = 6551240, upload-time = "2026-05-18T23:34:02.852Z" }, + { url = "https://files.pythonhosted.org/packages/c9/c6/50a46a6205feba2343f1d6d17438107c5dc491ed1c736e6ea68689fd906b/numpy-2.4.6-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f9fb9157b4ce2971008323afe46053787b526ef624fea915b261468a8421a0f", size = 15671012, upload-time = "2026-05-18T23:34:05.485Z" }, + { url = "https://files.pythonhosted.org/packages/99/60/14115e6364fa676c5397c2ad3004e527e9aa487abf5d0706ec81bbd08529/numpy-2.4.6-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:90f9849678c75fe7afa2d348ac842c168b0a4d3d61919687216dfc547976d853", size = 16645538, upload-time = "2026-05-18T23:34:09.265Z" }, + { url = "https://files.pythonhosted.org/packages/ae/c5/693cbe59e57db94d2231fa519ca3978dc9e19da5a8f088588f5c6e947ff2/numpy-2.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c1a2af6c6ef86344a6b0db6b97834208bf598db514f2b155042439b62605601a", size = 17020706, upload-time = "2026-05-18T23:34:13.053Z" }, + { url = "https://files.pythonhosted.org/packages/ef/fc/85b7c4eff9b4966ade25c2273cf7e7012e92366c032058653934b37de044/numpy-2.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e5805d5a22fd19c8ccff10a9561f9df94436b0545619ea579db2d3c35294bce2", size = 18368541, upload-time = "2026-05-18T23:34:17.024Z" }, + { url = "https://files.pythonhosted.org/packages/de/12/b422cc84439adc0d00de605bf4a308890ae5c26f2c71fbd73e5d08fbb0dd/numpy-2.4.6-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:55cced7c52e981362f708ad635198e97a752dfba412cc03c23bbf3bd8d5cd662", size = 16847511, upload-time = "2026-05-18T23:36:50.673Z" }, + { url = "https://files.pythonhosted.org/packages/44/53/f481bef68011740f8849418d82db07230e825013f31f4eef5ba5b805316a/numpy-2.4.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d6da64deb6b8ed903e7560180a92f2d804ee1ba5eeb849ac2748b8c1aba1f6d7", size = 14889064, upload-time = "2026-05-18T23:36:53.879Z" }, + { url = "https://files.pythonhosted.org/packages/7f/57/42ed575c10ced8af951d426bc4e1f8aff16fd851db33f067036215a7f860/numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:68a5124b13fa6cc2086764a20005d30bc0548146f7f5322f02fce212ca14317f", size = 5394157, upload-time = "2026-05-18T23:36:57.194Z" }, + { url = "https://files.pythonhosted.org/packages/6a/ef/f66cc724fcc36c1e364c67f51ae9146090b8b584f27d58b97fdae3edd737/numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:948424b06129ce883307e8cff868c31396d8dc7630a59c61d70d98dbe70f222c", size = 6708728, upload-time = "2026-05-18T23:36:59.575Z" }, + { url = "https://files.pythonhosted.org/packages/1a/9c/c531f2293b91265d8b48e9b329f54fdd7ffae73cb4134ea10cca4237e9cc/numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbbdb29840ca3d91ee0fece42fc29278886d908280bfec0a5846c6f901a3eb0", size = 15798374, upload-time = "2026-05-18T23:37:02.674Z" }, + { url = "https://files.pythonhosted.org/packages/1a/b0/413077f6b1153ed3cba361401c6783bbad6114804a000cc22eb71c13e190/numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8ad03c0965fb3c692200e74d458ca28c1dbb4ce96f9a479a8aa041ad5fabca02", size = 16747286, upload-time = "2026-05-18T23:37:06.327Z" }, +] + +[[package]] +name = "oauthlib" +version = "3.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/5f/19930f824ffeb0ad4372da4812c50edbd1434f678c90c2733e1188edfc63/oauthlib-3.3.1.tar.gz", hash = "sha256:0f0f8aa759826a193cf66c12ea1af1637f87b9b4622d46e866952bb022e538c9", size = 185918, upload-time = "2025-06-19T22:48:08.269Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl", hash = "sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1", size = 160065, upload-time = "2025-06-19T22:48:06.508Z" }, +] + +[[package]] +name = "opentelemetry-api" +version = "1.42.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b4/1c/125e1c936c0873796771b7f04f6c93b9f1bf5d424cea90fda94a99f61da8/opentelemetry_api-1.42.1.tar.gz", hash = "sha256:56c63bea9f77b62856be8c47600474acad853b2924b99b1687c4cb6297166716", size = 72296, upload-time = "2026-05-21T16:32:49.335Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/ca/9520cc1f3dfbbd03ac5903bbf55833e257bc64b1cf30fa8b0d6df374d821/opentelemetry_api-1.42.1-py3-none-any.whl", hash = "sha256:51a69edacadbc03a8950ace1c4c21099cacc538820ac2c9e36277e78cebba714", size = 61311, upload-time = "2026-05-21T16:32:28.822Z" }, +] + +[[package]] +name = "packaging" +version = "26.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, +] + +[[package]] +name = "pillow" +version = "12.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/21/c2bcdd5906101a30244eaffc1b6e6ce71a31bd0742a01eb89e660ebfac2d/pillow-12.2.0.tar.gz", hash = "sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5", size = 46987819, upload-time = "2026-04-01T14:46:17.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/e1/748f5663efe6edcfc4e74b2b93edfb9b8b99b67f21a854c3ae416500a2d9/pillow-12.2.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:8be29e59487a79f173507c30ddf57e733a357f67881430449bb32614075a40ab", size = 5354347, upload-time = "2026-04-01T14:42:44.255Z" }, + { url = "https://files.pythonhosted.org/packages/47/a1/d5ff69e747374c33a3b53b9f98cca7889fce1fd03d79cdc4e1bccc6c5a87/pillow-12.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:71cde9a1e1551df7d34a25462fc60325e8a11a82cc2e2f54578e5e9a1e153d65", size = 4695873, upload-time = "2026-04-01T14:42:46.452Z" }, + { url = "https://files.pythonhosted.org/packages/df/21/e3fbdf54408a973c7f7f89a23b2cb97a7ef30c61ab4142af31eee6aebc88/pillow-12.2.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f490f9368b6fc026f021db16d7ec2fbf7d89e2edb42e8ec09d2c60505f5729c7", size = 6280168, upload-time = "2026-04-01T14:42:49.228Z" }, + { url = "https://files.pythonhosted.org/packages/d3/f1/00b7278c7dd52b17ad4329153748f87b6756ec195ff786c2bdf12518337d/pillow-12.2.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8bd7903a5f2a4545f6fd5935c90058b89d30045568985a71c79f5fd6edf9b91e", size = 8088188, upload-time = "2026-04-01T14:42:51.735Z" }, + { url = "https://files.pythonhosted.org/packages/ad/cf/220a5994ef1b10e70e85748b75649d77d506499352be135a4989c957b701/pillow-12.2.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3997232e10d2920a68d25191392e3a4487d8183039e1c74c2297f00ed1c50705", size = 6394401, upload-time = "2026-04-01T14:42:54.343Z" }, + { url = "https://files.pythonhosted.org/packages/e9/bd/e51a61b1054f09437acfbc2ff9106c30d1eb76bc1453d428399946781253/pillow-12.2.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e74473c875d78b8e9d5da2a70f7099549f9eb37ded4e2f6a463e60125bccd176", size = 7079655, upload-time = "2026-04-01T14:42:56.954Z" }, + { url = "https://files.pythonhosted.org/packages/6b/3d/45132c57d5fb4b5744567c3817026480ac7fc3ce5d4c47902bc0e7f6f853/pillow-12.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:56a3f9c60a13133a98ecff6197af34d7824de9b7b38c3654861a725c970c197b", size = 6503105, upload-time = "2026-04-01T14:42:59.847Z" }, + { url = "https://files.pythonhosted.org/packages/7d/2e/9df2fc1e82097b1df3dce58dc43286aa01068e918c07574711fcc53e6fb4/pillow-12.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:90e6f81de50ad6b534cab6e5aef77ff6e37722b2f5d908686f4a5c9eba17a909", size = 7203402, upload-time = "2026-04-01T14:43:02.664Z" }, + { url = "https://files.pythonhosted.org/packages/58/be/7482c8a5ebebbc6470b3eb791812fff7d5e0216c2be3827b30b8bb6603ed/pillow-12.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2d192a155bbcec180f8564f693e6fd9bccff5a7af9b32e2e4bf8c9c69dbad6b5", size = 5308279, upload-time = "2026-04-01T14:43:13.246Z" }, + { url = "https://files.pythonhosted.org/packages/d8/95/0a351b9289c2b5cbde0bacd4a83ebc44023e835490a727b2a3bd60ddc0f4/pillow-12.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3f40b3c5a968281fd507d519e444c35f0ff171237f4fdde090dd60699458421", size = 4695490, upload-time = "2026-04-01T14:43:15.584Z" }, + { url = "https://files.pythonhosted.org/packages/de/af/4e8e6869cbed569d43c416fad3dc4ecb944cb5d9492defaed89ddd6fe871/pillow-12.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:03e7e372d5240cc23e9f07deca4d775c0817bffc641b01e9c3af208dbd300987", size = 6284462, upload-time = "2026-04-01T14:43:18.268Z" }, + { url = "https://files.pythonhosted.org/packages/e9/9e/c05e19657fd57841e476be1ab46c4d501bffbadbafdc31a6d665f8b737b6/pillow-12.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b86024e52a1b269467a802258c25521e6d742349d760728092e1bc2d135b4d76", size = 8094744, upload-time = "2026-04-01T14:43:20.716Z" }, + { url = "https://files.pythonhosted.org/packages/2b/54/1789c455ed10176066b6e7e6da1b01e50e36f94ba584dc68d9eebfe9156d/pillow-12.2.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7371b48c4fa448d20d2714c9a1f775a81155050d383333e0a6c15b1123dda005", size = 6398371, upload-time = "2026-04-01T14:43:23.443Z" }, + { url = "https://files.pythonhosted.org/packages/43/e3/fdc657359e919462369869f1c9f0e973f353f9a9ee295a39b1fea8ee1a77/pillow-12.2.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62f5409336adb0663b7caa0da5c7d9e7bdbaae9ce761d34669420c2a801b2780", size = 7087215, upload-time = "2026-04-01T14:43:26.758Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f8/2f6825e441d5b1959d2ca5adec984210f1ec086435b0ed5f52c19b3b8a6e/pillow-12.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:01afa7cf67f74f09523699b4e88c73fb55c13346d212a59a2db1f86b0a63e8c5", size = 6509783, upload-time = "2026-04-01T14:43:29.56Z" }, + { url = "https://files.pythonhosted.org/packages/67/f9/029a27095ad20f854f9dba026b3ea6428548316e057e6fc3545409e86651/pillow-12.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc3d34d4a8fbec3e88a79b92e5465e0f9b842b628675850d860b8bd300b159f5", size = 7212112, upload-time = "2026-04-01T14:43:32.091Z" }, + { url = "https://files.pythonhosted.org/packages/4e/b7/2437044fb910f499610356d1352e3423753c98e34f915252aafecc64889f/pillow-12.2.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0538bd5e05efec03ae613fd89c4ce0368ecd2ba239cc25b9f9be7ed426b0af1f", size = 5273969, upload-time = "2026-04-01T14:45:55.538Z" }, + { url = "https://files.pythonhosted.org/packages/f6/f4/8316e31de11b780f4ac08ef3654a75555e624a98db1056ecb2122d008d5a/pillow-12.2.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:394167b21da716608eac917c60aa9b969421b5dcbbe02ae7f013e7b85811c69d", size = 4659674, upload-time = "2026-04-01T14:45:58.093Z" }, + { url = "https://files.pythonhosted.org/packages/d4/37/664fca7201f8bb2aa1d20e2c3d5564a62e6ae5111741966c8319ca802361/pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5d04bfa02cc2d23b497d1e90a0f927070043f6cbf303e738300532379a4b4e0f", size = 5288479, upload-time = "2026-04-01T14:46:01.141Z" }, + { url = "https://files.pythonhosted.org/packages/49/62/5b0ed78fce87346be7a5cfcfaaad91f6a1f98c26f86bdbafa2066c647ef6/pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0c838a5125cee37e68edec915651521191cef1e6aa336b855f495766e77a366e", size = 7032230, upload-time = "2026-04-01T14:46:03.874Z" }, + { url = "https://files.pythonhosted.org/packages/c3/28/ec0fc38107fc32536908034e990c47914c57cd7c5a3ece4d8d8f7ffd7e27/pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a6c9fa44005fa37a91ebfc95d081e8079757d2e904b27103f4f5fa6f0bf78c0", size = 5355404, upload-time = "2026-04-01T14:46:06.33Z" }, + { url = "https://files.pythonhosted.org/packages/5e/8b/51b0eddcfa2180d60e41f06bd6d0a62202b20b59c68f5a132e615b75aecf/pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:25373b66e0dd5905ed63fa3cae13c82fbddf3079f2c8bf15c6fb6a35586324c1", size = 6002215, upload-time = "2026-04-01T14:46:08.83Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "propcache" +version = "0.5.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/44/c87281c333769159c50594f22610f77398a47ccbfbbf23074e744e86f87c/propcache-0.5.2.tar.gz", hash = "sha256:01c4fc7480cd0598bb4b57022df55b9ca296da7fc5a8760bd8451a7e63a7d427", size = 50208, upload-time = "2026-05-08T21:02:12.199Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/f1/8a8cc1c2c7e7934ab77e0163414f736fadbc0f5e8dd9673b952355ac175b/propcache-0.5.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:74b70780220e2dd89175ca24b81b68b67c83db499ae611e7f2313cb329801c78", size = 90744, upload-time = "2026-05-08T20:59:45.799Z" }, + { url = "https://files.pythonhosted.org/packages/c2/f4/651b1225e976bd1a2ba5cfba0c29d096581c2636b437e3a9a7ab6276270a/propcache-0.5.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a4840ab0ae0216d952f4b53dc6d0b992bfc2bedbfe360bdd9b548bc184c08959", size = 52033, upload-time = "2026-05-08T20:59:47.408Z" }, + { url = "https://files.pythonhosted.org/packages/15/a8/8ede85d6aa1f79fc7dc2f8fd2c8d65920b8272c3892903c8a1affde48cfb/propcache-0.5.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c6844ba6364fb12f403928a82cfd295ab103a2b315c77c747b2dbe4a41894ea7", size = 52754, upload-time = "2026-05-08T20:59:49.202Z" }, + { url = "https://files.pythonhosted.org/packages/7d/fe/b3551b41bbc2f5b5bb088fc6920567cd43101253e68fbaa261339eb96fe1/propcache-0.5.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2293949b855ce597f2826452d17c2d545fb5622379c4ea6fdf525e9b8e8a2511", size = 57573, upload-time = "2026-05-08T20:59:50.778Z" }, + { url = "https://files.pythonhosted.org/packages/83/27/ab851ebd1b7172e3e161f5f8d39e315d54a91bea246f01f4d872d3376aef/propcache-0.5.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0fd59b5af35f74da48d905dcbad55449ba13be91823cb05a9bd590bbf5b61660", size = 60645, upload-time = "2026-05-08T20:59:52.227Z" }, + { url = "https://files.pythonhosted.org/packages/95/7d/466b3d18022e9897cbda9c735c493c5bd747d7a4c6f5ea1480b4cec434b6/propcache-0.5.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29f9309a2e42b0d273be006fdb4be2d6c39a47f6f57d8fb1cf9f81481df81b66", size = 61563, upload-time = "2026-05-08T20:59:53.866Z" }, + { url = "https://files.pythonhosted.org/packages/27/1b/16ab7f2cf2041da2f60d156ba64c2484eadf9168075b4ff43c3ef60045af/propcache-0.5.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5aaa2b923c1944ac8febd6609cb373540a5563e7cbcb0fd770f75dace2eb817b", size = 58888, upload-time = "2026-05-08T20:59:55.457Z" }, + { url = "https://files.pythonhosted.org/packages/0a/67/bb777ffd907633563bf35fd859c4ce97b0512c32f4633cf5d1eb7c33512b/propcache-0.5.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:66ea454f095ddf5b6b14f56c064c0941c4788be11e18d2464cf643bf7203ff67", size = 59253, upload-time = "2026-05-08T20:59:57.075Z" }, + { url = "https://files.pythonhosted.org/packages/b9/42/64f8d90b73fd9cdc1499b48057ff6d9cd2a98a25734c9bb62ecf07e87061/propcache-0.5.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:95f1e3f4760d404b13c9976c0229b2b49a3c8e2c62a9ce92efdd2b11ada75e3f", size = 57558, upload-time = "2026-05-08T20:59:58.602Z" }, + { url = "https://files.pythonhosted.org/packages/eb/02/dba5bc03c9041f2092ea55a449caf5dfe68352c6654511b29ba0654ddb69/propcache-0.5.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:85341b12b9d55bad0bded24cac341bb34289469e03a11f3f583ea1cc1db0326c", size = 55007, upload-time = "2026-05-08T20:59:59.837Z" }, + { url = "https://files.pythonhosted.org/packages/14/c0/43f649c7aa2a77a3b100d84e9dea3a483120ecb608bfe36ce49eaff517fe/propcache-0.5.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:26a4dca084132874e639895c3135dfad5eb20bae209f62d1aeb31b03e601c3c0", size = 60355, upload-time = "2026-05-08T21:00:01.144Z" }, + { url = "https://files.pythonhosted.org/packages/83/c0/435dafd27f1cb4a495381dae60e25883ccfe4020bb72818e8184c1678092/propcache-0.5.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3b199b9b2b3d6a7edf3183ba8a9a137a22b97f7df525feb5ae1eccf026d2a9c6", size = 59057, upload-time = "2026-05-08T21:00:02.401Z" }, + { url = "https://files.pythonhosted.org/packages/53/ae/6e292df9135d659944e96cb3389258e4a663e5b2b5f6c217ef0ddc8d2f73/propcache-0.5.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e59bc9e66329185b93dab73f210f1a37f81cb40f321501db8017c9aea15dba27", size = 61938, upload-time = "2026-05-08T21:00:03.638Z" }, + { url = "https://files.pythonhosted.org/packages/0b/42/314ebc50d8159055411fd6b0bda322ff510e4b1f7d2e4927940ad0f6af20/propcache-0.5.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:552ffadf6ad409844bc5919c42a0a83d88314cedddaea0e41e80a8b8fffe881f", size = 59731, upload-time = "2026-05-08T21:00:04.881Z" }, + { url = "https://files.pythonhosted.org/packages/4a/cb/e27bc2b2737a0bb49962b275efa051e8f1c35a936df7d5139b6b658b7dc9/propcache-0.5.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:806719138ecd720339a12410fb9614ac9b2b2d3a5fdf8235d56981c36f4039ba", size = 95887, upload-time = "2026-05-08T21:00:11.277Z" }, + { url = "https://files.pythonhosted.org/packages/e6/13/b8ae04c59392f8d11c6cd9fb4011d1dc7c86b81225c770280300e259ffe1/propcache-0.5.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:db2b80ea58eab4f86b2beec3cc8b39e8ff9276ac20e96b7cce43c8ae84cd6b5a", size = 54654, upload-time = "2026-05-08T21:00:12.604Z" }, + { url = "https://files.pythonhosted.org/packages/2c/7d/49777a3e20b55863d4794384a38acd460c04157b0a00f8602b0d508b8431/propcache-0.5.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e5cbfac9f61484f7e9f3597775500cd3ebe8274e9b050c38f9525c77c97520bf", size = 55190, upload-time = "2026-05-08T21:00:13.935Z" }, + { url = "https://files.pythonhosted.org/packages/44/c7/085d0cd63062e84044e3f05797749c3f8e3938ff3aeb0eb2f69d43fafc91/propcache-0.5.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbc581d2814337da56222fab8dc5f161cd798a434e49bac27930aaef798e144", size = 59995, upload-time = "2026-05-08T21:00:15.526Z" }, + { url = "https://files.pythonhosted.org/packages/9c/42/32cf8e3009e92b2645cf1e944f701e8ea4e924dffde1ee26db860bcbf7e4/propcache-0.5.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:857187f381f88c8e2fa2fe56ab94879d011b883d5a2ee5a1b60a8cd2a06846d9", size = 63422, upload-time = "2026-05-08T21:00:16.824Z" }, + { url = "https://files.pythonhosted.org/packages/9e/1b/f112433f99fc979431b87a39ef169e3f8df070d99a72792c56d6937ac48b/propcache-0.5.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:178b4a2cdaac1818e2bf1c5a99b94383fa73ea5382e032a48dec07dc5668dc42", size = 64342, upload-time = "2026-05-08T21:00:18.362Z" }, + { url = "https://files.pythonhosted.org/packages/14/15/5574111ae50dd6e879456888c0eadd4c5a869959775854e18e18a6b345f3/propcache-0.5.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f328175a2cde1f0ff2c4ed8ce968b9dcfb55f3a7153f39e2957ed994da13476", size = 61639, upload-time = "2026-05-08T21:00:19.692Z" }, + { url = "https://files.pythonhosted.org/packages/cc/da/4d775080b1490c0ae604acda868bd71aabe3a89ed16f2aa4339eb8a283e7/propcache-0.5.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5671d09a36b06d0fd4a3da0fccbcae360e9b1570924171a15e9e0997f0249fba", size = 61588, upload-time = "2026-05-08T21:00:21.155Z" }, + { url = "https://files.pythonhosted.org/packages/04/ac/f076982cbe2195ee9cf32de5a1e46951d9fb399fc207f390562dd0fd8fb2/propcache-0.5.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80168e2ebe4d3ec6599d10ad8f520304ae1cad9b6c5a95372aef1b66b7bfb53a", size = 60029, upload-time = "2026-05-08T21:00:22.713Z" }, + { url = "https://files.pythonhosted.org/packages/70/60/189be62e0dd898dce3b331e1b8c7a543cd3a405ac0c81fe8ee8a9d5d77e1/propcache-0.5.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:45f11346f884bc47444f6e6647131055844134c3175b629f84952e2b5cd62b64", size = 56774, upload-time = "2026-05-08T21:00:24.001Z" }, + { url = "https://files.pythonhosted.org/packages/ea/9e/93377b9c7939c1ffae98f878dee955efadfd638078bc86dbc21f9d52f651/propcache-0.5.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8e778ebd44ef4f66ed60a0416b06b489687db264a9c0b3620362f26489492913", size = 63532, upload-time = "2026-05-08T21:00:25.545Z" }, + { url = "https://files.pythonhosted.org/packages/14/f9/590ef6cfb9b8028d516d287812ece32bb0bc5f11fbb9c8bf6b2e6313fec8/propcache-0.5.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:c0cb9ed24c8964e172768d455a38254c2dd8a552905729ce006cad3d3dda59b1", size = 61592, upload-time = "2026-05-08T21:00:27.186Z" }, + { url = "https://files.pythonhosted.org/packages/b4/5e/70958b3034c297a630bba2f17ca7abc2d5f39a803ad7e370ab79d1ecd022/propcache-0.5.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1d1ad32d9d4355e2be65574fd0bfd3677e7066b009cd5b9b2dee8aa6a6393b33", size = 64788, upload-time = "2026-05-08T21:00:28.8Z" }, + { url = "https://files.pythonhosted.org/packages/12/fd/77fe5936d8c3086ca9048f7f415f122ed82e53884a9ec193646b42deef06/propcache-0.5.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c80f4ba3e8f00189165999a742ee526ebeccedf6c3f7beb0c7df821e9772435a", size = 62514, upload-time = "2026-05-08T21:00:30.098Z" }, + { url = "https://files.pythonhosted.org/packages/3a/ed/1cdcab6ba3d6ab7feca11fc14f0eeea80755bb53ef4e892079f31b10a25f/propcache-0.5.2-py3-none-any.whl", hash = "sha256:be1ddfcbb376e3de5d2e2db1d58d6d67463e6b4f9f040c000de8e300295465fe", size = 14036, upload-time = "2026-05-08T21:02:10.673Z" }, +] + +[[package]] +name = "proto-plus" +version = "1.28.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c9/56/e647b0c675392d2da368da7b6f158f7368b18542fd6f7d7400a2f39de000/proto_plus-1.28.0.tar.gz", hash = "sha256:38e5696342835b08fc116f30a25665b29531cda9d5d5643e9b81fc312385abd9", size = 57221, upload-time = "2026-05-07T08:04:50.811Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/20/b122d4626976acb81132036d2ad1bb35a1a8775fceb837ec30964622516a/proto_plus-1.28.0-py3-none-any.whl", hash = "sha256:a630604310899e73c59ec302e5765c058d412b2f090b9c79c8822589f14955b8", size = 50410, upload-time = "2026-05-07T08:03:31.962Z" }, +] + +[[package]] +name = "protobuf" +version = "7.35.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/60/fd/5b1491d9e4b586d621c54f4c36b888714164b6875f8d6afa3f9072906a51/protobuf-7.35.0.tar.gz", hash = "sha256:a2efd84605f41e559f1881b0912b44099d0a2ac9bf46b3474823f10fb393b0e6", size = 458677, upload-time = "2026-05-19T23:02:29.197Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/ee/93d06e358a4aa32280b00e722d3ea0a1f25fc3cc5778d80581c9cca2c10e/protobuf-7.35.0-cp310-abi3-macosx_10_9_universal2.whl", hash = "sha256:66be6c513931c794fa92c080ffee41671390da3d79da219cf9c0c0907f035dda", size = 433225, upload-time = "2026-05-19T23:02:19.884Z" }, + { url = "https://files.pythonhosted.org/packages/8b/39/1c76c2da93f3c507e958e0aecee2391cc44d4625de6c728bbc555195b5a8/protobuf-7.35.0-cp310-abi3-manylinux2014_aarch64.whl", hash = "sha256:fcbe42a4ac09d3ec9c987ddfcd956afd0b15f1ff613bd8371bde9405ffd5c8e5", size = 328847, upload-time = "2026-05-19T23:02:22.3Z" }, + { url = "https://files.pythonhosted.org/packages/91/1a/39f7ce90a238c1a987a4d81ec26379e02ca0aff367de68e4a1fa474215b9/protobuf-7.35.0-cp310-abi3-manylinux2014_s390x.whl", hash = "sha256:4cbf5cc286130e06a6c9bbefac442431173906dfcc979712183d4adcc01b37ee", size = 344030, upload-time = "2026-05-19T23:02:23.591Z" }, + { url = "https://files.pythonhosted.org/packages/70/5b/6baf9008817964454055ff3fe65f1de0b5f1e26c80c82f7fb108b7cd4ea3/protobuf-7.35.0-cp310-abi3-manylinux2014_x86_64.whl", hash = "sha256:6c0f98f10c8a05ea30f8993dfef2de093d27b490fdae78bb60c8343795d55011", size = 327130, upload-time = "2026-05-19T23:02:24.637Z" }, + { url = "https://files.pythonhosted.org/packages/b8/ef/50433d346c56657a70d27f156c7b349ac59a068b01de4eb796e747eecc43/protobuf-7.35.0-py3-none-any.whl", hash = "sha256:c13f325cf242bad135c350629eeb5d54b24228eb472fb3e2e9ebbd4c5dc20ca0", size = 171659, upload-time = "2026-05-19T23:02:27.842Z" }, +] + +[[package]] +name = "pyarrow" +version = "24.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/91/13/13e1069b351bdc3881266e11147ffccf687505dbb0ea74036237f5d454a5/pyarrow-24.0.0.tar.gz", hash = "sha256:85fe721a14dd823aca09127acbb06c3ca723efbd436c004f16bca601b04dcc83", size = 1180261, upload-time = "2026-04-21T10:51:25.837Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/c9/a47ab7ece0d86cbe6678418a0fbd1ac4bb493b9184a3891dfa0e7f287ae0/pyarrow-24.0.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:b0e131f880cda8d04e076cee175a46fc0e8bc8b65c99c6c09dff6669335fde74", size = 35068898, upload-time = "2026-04-21T10:46:36.599Z" }, + { url = "https://files.pythonhosted.org/packages/d1/bc/8db86617a9a58008acf8913d6fed68ea2a46acb6de928db28d724c891a68/pyarrow-24.0.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:1b2fe7f9a5566401a0ef2571f197eb92358925c1f0c8dba305d6e43ea0871bb3", size = 36679915, upload-time = "2026-04-21T10:46:42.602Z" }, + { url = "https://files.pythonhosted.org/packages/eb/8e/fb178720400ef69db251eb4a9c3ccf4af269bc1feb5055529b8fc87170d1/pyarrow-24.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:0b3537c00fb8d384f15ac1e79b6eb6db04a16514c8c1d22e59a9b95c8ba42868", size = 45697931, upload-time = "2026-04-21T10:46:48.403Z" }, + { url = "https://files.pythonhosted.org/packages/f3/27/99c42abe8e21b44f4917f62631f3aa31404882a2c41d8a4cd5c110e13d52/pyarrow-24.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:14e31a3c9e35f1ab6356c6378f6f72830e6d2d5f1791df3774a7b097d18a6a1e", size = 48837449, upload-time = "2026-04-21T10:46:55.329Z" }, + { url = "https://files.pythonhosted.org/packages/36/b6/333749e2666e9032891125bf9c691146e92901bece62030ac1430e2e7c88/pyarrow-24.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b7d9a514e73bc42711e6a35aaccf3587c520024fe0a25d830a1a8a27c15f4f57", size = 49395949, upload-time = "2026-04-21T10:47:01.869Z" }, + { url = "https://files.pythonhosted.org/packages/17/25/c5201706a2dd374e8ba6ee3fd7a8c89fb7ffc16eed5217a91fd2bd7f7626/pyarrow-24.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b196eb3f931862af3fa84c2a253514d859c08e0d8fe020e07be12e75a5a9780c", size = 51912986, upload-time = "2026-04-21T10:47:09.872Z" }, + { url = "https://files.pythonhosted.org/packages/b4/a9/9686d9f07837f91f775e8932659192e02c74f9d8920524b480b85212cc68/pyarrow-24.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:6233c9ed9ab9d1db47de57d9753256d9dcffbf42db341576099f0fd9f6bf4810", size = 34981559, upload-time = "2026-04-21T10:47:22.17Z" }, + { url = "https://files.pythonhosted.org/packages/80/b6/0ddf0e9b6ead3474ab087ae598c76b031fc45532bf6a63f3a553440fb258/pyarrow-24.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:f7616236ec1bc2b15bfdec22a71ab38851c86f8f05ff64f379e1278cf20c634a", size = 36663654, upload-time = "2026-04-21T10:47:28.315Z" }, + { url = "https://files.pythonhosted.org/packages/7c/3b/926382efe8ce27ba729071d3566ade6dfb86bdf112f366000196b2f5780a/pyarrow-24.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:1617043b99bd33e5318ae18eb2919af09c71322ef1ca46566cdafc6e6712fb66", size = 45679394, upload-time = "2026-04-21T10:47:34.821Z" }, + { url = "https://files.pythonhosted.org/packages/b3/7a/829f7d9dfd37c207206081d6dad474d81dde29952401f07f2ba507814818/pyarrow-24.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:6165461f55ef6314f026de6638d661188e3455d3ec49834556a0ebbdbace18bb", size = 48863122, upload-time = "2026-04-21T10:47:42.056Z" }, + { url = "https://files.pythonhosted.org/packages/5f/e8/f88ce625fe8babaae64e8db2d417c7653adb3019b08aae85c5ed787dc816/pyarrow-24.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3b13dedfe76a0ad2d1d859b0811b53827a4e9d93a0bcb05cf59333ab4980cc7e", size = 49376032, upload-time = "2026-04-21T10:47:48.967Z" }, + { url = "https://files.pythonhosted.org/packages/36/7a/82c363caa145fff88fb475da50d3bf52bb024f61917be5424c3392eaf878/pyarrow-24.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:25ea65d868eb04015cd18e6df2fbe98f07e5bda2abefabcb88fce39a947716f6", size = 51929490, upload-time = "2026-04-21T10:47:55.981Z" }, +] + +[[package]] +name = "pyasn1" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5c/5f/6583902b6f79b399c9c40674ac384fd9cd77805f9e6205075f828ef11fb2/pyasn1-0.6.3.tar.gz", hash = "sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf", size = 148685, upload-time = "2026-03-17T01:06:53.382Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/a0/7d793dce3fa811fe047d6ae2431c672364b462850c6235ae306c0efd025f/pyasn1-0.6.3-py3-none-any.whl", hash = "sha256:a80184d120f0864a52a073acc6fc642847d0be408e7c7252f31390c0f4eadcde", size = 83997, upload-time = "2026-03-17T01:06:52.036Z" }, +] + +[[package]] +name = "pyasn1-modules" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyasn1", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892, upload-time = "2025-03-28T02:41:22.17Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z" }, +] + +[[package]] +name = "pycparser" +version = "3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, +] + +[[package]] +name = "pydantic" +version = "2.14.0a1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "pydantic-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "typing-inspection", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cb/71/0ae6d4e0a84faf0cd050033416509c44a19dc6dbec5bfdd3e1318cb1feb4/pydantic-2.14.0a1.tar.gz", hash = "sha256:2c3a5627d48f59725564c41b582328a29fa8d9b1108128ef6710463a0136d4fd", size = 844232, upload-time = "2026-05-22T13:23:43.109Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/4e/15d81754895da3f256273432da60fcb7c885177fefaf495c5b4364fadca5/pydantic-2.14.0a1-py3-none-any.whl", hash = "sha256:61a1ea8d65df95b681c1fab9cd7d01b2472837f798df53dc6d0f41f0c217b061", size = 470439, upload-time = "2026-05-22T13:23:40.57Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.47.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a7/74/319859f70c733f341df03823c8ca27ce9003faaac3ffa3110f3af1c8641a/pydantic_core-2.47.0.tar.gz", hash = "sha256:422c1797a7864b2a9a996435aba92fe571fb80190f67a31edbc1ac040c7b51fe", size = 476601, upload-time = "2026-05-22T13:19:00.229Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/e0/0da7149bbd84c438beb208e17174d03a798f2c8ce5a978cbc19fb3052be6/pydantic_core-2.47.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:fe87ccbc39a103709d0a5afa75240c15a94611af129261e9484bef0bc97960b2", size = 2114007, upload-time = "2026-05-22T13:20:19.05Z" }, + { url = "https://files.pythonhosted.org/packages/51/30/362c68ac4dc1a830066bf250850725d95be0567bb6a85434616ac68f5939/pydantic_core-2.47.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:45882c24324f123037982c65eb8d60da778447e6bc87c82241f81d6c6d2c307e", size = 1948028, upload-time = "2026-05-22T13:19:25.939Z" }, + { url = "https://files.pythonhosted.org/packages/60/d6/1dd26612212fab9145a2b0d876459764ad04248331cb6f61ab4909fddc43/pydantic_core-2.47.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:983e39b39547772543f3518557d0a86dbb3b7bb58bf8e82faba1e0cfa3e816d9", size = 1972491, upload-time = "2026-05-22T13:20:35.379Z" }, + { url = "https://files.pythonhosted.org/packages/ed/c4/00b31fd04acf601efff148add88cfe5f3c21f43f872e79f76e4580cfaf18/pydantic_core-2.47.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3eb92447d3a079b945b61b8cbd6c3ec2954de3655c4efa0ebd35b069e472c2a9", size = 2045790, upload-time = "2026-05-22T13:18:25.672Z" }, + { url = "https://files.pythonhosted.org/packages/8b/33/7d1a8116bde9d259b3289090a2d93b87562001731cdf81d78bef5d1060d8/pydantic_core-2.47.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3af9600c680bec4b8d23c32ddaf7a5d91ed39a2cf758c082e34e860140cdcd87", size = 2223039, upload-time = "2026-05-22T13:20:37.64Z" }, + { url = "https://files.pythonhosted.org/packages/cd/f6/4c94adcfe1bdaad3f8865a2115bed02e02cba809ffab25c0f85c0cc9b78a/pydantic_core-2.47.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9d6098342d4510a9034a500a53b1d737daf9cfc18a47cd21047d02d7d1587557", size = 2282031, upload-time = "2026-05-22T13:17:41.455Z" }, + { url = "https://files.pythonhosted.org/packages/b6/4f/7fe179b37f887297a5bfd7b38a4a26cd485748b6b424c43c73a36c02f57b/pydantic_core-2.47.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9a891c20be5110deb1904f639f3615ec5022b3495995850d1abe7b8fa1550b5", size = 2088600, upload-time = "2026-05-22T13:17:36.643Z" }, + { url = "https://files.pythonhosted.org/packages/fa/15/daef331c1571ee986e1410511218c4d58e6ead75e0db735051925caa572b/pydantic_core-2.47.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:13990d357a50078e382b15fa3ce3f08043223b4be3eaeb340b184f54c1a2397a", size = 2112087, upload-time = "2026-05-22T13:19:30.024Z" }, + { url = "https://files.pythonhosted.org/packages/f3/c7/7cfbb736f9ac501a92171e93bf640d065a13e12dcbcfb7effd21cce2f1ca/pydantic_core-2.47.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a0f7343305387bb5884f24d384b7978ad099a277b27529e592c041a502a37c32", size = 2171199, upload-time = "2026-05-22T13:19:12.183Z" }, + { url = "https://files.pythonhosted.org/packages/5f/03/48e63b1f7d2088d747dde98b5dd7b89a46fff9173b6c10dc6189289c4aa5/pydantic_core-2.47.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2c5e11bb1be2de2707c9367f364e73091ef30d34be54b3a4564d7421ac1a16bd", size = 2176850, upload-time = "2026-05-22T13:17:39.318Z" }, + { url = "https://files.pythonhosted.org/packages/15/8b/e653f7723f1dff5c054dada5f5d7f2c294067c087a52188847a0414cff2e/pydantic_core-2.47.0-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:e1b52aa981e034896712460c899ee30707c8c6a385e79bb7648aac76c748a3da", size = 2315368, upload-time = "2026-05-22T13:18:36.365Z" }, + { url = "https://files.pythonhosted.org/packages/fd/ff/5aa6fcde3bd427648fee1a363671d8b4852c716603acd77091fb4cf1fc3d/pydantic_core-2.47.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d57d46021c20d4efc28f69b4ca4670dffbb7bdafa51d1967b747849726ec643b", size = 2351750, upload-time = "2026-05-22T13:19:28.187Z" }, + { url = "https://files.pythonhosted.org/packages/19/23/2daeff7346433ad9a3567852dd7a716329f9a7952dcf1ee74b166271bdc7/pydantic_core-2.47.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:70a7aeba54854f5d97da65cb1a61f000f53df3704cab41cd81d65ab127ddd031", size = 2108216, upload-time = "2026-05-22T13:19:31.898Z" }, + { url = "https://files.pythonhosted.org/packages/6c/70/2989cb5112b892b7dc13af570ff57d0f383f770fc88bbb644262df1b3017/pydantic_core-2.47.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c0e97329e38228f57fe1f2d91ba0ef39cc75cc1a84fe6ef58942d2fc6cb406bf", size = 1951502, upload-time = "2026-05-22T13:19:54.702Z" }, + { url = "https://files.pythonhosted.org/packages/fe/a6/2417d8b1856cf7579a21e2679f02417b910e4f29120303e2c45137525072/pydantic_core-2.47.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:faf83e50714837af72f13e9369c50377552a4a74049d4477bed51c7e5822d94b", size = 1975590, upload-time = "2026-05-22T13:17:26.586Z" }, + { url = "https://files.pythonhosted.org/packages/fc/71/0cd0813355b385bef8fa9a719982e0b44847afedc043b988c9f7c5d02ca9/pydantic_core-2.47.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f50abe60347bf8afe2b2f58db86cf3ac6e418eec7ffa01d9dc90ba29fc64f243", size = 2055885, upload-time = "2026-05-22T13:20:42.378Z" }, + { url = "https://files.pythonhosted.org/packages/a3/33/5687c6dcf8ecca106c95b2a49f04a633320ab49961da6e57682db37b4482/pydantic_core-2.47.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f13b18e7dec056336f29ee77dea3cc5db0271d6215cac7249cc5c61b0a49d293", size = 2235292, upload-time = "2026-05-22T13:19:17.723Z" }, + { url = "https://files.pythonhosted.org/packages/36/98/08e81f1a0f0aa82cfa1a4d07b66eb116740fd6c82ba4baa8f96d4b377132/pydantic_core-2.47.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3aaabbdbbbb8dff33fa053ffb2c980f39dec745fc03592f50e1e010449129841", size = 2308945, upload-time = "2026-05-22T13:18:38.113Z" }, + { url = "https://files.pythonhosted.org/packages/04/f1/6e35bdbdb79f96e630265297452d709b2ebd5facfbd83d3eb702eee24939/pydantic_core-2.47.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d2907ee6d15cf26787bfbeb4c42e18e52f358086eab91baea961a0d909248d6", size = 2093861, upload-time = "2026-05-22T13:20:26.271Z" }, + { url = "https://files.pythonhosted.org/packages/1f/c4/6a48e5da6a567bcf6d3ab113d2d476e1b93e3be941b4c486918f1c9d6714/pydantic_core-2.47.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:c413761260967f4dbb51135e1b49f30a1c29e15bf371fbb39754ed6475739545", size = 2124948, upload-time = "2026-05-22T13:18:15.728Z" }, + { url = "https://files.pythonhosted.org/packages/94/a1/e356c98dfc6bd5e6e166f2c832865a62941370b17a7199d35bc5999fecc8/pydantic_core-2.47.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2bb527ac6e5a9721023b24615e1a55c01f47c60007f08b7d2afb89ff9c7a0e22", size = 2182065, upload-time = "2026-05-22T13:19:07.986Z" }, + { url = "https://files.pythonhosted.org/packages/63/aa/6e99aac0a891b05cc522cc464aebeec2bb877fa4744be641a013ebc1785a/pydantic_core-2.47.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b3abf7fd5e6abe483a63413f9cd26b7c93c20780e19c8556434c7279f6b2f10c", size = 2185830, upload-time = "2026-05-22T13:18:34.551Z" }, + { url = "https://files.pythonhosted.org/packages/99/68/c6c5c1ad40b4d58f11d6e754e9c3990a2a3cfff20a9cf5c1f0a855a791b2/pydantic_core-2.47.0-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:9cefe43d17a5a273d71697c084d3787defa7f578cd5fab4cef4c66d13c9e44b2", size = 2327330, upload-time = "2026-05-22T13:19:48.203Z" }, + { url = "https://files.pythonhosted.org/packages/e0/f8/064747538297e385a1f197f35551d1bf6093e88c5ce9b4e15c035f0a1cfe/pydantic_core-2.47.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:38a8b5371b7938e4f6c31060dbd51d3b3229aef7c43eaac2eb8b153e43c3f189", size = 2366505, upload-time = "2026-05-22T13:20:11.895Z" }, + { url = "https://files.pythonhosted.org/packages/1e/a3/7391a9a511aba60ba119dcbc221a36109dd05ff498dcd6e6ff7222a21b76/pydantic_core-2.47.0-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:dbe50ffd50c7e1b8e3bedafafd10fabe8a7cf51916f995fd57316eb1293f2439", size = 2112946, upload-time = "2026-05-22T13:18:54.846Z" }, + { url = "https://files.pythonhosted.org/packages/71/74/1d0fe337fa5a89069731206be4da2b41793b8f8fe28f34ed4d941028a62f/pydantic_core-2.47.0-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:67c3f4b57a1cda846a9b6258e0ef70f99b0757d0b6a55f7e3e5b100620937d2c", size = 1940704, upload-time = "2026-05-22T13:18:00.366Z" }, + { url = "https://files.pythonhosted.org/packages/ac/75/d40231aed211f97ffc3264d7a7c98d1f52f7b9c8d165cee75c8c1c13e5c3/pydantic_core-2.47.0-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33c1e025c83b3987784191ec5aa78cfb94686f1ba73d98ae4d520c09cdecda8d", size = 1985024, upload-time = "2026-05-22T13:17:29.331Z" }, + { url = "https://files.pythonhosted.org/packages/a6/bc/e27d3fb897c5b059a508b88eb7c4cc1f0fe600be3231728bb003e1e2b464/pydantic_core-2.47.0-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c547cac87585ab8a6e9495fd3be22ae8ccaabb3c909f6ae589a180677c105cc8", size = 2136202, upload-time = "2026-05-22T13:17:34.031Z" }, + { url = "https://files.pythonhosted.org/packages/4e/21/81cc7e3acbf7d4eed41e9585e81b5840aaf6ddbe65974a20946038d6516b/pydantic_core-2.47.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:d648515c65f8871ceca6a0446be32fb1a0aae414db3907ec0df6cc2380dd0c04", size = 2098016, upload-time = "2026-05-22T13:18:56.902Z" }, + { url = "https://files.pythonhosted.org/packages/4f/6e/c98e869e9f754f18088ad9b55887972d08606e7bf81dba088779e927eb65/pydantic_core-2.47.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:3292813ac8ed17671a5b1ac8256b6e98b96c29c1c6d061c35899e2f6e0444cac", size = 1931175, upload-time = "2026-05-22T13:20:07.566Z" }, + { url = "https://files.pythonhosted.org/packages/5d/65/ce0d773f4b51dc332764ba1bd585c81f62d5c92231a66d152aff16be8dfa/pydantic_core-2.47.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf3cfcea028f41a9b42a47f4a53d72ca4274d04e3ee525bd58ca89ea8c5e1910", size = 1995356, upload-time = "2026-05-22T13:19:44.143Z" }, + { url = "https://files.pythonhosted.org/packages/19/2c/6e63683a77d342c93bad67bb0762ca8fe1d8418acc8617d70ccdcc5ec5ff/pydantic_core-2.47.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52afdd8640d59890539060e91c8a494cf96b463e63b2ba499cc5c23abcb5e96b", size = 2144893, upload-time = "2026-05-22T13:17:59.008Z" }, + { url = "https://files.pythonhosted.org/packages/f1/bf/06a01e2f6668aa10646e5c9b050a609d975a93d58454e26e16fd9f82cd38/pydantic_core-2.47.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ff201e8b93cd6a3849ec3fecb1d642d0391ca4a387f1162adaadddfd1986598c", size = 2114751, upload-time = "2026-05-22T13:18:02.245Z" }, + { url = "https://files.pythonhosted.org/packages/41/50/caf4cc67fdd664baf89bfe264d72079e901a02d903c813ced9de173cdd69/pydantic_core-2.47.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:eba39fca6e1fc7c58a5e6b5c320c6eec86014f7075dfa0fb1a79076e00304b3a", size = 1947894, upload-time = "2026-05-22T13:19:36.12Z" }, + { url = "https://files.pythonhosted.org/packages/9f/c0/96ba102865c9ccb945a758272741d00175837bf32bb71750f6e2bf4543fb/pydantic_core-2.47.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b494566e8e7e1a3992a550900902d6c0051538e53f5957e24003e7775638a24", size = 2131698, upload-time = "2026-05-22T13:18:58.55Z" }, + { url = "https://files.pythonhosted.org/packages/b9/f8/ee7c5d4eafaecdeaf92ce84f48d748c139888e90fa25b2ae988c5aea874c/pydantic_core-2.47.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c96f745745fe778a92e74f40675c57b80dd46fc98186c9020e5ba4514a0470bd", size = 2157797, upload-time = "2026-05-22T13:17:53.079Z" }, + { url = "https://files.pythonhosted.org/packages/08/69/b3eba1d0e015cee3906f7056cfd9ed383f6cef486fde5398a002df2594e4/pydantic_core-2.47.0-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:dbfb8f8ac48a8fd5a9f83cdbba0a23796f3ff5f9421c735808ed134d3318046f", size = 2177829, upload-time = "2026-05-22T13:20:09.611Z" }, + { url = "https://files.pythonhosted.org/packages/8a/36/a0ceff9b7c3a0980128c9ef763db1e9fe9c2b1fcecf6ed055e88af39f123/pydantic_core-2.47.0-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:4ded2d8dc3beb086623ab3256b81a0b51fc026c94eb363f480aec09b204a1cbd", size = 2307425, upload-time = "2026-05-22T13:19:46.177Z" }, + { url = "https://files.pythonhosted.org/packages/d6/0a/930e210e95ade33ecacabc4ba822aa1662a0aaf843d2cf57abce27fb95b1/pydantic_core-2.47.0-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:25f8aa59fd3f85651387c37b289cd8f0bbc8f632e7dad13c6b837759e78be88d", size = 2351547, upload-time = "2026-05-22T13:17:48.597Z" }, +] + +[[package]] +name = "pygments" +version = "2.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, +] + +[[package]] +name = "pyjwt" +version = "2.13.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3b/81/58d0ac84e1ef3a3843791d6954d94c0b33d526c75eeb1efbce9d0a4c4077/pyjwt-2.13.0.tar.gz", hash = "sha256:41571c89ca91598c79e8ef18a2d07367d4810fbbd6f637794879baf1b7703423", size = 107515, upload-time = "2026-05-21T19:54:36.618Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl", hash = "sha256:66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728", size = 31274, upload-time = "2026-05-21T19:54:35.362Z" }, +] + +[[package]] +name = "pyparsing" +version = "3.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574, upload-time = "2026-01-21T03:57:59.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" }, +] + +[[package]] +name = "pyqwest" +version = "0.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/71/54/7c155961add9446697b3a75c60a2f6a41a41e6d8bbaa8f80227838f95973/pyqwest-0.6.0.tar.gz", hash = "sha256:c5be8afb01e3913d7c4b90ec68446625d2d4714d18daadfaa779c75eaa0f4f3c", size = 450676, upload-time = "2026-05-19T03:21:06.67Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/51/74f8e1d41fc14796727459281ba66df82aa6381e96b4bc3d8464cc04a930/pyqwest-0.6.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:dcd478df1a249f18449ba3194f791b29cde63b3b3415c8e21f05d907421742cc", size = 5064007, upload-time = "2026-05-19T03:19:56.946Z" }, + { url = "https://files.pythonhosted.org/packages/e6/52/d491d2b62eca54bfb8923eaa8026ccb78ecf4b806b348cf8f0f6423aa5f5/pyqwest-0.6.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df1a82dad0c647150fd8db90e2f7f343db0134b5772fb8c54bab57f36d60cf87", size = 5435054, upload-time = "2026-05-19T03:19:59.251Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e5/8e6972c61cf692fa95f9bedc788d8ba61b936ef7dddc7bde629c35a71cb4/pyqwest-0.6.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0cf9e8cd54dd626255af26b7dfd00174ebe5756b177392940ab826e3b89c8d31", size = 5454984, upload-time = "2026-05-19T03:20:01.068Z" }, + { url = "https://files.pythonhosted.org/packages/2e/84/6abad826b7792afd26292d227cf1e5d7d94dfdf453a0e2ad2ad3b41a3ea9/pyqwest-0.6.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7919a102dc8a9b9f52568cdc2c29bd38a87b1c2bcde3f61ca5230bb9a550f289", size = 5597544, upload-time = "2026-05-19T03:20:03.054Z" }, + { url = "https://files.pythonhosted.org/packages/ca/c0/d375594964285ca04b8d07352f3926e503da871bbf471d23d5a8cf911d36/pyqwest-0.6.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5ed6242c1b7ad22144225923567a5d34681e45b3e2a2612b06a817b4ece97f04", size = 5761206, upload-time = "2026-05-19T03:20:04.808Z" }, + { url = "https://files.pythonhosted.org/packages/28/63/2b6306c4b176bc2cc144105dbaa75b5f6b6cefcbddc91eaef83c13da7f6c/pyqwest-0.6.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:39f15aa1b6e8f97dfcd068ca1ebfe485c5040cb26f44d636420f7163d8a9343e", size = 5060563, upload-time = "2026-05-19T03:20:09.016Z" }, + { url = "https://files.pythonhosted.org/packages/ea/79/3d462e7d37595805df9b8d816b883405496466538b38aed497a4c91e1513/pyqwest-0.6.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70328d07e50554eb2566c78aea2c845b2053f8203b6c9710f3fc75264af88c7a", size = 5437225, upload-time = "2026-05-19T03:20:11.326Z" }, + { url = "https://files.pythonhosted.org/packages/88/12/5d60c15ec2d8536e518784d74b8dd54e602097b99a7919a42bad15713580/pyqwest-0.6.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaa9e3578df13a0ceababc60f0814afc6c9df135be324596a8cf49c318f402c4", size = 5457323, upload-time = "2026-05-19T03:20:13.563Z" }, + { url = "https://files.pythonhosted.org/packages/50/5a/24cb4672be3640d8fe2f46d46785295e71bd474a28a2e9bc6ff47ec38a34/pyqwest-0.6.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e69b2d46f4471d59e835207dad2d5bb41bb29b28c2d6accbbb4268f0d58ca9c", size = 5605485, upload-time = "2026-05-19T03:20:15.707Z" }, + { url = "https://files.pythonhosted.org/packages/0c/34/c110ecc14814be490f258549192579ec02a9f3792587e933b254bcd24aa7/pyqwest-0.6.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8de70fc18b524e09883b161f79fe21a647302a0c9950ecefeb34a1c1f27269a9", size = 5764873, upload-time = "2026-05-19T03:20:17.61Z" }, + { url = "https://files.pythonhosted.org/packages/02/3e/7176836cfa4735db3dcbe7118454dfa65c8981a6183c7b501505f91d3225/pyqwest-0.6.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:872ff68d661ef1da461f219867c4eb6af93ff87283ca43e5792d34c4f4c63626", size = 5061646, upload-time = "2026-05-19T03:20:55.839Z" }, + { url = "https://files.pythonhosted.org/packages/df/05/6dc58e3241b4ab2bf20d80d6af4b62c22797710bd744bdff1ae1047ea065/pyqwest-0.6.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6aae145e3fec2c26b8a68290329f48d6e9629af0e5ea8a3740e2168313b5c2d3", size = 5436293, upload-time = "2026-05-19T03:20:57.695Z" }, + { url = "https://files.pythonhosted.org/packages/5d/42/abe80684c5aa25a037499eda9cd94c06abb9a3e764c022f44eaf7469848d/pyqwest-0.6.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0867ded1254a0068d44fecd50131a7748703fb93b2085c109bd28cec19a952d4", size = 5454479, upload-time = "2026-05-19T03:20:59.348Z" }, + { url = "https://files.pythonhosted.org/packages/0e/1e/6a223051a2a0093c6338f9d2ec303303081638c07a5ada64fdef1dd783e1/pyqwest-0.6.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:1c82a9b7633a7b150fa0f9bfa545624c62b4297c15fc4ddf0e6117a8679ff2be", size = 5604800, upload-time = "2026-05-19T03:21:01.356Z" }, + { url = "https://files.pythonhosted.org/packages/5c/03/6e78ae4a10b3bdf2145135a32b813afee6cbf516e2938314e417aa297a05/pyqwest-0.6.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:a795226725e07f5af0ed9fb4d865acb64e53ceb409ee2c2f9b7b8c6a9f8f495f", size = 5761903, upload-time = "2026-05-19T03:21:03.118Z" }, +] + +[[package]] +name = "pytest" +version = "9.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "iniconfig", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "packaging", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "pluggy", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "pygments", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + +[[package]] +name = "python-dotenv" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, + { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, + { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, + { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, + { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, + { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, + { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, + { 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, upload-time = "2025-09-25T21:32:11.445Z" }, + { 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, upload-time = "2025-09-25T21:32:12.492Z" }, + { 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, upload-time = "2025-09-25T21:32:13.652Z" }, + { 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, upload-time = "2025-09-25T21:32:15.21Z" }, + { 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, upload-time = "2025-09-25T21:32:16.431Z" }, + { 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, upload-time = "2025-09-25T21:32:17.56Z" }, + { 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, upload-time = "2025-09-25T21:32:18.834Z" }, +] + +[[package]] +name = "regex" +version = "2026.5.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/0e/49aee608ad09480e7fd276898c99ec6192985fa331abe4eb3a986094490b/regex-2026.5.9.tar.gz", hash = "sha256:a8234aa23ec39894bfe4a3f1b85616a7032481964a13ac6fc9f10de4f6fca270", size = 416074, upload-time = "2026-05-09T23:15:19.37Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/dc/c1f2df4027e82fc54b5a473e4b250f5139faca49a0fbe29a48668d228f34/regex-2026.5.9-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ccf5249114cc3e772ecdd88a98a86eca0fd74c61ce32a94743758c083fc05d48", size = 489445, upload-time = "2026-05-09T23:12:06.111Z" }, + { url = "https://files.pythonhosted.org/packages/03/d2/59f01110660081cce9c0bc30ebd0b5ee250dacf658e3248ed92f01e0e8ee/regex-2026.5.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46f1326ca6e65b0879d23ca302c0f2415aad42ff0309b9c818e7949fe19a41d8", size = 291271, upload-time = "2026-05-09T23:12:07.731Z" }, + { url = "https://files.pythonhosted.org/packages/58/b6/14b2c84ff90ddb370c81d27503f4a0fcf071496416f4855f6cc8c5d81c35/regex-2026.5.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ef31cbfe458e21c6122ba8150ff060e0c7789ed0d26eb423f25472584920b555", size = 289212, upload-time = "2026-05-09T23:12:09.266Z" }, + { url = "https://files.pythonhosted.org/packages/03/d0/4db86529117320de0c84afd90e70bb47434625875e34fcef9d8c127c5b16/regex-2026.5.9-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:992604d02e6d9c6d786c24a706a71ecffe1020fc1ef264044474cd81fa2c3919", size = 792310, upload-time = "2026-05-09T23:12:11.416Z" }, + { url = "https://files.pythonhosted.org/packages/07/78/fe4800cd322f862ecffd2d553409b20d80650e5ed71b9d178f853d020b82/regex-2026.5.9-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c9411dd64ca95477225734a93dfc8583b51916b8d5942f99d6cac21e09965451", size = 861721, upload-time = "2026-05-09T23:12:13.681Z" }, + { url = "https://files.pythonhosted.org/packages/b5/d0/b3618a895dd8feb897c61bb2954edd265e1767d82a01d53065d5871127a3/regex-2026.5.9-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3dd4a3ff360dfb836fecdb93a4598f9d6e2ac81e3e397125145c6221bf58cf4c", size = 906460, upload-time = "2026-05-09T23:12:15.443Z" }, + { url = "https://files.pythonhosted.org/packages/33/6f/1481597e859ef19508b345eec4afd1416ed6e6b459c75a64026ef193aecf/regex-2026.5.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2a661a7d270a61f7cf460caee8b9fa2d5ef9e5c681234bcb9e0fe14f488e7dfc", size = 799843, upload-time = "2026-05-09T23:12:16.892Z" }, + { url = "https://files.pythonhosted.org/packages/73/59/955734c803f59108deccba3597ae440c76b62a652733c0006e6243758420/regex-2026.5.9-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f079e50a0d3cc3cd5091fa9ff45869a2e6b2cd35895731edafb0327901a8d86d", size = 773610, upload-time = "2026-05-09T23:12:19.127Z" }, + { url = "https://files.pythonhosted.org/packages/68/8f/70c04a236d651c81881dac42ef8538bddda6121434509d0a22d9e601503b/regex-2026.5.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4ebe8f0b5ec5a5024dc4a4c59f444c4e9afc5f2abdbb8962065b75d27fb971f9", size = 781645, upload-time = "2026-05-09T23:12:20.806Z" }, + { url = "https://files.pythonhosted.org/packages/1d/96/05c7434d88185e5d27fe54aeb74df86bd77cd79f52f0b4eae54faa8fea70/regex-2026.5.9-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:97cf3bc1b7d7d2306772ec07366c80d9df00ff79e79cea32898883a646d2fae2", size = 854473, upload-time = "2026-05-09T23:12:22.465Z" }, + { url = "https://files.pythonhosted.org/packages/4e/c1/6e3d8202d981f3117004bf341ee74893ba4ba8a9fbaf4b94615846550a08/regex-2026.5.9-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0f9eede6a5cbdc02d4978090186390936e1776a7d1359b21e41014c609880bcf", size = 763311, upload-time = "2026-05-09T23:12:24.351Z" }, + { url = "https://files.pythonhosted.org/packages/93/c7/e7737f1526b3fb32bd4c337fd6c71c3ebb5c8296fc34d11197e0955d2e35/regex-2026.5.9-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:01f0f5f55f4b64dacec85dc116d3c05fd23ad3ff037bbc73a2085775953c2611", size = 844593, upload-time = "2026-05-09T23:12:26.341Z" }, + { url = "https://files.pythonhosted.org/packages/a5/27/0daffb1a535bb39f422c3d200f4ab023c71110ad66a32b366bee708baba0/regex-2026.5.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1268eddd8486dc561d08eee1156e40aa3a8fe10f4bdec8fa653b455fcbffd12c", size = 789167, upload-time = "2026-05-09T23:12:27.975Z" }, + { url = "https://files.pythonhosted.org/packages/50/9b/6550044bc44e17c84d312c031c2ec42fbdb6a4ec4e29093be3a172d08772/regex-2026.5.9-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:57eeeb05db7979413dec5438f2db21d7ecbba787cde7a711df1a6f6df672aa06", size = 490451, upload-time = "2026-05-09T23:12:34.72Z" }, + { url = "https://files.pythonhosted.org/packages/1e/95/fc7ba4303b5a0f92446a12ee6778ef2c6c799233f5060042a31bf390cfe9/regex-2026.5.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:398c521292f4c7fb807001dcd54694d3a1fcafc179a36ad9cc56f98df85930b6", size = 292112, upload-time = "2026-05-09T23:12:36.285Z" }, + { url = "https://files.pythonhosted.org/packages/54/4b/ee27938d1b2c443e89a9a10e00d2d19aa5ee300cd3d61140644e93bb083e/regex-2026.5.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f7a7c26137296beba7784de6eba69c6a93a63ccebc385e4962fe67e267a91225", size = 289599, upload-time = "2026-05-09T23:12:38.089Z" }, + { url = "https://files.pythonhosted.org/packages/d8/dd/ba103dc19614e25f3880800ca67ce093d6e21b325d72b8383c7bf906e9fa/regex-2026.5.9-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6441cc660d76107934a09c22167200839a0e89604a6297f78a974e66e931d2c0", size = 796732, upload-time = "2026-05-09T23:12:40.062Z" }, + { url = "https://files.pythonhosted.org/packages/cf/e7/f035b4fd858b050b0080bf302968dc0f59ba34e391872d54936758e6844e/regex-2026.5.9-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:91328f1c23d47595ca3ef0a7557fa129c5a23404b775c770697d2f35b33e0107", size = 865440, upload-time = "2026-05-09T23:12:42.059Z" }, + { url = "https://files.pythonhosted.org/packages/0a/51/8cd301ecc899aea28124357f729f4272f44de7806fc7ca02490bfbe253e8/regex-2026.5.9-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:93a7860539414dddaefba2b40f8771765ae17949d4c7182b876ce429e11a8309", size = 912329, upload-time = "2026-05-09T23:12:44.373Z" }, + { url = "https://files.pythonhosted.org/packages/cc/1e/3fbe2fa1e8cebd62f3bb7d3321cff1640aca2e240b51d9bd624aad949260/regex-2026.5.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd2810d22146b6d838acc5ec15602cb6b47920aa4e33015df3868eedfd20bab8", size = 801239, upload-time = "2026-05-09T23:12:46.268Z" }, + { url = "https://files.pythonhosted.org/packages/17/2f/6f6008682bf2cf98040a0d3153a8e557b6ab728d7713d045cee4ce544ab8/regex-2026.5.9-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:daff2bdbaf1d23e52fdff7c0b7bc2048b68f978df6a4d107ac981f94caef2e66", size = 777054, upload-time = "2026-05-09T23:12:48.051Z" }, + { url = "https://files.pythonhosted.org/packages/19/2b/eee0d20a6842ba04df4b8847a920b57ef56853f14ef85405473e586b605a/regex-2026.5.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4eeb011098fcb77af513dcef521a3dbecbf8849b1e38940759d293b7a93f5026", size = 785098, upload-time = "2026-05-09T23:12:49.851Z" }, + { url = "https://files.pythonhosted.org/packages/4a/98/6fc1e6410feefb92159edaed5041992bfe390e8d26c721865434acbca558/regex-2026.5.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ea9c8ecfa1b73c73b626534d6626e5340d429630943672b8480724f44e84b962", size = 860095, upload-time = "2026-05-09T23:12:51.666Z" }, + { url = "https://files.pythonhosted.org/packages/18/a3/bd855e0f2cb1a978ecf6fa6bb69632dd9c3f6ea3b81cde62fde14c9daec7/regex-2026.5.9-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:cd2846168eb9ee3c513902bc8225409cb1caab31d04728b145171fa1625d9621", size = 765762, upload-time = "2026-05-09T23:12:53.413Z" }, + { url = "https://files.pythonhosted.org/packages/dc/66/0ae8c092e60b14c79d24f8e0b7f0aea5bfbffdcab00b5483d13404d3c3a5/regex-2026.5.9-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:39617fb0cde9c0e6306dc70e3bfc096f3da793219879f7ae7aa341a69fbdcf6d", size = 852100, upload-time = "2026-05-09T23:12:55.256Z" }, + { url = "https://files.pythonhosted.org/packages/21/de/8dfde60fc1b21c946a893ba273403b72617edb261370cb1087099a83f088/regex-2026.5.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fd03c4f0e33280d15cae17159b899245d6b7c53d21def19b263b39655061f5ce", size = 789479, upload-time = "2026-05-09T23:12:57.573Z" }, +] + +[[package]] +name = "requests" +version = "2.34.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "charset-normalizer", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "idna", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "urllib3", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, +] + +[[package]] +name = "requests-oauthlib" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "oauthlib", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "requests", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/f2/05f29bc3913aea15eb670be136045bf5c5bbf4b99ecb839da9b422bb2c85/requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9", size = 55650, upload-time = "2024-03-22T20:32:29.939Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/5d/63d4ae3b9daea098d5d6f5da83984853c1bbacd5dc826764b249fe119d24/requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36", size = 24179, upload-time = "2024-03-22T20:32:28.055Z" }, +] + +[[package]] +name = "s3fs" +version = "2026.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiobotocore", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "aiohttp", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "fsspec", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cb/d8/76f3dc1558bdf4494b117a9f7a9cc0a5d9d34edadc9e5d7ceabc5a6a7c37/s3fs-2026.4.0.tar.gz", hash = "sha256:5bdce0abb00b0435ee150807a45fea727451dbc22de4cbc116464f8504ab9d37", size = 85986, upload-time = "2026-04-29T20:52:51.748Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/a4/9d1ea10ebc9e028a289a72fec84da170689549a8102c8aacfcad26bc5035/s3fs-2026.4.0-py3-none-any.whl", hash = "sha256:de0d2a1f33cdf03831fd2382d278c6e4e31fe57c3bf2f703c61f8aec6b703e2a", size = 32392, upload-time = "2026-04-29T20:52:50.295Z" }, +] + +[[package]] +name = "safetensors" +version = "0.8.0rc0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/76/70a044292cabc4b591b9b7947aa7d5dd346647acab18532e7e971a02141e/safetensors-0.8.0rc0.tar.gz", hash = "sha256:b4168a839ff287dc26b0d843e1760962b2e92ed5645f95e8ab3f4b9401807e6a", size = 235447, upload-time = "2026-04-14T14:30:42.125Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/c4/8ae3b9b8159babed52fe67698e4095858787dafb3363fa3500c150eef5d5/safetensors-0.8.0rc0-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c1e7a6a1c0dd0128888bc47aca0a9625855673f44f275bf4073088563bf7121b", size = 469331, upload-time = "2026-04-14T14:30:35.024Z" }, + { url = "https://files.pythonhosted.org/packages/7d/28/5322eb9057aeccb8492546a8e7fc070a8490afcca6e658f0a53e2279cca8/safetensors-0.8.0rc0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:c052d1706567487bc103088fe02daf05132dbccbbc3d798753541b66eb37fb14", size = 450714, upload-time = "2026-04-14T14:30:33.884Z" }, + { url = "https://files.pythonhosted.org/packages/85/10/8aedf0becbe6ba019f0be2ab1efbf124d1319d7daaea5f1e3c165670a162/safetensors-0.8.0rc0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79547625fa84f3a9b28b933e44c67d012edf22a0c7170ed68835b9f467dda836", size = 493726, upload-time = "2026-04-14T14:30:23.641Z" }, + { url = "https://files.pythonhosted.org/packages/b8/de/9a6d5d2b842814ff7a715169054235b6141924350be746b02f7906dd0756/safetensors-0.8.0rc0-cp310-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a132d3cf5f63c3f02b82c4abf65c58d33a8422199ae34e09a9a7edb661bd2ca9", size = 502966, upload-time = "2026-04-14T14:30:25.344Z" }, + { url = "https://files.pythonhosted.org/packages/ee/aa/29be34707d27b81b280759f4e52fb38fc6955e2d5e053164b9ab9eabee77/safetensors-0.8.0rc0-cp310-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d42f6c44773901ce1a021d2372747a559e9ec5aa59d044c0d711c273bff21c67", size = 621250, upload-time = "2026-04-14T14:30:26.746Z" }, + { url = "https://files.pythonhosted.org/packages/7d/fa/5b0997ca9cc70c4e6e6ed2afb59506c7065df29bc4771df8f7be61c3bc90/safetensors-0.8.0rc0-cp310-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b40d25911c5f241cad874ad1ea4100a9a9e3c2d469a73a38b47af759d239f44", size = 527309, upload-time = "2026-04-14T14:30:29.722Z" }, + { url = "https://files.pythonhosted.org/packages/25/e0/be46e568cc05530f106ab5dc2faa383ba51533022d735df32db5d550d598/safetensors-0.8.0rc0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf0d366f75f63867f1ede90f87090450c7cec320da1fc2a597f9bb8cb73460db", size = 509088, upload-time = "2026-04-14T14:30:32.377Z" }, + { url = "https://files.pythonhosted.org/packages/88/5c/497168a26d656fbf39e20470ad8be60d3bb766267792d999061a6e164bb6/safetensors-0.8.0rc0-cp310-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:50c56d7b6a2f44c3f4ab130bfeb6a8a51ce72bec152805f9c5a46bdf6addb6c5", size = 509345, upload-time = "2026-04-14T14:30:28.235Z" }, + { url = "https://files.pythonhosted.org/packages/01/a4/54fbeed1447bba46bf8715cbf0d45c11339deeb66afde9ced01ead9233c9/safetensors-0.8.0rc0-cp310-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:94d9c0d569a124fe3074b9934031c2cdcfab12d4d7b64ae17343fac4a92081e8", size = 543961, upload-time = "2026-04-14T14:30:31.135Z" }, + { url = "https://files.pythonhosted.org/packages/4f/18/af173ce378d316352a5a20fe4b161cf54366519db587cc12b1aa9771be17/safetensors-0.8.0rc0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b4fcccda047df747e2463744428cba352d99527c4e52545d07f8c3a8583136f1", size = 668965, upload-time = "2026-04-14T14:30:36.24Z" }, + { url = "https://files.pythonhosted.org/packages/47/bf/de0c22d52d4006f682dec432d237bce71418c236f12accff6e9d614ec66d/safetensors-0.8.0rc0-cp310-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:2ef8ab6704ea895cb13c89d5825f49e87328cac2093e7e45fb3cb615bd457fb2", size = 778061, upload-time = "2026-04-14T14:30:37.522Z" }, + { url = "https://files.pythonhosted.org/packages/6f/f9/bd146043d920cd3fa0b62fd2f548f7b73f0a6212ed960546055bbb11d62a/safetensors-0.8.0rc0-cp310-abi3-musllinux_1_2_i686.whl", hash = "sha256:35bf158d1555df7a529c844ae8ab89355c9df34546de0f94c47d538902bcc07c", size = 751302, upload-time = "2026-04-14T14:30:39.191Z" }, + { url = "https://files.pythonhosted.org/packages/44/58/448c080cd6c2b46662dd0fe93e3814e9ea7e1f818ddf8c0d13ca75eda47a/safetensors-0.8.0rc0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:98b0f6f2a14a6bde7f6acaa5f0381baef9a87c6a3124338affe4e4bb40bf826b", size = 713576, upload-time = "2026-04-14T14:30:40.49Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "sqlalchemy" +version = "2.1.0b2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c1/15/e21c0f10756f47a0b7758326c60b6bdfe6e2a5bd1787f4474d42de78ba79/sqlalchemy-2.1.0b2.tar.gz", hash = "sha256:5894d52de4fe6927f3db054999c66c919f53de082a0782b8db53977c50163de7", size = 10225731, upload-time = "2026-04-16T20:06:52.22Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/ba/87958f44a2569dcca85f44519793f4db9fd2488fe2660a184410389dc8aa/sqlalchemy-2.1.0b2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8b67d421278490802434a91cc80d77c4625aa25fc20a5a326340fce37f154c87", size = 2315496, upload-time = "2026-04-16T20:52:34.935Z" }, + { url = "https://files.pythonhosted.org/packages/0c/a7/2531dc02647fcf1fd6ee73691a54a1970451bdf0b66276016da0c96967e2/sqlalchemy-2.1.0b2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:27929553e4dc124a8b5573d016250a9ec82b43e642492ae40c30579ff5861006", size = 4012506, upload-time = "2026-04-16T20:59:12.838Z" }, + { url = "https://files.pythonhosted.org/packages/f5/90/136a4e412ae6a94080d59c11345c16862fdad587680188430fd59786d3a9/sqlalchemy-2.1.0b2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:38b82f852b2795806d3789242d0de7cde42238f2c460dbd373a2d7dd9e1107d7", size = 4025994, upload-time = "2026-04-16T21:08:19.143Z" }, + { url = "https://files.pythonhosted.org/packages/ad/2b/f963a0f95f200354793d308eac133b5b2378ac5e7198fbbe5bd0c469f16f/sqlalchemy-2.1.0b2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e243de6f864c6329dffc0417d5671d1383b945b614286657740d2e8794844247", size = 3818755, upload-time = "2026-04-17T00:02:11.232Z" }, + { url = "https://files.pythonhosted.org/packages/35/90/5646a5756dc2b6dbf4d69578d67cb2ff21916849ecb8fc05387a26857c59/sqlalchemy-2.1.0b2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8a9120b11be620d19aeb912d7b8e77fffdc79f6d7de5efbab5cc9722caf36fd5", size = 3968914, upload-time = "2026-04-16T20:59:14.627Z" }, + { url = "https://files.pythonhosted.org/packages/5f/29/2d56308ff646bd61be69061363f3c2a07ff524fe031b771353a7458eddb4/sqlalchemy-2.1.0b2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:cb3ef639f3e7d0279100c1a17ddd8b0ced522928e8bdb4c3daf6247bb1b057d1", size = 3811765, upload-time = "2026-04-17T00:02:13.55Z" }, + { url = "https://files.pythonhosted.org/packages/03/86/1b20d1dff0d3b5195cd46ac8c6ed5dacb8781a1a05a2639b2273ee90b5e6/sqlalchemy-2.1.0b2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d03d56cb789c74fae0768455c98f115c11b0e60ac3a59c7a63f7afe756bb9653", size = 4009123, upload-time = "2026-04-16T21:08:20.829Z" }, + { url = "https://files.pythonhosted.org/packages/4e/1e/8bef6092dc2ac6e922422fbd70926291fb1f09583f8b87b94b0774ba4e79/sqlalchemy-2.1.0b2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3e21c1fa4ec770f40b9260f7930bb5f71a9f341bb45f340562c46506cfa8e248", size = 2314845, upload-time = "2026-04-16T20:53:14.873Z" }, + { url = "https://files.pythonhosted.org/packages/5c/b4/3e1e70ea5ed07330e86bfd17527e57d566055d4e0476db0418d3e6e59657/sqlalchemy-2.1.0b2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0493f5895ed4ded776fc8ab2a01381767f89e2d33418f0a2fd8bc07bb2c416ce", size = 4070105, upload-time = "2026-04-16T21:06:35.967Z" }, + { url = "https://files.pythonhosted.org/packages/6f/b8/9980793307236162d62723246cb060b8d2b5d45aea23b80426380301d1e9/sqlalchemy-2.1.0b2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f6e1c10d65f2bd498b4660cf1a1149c4670b351b1aa61eb44047c765099def07", size = 4108050, upload-time = "2026-04-16T21:10:04.048Z" }, + { url = "https://files.pythonhosted.org/packages/cb/19/4746fd6c436dcfd9881bebf189793fd356770ffb777b75ab0de742fddbfb/sqlalchemy-2.1.0b2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:575c2fdb2301f1ba75195f08c081065a1930a2a557bddd06e8faa90092767e8b", size = 3829936, upload-time = "2026-04-17T00:23:08.971Z" }, + { url = "https://files.pythonhosted.org/packages/74/24/7812ccd596b8b3675dc59d9494f4e1ff245ee8cbdfe0c642c1628b2cf9c9/sqlalchemy-2.1.0b2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8f0fc4aa64cada0c3804bfeeddf3d166f7a70682acfbdbfa93b034501340433f", size = 4005119, upload-time = "2026-04-16T21:06:37.489Z" }, + { url = "https://files.pythonhosted.org/packages/86/88/a7f280022ece60b54bd17c393b729393afaeaaad40dae0590b3d60f5821f/sqlalchemy-2.1.0b2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:4942cd00701562dd74802c0e2ef291fb05273db314bc31fbb7fd18ae1f1254b8", size = 3827383, upload-time = "2026-04-17T00:23:11.047Z" }, + { url = "https://files.pythonhosted.org/packages/3a/b0/e1fdfe5bfd6600822a76b16c3d73841cd4843e9cffc859f05ede5fa374bf/sqlalchemy-2.1.0b2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:be52d2b25b76ba0289030ccde79d17480d0b79eee32043b73c6232cd5709e569", size = 4071127, upload-time = "2026-04-16T21:10:06.051Z" }, + { url = "https://files.pythonhosted.org/packages/84/58/56810f8c4fe3fb14665f495852bee9452583b7b0c61cb43c062e9dfa7a97/sqlalchemy-2.1.0b2-py3-none-any.whl", hash = "sha256:98605269d1b7e1d5d3ef680995e4714fc0c5aaa43615684d316079b6dbed826c", size = 1985929, upload-time = "2026-04-16T20:56:46.059Z" }, +] + +[[package]] +name = "starlette" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c5/bf/616a066c2760f6c2b1ae3437cc28149734d069fbb46511712beae118a68c/starlette-1.2.0.tar.gz", hash = "sha256:3c5a6b23fff42492914e93890bb80cbfea72dbf37de268eec06185d62a4ca553", size = 2668923, upload-time = "2026-05-28T11:42:50.568Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9f/85/492183764d5d01d4514be3730fdb8e228a80605783099551c51627578b5d/starlette-1.2.0-py3-none-any.whl", hash = "sha256:36e0c76ac59157e75dc4b3bdeafba97fb04eaf1878045f15dbef666a6f092ed7", size = 73213, upload-time = "2026-05-28T11:42:48.801Z" }, +] + +[[package]] +name = "substrait" +version = "0.24.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/40/99/48c601b6627609ffaea473f603006ff1ded88189ccd1260f58d9a340d88c/substrait-0.24.2.tar.gz", hash = "sha256:743cc352e96b0927b2cd37cd5a8fdac0a96a68df9600bd104fc36aebd222a836", size = 185019, upload-time = "2025-09-15T14:58:29.678Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d3/9c/a67b8d3e10e0bb26d68325bbd3752422aa8dfcc324f2fa18adf0e6acfb15/substrait-0.24.2-py3-none-any.whl", hash = "sha256:d1d475833566fa9d67eed3273456883c0568486ccced92b524b31709d2817e19", size = 155429, upload-time = "2025-09-15T14:58:28.031Z" }, +] + +[[package]] +name = "tabulate" +version = "0.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/46/58/8c37dea7bbf769b20d58e7ace7e5edfe65b849442b00ffcdd56be88697c6/tabulate-0.10.0.tar.gz", hash = "sha256:e2cfde8f79420f6deeffdeda9aaec3b6bc5abce947655d17ac662b126e48a60d", size = 91754, upload-time = "2026-03-04T18:55:34.402Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl", hash = "sha256:f0b0622e567335c8fabaaa659f1b33bcb6ddfe2e496071b743aa113f8774f2d3", size = 39814, upload-time = "2026-03-04T18:55:31.284Z" }, +] + +[[package]] +name = "tokenizers" +version = "0.23.0rc0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0b/dc/2ba78324f6c82284f8d3d03bba16e5771d075aa4d5e9b4ecbd87af846af2/tokenizers-0.23.0rc0.tar.gz", hash = "sha256:685c6d269444451a2cf276d3f2bf655f3d7094be20c6553e413ede86b03c637b", size = 361629, upload-time = "2026-04-24T05:37:42.81Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/b9/dda4065e0f4b62e0e5a625cbaeb928a611d847171e059066b3adfdb3866f/tokenizers-0.23.0rc0-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:bed69208ba6f74057e18e3c8ed73d62e681ff44f7be642ddeff747247c8a7a98", size = 3134709, upload-time = "2026-04-24T05:37:31.89Z" }, + { url = "https://files.pythonhosted.org/packages/fa/16/54bd9f9e5c3641fe3d6d0e5b1cee37c58cb7520d22752c2065fc5a83caff/tokenizers-0.23.0rc0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:951be943c0657d8fd12e104731165a56d995c87533cd7f70a9444ddd7afa7708", size = 3043651, upload-time = "2026-04-24T05:37:30.305Z" }, + { url = "https://files.pythonhosted.org/packages/86/11/54c1040ee93c8d74a364fbf4e17fd5d88e2eea940cbdba69d48d42a5a0c0/tokenizers-0.23.0rc0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:704ffd50130f6c85aa76ad16c8218ff0f966b14c6e6cab7d0636e492e487ffa5", size = 3365683, upload-time = "2026-04-24T05:37:18.674Z" }, + { url = "https://files.pythonhosted.org/packages/14/79/c8a7bdfee971346119349dab62f9918de512a7e5a8177555eaa50d854e1f/tokenizers-0.23.0rc0-cp310-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bcd2a49117ad88999bc5d18d05addf67ec28e69f53e609ab07733c1f96404583", size = 3228688, upload-time = "2026-04-24T05:37:21.137Z" }, + { url = "https://files.pythonhosted.org/packages/e1/32/a46ab1348d0b573dab69860eee601927b9934323e40f6f6018bb362a6013/tokenizers-0.23.0rc0-cp310-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c52f927516521a3e1f6b6347f8bacedaf589eadd682e7ac87dac911d832c3a73", size = 3565137, upload-time = "2026-04-24T05:37:27.101Z" }, + { url = "https://files.pythonhosted.org/packages/9c/f1/1a3b6a30388fe7d4b57b1ea7fcd6192341e479d65e50366ee0ba13d96d14/tokenizers-0.23.0rc0-cp310-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d6add82746146a6e052295ac429949c2d8e723244aa97ffe30cfee6cd788e98", size = 3826198, upload-time = "2026-04-24T05:37:22.783Z" }, + { url = "https://files.pythonhosted.org/packages/a4/cb/161e52a424aa7ffb4097e8ce343d8dc2bdc42d590601032d4a9e6e5f7da5/tokenizers-0.23.0rc0-cp310-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:564115d3d6d2560b0a6b833d7dc39330d2328262557fbbd5bb0a14fb09b2b6cb", size = 3449011, upload-time = "2026-04-24T05:37:25.324Z" }, + { url = "https://files.pythonhosted.org/packages/ff/31/0e4b77ca48b302a5db827584c9784f6cdbb35380c0dd1d7668712d477bb5/tokenizers-0.23.0rc0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82167864c62a3d83880ed23dea267aa5760e3fcf16fd73f94d413baf1968b211", size = 3337931, upload-time = "2026-04-24T05:37:28.723Z" }, + { url = "https://files.pythonhosted.org/packages/50/e4/939249edee0073417b2c9447fd3b06e90c283ef6df72f3124427edae1f96/tokenizers-0.23.0rc0-cp310-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:85f29751c4490bfaefe7e0d4b18ef28cd6d5f84c411e88ca896832eb4f18dd69", size = 3416560, upload-time = "2026-04-24T05:37:24.091Z" }, + { url = "https://files.pythonhosted.org/packages/46/48/3a4bd2ba88af778e6fa6d03e271b2bc868f495745c8be91616781bf460d9/tokenizers-0.23.0rc0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f82b7578eaad0cbb72765d1fbaa7e7bc04c531337513a21f437b73e4617fcf46", size = 9810112, upload-time = "2026-04-24T05:37:33.679Z" }, + { url = "https://files.pythonhosted.org/packages/45/8a/70c9919aefc7f514d6e98fb9be379b2850ca071a841d88900278781a07b0/tokenizers-0.23.0rc0-cp310-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:e61dff90a4ad8dc7e7e124d67756d63cf3ae57e32f04fb35bb408af91f47ea70", size = 9631038, upload-time = "2026-04-24T05:37:36.207Z" }, + { url = "https://files.pythonhosted.org/packages/f9/f6/c15a5514f50bf953b70d3d2b7fd1829aa327ba8c9c519c54623510d6f459/tokenizers-0.23.0rc0-cp310-abi3-musllinux_1_2_i686.whl", hash = "sha256:5835b35d9a4815c8a4097d4dbac79c39b780684ea417fa4a93b9165e12ff1383", size = 9959195, upload-time = "2026-04-24T05:37:38.194Z" }, + { url = "https://files.pythonhosted.org/packages/11/95/d1a6a0e6d6a9bc81b8124d83beb1fb1230310ee93938095f984a12fa336d/tokenizers-0.23.0rc0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:33ed7df57a040ffb6f0244639619632a06f4c287ed1e77b5e70febb58f9e9a8b", size = 10106242, upload-time = "2026-04-24T05:37:40.745Z" }, +] + +[[package]] +name = "tqdm" +version = "4.67.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/09/a9/6ba95a270c6f1fbcd8dac228323f2777d886cb206987444e4bce66338dd4/tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb", size = 169598, upload-time = "2026-02-03T17:35:53.048Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374, upload-time = "2026-02-03T17:35:50.982Z" }, +] + +[[package]] +name = "tqdm-loggable" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "tqdm", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d1/c1/bd361c24a76e7c0d137e299ca1e86bbe188e4c08e3e46674a285aa762051/tqdm_loggable-0.4.1.tar.gz", hash = "sha256:49123ffcb2deb948edb7121d7e09a6008914e0d0333154f060e435deba9e547a", size = 8170, upload-time = "2026-03-16T18:38:50.774Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/24/303708f276f2a81bd0551f5e93b5dd51201ca0d73aed0dc50be6976a3443/tqdm_loggable-0.4.1-py3-none-any.whl", hash = "sha256:e3b394a6fc85a1b1b0dad52a63ac926ecb3b478acf4f4bae570d9189f00ab91d", size = 10527, upload-time = "2026-03-16T18:38:49.808Z" }, +] + +[[package]] +name = "transformers" +version = "4.57.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "huggingface-hub", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "numpy", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "packaging", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "pyyaml", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "regex", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "requests", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "safetensors", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "tokenizers", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "tqdm", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c4/35/67252acc1b929dc88b6602e8c4a982e64f31e733b804c14bc24b47da35e6/transformers-4.57.6.tar.gz", hash = "sha256:55e44126ece9dc0a291521b7e5492b572e6ef2766338a610b9ab5afbb70689d3", size = 10134912, upload-time = "2026-01-16T10:38:39.284Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/03/b8/e484ef633af3887baeeb4b6ad12743363af7cce68ae51e938e00aaa0529d/transformers-4.57.6-py3-none-any.whl", hash = "sha256:4c9e9de11333ddfe5114bc872c9f370509198acf0b87a832a0ab9458e2bd0550", size = 11993498, upload-time = "2026-01-16T10:38:31.289Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, +] + +[[package]] +name = "urllib3" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, +] + +[[package]] +name = "uvicorn" +version = "0.48.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "h11", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e6/bf/f6544ba992ddb9a6077343a576f9844f7f8f06ab819aefd00206e9255f18/uvicorn-0.48.0.tar.gz", hash = "sha256:a5504207195d08c2511bf9125ede5ac4a4b71725d519e758d01dcf0bc2d31c37", size = 91074, upload-time = "2026-05-24T12:08:41.925Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/01/be/72532be3da7acc5fdfbccdb95215cd04f995a0886532a5b423f929cda4cc/uvicorn-0.48.0-py3-none-any.whl", hash = "sha256:48097851328b87ec36117d3d575234519eb58c2b22d79666e9bbc6c49a761dad", size = 71410, upload-time = "2026-05-24T12:08:40.258Z" }, +] + +[package.optional-dependencies] +standard = [ + { name = "httptools", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "python-dotenv", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "pyyaml", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "uvloop", marker = "(platform_python_implementation != 'PyPy' and sys_platform == 'darwin') or (platform_python_implementation != 'PyPy' and sys_platform == 'linux')" }, + { name = "watchfiles", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "websockets", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] + +[[package]] +name = "uvloop" +version = "0.22.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250, upload-time = "2025-10-16T22:17:19.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/d5/69900f7883235562f1f50d8184bb7dd84a2fb61e9ec63f3782546fdbd057/uvloop-0.22.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c60ebcd36f7b240b30788554b6f0782454826a0ed765d8430652621b5de674b9", size = 1352420, upload-time = "2025-10-16T22:16:21.187Z" }, + { url = "https://files.pythonhosted.org/packages/a8/73/c4e271b3bce59724e291465cc936c37758886a4868787da0278b3b56b905/uvloop-0.22.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b7f102bf3cb1995cfeaee9321105e8f5da76fdb104cdad8986f85461a1b7b77", size = 748677, upload-time = "2025-10-16T22:16:22.558Z" }, + { url = "https://files.pythonhosted.org/packages/86/94/9fb7fad2f824d25f8ecac0d70b94d0d48107ad5ece03769a9c543444f78a/uvloop-0.22.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53c85520781d84a4b8b230e24a5af5b0778efdb39142b424990ff1ef7c48ba21", size = 3753819, upload-time = "2025-10-16T22:16:23.903Z" }, + { url = "https://files.pythonhosted.org/packages/74/4f/256aca690709e9b008b7108bc85fba619a2bc37c6d80743d18abad16ee09/uvloop-0.22.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:56a2d1fae65fd82197cb8c53c367310b3eabe1bbb9fb5a04d28e3e3520e4f702", size = 3804529, upload-time = "2025-10-16T22:16:25.246Z" }, + { url = "https://files.pythonhosted.org/packages/7f/74/03c05ae4737e871923d21a76fe28b6aad57f5c03b6e6bfcfa5ad616013e4/uvloop-0.22.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:40631b049d5972c6755b06d0bfe8233b1bd9a8a6392d9d1c45c10b6f9e9b2733", size = 3621267, upload-time = "2025-10-16T22:16:26.819Z" }, + { url = "https://files.pythonhosted.org/packages/75/be/f8e590fe61d18b4a92070905497aec4c0e64ae1761498cad09023f3f4b3e/uvloop-0.22.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:535cc37b3a04f6cd2c1ef65fa1d370c9a35b6695df735fcff5427323f2cd5473", size = 3723105, upload-time = "2025-10-16T22:16:28.252Z" }, + { 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, upload-time = "2025-10-16T22:16:29.436Z" }, + { 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, upload-time = "2025-10-16T22:16:30.493Z" }, + { 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, upload-time = "2025-10-16T22:16:31.644Z" }, + { 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, upload-time = "2025-10-16T22:16:32.917Z" }, + { 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, upload-time = "2025-10-16T22:16:34.015Z" }, + { 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, upload-time = "2025-10-16T22:16:35.149Z" }, +] + +[[package]] +name = "vortex-data" +version = "0.73.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyarrow", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "substrait", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/77/40a4e6e6d3b8682a242a98bc8fe315e7fcde14e0f3e8efba97a8843af511/vortex_data-0.73.0-cp311-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c8b7f6d59b4288a0c5d35e522b939ed04458902c1e5e962eeaadf8784d332bea", size = 37434561, upload-time = "2026-05-27T16:40:51.185Z" }, + { url = "https://files.pythonhosted.org/packages/1e/05/65d0ce6e677bb7e51ec00e8cb69fea8334b105ee72a181ef871de2b1852a/vortex_data-0.73.0-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:bf5edff044452bf68d782286028615fb52d75b0a639063aebc8277565d6502f8", size = 35671662, upload-time = "2026-05-27T16:40:55.005Z" }, + { url = "https://files.pythonhosted.org/packages/d6/ee/57b2b36f54589ce081b72dceab0f25756fc4621b702fe596b9dc1a08d198/vortex_data-0.73.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:485a0ed110bda35730e3981bb0d1de0a93af82b37d1e630394c2350ad6c7fdf5", size = 33034023, upload-time = "2026-05-27T16:40:59.112Z" }, + { url = "https://files.pythonhosted.org/packages/21/d0/25474b6e3f9fe3d363341add1ffb403c8a672e1d46a8bb49092b2e2c475b/vortex_data-0.73.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f17b2244aa6988fff6dc83cb1ff946d48a84fed30c12e4c5be72a3b193a3669e", size = 34869977, upload-time = "2026-05-27T16:41:02.945Z" }, +] + +[[package]] +name = "watchfiles" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cd/41/5e1a4bb12aac5f1493fa1bdc11154eca3b258ca4eba65d39c473fe19d8e9/watchfiles-1.2.0.tar.gz", hash = "sha256:c995fba777f1ea992f090f9236e9284cf7a5d1a0130dd5a3d82c598cacd76838", size = 108252, upload-time = "2026-05-18T04:32:04.251Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/3d/8024c801df84d1587740d0359e7fdd80afeae3d159011f3d5376dd82f18e/watchfiles-1.2.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:704fd259e332e01f9b9c178f4bce9e49027e5587cc2600eeeaf8e76e1c846201", size = 400242, upload-time = "2026-05-18T04:31:19.014Z" }, + { url = "https://files.pythonhosted.org/packages/87/5b/f4dfd45323e949984a3a7f9dc31d1cbb049921e7d98253488dda72ccdaa9/watchfiles-1.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6543cf55d170003296d185c0af981f3e1311564907e1f4e08671fc7693a890a5", size = 394562, upload-time = "2026-05-18T04:30:08.46Z" }, + { url = "https://files.pythonhosted.org/packages/98/d8/19483ef075d601c409bce8bcbb5c0f81a10876fff870400568f08ce484a1/watchfiles-1.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89d8c2394a065ca86f5d2910ff263ae67c127e1376ccc4f9fc35c71db879f80a", size = 456611, upload-time = "2026-05-18T04:30:45.723Z" }, + { url = "https://files.pythonhosted.org/packages/b1/6a/cc81fbe7ee42f2f22e661a6e12def7807e01b14b2f39e0ff83fd373fd307/watchfiles-1.2.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:772b80df316480d894a0e3165fdd19cf77f5d17f9a787f94029465ad0e3529d1", size = 461379, upload-time = "2026-05-18T04:31:29.292Z" }, + { url = "https://files.pythonhosted.org/packages/b1/57/7e669002082c0a0f4fb5113bb70125f7110124b846b0a11bc5ae8e90eac1/watchfiles-1.2.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d158cd89df6053823533e06fb1d73c549133bff5f0396170c0e53d9559340717", size = 493556, upload-time = "2026-05-18T04:30:05.44Z" }, + { url = "https://files.pythonhosted.org/packages/45/7d/f60a2b19807b21fe8281f3a8da4f59eef0d5f96825ac4680ba2d4f2ebf91/watchfiles-1.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d516b3283a758e087841aedb8031549fb41ced08f3db10aa6d2bf32dc042525b", size = 575255, upload-time = "2026-05-18T04:30:40.568Z" }, + { url = "https://files.pythonhosted.org/packages/bd/49/77f5b5e6efbcd57482f74948ebb1b97e5c0046d6b61475042d830c84b3ff/watchfiles-1.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:53b2290c92e0506d102cd448fbc610d87079553f86caa39d67440856a8b8bba5", size = 467052, upload-time = "2026-05-18T04:31:17.942Z" }, + { url = "https://files.pythonhosted.org/packages/ee/5a/73e2959af1b97fd5d556f9a8bdba017be23ceeef731869d5eaa0a753d5a3/watchfiles-1.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a711b51aec4370d0dcda5b6c09463206f133a5759341d7744b953a7b62e1100e", size = 456858, upload-time = "2026-05-18T04:30:30.182Z" }, + { url = "https://files.pythonhosted.org/packages/50/57/1bc8c27fad7e6c19bddee15d276dbb6ab72480ec01c127afff1673aee417/watchfiles-1.2.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:e2ca07fa7d89195ec0865d3d285666286740bfa83d83e5cee204043a31ecc165", size = 467579, upload-time = "2026-05-18T04:32:15.897Z" }, + { url = "https://files.pythonhosted.org/packages/09/6c/3c2e44edba3553c5e3c3b8c8a2a6dee6b9e12ae2cf4bd2378bebf9dc3038/watchfiles-1.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e0618518f282c4ebff60f5e5b1247b6d91bb8b9f4476947563a1e74acc66f3c6", size = 633253, upload-time = "2026-05-18T04:31:37.123Z" }, + { url = "https://files.pythonhosted.org/packages/30/c2/d8c84a882ab39bbefcc4915ab3e91830b7a7e990c5570b0b69075aba3faf/watchfiles-1.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0d191c054d0715c3c95c99df9b8dbf6fd096d8c1e021e8f212e1bd8bc444ccb5", size = 660713, upload-time = "2026-05-18T04:31:24.62Z" }, + { url = "https://files.pythonhosted.org/packages/b8/2f/e42c992d2afda3108ea1c02acecc991b9f31d05c14adc2a7cee9ee211fc4/watchfiles-1.2.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:bc13eb17538be00c874699dc0abe4ee2bc8d50bb1166a6b9e175ef3fd7eb8f26", size = 400115, upload-time = "2026-05-18T04:32:02.06Z" }, + { url = "https://files.pythonhosted.org/packages/5f/8f/6af2ea19065c91d8b0ea3516fdfc8c0d349f407e8e9fbf4e5a17360de8ad/watchfiles-1.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d95ddc1eb6914154253d239089900813f6a767e174b8e6a50e7fdacb7e4236c", size = 393659, upload-time = "2026-05-18T04:30:50.951Z" }, + { url = "https://files.pythonhosted.org/packages/13/01/b32a967c56fb3e3e5be3db52c3d3b87fa4513aa367d8ed1ad96d42952e5f/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f70d8b291ef6e88d19b1f297a6905ddb978888d9272b0d05e6f53309856bcfc", size = 453207, upload-time = "2026-05-18T04:31:04.231Z" }, + { url = "https://files.pythonhosted.org/packages/04/98/97557a812180338cb1abd32e1cffcc4588f59b5f23e0cb006b2ba95ba64a/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:56d8641cf834c2836922899105bd3ce3d0dfc69291d52edf0b4d0436829b34c0", size = 459273, upload-time = "2026-05-18T04:31:50.377Z" }, + { url = "https://files.pythonhosted.org/packages/e8/a8/b4b08dcb7653b8087c6586f7ce649505900e866bbcfe40dc9587af02e686/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2581a94056e55d7d0a31a823ea92bf73749c489ca2285bfdc0fbe6b2bb49d50c", size = 489927, upload-time = "2026-05-18T04:31:42.485Z" }, + { url = "https://files.pythonhosted.org/packages/50/94/3dceea03545d2e5ddfd839f0ddd5e1cecbf1697b5a428d5ba11cef6af95d/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:41bc1199f7523b3f82843c88cbb979180c949caef0342cf90968f178e5d49b01", size = 570476, upload-time = "2026-05-18T04:31:03.071Z" }, + { url = "https://files.pythonhosted.org/packages/cc/f2/d39a5450c3532092b91f81d274360e613c2371bc874a89c7a1a3c5e8d138/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7571e4464cb6e434958f867f7f730b8ab0b75e3f8e5eac0499168486ab3c33a8", size = 465650, upload-time = "2026-05-18T04:30:12.701Z" }, + { url = "https://files.pythonhosted.org/packages/22/24/ed72f68cbc1333ca9b9f2200aa048bb6658ae41709bc1caad4310f4bdffd/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e53a384f76b631c3ae5334ce6a52f0baa3a911eb94a4eac7f160079868b716d5", size = 456398, upload-time = "2026-05-18T04:30:13.784Z" }, + { url = "https://files.pythonhosted.org/packages/0d/64/982ef4a4e5bab5b6e5b6becc8cd5e732f6130a78b855f0abec6439a9a135/watchfiles-1.2.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:d20029a60a71a052a24c4db7673bc4de39ab89adbaccbfb5d67987c5d73f424d", size = 465140, upload-time = "2026-05-18T04:31:52.111Z" }, + { url = "https://files.pythonhosted.org/packages/a0/0c/95282abf4ed680b6096010bcfc30c5fa7a041fc5aa5a2ad17a2cc6c75bba/watchfiles-1.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:2cb93af48550faf1cea04c303107c8b75833de7013e57ce27d3b8d21d8d0f58c", size = 630259, upload-time = "2026-05-18T04:31:25.676Z" }, + { url = "https://files.pythonhosted.org/packages/30/45/607c1de1530c4bdcf2cf1d1ecc2505ddba5d96bd43ba9f2b0e79876f850f/watchfiles-1.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2995c176de7692b86a2e4c58d9ec718f753150a979cb4a754e2b4ffa38e70906", size = 659859, upload-time = "2026-05-18T04:30:24.333Z" }, + { url = "https://files.pythonhosted.org/packages/23/f4/7513ef1e85fc4c6331b59479d6d72661fc391fbe543678052ac72c8b6c19/watchfiles-1.2.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:4674d49eb94706dfe666c069fc0a1b646ffcf920473492e209f6d5f60d3f0cc2", size = 403050, upload-time = "2026-05-18T04:30:36.753Z" }, + { url = "https://files.pythonhosted.org/packages/27/0b/a54103cfd732bb703c7a749222011a0483ef3705948dae3b203158601119/watchfiles-1.2.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:094b9b70103d4e963499bdea001ee3c2697b144cd9ae6218a62c0f89ec9e31db", size = 396629, upload-time = "2026-05-18T04:32:03.268Z" }, + { url = "https://files.pythonhosted.org/packages/5e/2c/73f31a3b893886206c3f54d73e8ad8dee58cdb2f69ad2622e0a8a9e07f4e/watchfiles-1.2.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0ef001f8c25ad0fa9529f914c1600647ecd0f542d11c19b7894768c67b6acb7", size = 457318, upload-time = "2026-05-18T04:31:01.932Z" }, + { url = "https://files.pythonhosted.org/packages/e9/f9/45d021e4a5cc7b9dd567f7cbb06d3b75f751a690063fb6cc7ec60f4e46b7/watchfiles-1.2.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a88fc94e647bc4eec523f1caa540258eb71d14278b9daf72fa1e2658a98df0f0", size = 457771, upload-time = "2026-05-18T04:30:56.331Z" }, +] + +[[package]] +name = "websockets" +version = "16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/04/24/4b2031d72e840ce4c1ccb255f693b15c334757fc50023e4db9537080b8c4/websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", size = 179346, upload-time = "2026-01-10T09:23:47.181Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/db/de907251b4ff46ae804ad0409809504153b3f30984daf82a1d84a9875830/websockets-16.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:31a52addea25187bde0797a97d6fc3d2f92b6f72a9370792d65a6e84615ac8a8", size = 177340, upload-time = "2026-01-10T09:22:34.539Z" }, + { url = "https://files.pythonhosted.org/packages/f3/fa/abe89019d8d8815c8781e90d697dec52523fb8ebe308bf11664e8de1877e/websockets-16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:417b28978cdccab24f46400586d128366313e8a96312e4b9362a4af504f3bbad", size = 175022, upload-time = "2026-01-10T09:22:36.332Z" }, + { url = "https://files.pythonhosted.org/packages/58/5d/88ea17ed1ded2079358b40d31d48abe90a73c9e5819dbcde1606e991e2ad/websockets-16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:af80d74d4edfa3cb9ed973a0a5ba2b2a549371f8a741e0800cb07becdd20f23d", size = 175319, upload-time = "2026-01-10T09:22:37.602Z" }, + { url = "https://files.pythonhosted.org/packages/d2/ae/0ee92b33087a33632f37a635e11e1d99d429d3d323329675a6022312aac2/websockets-16.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:08d7af67b64d29823fed316505a89b86705f2b7981c07848fb5e3ea3020c1abe", size = 184631, upload-time = "2026-01-10T09:22:38.789Z" }, + { url = "https://files.pythonhosted.org/packages/c8/c5/27178df583b6c5b31b29f526ba2da5e2f864ecc79c99dae630a85d68c304/websockets-16.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7be95cfb0a4dae143eaed2bcba8ac23f4892d8971311f1b06f3c6b78952ee70b", size = 185870, upload-time = "2026-01-10T09:22:39.893Z" }, + { url = "https://files.pythonhosted.org/packages/87/05/536652aa84ddc1c018dbb7e2c4cbcd0db884580bf8e95aece7593fde526f/websockets-16.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d6297ce39ce5c2e6feb13c1a996a2ded3b6832155fcfc920265c76f24c7cceb5", size = 185361, upload-time = "2026-01-10T09:22:41.016Z" }, + { url = "https://files.pythonhosted.org/packages/6d/e2/d5332c90da12b1e01f06fb1b85c50cfc489783076547415bf9f0a659ec19/websockets-16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1c1b30e4f497b0b354057f3467f56244c603a79c0d1dafce1d16c283c25f6e64", size = 184615, upload-time = "2026-01-10T09:22:42.442Z" }, + { url = "https://files.pythonhosted.org/packages/84/7b/bac442e6b96c9d25092695578dda82403c77936104b5682307bd4deb1ad4/websockets-16.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:71c989cbf3254fbd5e84d3bff31e4da39c43f884e64f2551d14bb3c186230f00", size = 177365, upload-time = "2026-01-10T09:22:46.787Z" }, + { 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, upload-time = "2026-01-10T09:22:47.999Z" }, + { url = "https://files.pythonhosted.org/packages/40/1e/9771421ac2286eaab95b8575b0cb701ae3663abf8b5e1f64f1fd90d0a673/websockets-16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:86890e837d61574c92a97496d590968b23c2ef0aeb8a9bc9421d174cd378ae39", size = 175328, upload-time = "2026-01-10T09:22:49.809Z" }, + { 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, upload-time = "2026-01-10T09:22:51.071Z" }, + { 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, upload-time = "2026-01-10T09:22:52.224Z" }, + { url = "https://files.pythonhosted.org/packages/4a/34/9bf8df0c0cf88fa7bfe36678dc7b02970c9a7d5e065a3099292db87b1be2/websockets-16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a0b31e0b424cc6b5a04b8838bbaec1688834b2383256688cf47eb97412531da1", size = 185583, upload-time = "2026-01-10T09:22:53.443Z" }, + { 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, upload-time = "2026-01-10T09:22:55.033Z" }, + { url = "https://files.pythonhosted.org/packages/72/07/c98a68571dcf256e74f1f816b8cc5eae6eb2d3d5cfa44d37f801619d9166/websockets-16.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:349f83cd6c9a415428ee1005cadb5c2c56f4389bc06a9af16103c3bc3dcc8b7d", size = 174947, upload-time = "2026-01-10T09:23:36.166Z" }, + { url = "https://files.pythonhosted.org/packages/7e/52/93e166a81e0305b33fe416338be92ae863563fe7bce446b0f687b9df5aea/websockets-16.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:4a1aba3340a8dca8db6eb5a7986157f52eb9e436b74813764241981ca4888f03", size = 175260, upload-time = "2026-01-10T09:23:37.409Z" }, + { url = "https://files.pythonhosted.org/packages/56/0c/2dbf513bafd24889d33de2ff0368190a0e69f37bcfa19009ef819fe4d507/websockets-16.0-pp311-pypy311_pp73-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f4a32d1bd841d4bcbffdcb3d2ce50c09c3909fbead375ab28d0181af89fd04da", size = 176071, upload-time = "2026-01-10T09:23:39.158Z" }, + { url = "https://files.pythonhosted.org/packages/a5/8f/aea9c71cc92bf9b6cc0f7f70df8f0b420636b6c96ef4feee1e16f80f75dd/websockets-16.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0298d07ee155e2e9fda5be8a9042200dd2e3bb0b8a38482156576f863a9d457c", size = 176968, upload-time = "2026-01-10T09:23:41.031Z" }, + { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" }, +] + +[[package]] +name = "werkzeug" +version = "3.1.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dd/b2/381be8cfdee792dd117872481b6e378f85c957dd7c5bca38897b08f765fd/werkzeug-3.1.8.tar.gz", hash = "sha256:9bad61a4268dac112f1c5cd4630a56ede601b6ed420300677a869083d70a4c44", size = 875852, upload-time = "2026-04-02T18:49:14.268Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/8c/2e650f2afeb7ee576912636c23ddb621c91ac6a98e66dc8d29c3c69446e1/werkzeug-3.1.8-py3-none-any.whl", hash = "sha256:63a77fb8892bf28ebc3178683445222aa500e48ebad5ec77b0ad80f8726b1f50", size = 226459, upload-time = "2026-04-02T18:49:12.72Z" }, +] + +[[package]] +name = "wrapt" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/9f/06263fcd8ad6c405f05a3905fd7a84dd3176eb5ad46e44bccc0cd16348bb/wrapt-2.2.1.tar.gz", hash = "sha256:6744f504375775d7609c82c8d3d94af1c9a6f05586984536905908ba905277b9", size = 127620, upload-time = "2026-05-22T14:49:43.056Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/ac/4370bde262c0e633e6c4f0e56d55095710024cf9a5cecc20c59a10de483c/wrapt-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dd57607acc85678925940bd5df0385ff8332083a32fa8d7a43f8767f4997263c", size = 80321, upload-time = "2026-05-22T14:47:43.996Z" }, + { url = "https://files.pythonhosted.org/packages/eb/79/b8ff3a61e71babf58a8cf4c0d63358e8bad383e15bf7f35e62d2f6b6e4a4/wrapt-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1ae574d65c9fa8e86f64f6a7c2668f9fcd507b183e0e577619f504b883cb0a6c", size = 81216, upload-time = "2026-05-22T14:47:45.243Z" }, + { url = "https://files.pythonhosted.org/packages/6e/fd/c0cac1f77c9c4f6fe58a920ca632ce379bb8be928720e11e8d73de28a5e9/wrapt-2.2.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9a04c28c10ba7fd12842b109d2edb0678872a2fe65277ca4ff06a0d61edee245", size = 159208, upload-time = "2026-05-22T14:47:47.176Z" }, + { url = "https://files.pythonhosted.org/packages/d9/4f/744132a7b2fbefa6b81118ec5942eca5fc2e9a129f9055a0c5e46885a549/wrapt-2.2.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3e2f02472a1cbbf3884b365714a810b5947134a95ad6952b554cb8cce9d492b0", size = 160322, upload-time = "2026-05-22T14:47:49.04Z" }, + { url = "https://files.pythonhosted.org/packages/d6/95/b7cd9a22a06cf93e6482904ee6afc956248983553593fd1009296d1b3b31/wrapt-2.2.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac2745950b2bff80219c15ebf2fa9d8427eba7e249739f97e55c9d169e47e9e1", size = 153243, upload-time = "2026-05-22T14:47:50.386Z" }, + { url = "https://files.pythonhosted.org/packages/4c/4a/eb79423192015f46f0db2872e7e04a3dde8d359b83411e8959e7c9287eaa/wrapt-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:67a97e5b6c457f0cd3cfc19ebb2d84463e60c3ece754cc831e4281a3ca29bb18", size = 159231, upload-time = "2026-05-22T14:47:51.753Z" }, + { url = "https://files.pythonhosted.org/packages/ec/dc/435015b58ce33c6fc4104158fa91ddb0e809ab03a5751fb7465d1d461456/wrapt-2.2.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:c803a3d331796255af51ba2c79ed0ac8275865b516c09e61f248d1e7aff31ce9", size = 152351, upload-time = "2026-05-22T14:47:53.214Z" }, + { url = "https://files.pythonhosted.org/packages/77/ac/5d203f98df8fd136b95c5227139aea02d34505e18baf812d0c005df61963/wrapt-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9b984d1eb252145d6302c1dbd5e87fc6d404d45531447c84eadec04bf1fcb027", size = 158347, upload-time = "2026-05-22T14:47:54.982Z" }, + { url = "https://files.pythonhosted.org/packages/89/0c/bfae7b9401583b6d05938cd16dedc43857d96da2f8a3d50d78cc515bf6ff/wrapt-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3ffad790d9d11d8ecf9f17c4bb671a5b4089e4d8b575c46c5129597f41f836b0", size = 81021, upload-time = "2026-05-22T14:48:00.313Z" }, + { url = "https://files.pythonhosted.org/packages/26/58/80f6a6599f933f4caecc1cb3ee88a04faf81e8b9bddbd6109c688dd63e0f/wrapt-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:628f5220c7a904d5fc78f7075c8d7871433eb6d035c94728a22fdf85f193d2a8", size = 81692, upload-time = "2026-05-22T14:48:01.49Z" }, + { url = "https://files.pythonhosted.org/packages/17/93/fb357cc7847c58a8ae790be718903afa81a28d23e642c843dc4129e8a0b2/wrapt-2.2.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:61acce4257a9883669703c525447c5b4c392edf0f987ae77ec32668440158f0e", size = 169364, upload-time = "2026-05-22T14:48:02.791Z" }, + { url = "https://files.pythonhosted.org/packages/aa/0b/76b601ee309a8bd556af0eecb184394c20b3c49aa9c8e085aa1ffacc2568/wrapt-2.2.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:727ab4244622cd6ad2390f322642090c877d2e83a608d2653a7643ae5368d926", size = 171079, upload-time = "2026-05-22T14:48:04.22Z" }, + { url = "https://files.pythonhosted.org/packages/cd/87/ee3f32d5658e3e26d3e0e457922b47a36dd3bfbdfee7f97bb3e802344a66/wrapt-2.2.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:03df9ebed4c73ab93fa8c07e3d41d818dfca1852b15731a3de59457b27814624", size = 160205, upload-time = "2026-05-22T14:48:05.553Z" }, + { url = "https://files.pythonhosted.org/packages/b1/d0/ae2fd64277a67f5d7bffcf2d05eea1e476263fb2a072baf0b0129ab85984/wrapt-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0d9ff006f420b2ec8296aa56ade43ea7da3e997e85769f0aafc5e0661aacb710", size = 168922, upload-time = "2026-05-22T14:48:07.132Z" }, + { url = "https://files.pythonhosted.org/packages/b1/f3/2d541a060c5bbafb9400bca4917e4d78bfd1f239f404782c86831a8f6b29/wrapt-2.2.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:844c858fc3bb7eacc0ba8efa904935d16aac6a4470948ad1e7e55c9f5a2a665f", size = 158388, upload-time = "2026-05-22T14:48:08.629Z" }, + { url = "https://files.pythonhosted.org/packages/1d/68/8d92c8800c57e93cb116ae9e9d6cbafc34fade5ee9f9107b6f203fb4dc35/wrapt-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:87bacdaf225117a342a20d9c03438d701c02112f6e3f351ce9b7f32354f14797", size = 167682, upload-time = "2026-05-22T14:48:10.042Z" }, + { url = "https://files.pythonhosted.org/packages/53/46/29ac9daf11a86c22a8c38cd9236c62928ccae83f7ceb06bd3b0467cf9d05/wrapt-2.2.1-py3-none-any.whl", hash = "sha256:3aafea2975caef8ca49400640dde02cc7426e798f24870ed01f490bc3cffd32f", size = 61000, upload-time = "2026-05-22T14:49:41.593Z" }, +] + +[[package]] +name = "xxhash" +version = "3.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/2f/e183a1b407002f5af81822bee18b61cdb94b8670208ef34734d8d2b8ebe9/xxhash-3.7.0.tar.gz", hash = "sha256:6cc4eefbb542a5d6ffd6d70ea9c502957c925e800f998c5630ecc809d6702bae", size = 82022, upload-time = "2026-04-25T11:10:32.553Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/f4/7bd35089ff1f8e2c96baa2dce05775a122aacd2e3830a73165e27a4d0848/xxhash-3.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fdc7d06929ae28dda98297a18eef7b0fd38991a3b405d8d7b55c9ef24c296958", size = 33423, upload-time = "2026-04-25T11:05:47.628Z" }, + { url = "https://files.pythonhosted.org/packages/a3/26/4e00c88a6a2c8a759cfb77d2a9a405f901e8aa66e60ef1fd0aeb35edda48/xxhash-3.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea6daa712f4e094a30830cf01e9b47d03b24d05cc9dab8609f0d9a9db8454712", size = 30857, upload-time = "2026-04-25T11:05:49.189Z" }, + { url = "https://files.pythonhosted.org/packages/82/2f/eeb942c17a5a761a8f01cb9180a0b76bfb62a2c39e6f46b1f9001899027a/xxhash-3.7.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9e6c0d843f1daf85ea23aeb053579135552bde575b7b98af20bfc667b6e4548d", size = 194702, upload-time = "2026-04-25T11:05:50.457Z" }, + { url = "https://files.pythonhosted.org/packages/0e/fd/96f132c08b1e5951c68691d3b9ec351ec2edc028f6a01fcd294f46b9d9f0/xxhash-3.7.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:363c139bf15e1ac5f136b981d3c077eb551299b1effede7f12faa010b8590a60", size = 213613, upload-time = "2026-04-25T11:05:52.571Z" }, + { url = "https://files.pythonhosted.org/packages/82/89/d4e92b796c5ed052d29ed324dbfc1dc1188e0c4bf64bebbf0f8fc20698df/xxhash-3.7.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a778b25874cb0f862eaab5986bff4ca49ffb0def7c0a34c237b948b3c6c775b2", size = 236726, upload-time = "2026-04-25T11:05:54.395Z" }, + { url = "https://files.pythonhosted.org/packages/40/f1/81fc4361921dc6e557a9c60cb3712f36d244d06eeeb71cd2f4252ac42678/xxhash-3.7.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3e1860f1e43d40e9d904cf22d93e587ea42e010ebce4160877e46bcab4bc232a", size = 212443, upload-time = "2026-04-25T11:05:56.334Z" }, + { url = "https://files.pythonhosted.org/packages/6a/d0/afeddd4cff50a332f50d4b8a2e8857673153ab0564ef472fcdeb0b5430df/xxhash-3.7.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9122ad6f867c4a0f5e655f5c3bdf89103852009dbb442a3d23e688b9e699e800", size = 445793, upload-time = "2026-04-25T11:05:58.953Z" }, + { url = "https://files.pythonhosted.org/packages/f7/d0/3c91e4e6a05ca4d7df8e39ec3a75b713609258ec84705ab34be6430826a1/xxhash-3.7.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7d9110d0c3fb02679972837a033251fd186c529aa62f19c132fc909c74052b8", size = 193937, upload-time = "2026-04-25T11:06:00.546Z" }, + { url = "https://files.pythonhosted.org/packages/4e/3a/a6b0772d9801dd4bea4ca4fd34734d6e9b51a711c8a611a24a79de26a878/xxhash-3.7.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:347a93f2b4ce67ce61959665e32a7447c380f8347e55e100daa23766baacf0e5", size = 285188, upload-time = "2026-04-25T11:06:01.96Z" }, + { url = "https://files.pythonhosted.org/packages/6c/f8/cf8e31fd7282230fe7367cd501a2e75b4b67b222bfc7eacccfc20d2652cb/xxhash-3.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:acbb48679ddf3852c45280c10ff10d52ca2cd1da2e552fb81db1ff786c75d0e4", size = 210966, upload-time = "2026-04-25T11:06:03.453Z" }, + { url = "https://files.pythonhosted.org/packages/cc/f0/fd36cc4a81bf52ee5633275daae2b93dd958aace67fd4f5d466ec83b5f35/xxhash-3.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:fe14c356f8b23ad811dc026077a6d4abccdaa7bce5ca98579605550657b6fcfb", size = 241994, upload-time = "2026-04-25T11:06:05.264Z" }, + { url = "https://files.pythonhosted.org/packages/08/e1/67f5d9c9369be42eaf99ba02c01bf14c5ecd67087b02567960bfcee43b63/xxhash-3.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f420ad3d41e38194353a498bbc9561fd5a9973a27b536ce46d8583479cf44335", size = 198707, upload-time = "2026-04-25T11:06:07.044Z" }, + { url = "https://files.pythonhosted.org/packages/50/17/a4c865ca22d2da6b1bc7d739bf88cab209533cf52ba06ca9da27c3039bee/xxhash-3.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:693d02c6dc7d1aa0a45921d54cd8c1ff629e09dfdc2238471507af1f7a1c6f04", size = 210917, upload-time = "2026-04-25T11:06:08.853Z" }, + { url = "https://files.pythonhosted.org/packages/49/8b/453b35810d697abac3c96bde3528bece685869227da274eb80a4a4d4a119/xxhash-3.7.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:14bf7a54e43825ec131ee7fe3c60e142e7c2c1e676ad0f93fc893432d15414af", size = 275772, upload-time = "2026-04-25T11:06:10.645Z" }, + { url = "https://files.pythonhosted.org/packages/b5/ad/4eed7eab07fd3ee6678f416190f0413d097ab5d7c1278906bf1e9549d789/xxhash-3.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ae3a39a4d96bdb6f8d154fd7f490c4ad06f0532fcd2bb656052a9a7762cf5d31", size = 414068, upload-time = "2026-04-25T11:06:12.511Z" }, + { url = "https://files.pythonhosted.org/packages/d3/4e/fd6f8a680ba248fdb83054fa71a8bfa3891225200de1708b888ef2c49829/xxhash-3.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1cc07c639e3a77ef1d32987464d3e408565b8a3be57b545d3542b191054d9923", size = 191459, upload-time = "2026-04-25T11:06:14.07Z" }, + { url = "https://files.pythonhosted.org/packages/f2/8a/51a14cdef4728c6c2337db8a7d8704422cc65676d9199d77215464c880af/xxhash-3.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:082c87bfdd2b9f457606c7a4a53457f4c4b48b0cdc48de0277f4349d79bb3d7a", size = 33357, upload-time = "2026-04-25T11:06:20.44Z" }, + { url = "https://files.pythonhosted.org/packages/b9/1b/0c2c933809421ffd9bf42b59315552c143c755db5d9a816b2f1ae273e884/xxhash-3.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5e7ce913b61f35b0c1c839a49ac9c8e75dd8d860150688aed353b0ce1bf409d8", size = 30869, upload-time = "2026-04-25T11:06:21.989Z" }, + { url = "https://files.pythonhosted.org/packages/03/a8/89d5fdd6ee12d70ba99451de46dd0e8010167468dcd913ec855653f4dd50/xxhash-3.7.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3beb1de3b1e9694fcdd853e570ee64c631c7062435d2f8c69c1adf809bc086f0", size = 194100, upload-time = "2026-04-25T11:06:23.586Z" }, + { url = "https://files.pythonhosted.org/packages/87/ee/2f9f2ed993e77206d1e66991290a1ebe22e843351ca3ebec8e49e01ba186/xxhash-3.7.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3e7b689c3bce16699efcf736066f5c6cc4472c3840fe4b22bd8279daf4abdac", size = 212977, upload-time = "2026-04-25T11:06:25.019Z" }, + { url = "https://files.pythonhosted.org/packages/de/60/5a91644615a9e9d4e42c2e9925f1908e3a24e4e691d9de7340d565bea024/xxhash-3.7.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a6545e6b409e3d5cbafc850fb84c55a1ca26ed15a6b11e3bf07a0e0cd84517c8", size = 236373, upload-time = "2026-04-25T11:06:26.482Z" }, + { url = "https://files.pythonhosted.org/packages/22/c0/f3a9384eaaed9d14d4d062a5d953aa0da489bfe9747877aa994caa87cd0b/xxhash-3.7.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:31ab1461c77a11461d703c88eb949e132a1c6515933cf675d97ec680f4bd18de", size = 212229, upload-time = "2026-04-25T11:06:28.065Z" }, + { url = "https://files.pythonhosted.org/packages/2e/67/02f07a9fd79726804190f2172c4894c3ed9a4ebccaca05653c84beb58025/xxhash-3.7.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7c4d596b7676f811172687ec567cbafb9e4dea2f9be1bbb4f622410cb7f40f40", size = 445462, upload-time = "2026-04-25T11:06:30.048Z" }, + { url = "https://files.pythonhosted.org/packages/40/37/558f5a90c0672fc9b4402dc25d87ac5b7406616e8969430c9ca4e52ee74d/xxhash-3.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13805f0461cba0a857924e70ff91ae6d52d2598f79a884e788db80532614a4a1", size = 193932, upload-time = "2026-04-25T11:06:31.857Z" }, + { url = "https://files.pythonhosted.org/packages/d5/90/aaa09cd58661d32044dbbad7df55bbe22a623032b810e7ed3b8c569a2a6f/xxhash-3.7.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1d398f372496152f1c6933a33566373f8d1b37b98b8c9d608fa6edc0976f23b2", size = 284807, upload-time = "2026-04-25T11:06:33.697Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f3/53df3719ab127a02c174f0c1c74924fcd110866e89c966bc7909cfa8fa84/xxhash-3.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d610aa62cdb7d4d497740741772a24a794903bf3e79eaa51d2e800082abe11e5", size = 210445, upload-time = "2026-04-25T11:06:35.488Z" }, + { url = "https://files.pythonhosted.org/packages/72/33/d219975c0e8b6fa2eb9ccd486fe47e21bf1847985b878dd2fbc3126e0d5c/xxhash-3.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:073c23900a9fbf3d26616c17c830db28af9803677cd5b33aea3224d824111514", size = 241273, upload-time = "2026-04-25T11:06:37.24Z" }, + { url = "https://files.pythonhosted.org/packages/3e/50/49b1afe610eb3964cedcb90a4d4c3d46a261ee8669cbd4f060652619ae3c/xxhash-3.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:418a463c3e6a590c0cdc890f8be19adb44a8c8acd175ca5b2a6de77e61d0b386", size = 197950, upload-time = "2026-04-25T11:06:39.148Z" }, + { url = "https://files.pythonhosted.org/packages/c6/75/5f42a1a4c78717d906a4b6a140c6dbf837ab1f547a54d23c4e2903310936/xxhash-3.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:03f8ff4474ee61c845758ce00711d7087a770d77efb36f7e74a6e867301000b8", size = 210709, upload-time = "2026-04-25T11:06:40.958Z" }, + { url = "https://files.pythonhosted.org/packages/8a/85/237e446c25abced71e9c53d269f2cef5bab8a82b3f88a12e00c5368e7368/xxhash-3.7.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:44fba4a5f1d179b7ddc7b3dc40f56f9209046421679b57025d4d8821b376fd8d", size = 275345, upload-time = "2026-04-25T11:06:42.525Z" }, + { url = "https://files.pythonhosted.org/packages/62/34/c2c26c0a6a9cc739bc2a5f0ae03ba8b87deb12b8bce35f7ac495e790dc6d/xxhash-3.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31e3516a0f829d06ded4a2c0f3c7c5561993256bfa1c493975fb9dc7bfa828a1", size = 414056, upload-time = "2026-04-25T11:06:44.343Z" }, + { url = "https://files.pythonhosted.org/packages/a0/aa/5c58e9bc8071b8afd8dcf297ff362f723c4892168faba149f19904132bf4/xxhash-3.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b59ee2ac81de57771a09ecad09191e840a1d2fae1ef684208320591055768f83", size = 191485, upload-time = "2026-04-25T11:06:46.262Z" }, + { url = "https://files.pythonhosted.org/packages/54/c1/e57ac7317b1f58a92bab692da6d497e2a7ce44735b224e296347a7ecc754/xxhash-3.7.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ad3aa71e12ee634f22b39a0ff439357583706e50765f17f05550f92dbf128a23", size = 31232, upload-time = "2026-04-25T11:10:21.51Z" }, + { url = "https://files.pythonhosted.org/packages/4f/4e/075559bd712bc62e84915ea46bbee859f935d285659082c129bdbff679dd/xxhash-3.7.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:5de686e73690cdaf72b96d4fa083c230ec9020bcc2627ce6316138e2cf2fe2d1", size = 28553, upload-time = "2026-04-25T11:10:23.1Z" }, + { url = "https://files.pythonhosted.org/packages/92/ca/a9c78cb384d4b033b0c58196bd5c8509873cabe76389e195127b0302a741/xxhash-3.7.0-pp311-pypy311_pp73-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7fbec49f5341bbdea0c471f7d1e2fb41ae8925af9b6f28025c28defd8eb94274", size = 41109, upload-time = "2026-04-25T11:10:25.022Z" }, + { url = "https://files.pythonhosted.org/packages/bd/b1/dfe2629f7c77eb2fa234c72ff537cdd64939763df704e256446ed364a16d/xxhash-3.7.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48b542c347c2089f43dc5a6db31d2a6f3cdb04ee33505ec6e9f653834dbb0bde", size = 36307, upload-time = "2026-04-25T11:10:26.949Z" }, + { url = "https://files.pythonhosted.org/packages/e7/f7/5a484afce0f48dd8083208b42e4911f290a82c7b52458ef2927e4d421a45/xxhash-3.7.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a169a036bed0995e090d1493b283cc2cc8a6f5046821086b843abefff80643bc", size = 32534, upload-time = "2026-04-25T11:10:29.01Z" }, +] + +[[package]] +name = "yarl" +version = "1.24.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "multidict", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "propcache", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/12/1e8f37460ea0f7eb59c221fdaf0ed75e7ac43e97f8093b9c6f411df50a78/yarl-1.24.2.tar.gz", hash = "sha256:9ac374123c6fd7abf64d1fec93962b0bd4ee2c19751755a762a72dd96c0378f8", size = 210798, upload-time = "2026-05-19T21:31:05.599Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/c5/1ce244152ff2839645e7cae92f90e7bafcb2c52bea7ff586ac714f14f5df/yarl-1.24.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:36348bebb147b83818b9d7e673ea4debc75970afc6ffdc7e3975ad05ce5a58c1", size = 128971, upload-time = "2026-05-19T21:28:20.543Z" }, + { url = "https://files.pythonhosted.org/packages/87/5a/00f36967203ed89cb3acd2c8ed526cc3fed9418eb70ce128160a911c8499/yarl-1.24.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a97e42c8a2233f2f279ecadd9e4a037bcb5d813b78435e8eedd4db5a9e9708c", size = 91507, upload-time = "2026-05-19T21:28:22.556Z" }, + { url = "https://files.pythonhosted.org/packages/31/d0/1fb0c1cd27288f39f6974da4318c32768d72c9890984541fdf1e2e32a51d/yarl-1.24.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8d027d56f1035e339d1001ac33eceab5b2ec8e42e449787bb75e289fb9a5cd1d", size = 91343, upload-time = "2026-05-19T21:28:24.092Z" }, + { url = "https://files.pythonhosted.org/packages/03/ce/d4a646508bed2f8dec6435b40166fe9308dd191262033d3f307b2bbcaecd/yarl-1.24.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a6377060e7927187a42b7eb202090cbe2b34933a4eeaf90e3bd9e33432e5cae", size = 105704, upload-time = "2026-05-19T21:28:25.872Z" }, + { url = "https://files.pythonhosted.org/packages/4b/07/b3278e82d8bc41485bcf6d856cd0433262593de615b1d3dc43bd3f5bead4/yarl-1.24.2-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:17076578bce0049a5ce57d14ad1bded391b68a3b213e9b81b0097b090244999a", size = 97281, upload-time = "2026-05-19T21:28:27.352Z" }, + { url = "https://files.pythonhosted.org/packages/17/5b/4cee6e7c92e487bebe7afc797da0aa54a248ab4e776a68fe369ec29665a5/yarl-1.24.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:50713f1d4d6be6375bb178bb43d140ee1acb8abe589cd723320b7925a275be1e", size = 114020, upload-time = "2026-05-19T21:28:29.458Z" }, + { url = "https://files.pythonhosted.org/packages/5c/82/111076571545a7d4f9cca3fbd5c6f40615af58642be09f12328f48022468/yarl-1.24.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:34263e2fa8fb5bb63a0d97706cda38edbad62fddb58c7f12d6acbc092812aa50", size = 111450, upload-time = "2026-05-19T21:28:31.262Z" }, + { url = "https://files.pythonhosted.org/packages/b6/ec/08f671f69a444d704aeecebf92af659b67b97a869942411d0a578b08c334/yarl-1.24.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:49016d82f032b1bd1e10b01078a7d29ae71bf468eeae0ea22df8bab691e60003", size = 106384, upload-time = "2026-05-19T21:28:32.856Z" }, + { url = "https://files.pythonhosted.org/packages/e5/86/ce41e7a7a199340b2330d52b60f25c4074b6636dd0e60b1a80d31a9db042/yarl-1.24.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3f6d2c216318f8f32038ca3f72501ba08536f0fd18a36e858836b121b2deed9f", size = 106153, upload-time = "2026-05-19T21:28:35.222Z" }, + { url = "https://files.pythonhosted.org/packages/c4/5d/31be8a729531ab3e55ac3e7e5c800be8c89ea98947f418b2f6ea259fb6ee/yarl-1.24.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:08d3a33218e0c64393e7610284e770409a9c31c429b078bcb24096ed0a783b8f", size = 105322, upload-time = "2026-05-19T21:28:36.642Z" }, + { url = "https://files.pythonhosted.org/packages/47/9b/b57afb22b386ae87ac9940f09878b98d8c333f89113e6fc96fcf4ca9eb64/yarl-1.24.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5d699376c4ca3cba49bbfae3a05b5b70ded572937171ce1e0b8d87118e2ba294", size = 99057, upload-time = "2026-05-19T21:28:38.386Z" }, + { url = "https://files.pythonhosted.org/packages/a3/4f/06348c27c8389256c313e8a57d796808fc0264c915dd5e7cfd3c0e314dc7/yarl-1.24.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a1cab588b4fa14bea2e55ebea27478adfb05372f47573738e1acc4a36c0b05d2", size = 113502, upload-time = "2026-05-19T21:28:40.091Z" }, + { url = "https://files.pythonhosted.org/packages/5f/1c/284f307b298e4a17b7943b07d9d7ecc4151537f8d137ba51f3bb6c31ca20/yarl-1.24.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:ec87ccc31bd21db7ad009d8572c127c1000f268517618a4cc09adba3c2a7f21c", size = 105253, upload-time = "2026-05-19T21:28:41.987Z" }, + { url = "https://files.pythonhosted.org/packages/c8/bf/0de123bec8619e45c80cbded9085f61b5b4a9eddb8abe6d25d28ee1ec866/yarl-1.24.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d1dd47a22843b212baa8d74f37796815d43bd046b42a0f41e9da433386c3136b", size = 111345, upload-time = "2026-05-19T21:28:43.93Z" }, + { url = "https://files.pythonhosted.org/packages/90/af/0248eb065e51129d2a9b2436cd1b5c772c19a6b04e5b6a186955671e3319/yarl-1.24.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7b54b9c67c2b06bd7b9a77253d242124b9c95d2c02def5a1144001ee547dd9d5", size = 106558, upload-time = "2026-05-19T21:28:45.806Z" }, + { url = "https://files.pythonhosted.org/packages/f0/da/866bcb01076ba49d2b42b309867bed3826421f1c479655eb7a607b44f20b/yarl-1.24.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b975866c184564c827e0877380f0dae57dcca7e52782128381b72feff6dfceb8", size = 129957, upload-time = "2026-05-19T21:28:51.695Z" }, + { url = "https://files.pythonhosted.org/packages/bf/1d/fcefb70922ea2268a8971d8e5874d9a8218644200fb8465f1dcad55e6851/yarl-1.24.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3b075301a2836a0e297b1b658cb6d6135df535d62efefdd60366bd589c2c82f2", size = 92164, upload-time = "2026-05-19T21:28:53.242Z" }, + { url = "https://files.pythonhosted.org/packages/29/b6/170e2b8d4e3bc30e6bfdcca53556537f5bf595e938632dfcb059311f3ff6/yarl-1.24.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8ae44649b00947634ab0dab2a374a638f52923a6e67083f2c156cd5cbd1a881d", size = 91688, upload-time = "2026-05-19T21:28:54.865Z" }, + { url = "https://files.pythonhosted.org/packages/fe/a5/c9f655d5553ea0b99fdac9d6a99ad3f9b3e73b8e5758bb46f58c9831f74c/yarl-1.24.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:507cc19f0b45454e2d6dcd62ff7d062b9f77a2812404e62dbdaec05b50faa035", size = 102902, upload-time = "2026-05-19T21:28:56.963Z" }, + { url = "https://files.pythonhosted.org/packages/5d/bc/6b9664d815d79af4ee553337f9d606c56bbf269186ada9172de45f1b5f60/yarl-1.24.2-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c4c17bad5a530912d2111825d3f05e89bab2dd376aaa8cbc77e449e6db63e576", size = 97931, upload-time = "2026-05-19T21:28:58.56Z" }, + { url = "https://files.pythonhosted.org/packages/98/ec/32ba48acae30fecd60928f5791188b80a9d6ee3840507ffda29fecd37b71/yarl-1.24.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f5f0cbb112838a4a293985b6ed73948a547dadcc1ba6d2089938e7abdedceef8", size = 111030, upload-time = "2026-05-19T21:29:00.148Z" }, + { url = "https://files.pythonhosted.org/packages/82/5a/6f4cd081e5f4934d2ae3a8ef4abe3afacc010d26f0035ee91b35cd7d7c37/yarl-1.24.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ec8356b8a6afcf81fc7aeeef13b1ff7a49dec00f313394bbb9e83830d32ccd7", size = 110392, upload-time = "2026-05-19T21:29:02.155Z" }, + { url = "https://files.pythonhosted.org/packages/7a/da/323a01c349bd5fb01bb6652e314d9bb218cee630a736bdb810ad50e4013f/yarl-1.24.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7e7ebcdef69dec6c6451e616f32b622a6d4a2e92b445c992f7c8e5274a6bbc4c", size = 105612, upload-time = "2026-05-19T21:29:04.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/80/264ab684f181e1a876389374519ff05d10248725535ae2ac4e8ac4e563d6/yarl-1.24.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:47a55d6cf6db2f401017a9e96e5288844e5051911fb4e0c8311a3980f5e59a7d", size = 104487, upload-time = "2026-05-19T21:29:06.491Z" }, + { url = "https://files.pythonhosted.org/packages/41/07/efabe5df87e96d7ad5959760b888344be48cd6884db127b407c6b5503adc/yarl-1.24.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3065657c80a2321225e804048597ad55658a7e76b32d6f5ee4074d04c50401db", size = 102333, upload-time = "2026-05-19T21:29:08.267Z" }, + { url = "https://files.pythonhosted.org/packages/44/0c/bcf7c42603e1009295f586d8890f2ba032c8b53310e815adf0a202c73d9f/yarl-1.24.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:cb84b80d88e19ede158619b80813968713d8d008b0e2497a576e6a0557d50712", size = 99025, upload-time = "2026-05-19T21:29:10.682Z" }, + { url = "https://files.pythonhosted.org/packages/4f/82/84482ab1a57a0f21a08afe6a7004c61d741f8f2ecc3b05c321577c612164/yarl-1.24.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:990de4f680b1c217e77ff0d6aa0029f9eb79889c11fb3e9a3942c7eba29c1996", size = 110507, upload-time = "2026-05-19T21:29:12.954Z" }, + { url = "https://files.pythonhosted.org/packages/c4/8d/a546ba1dfe1b0f290e05fef145cd07614c0f15df1a707195e512d1e39d1d/yarl-1.24.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:abb8ec0323b80161e3802da3150ef660b41d0e9be2048b76a363d93eee992c2b", size = 103719, upload-time = "2026-05-19T21:29:14.893Z" }, + { url = "https://files.pythonhosted.org/packages/1a/b6/267f2a09213138473adfce6b8a6e17791d7fee70bd4d9003218e4dec58b0/yarl-1.24.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e7977781f83638a4c73e0f88425563d70173e0dfd90ac006a45c65036293ee3c", size = 110438, upload-time = "2026-05-19T21:29:16.485Z" }, + { url = "https://files.pythonhosted.org/packages/48/2d/1c8d89c7c5f9cad9fb2902445d94e2ab1d7aa35de029afbb8ae95c42d00f/yarl-1.24.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e30dd55825dc554ec5b66a94953b8eda8745926514c5089dfcacecb9c99b5bd1", size = 105719, upload-time = "2026-05-19T21:29:18.367Z" }, + { url = "https://files.pythonhosted.org/packages/fd/4d/4b880086bd0d3e034d25647be1d830afc3e3f610e98c4ab3490af6b1b6d5/yarl-1.24.2-py3-none-any.whl", hash = "sha256:2783d9226db8797636cd6896e4de81feed252d1db72265686c9558d97a4d94b9", size = 53576, upload-time = "2026-05-19T21:31:03.909Z" }, +] + +[[package]] +name = "zstandard" +version = "0.25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/aa/3e0508d5a5dd96529cdc5a97011299056e14c6505b678fd58938792794b1/zstandard-0.25.0.tar.gz", hash = "sha256:7713e1179d162cf5c7906da876ec2ccb9c3a9dcbdffef0cc7f70c3667a205f0b", size = 711513, upload-time = "2025-09-14T22:15:54.002Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/83/c3ca27c363d104980f1c9cee1101cc8ba724ac8c28a033ede6aab89585b1/zstandard-0.25.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:933b65d7680ea337180733cf9e87293cc5500cc0eb3fc8769f4d3c88d724ec5c", size = 795254, upload-time = "2025-09-14T22:16:26.137Z" }, + { url = "https://files.pythonhosted.org/packages/ac/4d/e66465c5411a7cf4866aeadc7d108081d8ceba9bc7abe6b14aa21c671ec3/zstandard-0.25.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3f79487c687b1fc69f19e487cd949bf3aae653d181dfb5fde3bf6d18894706f", size = 640559, upload-time = "2025-09-14T22:16:27.973Z" }, + { url = "https://files.pythonhosted.org/packages/12/56/354fe655905f290d3b147b33fe946b0f27e791e4b50a5f004c802cb3eb7b/zstandard-0.25.0-cp311-cp311-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:0bbc9a0c65ce0eea3c34a691e3c4b6889f5f3909ba4822ab385fab9057099431", size = 5348020, upload-time = "2025-09-14T22:16:29.523Z" }, + { url = "https://files.pythonhosted.org/packages/3b/13/2b7ed68bd85e69a2069bcc72141d378f22cae5a0f3b353a2c8f50ef30c1b/zstandard-0.25.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:01582723b3ccd6939ab7b3a78622c573799d5d8737b534b86d0e06ac18dbde4a", size = 5058126, upload-time = "2025-09-14T22:16:31.811Z" }, + { url = "https://files.pythonhosted.org/packages/c9/dd/fdaf0674f4b10d92cb120ccff58bbb6626bf8368f00ebfd2a41ba4a0dc99/zstandard-0.25.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5f1ad7bf88535edcf30038f6919abe087f606f62c00a87d7e33e7fc57cb69fcc", size = 5405390, upload-time = "2025-09-14T22:16:33.486Z" }, + { url = "https://files.pythonhosted.org/packages/0f/67/354d1555575bc2490435f90d67ca4dd65238ff2f119f30f72d5cde09c2ad/zstandard-0.25.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:06acb75eebeedb77b69048031282737717a63e71e4ae3f77cc0c3b9508320df6", size = 5452914, upload-time = "2025-09-14T22:16:35.277Z" }, + { url = "https://files.pythonhosted.org/packages/bb/1f/e9cfd801a3f9190bf3e759c422bbfd2247db9d7f3d54a56ecde70137791a/zstandard-0.25.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9300d02ea7c6506f00e627e287e0492a5eb0371ec1670ae852fefffa6164b072", size = 5559635, upload-time = "2025-09-14T22:16:37.141Z" }, + { url = "https://files.pythonhosted.org/packages/21/88/5ba550f797ca953a52d708c8e4f380959e7e3280af029e38fbf47b55916e/zstandard-0.25.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfd06b1c5584b657a2892a6014c2f4c20e0db0208c159148fa78c65f7e0b0277", size = 5048277, upload-time = "2025-09-14T22:16:38.807Z" }, + { url = "https://files.pythonhosted.org/packages/46/c0/ca3e533b4fa03112facbe7fbe7779cb1ebec215688e5df576fe5429172e0/zstandard-0.25.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f373da2c1757bb7f1acaf09369cdc1d51d84131e50d5fa9863982fd626466313", size = 5574377, upload-time = "2025-09-14T22:16:40.523Z" }, + { url = "https://files.pythonhosted.org/packages/12/9b/3fb626390113f272abd0799fd677ea33d5fc3ec185e62e6be534493c4b60/zstandard-0.25.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6c0e5a65158a7946e7a7affa6418878ef97ab66636f13353b8502d7ea03c8097", size = 4961493, upload-time = "2025-09-14T22:16:43.3Z" }, + { url = "https://files.pythonhosted.org/packages/cb/d3/23094a6b6a4b1343b27ae68249daa17ae0651fcfec9ed4de09d14b940285/zstandard-0.25.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c8e167d5adf59476fa3e37bee730890e389410c354771a62e3c076c86f9f7778", size = 5269018, upload-time = "2025-09-14T22:16:45.292Z" }, + { url = "https://files.pythonhosted.org/packages/8c/a7/bb5a0c1c0f3f4b5e9d5b55198e39de91e04ba7c205cc46fcb0f95f0383c1/zstandard-0.25.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:98750a309eb2f020da61e727de7d7ba3c57c97cf6213f6f6277bb7fb42a8e065", size = 5443672, upload-time = "2025-09-14T22:16:47.076Z" }, + { url = "https://files.pythonhosted.org/packages/27/22/503347aa08d073993f25109c36c8d9f029c7d5949198050962cb568dfa5e/zstandard-0.25.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:22a086cff1b6ceca18a8dd6096ec631e430e93a8e70a9ca5efa7561a00f826fa", size = 5822753, upload-time = "2025-09-14T22:16:49.316Z" }, + { url = "https://files.pythonhosted.org/packages/e2/be/94267dc6ee64f0f8ba2b2ae7c7a2df934a816baaa7291db9e1aa77394c3c/zstandard-0.25.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:72d35d7aa0bba323965da807a462b0966c91608ef3a48ba761678cb20ce5d8b7", size = 5366047, upload-time = "2025-09-14T22:16:51.328Z" }, + { url = "https://files.pythonhosted.org/packages/82/fc/f26eb6ef91ae723a03e16eddb198abcfce2bc5a42e224d44cc8b6765e57e/zstandard-0.25.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7b3c3a3ab9daa3eed242d6ecceead93aebbb8f5f84318d82cee643e019c4b73b", size = 795738, upload-time = "2025-09-14T22:16:56.237Z" }, + { url = "https://files.pythonhosted.org/packages/aa/1c/d920d64b22f8dd028a8b90e2d756e431a5d86194caa78e3819c7bf53b4b3/zstandard-0.25.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:913cbd31a400febff93b564a23e17c3ed2d56c064006f54efec210d586171c00", size = 640436, upload-time = "2025-09-14T22:16:57.774Z" }, + { url = "https://files.pythonhosted.org/packages/53/6c/288c3f0bd9fcfe9ca41e2c2fbfd17b2097f6af57b62a81161941f09afa76/zstandard-0.25.0-cp312-cp312-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:011d388c76b11a0c165374ce660ce2c8efa8e5d87f34996aa80f9c0816698b64", size = 5343019, upload-time = "2025-09-14T22:16:59.302Z" }, + { url = "https://files.pythonhosted.org/packages/1e/15/efef5a2f204a64bdb5571e6161d49f7ef0fffdbca953a615efbec045f60f/zstandard-0.25.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dffecc361d079bb48d7caef5d673c88c8988d3d33fb74ab95b7ee6da42652ea", size = 5063012, upload-time = "2025-09-14T22:17:01.156Z" }, + { url = "https://files.pythonhosted.org/packages/b7/37/a6ce629ffdb43959e92e87ebdaeebb5ac81c944b6a75c9c47e300f85abdf/zstandard-0.25.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7149623bba7fdf7e7f24312953bcf73cae103db8cae49f8154dd1eadc8a29ecb", size = 5394148, upload-time = "2025-09-14T22:17:03.091Z" }, + { url = "https://files.pythonhosted.org/packages/e3/79/2bf870b3abeb5c070fe2d670a5a8d1057a8270f125ef7676d29ea900f496/zstandard-0.25.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6a573a35693e03cf1d67799fd01b50ff578515a8aeadd4595d2a7fa9f3ec002a", size = 5451652, upload-time = "2025-09-14T22:17:04.979Z" }, + { url = "https://files.pythonhosted.org/packages/53/60/7be26e610767316c028a2cbedb9a3beabdbe33e2182c373f71a1c0b88f36/zstandard-0.25.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5a56ba0db2d244117ed744dfa8f6f5b366e14148e00de44723413b2f3938a902", size = 5546993, upload-time = "2025-09-14T22:17:06.781Z" }, + { url = "https://files.pythonhosted.org/packages/85/c7/3483ad9ff0662623f3648479b0380d2de5510abf00990468c286c6b04017/zstandard-0.25.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:10ef2a79ab8e2974e2075fb984e5b9806c64134810fac21576f0668e7ea19f8f", size = 5046806, upload-time = "2025-09-14T22:17:08.415Z" }, + { url = "https://files.pythonhosted.org/packages/08/b3/206883dd25b8d1591a1caa44b54c2aad84badccf2f1de9e2d60a446f9a25/zstandard-0.25.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aaf21ba8fb76d102b696781bddaa0954b782536446083ae3fdaa6f16b25a1c4b", size = 5576659, upload-time = "2025-09-14T22:17:10.164Z" }, + { url = "https://files.pythonhosted.org/packages/9d/31/76c0779101453e6c117b0ff22565865c54f48f8bd807df2b00c2c404b8e0/zstandard-0.25.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1869da9571d5e94a85a5e8d57e4e8807b175c9e4a6294e3b66fa4efb074d90f6", size = 4953933, upload-time = "2025-09-14T22:17:11.857Z" }, + { url = "https://files.pythonhosted.org/packages/18/e1/97680c664a1bf9a247a280a053d98e251424af51f1b196c6d52f117c9720/zstandard-0.25.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:809c5bcb2c67cd0ed81e9229d227d4ca28f82d0f778fc5fea624a9def3963f91", size = 5268008, upload-time = "2025-09-14T22:17:13.627Z" }, + { url = "https://files.pythonhosted.org/packages/1e/73/316e4010de585ac798e154e88fd81bb16afc5c5cb1a72eeb16dd37e8024a/zstandard-0.25.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f27662e4f7dbf9f9c12391cb37b4c4c3cb90ffbd3b1fb9284dadbbb8935fa708", size = 5433517, upload-time = "2025-09-14T22:17:16.103Z" }, + { url = "https://files.pythonhosted.org/packages/5b/60/dd0f8cfa8129c5a0ce3ea6b7f70be5b33d2618013a161e1ff26c2b39787c/zstandard-0.25.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:99c0c846e6e61718715a3c9437ccc625de26593fea60189567f0118dc9db7512", size = 5814292, upload-time = "2025-09-14T22:17:17.827Z" }, + { url = "https://files.pythonhosted.org/packages/fc/5f/75aafd4b9d11b5407b641b8e41a57864097663699f23e9ad4dbb91dc6bfe/zstandard-0.25.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:474d2596a2dbc241a556e965fb76002c1ce655445e4e3bf38e5477d413165ffa", size = 5360237, upload-time = "2025-09-14T22:17:19.954Z" }, +] diff --git a/experiments/exp5_data_contacts_and_distances_v2_zephyr/vocab.py b/experiments/exp5_data_contacts_and_distances_v2_zephyr/vocab.py new file mode 100644 index 0000000..898c694 --- /dev/null +++ b/experiments/exp5_data_contacts_and_distances_v2_zephyr/vocab.py @@ -0,0 +1,68 @@ +# Copyright The MarinFold Authors +# SPDX-License-Identifier: Apache-2.0 + +"""Canonical vocabulary for the contacts-and-distances-v2 format. + +Identical to exp34's vocab.py — v2 reuses every v1 token in its existing +order, then appends two new tokens (```` and +````). Append-only is load-bearing: it keeps every v1 token ID +stable, so a checkpoint pretrained on v1 can be warm-started on v2 simply +by growing the embedding table by 2 rows. + +The v1 vocab is imported directly from the marinfold kind library, so v1 +and v2 can never silently disagree on the position of a shared token. +""" + +from marinfold.document_structures.contacts_and_distances_v1.vocab import ( + AMINO_ACIDS, + ATOM_NAMES, + CONTACT_TYPES, + CONTROL_TOKENS as V1_CONTROL_TOKENS, + DISTANCE_BINS, + DISTANCE_MARKER, + MAX_POSITION, + PLDDT_BINS, + POSITION_TOKENS, + UNK_TOKEN, +) +from marinfold.document_structures.contacts_and_distances_v1.vocab import ( + all_domain_tokens as v1_all_domain_tokens, +) + + +NAME = "contacts-and-distances-v2" +CONTEXT_LENGTH = 8192 + +THINK_TOKEN = "" + +V2_NEW_TOKENS = [ + f"<{NAME}>", + THINK_TOKEN, +] + + +def all_domain_tokens() -> list[str]: + """v2 domain vocabulary = v1 domain vocab ++ [````, ````].""" + out = list(v1_all_domain_tokens()) + out.extend(V2_NEW_TOKENS) + return out + + +__all__ = [ + "AMINO_ACIDS", + "ATOM_NAMES", + "CONTACT_TYPES", + "CONTEXT_LENGTH", + "DISTANCE_BINS", + "DISTANCE_MARKER", + "MAX_POSITION", + "NAME", + "PLDDT_BINS", + "POSITION_TOKENS", + "THINK_TOKEN", + "UNK_TOKEN", + "V1_CONTROL_TOKENS", + "V2_NEW_TOKENS", + "all_domain_tokens", + "v1_all_domain_tokens", +] From a948b6b42ce5b6abab0cbee4ac7e693b2cbf19cb Mon Sep 17 00:00:00 2001 From: Alex Merose Date: Thu, 28 May 2026 15:41:17 -0700 Subject: [PATCH 2/3] Vectorize. --- .../generate.py | 86 +++++++++++++------ .../parse.py | 47 +++++++--- 2 files changed, 93 insertions(+), 40 deletions(-) diff --git a/experiments/exp5_data_contacts_and_distances_v2_zephyr/generate.py b/experiments/exp5_data_contacts_and_distances_v2_zephyr/generate.py index 62fb7bb..e8f05f3 100644 --- a/experiments/exp5_data_contacts_and_distances_v2_zephyr/generate.py +++ b/experiments/exp5_data_contacts_and_distances_v2_zephyr/generate.py @@ -196,20 +196,33 @@ def generate_one( selected_medium = rng.sample(contacts_medium, n_medium) if n_medium > 0 else [] selected_short = rng.sample(contacts_short, n_short) if n_short > 0 else [] - # ---- distance statements ---------------------------------------------- - # ``distance_indices`` = 1-based residue indices with at least one - # in-vocab atom — preserves legacy iteration order (ascending). - atoms_per_residue = np.diff(structure.atom_offsets) - distance_indices = (np.flatnonzero(atoms_per_residue > 0) + 1).tolist() + # ---- distance statements (two-phase: serial RNG → vectorized math) ---- + # Phase 1 walks the RNG serially (the only legal way: byte-identity vs + # exp34 requires Mersenne-Twister draws in this exact order), but + # records only cheap integer indices into per-residue + per-atom plans. + # Phase 2 then does ALL xyz lookups, distance math, and bin tokenization + # vectorized over the plan via fancy indexing into the CSR atom table. + # Net win: ~12 scalar ops per statement (subtract/square/add/sqrt) + # collapse into one C-level numpy call across all statements. atom_offsets = structure.atom_offsets - atom_name_id = structure.atom_name_id atom_xyz = structure.atom_xyz + atom_name_id = structure.atom_name_id + atoms_per_residue = np.diff(atom_offsets) + distance_indices = (np.flatnonzero(atoms_per_residue > 0) + 1).tolist() distance_statements: list[tuple[int, int, str, str, str]] = [] - if len(distance_indices) >= 2: + if len(distance_indices) >= 2 and n_distance > 0: + # Phase 1: serial RNG → integer plan arrays. RNG advance is + # bit-for-bit unchanged vs the old per-statement loop (same + # rng.sample + rng.randrange call sequence, same retry policy). + # `rng.randrange(K)` invokes the same `_randbelow(K)` as + # `rng.choice(seq_of_len_K)` — sequence content doesn't matter for + # the MT state, only the length. + plan_i: list[int] = [] + plan_j: list[int] = [] + plan_atom_a: list[int] = [] # 0-based atom index within residue (i - 1) + plan_atom_b: list[int] = [] for _ in range(n_distance): - # Draw: residue pair (with up to 10 retries when sep <= 1, exactly - # matching the legacy retry loop). a, b = rng.sample(distance_indices, 2) i, j = (a, b) if a < b else (b, a) if j - i <= 1: @@ -222,24 +235,43 @@ def generate_one( break if not ok: continue - # Draw: per-residue atom pick. ``rng.choice(tuple)`` and - # ``rng.randrange(len)`` both call ``self._randbelow(len)``, so - # the RNG state evolves identically as long as the lengths match. - ki = int(atom_offsets[i] - atom_offsets[i - 1]) # i is 1-based - kj = int(atom_offsets[j] - atom_offsets[j - 1]) - idx_a = rng.randrange(ki) - idx_b = rng.randrange(kj) - i0_start = int(atom_offsets[i - 1]) - j0_start = int(atom_offsets[j - 1]) - ai_name = _ATOM_NAMES_TUPLE[int(atom_name_id[i0_start + idx_a])] - aj_name = _ATOM_NAMES_TUPLE[int(atom_name_id[j0_start + idx_b])] - # Pull as Python floats (.tolist on a (3,) float64 row) so the - # subsequent (ax - bx)**2 + … + math.sqrt sequence is identical - # to exp34's tuple-based computation. - ax, ay, az = atom_xyz[i0_start + idx_a].tolist() - bx, by, bz = atom_xyz[j0_start + idx_b].tolist() - d = math.sqrt((ax - bx) ** 2 + (ay - by) ** 2 + (az - bz) ** 2) - distance_statements.append((i, j, ai_name, aj_name, _distance_token(d))) + k_i = int(atoms_per_residue[i - 1]) + k_j = int(atoms_per_residue[j - 1]) + plan_i.append(i) + plan_j.append(j) + plan_atom_a.append(rng.randrange(k_i)) + plan_atom_b.append(rng.randrange(k_j)) + + # Phase 2: vectorized lookup + distance + tokenize over the plan. + if plan_i: + i_arr = np.asarray(plan_i, dtype=np.int32) + j_arr = np.asarray(plan_j, dtype=np.int32) + flat_a = atom_offsets[i_arr - 1] + np.asarray(plan_atom_a, dtype=np.int32) + flat_b = atom_offsets[j_arr - 1] + np.asarray(plan_atom_b, dtype=np.int32) + diffs = atom_xyz[flat_a] - atom_xyz[flat_b] + d_arr = np.sqrt((diffs * diffs).sum(axis=1)) + # Vectorized _distance_token: ceil(d/0.5) clipped to [1, 64]. + # Identical mapping to the per-element function, just in numpy. + bin_arr = np.clip(np.ceil(d_arr / 0.5).astype(np.int64), 1, 64) + # Name lookup is one fancy-index into the uint8 CSR column + + # one tuple-index per element into the (~37-entry) vocab. The + # final ``distance_statements`` build is in Python because the + # downstream consumer is a list of mixed-type tuples — but the + # per-element body is now bare list ops, no math, no rng. + name_a_ids = atom_name_id[flat_a] + name_b_ids = atom_name_id[flat_b] + i_list = i_arr.tolist() + j_list = j_arr.tolist() + bin_list = bin_arr.tolist() + name_a_list = name_a_ids.tolist() + name_b_list = name_b_ids.tolist() + distance_statements = [ + (i_list[k], j_list[k], + _ATOM_NAMES_TUPLE[name_a_list[k]], + _ATOM_NAMES_TUPLE[name_b_list[k]], + f"d{bin_list[k] * 0.5:.1f}") + for k in range(len(plan_i)) + ] # ---- assemble statements + ranks -------------------------------------- statements: list[_Statement] = [] diff --git a/experiments/exp5_data_contacts_and_distances_v2_zephyr/parse.py b/experiments/exp5_data_contacts_and_distances_v2_zephyr/parse.py index 1dc9296..6d30e4c 100644 --- a/experiments/exp5_data_contacts_and_distances_v2_zephyr/parse.py +++ b/experiments/exp5_data_contacts_and_distances_v2_zephyr/parse.py @@ -88,6 +88,12 @@ class ParsedStructure: atom_name_id: np.ndarray # uint8[T] — index into ``parse._ATOM_NAMES_TUPLE`` atom_xyz: np.ndarray # float64[T, 3] + # Per-residue ``(name_ids, xyz)`` slices into the flat tables, materialized + # ONCE in ``from_gemmi``. Numpy slices are views (no copy), so this is + # cheap to build and zero-cost to keep alongside the flat arrays. Used by + # ``atoms_for(i)`` for downstream vectorized per-residue analytics. + per_residue_atoms: tuple[tuple[np.ndarray, np.ndarray], ...] + # Precomputed once at parse time so generate.py doesn't have to choose # between FP-fidelity (sum/N over Python floats) and vectorization. global_plddt: float @@ -216,21 +222,35 @@ def _flush_residue(name: str, b_values: list[float], # property — keeps RNG-seeded doc content byte-identical. global_plddt = sum(plddt_values) / len(plddt_values) + atom_offsets_arr = np.asarray(atom_offsets, dtype=np.int32) + atom_name_id_arr = ( + np.asarray(atom_name_id_list, dtype=np.uint8) + if atom_name_id_list else np.empty(0, dtype=np.uint8) + ) + atom_xyz_arr = ( + np.asarray(atom_xyz_list, dtype=np.float64).reshape(-1, 3) + if atom_xyz_list else np.empty((0, 3), dtype=np.float64) + ) + + # Materialize per-residue views once. Numpy slices share memory with + # the flat arrays — no copies, just (PyObject + view header) per + # residue. For ~250 residues this is ~25 KB of overhead total. + per_residue_atoms = tuple( + (atom_name_id_arr[atom_offsets[i]:atom_offsets[i + 1]], + atom_xyz_arr[atom_offsets[i]:atom_offsets[i + 1]]) + for i in range(len(sequence)) + ) + return cls( entry_id=entry_id, source=source, sequence=tuple(sequence), plddt_per_residue=np.asarray(plddt_values, dtype=np.float64), cb_or_ca_xyz=np.asarray(cb_or_ca_rows, dtype=np.float64), - atom_offsets=np.asarray(atom_offsets, dtype=np.int32), - atom_name_id=( - np.asarray(atom_name_id_list, dtype=np.uint8) - if atom_name_id_list else np.empty(0, dtype=np.uint8) - ), - atom_xyz=( - np.asarray(atom_xyz_list, dtype=np.float64).reshape(-1, 3) - if atom_xyz_list else np.empty((0, 3), dtype=np.float64) - ), + atom_offsets=atom_offsets_arr, + atom_name_id=atom_name_id_arr, + atom_xyz=atom_xyz_arr, + per_residue_atoms=per_residue_atoms, global_plddt=global_plddt, ) @@ -239,10 +259,11 @@ def num_residues(self) -> int: return len(self.sequence) def atoms_for(self, i: int) -> tuple[np.ndarray, np.ndarray]: - """Atoms of residue ``i`` (0-based): ``(name_ids[K], xyz[K, 3])``.""" - start = int(self.atom_offsets[i]) - end = int(self.atom_offsets[i + 1]) - return self.atom_name_id[start:end], self.atom_xyz[start:end] + """Atoms of residue ``i`` (0-based): ``(name_ids[K], xyz[K, 3])``. + + O(1) tuple-index into the precomputed ``per_residue_atoms``. + """ + return self.per_residue_atoms[i] # -------------------------------------------------------------------------- From dfbc52bd90b7d435325e2fcb14fe1f09d0322495 Mon Sep 17 00:00:00 2001 From: Alex Merose Date: Fri, 29 May 2026 18:07:48 -0700 Subject: [PATCH 3/3] Simpler approach given suggestions! --- .../README.md | 137 ++++--- .../cli.py | 174 ++++++--- .../generate.py | 339 ------------------ .../parse.py | 323 ----------------- .../pyproject.toml | 39 +- .../tests/conftest.py | 27 +- .../tests/test_byte_identity.py | 104 ------ .../tests/test_parse.py | 79 ---- .../vocab.py | 68 ---- 9 files changed, 220 insertions(+), 1070 deletions(-) delete mode 100644 experiments/exp5_data_contacts_and_distances_v2_zephyr/generate.py delete mode 100644 experiments/exp5_data_contacts_and_distances_v2_zephyr/parse.py delete mode 100644 experiments/exp5_data_contacts_and_distances_v2_zephyr/tests/test_byte_identity.py delete mode 100644 experiments/exp5_data_contacts_and_distances_v2_zephyr/tests/test_parse.py delete mode 100644 experiments/exp5_data_contacts_and_distances_v2_zephyr/vocab.py diff --git a/experiments/exp5_data_contacts_and_distances_v2_zephyr/README.md b/experiments/exp5_data_contacts_and_distances_v2_zephyr/README.md index 5c26838..e4306ff 100644 --- a/experiments/exp5_data_contacts_and_distances_v2_zephyr/README.md +++ b/experiments/exp5_data_contacts_and_distances_v2_zephyr/README.md @@ -55,39 +55,45 @@ single-digit minutes: ## Approach -This is a fresh implementation. It calls out to nothing in exp34 at -runtime; the byte-identity contract is enforced by a test that -imports exp34's reference via `sys.path` and SHA-1-compares the -generated docs. +**exp5 is a Zephyr wrapper around exp34's local impl.** The v2 doc +algorithm — parse + generate + vocab — comes from +[`exp34`](../exp34_document_structures_contacts_and_distances_v2/), +imported directly via a `sys.path` shim in `cli.py`. Two copies of an +algorithm always drift; one source of truth doesn't (see Tim's review on +PR [#38](https://github.com/Open-Athena/MarinFold/pull/38)). The +trade-off is that we accept exp34's per-row CPU cost as-is rather than +re-implementing for speed — the prior production runs landed at ~8 min +end-to-end on the cluster, well within budget for a one-time job. + +Performance-oriented re-engineering of the parse + generate inner +loop — and the columnar `ParsedStructure` + CSR substrate that comes +with it — lives in +[`exp42`](../exp42_data_shared_protein_data_substrate_csr_parquet/), where +training-time on-the-fly doc generation actually needs the speedup. +exp5 stays small. ### Design choices -- **Columnar in-memory structure** (`parse.ParsedStructure`). - Per-residue arrays at the *structure* level: `plddt_per_residue: - float64[N]`, `cb_or_ca_xyz: float64[N, 3]`, plus a flat CSR atom - table (`atom_offsets: int32[N+1]`, `atom_name_id: uint8[T]`, - `atom_xyz: float64[T, 3]`). No backward compat with marinfold's - tuple-of-tuples `Residue.atoms` — designed to round-trip zero-copy - to `pyarrow.RecordBatch` if a precomputed-store experiment ever - needs it. -- **Single-pass parser** with the gemmi fast path: `Model.all()` flat - CRA iteration + `atom.pos.tolist()` (one nanobind call vs three - attribute accesses). Polymer-iteration fallback for non-AFDB - inputs. See `parse.py` for the determinism contract that keeps docs - byte-identical to exp34. -- **Vectorized contact eligibility.** `np.triu_indices(k=1)` over the - `cb_or_ca_xyz` array replaces a ~1.7 M-call Python `euclidean` - loop. Row-major order is preserved so `rng.sample` over each - contacts-by-mode list sees identical inputs to exp34. +- **One source of truth.** `cli.py` does `import parse, generate, vocab` + — all three modules resolve to exp34 via a `sys.path` shim at module + load (exp34 is `package = false` so we can't path-dep it as an + installable package; the long-term fix is to graduate v2 to + `marinfold/marinfold/document_structures/contacts_and_distances_v2/`, + at which point the shim disappears). +- **Per-row I/O adapter.** exp34's `parse.parse_structure(path)` takes + a local filesystem path; the manifest gives us a `gs://` URI. The + worker fetches via `fsspec` into a `NamedTemporaryFile`, then calls + the parser. exp34's parser derives `entry_id` from the cif's + `_entry.id` field, so the random tempfile name is harmless. Per-row + cost: one tempfile open/write/unlink, ~negligible. - **URI-mode input is the default.** `--cif-uri-column` defaults to `gcs_uri`. The bulky `cif_content` path is still available via `--cif-text-column` for testing or datasets without URI columns. - **In-shard concurrent fetches** via `Dataset.map_shard` + - `ThreadPoolExecutor` (`--fetch-concurrency`, default 32). This is - the new lever — the prior production run landed at ~128 s/shard - because the 2,000 per-row GCS GETs ran sequentially. The fetch - backend is `fsspec` + `gcsfs`; see "On obstore" below for why we - didn't switch. + `ThreadPoolExecutor` (`--fetch-concurrency`, default 32). The + prior production run landed at ~128 s/shard because the 2,000 per-row + GCS GETs ran sequentially. Threading them overlaps GETs with gemmi + parse (gemmi releases the GIL during the C++ parse). - **Resource defaults bake in the lesson:** `--worker-cpu 1`, `--worker-memory 4g`, `--max-workers` left to Zephyr's default of 128 on a cluster (raise via the flag or `ZEPHYR_MAX_WORKERS`). @@ -126,55 +132,33 @@ read deduplicates it before calling `load_parquet(columns=...)`. Schema detection happens once at submission time (peek the first matching parquet file), so the output schema is stable across shards. -### On obstore (and why we didn't switch) - -We evaluated [obstore](https://developmentseed.org/obstore/) (Rust -`object_store`-backed, advertised ~9× throughput vs `fsspec` on -parallel small-object GETs) as the per-row fetch backend. It works on -single-region GCS buckets like our output bucket -`gs://marin-us-central1/...`, but it **cannot read AFDB** as shipped -(obstore 0.9.5). The root cause is upstream: `object_store`'s HTTP -handler hard-requires a `Content-Length` header on every GCS GET -response, and AFDB cifs are stored with `Content-Encoding: gzip` so -GCS transparently decompresses on the wire (visible in the response -as `x-guploader-response-body-transformations: gunzipped`) and omits -`Content-Length`. The documented client-side workaround -(`Accept-Encoding: gzip`) can only be set via -`client_options.default_headers` today, and those headers leak into -the OAuth2 token fetch and break it. - -Variants we tried (all fail identically on AFDB): - -* `GCSStore.from_url` / `GCSStore(bucket=…)` / `.get_range` / - `.stream`, -* `client_options={"http1_only"/"http2_only"/"allow_http"}`, -* `client_options={"default_headers": {"Accept-Encoding": "gzip"}}` - — breaks the token fetch, -* static `credential_provider` + `default_headers` — Accept-Encoding - doesn't reach the GCS GET, -* `obstore.fsspec.register("gs")` + `fsspec.open(...)`, -* `FsspecStore("gs", ...)`. - -Until either `object_store` relaxes the `Content-Length` invariant or -obstore exposes per-request / per-bucket header overrides that don't -touch the auth flow, we stay on `fsspec/gcsfs` for the fetch. -Worth filing upstream the next time someone touches obstore. +### See also: on-the-fly training-time generation (#42) + +For training-time doc generation directly from the parsed substrate — +with per-epoch RNG variation as free data augmentation, ~12× smaller +artifact than raw CIFs, and a doc-format-agnostic dataloader callback +API — see +[`exp42`](../exp42_data_shared_protein_data_substrate_csr_parquet/) (the +"shared protein-data substrate" experiment). exp5 publishes the static +v2 corpus to HF; exp42 publishes a CSR-parquet substrate that any +doc-format experiment can consume on the fly. ### Layout ``` experiments/exp5_data_contacts_and_distances_v2_zephyr/ -├── vocab.py # re-exports marinfold v1 vocab + appends V2_NEW_TOKENS -├── parse.py # columnar ParsedStructure + gemmi fast-path extractor -├── generate.py # vectorized v2 generation; byte-identical RNG stream vs exp34 -├── cli.py # `generate` (Zephyr pipeline) + `tokenizer` subcommands +├── cli.py # `generate` (Zephyr pipeline) + `tokenizer` subcommands. +│ # Imports parse/generate/vocab from exp34 via sys.path shim. ├── pyproject.toml # marinfold + gemmi/numpy/pyarrow + marin-zephyr/fsspec/gcsfs/hf └── tests/ - ├── test_parse.py # columnar shape + per-residue invariants - ├── test_byte_identity.py # SHA-1 match against exp34 reference └── test_cli.py # end-to-end cli.cmd_generate smoke ``` +Notably absent: `parse.py`, `generate.py`, `vocab.py` — all three live +in [`exp34`](../exp34_document_structures_contacts_and_distances_v2/) +and are imported at runtime. exp5's job is the Zephyr pipeline around +those modules, nothing more. + ### Running it locally ```bash @@ -216,19 +200,22 @@ shell. Cancel a running job with ## Success criteria -1. **Byte-identity vs exp34.** Generated v2 docs are SHA-1-equal to - exp34's reference implementation for the same structure + entry_id, - across multiple seeds — covered by - `tests/test_byte_identity.py::test_doc_byte_identical_to_exp34`. +1. **One source of truth for v2.** Generated docs come from exp34's + `parse.parse_structure` + `generate._generate_one` (no fork, no + re-implementation). Verifiable by `git grep -rn 'def parse_structure\|def _generate_one'` + returning hits only under `exp34_*/`. 2. **Full 1.6M-structure generation completes** on the marin Iris cluster, writing one parquet per input shard to GCS, with no shard left unwritten and no `ZephyrWorkerError` propagating. -3. **Wall-clock improves over the prior runs** — the v2 production - runs from the exp34 session landed at ~14 min (sequential - `cif_content`) and ~8 min (`gcs_uri` mode without intra-shard - concurrency). With the thread-pool fetches the per-shard time - should drop below the prior ~128 s, and end-to-end wall should - come in well under ~5 min compute. +3. **Wall-clock within budget** — the v2 production runs from the + exp34 session landed at ~14 min (sequential `cif_content`) and ~8 + min (`gcs_uri` mode without intra-shard concurrency). The + thread-pool fetches should keep the per-shard time well under the + prior ~128 s. We're explicitly *not* targeting the 5-min number + from the earlier perf-focused branch — that speedup required the + forked parse/generate, which we've intentionally dropped per + @timodonnell's PR [#38](https://github.com/Open-Athena/MarinFold/pull/38) + review. ## Results diff --git a/experiments/exp5_data_contacts_and_distances_v2_zephyr/cli.py b/experiments/exp5_data_contacts_and_distances_v2_zephyr/cli.py index 584098b..df3e6c0 100644 --- a/experiments/exp5_data_contacts_and_distances_v2_zephyr/cli.py +++ b/experiments/exp5_data_contacts_and_distances_v2_zephyr/cli.py @@ -3,21 +3,38 @@ """contacts-and-distances-v2-on-zephyr driver. +exp5 is the at-scale, Zephyr-parallelized runner for the v2 doc +algorithm — it does *not* re-implement the algorithm. The parse + +generate code is imported from exp34 +(``exp34_document_structures_contacts_and_distances_v2``), the +local-only reference impl, so the two can never drift. exp5's value +is everything *around* that impl: the Zephyr pipeline, in-shard +fetch concurrency, manifest passthrough, output schema, Iris +resource defaults. + Two subcommands: * ``generate`` — runs the v2 generator at scale on the marin Iris - cluster. Input is the timodonnell/afdb-1.6M parquet manifest on - HuggingFace; per-row mmCIFs are fetched concurrently from the - ``gcs_uri`` column inside each Zephyr shard (the perf finding - from the prior production run: sequential per-row GCS fetches - were the residual bottleneck once the URI mode killed the - cross-cloud HF egress). + cluster. Input is the ``timodonnell/afdb-1.6M`` parquet manifest + on HuggingFace; per-row mmCIFs are fetched concurrently from the + ``gcs_uri`` column inside each Zephyr shard. -* ``tokenizer`` — build / save / push the v2 tokenizer (same as - exp34's; included so a downstream training job can pin a single - experiment for both the dataset and the tokenizer build). +* ``tokenizer`` — build / save / push the v2 tokenizer (delegates to + exp34's vocab so the published tokenizer pins back to one source). Run examples are in the experiment README. + +On the import shim +================== + +exp34 lives at ``../exp34_*`` and is declared ``package = false`` in +its pyproject — it's an experiment dir, not an installable package. +We make its modules importable here by prepending its path to +``sys.path`` once at module load. Tim's #38 review comment notes +this is a long-term smell; the right end-state is to graduate v2 to +``marinfold/marinfold/document_structures/contacts_and_distances_v2/`` +(per the same pattern as v1) and then this shim disappears entirely. +Until that lands, this is the cleanest "one source of truth" we have. """ from __future__ import annotations @@ -26,18 +43,27 @@ import hashlib import os import sys +import tempfile import typing from concurrent.futures import ThreadPoolExecutor from functools import partial from pathlib import Path -from fray import ResourceConfig -from marinfold import build_tokenizer -from zephyr import Dataset, ZephyrContext +# Make exp34's modules (``parse``, ``generate``, ``vocab``) importable. +# Insert at position 0 so they win over anything else on sys.path with the +# same module name. Must happen *before* the exp34 imports below. +_EXP34_DIR = Path(__file__).resolve().parent.parent / "exp34_document_structures_contacts_and_distances_v2" +if str(_EXP34_DIR) not in sys.path: + sys.path.insert(0, str(_EXP34_DIR)) + +import fsspec # noqa: E402 +from fray import ResourceConfig # noqa: E402 +from marinfold import build_tokenizer # noqa: E402 +from zephyr import Dataset, ZephyrContext # noqa: E402 -import generate -import parse -from vocab import CONTEXT_LENGTH, NAME, all_domain_tokens +import generate # exp34's generate.py # noqa: E402 +import parse # exp34's parse.py # noqa: E402 +from vocab import CONTEXT_LENGTH, NAME, all_domain_tokens # exp34's vocab.py # noqa: E402 # Manifest columns we'll copy verbatim to the output *if* the input parquet @@ -77,7 +103,6 @@ def _resolve_input_columns(input_path: str, cif_col: str) -> tuple[list[str], li time so the same column list is used for every shard's ``load_parquet(columns=...)`` call — keeping the output schema stable. """ - import fsspec import pyarrow.parquet as pq fs, _ = fsspec.core.url_to_fs(input_path) @@ -108,22 +133,92 @@ def _resolve_input_columns(input_path: str, cif_col: str) -> tuple[list[str], li return columns, passthrough +def _parse_bytes_via_tempfile(data: bytes, *, suffix: str = ".cif") -> parse.ParsedStructure: + """Wrap exp34's path-only ``parse.parse_structure`` for our bytes pipeline. + + exp34's parser takes a ``Path`` (and reads through ``gemmi.read_structure``); + we fetch cif bytes from gs:// or HF and need to feed them in. Cheapest + + most faithful adapter: spool to a ``NamedTemporaryFile`` and pass the + path. ``entry_id`` is derived inside ``parse_structure`` from the cif's + ``_entry.id`` field (``structure.name``), NOT the filename — so the + tempfile's random name is harmless. The file is unlinked on exit. + """ + with tempfile.NamedTemporaryFile(suffix=suffix, delete=False) as tmp: + tmp.write(data) + tmp_path = Path(tmp.name) + try: + return parse.parse_structure(tmp_path) + finally: + try: + tmp_path.unlink() + except OSError: + pass + + +def _fetch_and_parse(uri: str) -> parse.ParsedStructure | None: + """Fetch a cif via fsspec (transparent gunzip if applicable) and parse + it through exp34's parser. Returns ``None`` on fetchable / parseable + failure so a single bad row never kills a Zephyr worker. + + Mirrors the original exp5 ``try_parse_cif_from_uri`` surface — caller + treats ``None`` as "skip this row." + """ + import warnings + + try: + with fsspec.open(uri, "rb", compression="infer") as f: + data = f.read() + except (OSError, ValueError) as exc: + warnings.warn(f"fetch failed for {uri}: {exc}", stacklevel=2) + return None + try: + return _parse_bytes_via_tempfile(data) + except (ValueError, OSError) as exc: + warnings.warn(f"parse failed for {uri}: {exc}", stacklevel=2) + return None + + +def _try_parse_inline_cif(data, *, entry_id: str | None) -> parse.ParsedStructure | None: + """Inline-cif fallback (--cif-text-column): parse bytes already in the row. + + Same ``None``-on-failure contract as :func:`_fetch_and_parse`. + """ + import warnings + + if data is None: + return None + if isinstance(data, str): + data = data.encode() + try: + return _parse_bytes_via_tempfile(data) + except (ValueError, OSError) as exc: + warnings.warn(f"parse failed for {entry_id}: {exc}", stacklevel=2) + return None + + def _build_output_row(row: dict, doc: str, structure: parse.ParsedStructure, passthrough_columns: list[str]) -> dict: """Assemble one output row: doc text + computed provenance + manifest passthrough. - ``global_plddt`` and ``seq_len`` use the *parsed-structure* values (what - ``generate_one`` actually serialized into the doc's pLDDT bin and - sequence-token block), not the manifest's. Keeps each row internally - consistent — the metadata describes what was emitted. + ``entry_id`` comes from the manifest (it's the authoritative key the + downstream consumer joins on); ``structure.entry_id`` (derived inside + exp34's parser from the cif's ``_entry.id`` field) is used only as a + fallback when the manifest didn't supply one. For real AFDB rows + both agree; for synthetic / unkeyed inputs, manifest-wins is the right + policy. + + ``global_plddt`` and ``seq_len`` use the *parsed-structure* values + (what ``_generate_one`` actually serialized into the doc's pLDDT bin + and sequence-token block), not the manifest's. Keeps each row + internally consistent — the metadata describes what was emitted. """ contacts_emitted = sum(1 for t in doc.split() if t in _CONTACT_MARKERS) out = { - "entry_id": row.get("entry_id"), + "entry_id": row.get("entry_id") or structure.entry_id, "structure": NAME, "document": doc, "sha1": hashlib.sha1(doc.encode()).hexdigest(), - "seq_len": int(structure.num_residues), + "seq_len": len(structure.residues), "global_plddt": float(structure.global_plddt), "contacts_emitted": int(contacts_emitted), } @@ -150,23 +245,21 @@ def _generate_doc_for_row( Returns an output-row dict (with ``document`` + provenance metadata) on success, or ``None`` when the structure was unfetchable / unparseable / - too small to fit the token budget. Designed to be safe inside a Zephyr - worker — never raises on transient I/O errors. + too small to fit the token budget. Safe inside a Zephyr worker — never + raises on transient I/O errors. """ - entry_id = row.get("entry_id") if cif_text_column is not None: - cif = row.get(cif_text_column) - if cif is None: - return None - structure = parse.try_parse_cif_content(cif, entry_id=entry_id, source=entry_id) + structure = _try_parse_inline_cif( + row.get(cif_text_column), entry_id=row.get("entry_id"), + ) else: uri = row.get(cif_uri_column) if uri is None: return None - structure = parse.try_parse_cif_from_uri(uri, entry_id=entry_id) + structure = _fetch_and_parse(uri) if structure is None: return None - doc = generate.generate_one(structure, context_length=context_length, cfg=cfg) + doc = generate._generate_one(structure, context_length=context_length, cfg=cfg) if doc is None: return None return _build_output_row(row, doc, structure, passthrough_columns) @@ -185,18 +278,17 @@ def _generate_shard( ): """``map_shard`` body: fetch all rows' cifs concurrently within the shard. - The CPU work (gemmi parse + doc generation) takes ~6 ms per row; - the GCS GET is ~30-80 ms on a cold connection. Without intra-shard - concurrency, per-shard latency is bounded by the sum of GETs - (sequential I/O), which is what made run 233550 land at ~128 s/shard - despite vectorized parse. A ThreadPoolExecutor of ``fetch_concurrency`` - workers overlaps the I/O — gemmi releases the GIL during the C++ - parse, so the threads make real progress in parallel. + The CPU work (gemmi parse + doc generation) takes a few ms per row; the + GCS GET is ~30-80 ms on a cold connection. Without intra-shard + concurrency, per-shard latency is bounded by the sum of GETs (sequential + I/O), which is what made the prior production run land at ~128 s/shard. + A ThreadPoolExecutor of ``fetch_concurrency`` workers overlaps the I/O — + gemmi releases the GIL during the C++ parse, so the threads make real + progress in parallel. Order within the output shard mirrors input row order (we use - ``executor.map`` not ``as_completed``). That makes the run - deterministic per-shard, which matters for byte-comparing outputs - across runs. + ``executor.map`` not ``as_completed``). That makes the run deterministic + per-shard, which matters for byte-comparing outputs across runs. """ rows = list(items) if not rows: diff --git a/experiments/exp5_data_contacts_and_distances_v2_zephyr/generate.py b/experiments/exp5_data_contacts_and_distances_v2_zephyr/generate.py deleted file mode 100644 index e8f05f3..0000000 --- a/experiments/exp5_data_contacts_and_distances_v2_zephyr/generate.py +++ /dev/null @@ -1,339 +0,0 @@ -# Copyright The MarinFold Authors -# SPDX-License-Identifier: Apache-2.0 - -"""contacts-and-distances-v2 doc generation, vectorized. - -Faithful port of the exp34 v2 algorithm — same RNG stream order, same -statement build order, same float arithmetic. Two differences from -exp34's implementation: - -* Consumes the columnar :class:`parse.ParsedStructure` (numpy arrays - for cb_or_ca_xyz / plddt_per_residue + CSR-style atom table). All - per-atom Python loops in the hot path are gone. -* The CB-CB eligibility loop is one ``np.triu_indices`` + a vectorized - distance computation instead of an O(N²) Python nested loop. The - resulting ``contacts_long``/``contacts_medium``/``contacts_short`` - lists are in the same row-major order as the legacy nested loop, so - ``rng.sample`` sees identical inputs. - -Byte-identity vs exp34's reference is enforced by -``tests/test_byte_identity.py``. Any change here that perturbs the RNG -stream order — adding or removing a draw, swapping ``rng.choice`` for -``rng.randrange`` on a different-sized sequence, etc. — will break that -test and is forbidden without an explicit version bump. -""" - -from __future__ import annotations - -import hashlib -import math -import random -from dataclasses import dataclass - -import numpy as np - -from parse import ParsedStructure, _ATOM_NAMES_TUPLE -from vocab import NAME, THINK_TOKEN - - -CONTACT_TOKENS_PER_STATEMENT = 3 # -DISTANCE_TOKENS_PER_STATEMENT = 6 # - - -@dataclass(frozen=True) -class GenerationConfig: - """Hyperparameters for :func:`generate_one`. - - The non-``think_*`` fields reproduce v1's defaults exactly. - The ``think_*`` fields are the v2 think-token knobs, pinned to the - values from issue #34 (0.75 / Geom(0.13) / Uniform(-4, 4) / Geom(0.25)). - """ - - # ---- inherited from v1 ------------------------------------------------- - contact_cutoff_angstrom: float = 8.0 - long_range_sep: int = 24 - medium_range_sep: int = 12 - short_range_sep: int = 6 - contact_f_range: tuple[float, float] = (-0.1, 0.2) - contact_rank_mean: float = 2.0 - distance_rank_mean: float = 0.0 - rank_std: float = 1.0 - residue_plddt_min: float = 70.0 - plddt_bin_edges: tuple[float, ...] = (70.0, 75.0, 80.0, 85.0, 90.0, 95.0) - # ---- v2 think-token knobs ---------------------------------------------- - think_initial_prob: float = 0.75 - think_initial_geom_p: float = 0.13 - think_additional_count_range: tuple[float, float] = (-4.0, 4.0) - think_run_length_geom_p: float = 0.25 - - -def _plddt_bin_token(global_plddt: float, bin_edges: tuple[float, ...]) -> str: - for i, edge in enumerate(bin_edges): - if global_plddt < edge: - if i == 0: - return f"plddt_lt{int(edge)}" - return f"plddt_{int(bin_edges[i - 1])}_{int(edge)}" - return f"plddt_{int(bin_edges[-1])}_100" - - -def _distance_token(dist_angstrom: float) -> str: - bin_idx = int(math.ceil(dist_angstrom / 0.5)) - if bin_idx < 1: - bin_idx = 1 - if bin_idx > 64: - bin_idx = 64 - return f"d{bin_idx * 0.5:.1f}" - - -def _geometric(rng: random.Random, p: float) -> int: - """Sample Geometric(p), support {1, 2, ...}, via inverse-CDF.""" - if not (0.0 < p <= 1.0): - raise ValueError(f"p must be in (0, 1]; got {p!r}") - if p == 1.0: - return 1 - u = 1.0 - rng.random() - return int(math.ceil(math.log(u) / math.log(1.0 - p))) - - -def _sample_think_overhead(rng: random.Random, cfg: GenerationConfig - ) -> tuple[int, list[int]]: - if rng.random() < cfg.think_initial_prob: - k1 = _geometric(rng, cfg.think_initial_geom_p) - else: - k1 = 0 - lo, hi = cfg.think_additional_count_range - k2 = rng.uniform(lo, hi) - n_additional = max(int(k2), 0) - additional_lengths = [ - _geometric(rng, cfg.think_run_length_geom_p) for _ in range(n_additional) - ] - return k1, additional_lengths - - -@dataclass(frozen=True) -class _Statement: - rank: float - tokens: tuple[str, ...] - - -def generate_one( - structure: ParsedStructure, - *, - context_length: int, - cfg: GenerationConfig, -) -> str | None: - """Build one v2 document string for ``structure``, or ``None`` if no fit. - - Byte-identical to exp34's ``_generate_one`` on the same ``entry_id`` - + structure (enforced by ``tests/test_byte_identity.py``). - """ - num_residues = structure.num_residues - if num_residues < 2: - return None - - seed = int(hashlib.sha1(structure.entry_id.encode()).hexdigest()[:8], 16) - rng = random.Random(seed) - - # Draw #1: think-token overhead, MUST come before any contact/distance - # sampling so the RNG stream layout matches exp34 + future generators - # that share the v2 entry_id seeding contract. - k1, additional_run_lengths = _sample_think_overhead(rng, cfg) - total_think_tokens = k1 + sum(additional_run_lengths) - - # ---- vectorized CB-CB contact eligibility ------------------------------ - # Legacy did a per-residue Python loop building cb_positions, then a - # nested loop over all (i < j) computing euclidean. Here we keep the - # exact same row-major (i, j) emission order via np.triu_indices(k=1) - # and the same FP via np.sqrt((diffs**2).sum(axis=1)) on float64. - cb_xyz = structure.cb_or_ca_xyz - plddt_arr = structure.plddt_per_residue - valid_mask = (plddt_arr >= cfg.residue_plddt_min) & np.isfinite(cb_xyz[:, 0]) - valid_indices = np.flatnonzero(valid_mask) + 1 # 1-based residue indices - valid_xyz = cb_xyz[valid_mask] - - contacts_long: list[tuple[int, int]] = [] - contacts_medium: list[tuple[int, int]] = [] - contacts_short: list[tuple[int, int]] = [] - if valid_indices.size >= 2: - ii_arr, jj_arr = np.triu_indices(valid_indices.size, k=1) - diffs = valid_xyz[ii_arr] - valid_xyz[jj_arr] - dists = np.sqrt((diffs * diffs).sum(axis=1)) - i_arr = valid_indices[ii_arr] - j_arr = valid_indices[jj_arr] - sep_arr = j_arr - i_arr - elig = ( - (sep_arr > 1) - & (sep_arr >= cfg.short_range_sep) - & (dists <= cfg.contact_cutoff_angstrom) - ) - long_mask = elig & (sep_arr >= cfg.long_range_sep) - medium_mask = elig & (sep_arr >= cfg.medium_range_sep) & (sep_arr < cfg.long_range_sep) - short_mask = elig & (sep_arr < cfg.medium_range_sep) - contacts_long = list(zip(i_arr[long_mask].tolist(), j_arr[long_mask].tolist())) - contacts_medium = list(zip(i_arr[medium_mask].tolist(), j_arr[medium_mask].tolist())) - contacts_short = list(zip(i_arr[short_mask].tolist(), j_arr[short_mask].tolist())) - - # 5 overhead = + + + + - fixed_overhead = 5 + num_residues + total_think_tokens - available_tokens = context_length - fixed_overhead - if available_tokens <= 0: - return None - - # Draws #2-4: per-mode fractions. - f_long = max(0.0, rng.uniform(*cfg.contact_f_range)) - f_medium = max(0.0, rng.uniform(*cfg.contact_f_range)) - f_short = max(0.0, rng.uniform(*cfg.contact_f_range)) - - n_long = min(int(available_tokens * f_long) // CONTACT_TOKENS_PER_STATEMENT, len(contacts_long)) - n_medium = min(int(available_tokens * f_medium) // CONTACT_TOKENS_PER_STATEMENT, len(contacts_medium)) - n_short = min(int(available_tokens * f_short) // CONTACT_TOKENS_PER_STATEMENT, len(contacts_short)) - - contact_tokens_used = (n_long + n_medium + n_short) * CONTACT_TOKENS_PER_STATEMENT - n_distance = (available_tokens - contact_tokens_used) // DISTANCE_TOKENS_PER_STATEMENT - - # Draws #5-7: contact sampling. - selected_long = rng.sample(contacts_long, n_long) if n_long > 0 else [] - selected_medium = rng.sample(contacts_medium, n_medium) if n_medium > 0 else [] - selected_short = rng.sample(contacts_short, n_short) if n_short > 0 else [] - - # ---- distance statements (two-phase: serial RNG → vectorized math) ---- - # Phase 1 walks the RNG serially (the only legal way: byte-identity vs - # exp34 requires Mersenne-Twister draws in this exact order), but - # records only cheap integer indices into per-residue + per-atom plans. - # Phase 2 then does ALL xyz lookups, distance math, and bin tokenization - # vectorized over the plan via fancy indexing into the CSR atom table. - # Net win: ~12 scalar ops per statement (subtract/square/add/sqrt) - # collapse into one C-level numpy call across all statements. - atom_offsets = structure.atom_offsets - atom_xyz = structure.atom_xyz - atom_name_id = structure.atom_name_id - atoms_per_residue = np.diff(atom_offsets) - distance_indices = (np.flatnonzero(atoms_per_residue > 0) + 1).tolist() - - distance_statements: list[tuple[int, int, str, str, str]] = [] - if len(distance_indices) >= 2 and n_distance > 0: - # Phase 1: serial RNG → integer plan arrays. RNG advance is - # bit-for-bit unchanged vs the old per-statement loop (same - # rng.sample + rng.randrange call sequence, same retry policy). - # `rng.randrange(K)` invokes the same `_randbelow(K)` as - # `rng.choice(seq_of_len_K)` — sequence content doesn't matter for - # the MT state, only the length. - plan_i: list[int] = [] - plan_j: list[int] = [] - plan_atom_a: list[int] = [] # 0-based atom index within residue (i - 1) - plan_atom_b: list[int] = [] - for _ in range(n_distance): - a, b = rng.sample(distance_indices, 2) - i, j = (a, b) if a < b else (b, a) - if j - i <= 1: - ok = False - for _retry in range(10): - a, b = rng.sample(distance_indices, 2) - i, j = (a, b) if a < b else (b, a) - if j - i > 1: - ok = True - break - if not ok: - continue - k_i = int(atoms_per_residue[i - 1]) - k_j = int(atoms_per_residue[j - 1]) - plan_i.append(i) - plan_j.append(j) - plan_atom_a.append(rng.randrange(k_i)) - plan_atom_b.append(rng.randrange(k_j)) - - # Phase 2: vectorized lookup + distance + tokenize over the plan. - if plan_i: - i_arr = np.asarray(plan_i, dtype=np.int32) - j_arr = np.asarray(plan_j, dtype=np.int32) - flat_a = atom_offsets[i_arr - 1] + np.asarray(plan_atom_a, dtype=np.int32) - flat_b = atom_offsets[j_arr - 1] + np.asarray(plan_atom_b, dtype=np.int32) - diffs = atom_xyz[flat_a] - atom_xyz[flat_b] - d_arr = np.sqrt((diffs * diffs).sum(axis=1)) - # Vectorized _distance_token: ceil(d/0.5) clipped to [1, 64]. - # Identical mapping to the per-element function, just in numpy. - bin_arr = np.clip(np.ceil(d_arr / 0.5).astype(np.int64), 1, 64) - # Name lookup is one fancy-index into the uint8 CSR column + - # one tuple-index per element into the (~37-entry) vocab. The - # final ``distance_statements`` build is in Python because the - # downstream consumer is a list of mixed-type tuples — but the - # per-element body is now bare list ops, no math, no rng. - name_a_ids = atom_name_id[flat_a] - name_b_ids = atom_name_id[flat_b] - i_list = i_arr.tolist() - j_list = j_arr.tolist() - bin_list = bin_arr.tolist() - name_a_list = name_a_ids.tolist() - name_b_list = name_b_ids.tolist() - distance_statements = [ - (i_list[k], j_list[k], - _ATOM_NAMES_TUPLE[name_a_list[k]], - _ATOM_NAMES_TUPLE[name_b_list[k]], - f"d{bin_list[k] * 0.5:.1f}") - for k in range(len(plan_i)) - ] - - # ---- assemble statements + ranks -------------------------------------- - statements: list[_Statement] = [] - for i, j in selected_long: - statements.append(_Statement( - rank=rng.gauss(cfg.contact_rank_mean, cfg.rank_std), - tokens=("", f"", f""), - )) - for i, j in selected_medium: - statements.append(_Statement( - rank=rng.gauss(cfg.contact_rank_mean, cfg.rank_std), - tokens=("", f"", f""), - )) - for i, j in selected_short: - statements.append(_Statement( - rank=rng.gauss(cfg.contact_rank_mean, cfg.rank_std), - tokens=("", f"", f""), - )) - for i, j, ai, aj, dist_tok in distance_statements: - statements.append(_Statement( - rank=rng.gauss(cfg.distance_rank_mean, cfg.rank_std), - tokens=("", f"", f"", f"<{ai}>", f"<{aj}>", f"<{dist_tok}>"), - )) - statements.sort(key=lambda s: -s.rank) - - plddt_token = _plddt_bin_token(structure.global_plddt, cfg.plddt_bin_edges) - plddt_at_end = rng.random() < 0.5 - plddt_insert_idx = None if plddt_at_end else rng.randint(0, len(statements)) - - # Additional-run slot assignment — identical to exp34. When statements - # is empty, additional runs are dropped (no slot anchor). - think_at_slot: dict[int, int] = {} - if k1 > 0: - think_at_slot[0] = k1 - if statements and additional_run_lengths: - n_stmts = len(statements) - for length in additional_run_lengths: - slot = rng.randint(0, n_stmts - 1) - think_at_slot[slot] = think_at_slot.get(slot, 0) + length - - out: list[str] = [] - out.append(f"<{NAME}>") - out.append("") - for name in structure.sequence: - out.append(f"<{name}>") - out.append("") - for idx, stmt in enumerate(statements): - # Think tokens always come *before* any pLDDT in the same slot so - # the "immediately after " wording holds at slot 0. - n_think = think_at_slot.get(idx, 0) - if n_think: - out.extend([THINK_TOKEN] * n_think) - if plddt_insert_idx is not None and idx == plddt_insert_idx: - out.append(f"<{plddt_token}>") - out.extend(stmt.tokens) - if not statements and k1 > 0: - # No statements but the initial run still landed — emit it anyway - # so the document captures the sampled overhead. - out.extend([THINK_TOKEN] * k1) - if plddt_insert_idx is not None and plddt_insert_idx >= len(statements): - out.append(f"<{plddt_token}>") - if plddt_at_end: - out.append(f"<{plddt_token}>") - out.append("") - return " ".join(out) diff --git a/experiments/exp5_data_contacts_and_distances_v2_zephyr/parse.py b/experiments/exp5_data_contacts_and_distances_v2_zephyr/parse.py deleted file mode 100644 index 6d30e4c..0000000 --- a/experiments/exp5_data_contacts_and_distances_v2_zephyr/parse.py +++ /dev/null @@ -1,323 +0,0 @@ -# Copyright The MarinFold Authors -# SPDX-License-Identifier: Apache-2.0 - -"""Columnar gemmi-backed parser for contacts-and-distances-v2 generation. - -A from-scratch design — no backward compatibility with marinfold's -``ParsedStructure`` shape. Every per-residue / per-atom array lives at -the *structure* level as numpy arrays, so the doc-generation hot paths -in :mod:`generate` can vectorize (``np.triu_indices`` over the CB-CB -distance matrix, etc.) instead of walking gemmi proxy objects from -Python. - -The atom data is stored CSR-style — one flat ``atom_xyz`` array of -shape ``(T, 3)`` plus an ``atom_offsets`` array of length ``N + 1`` so -the atoms of residue ``i`` are ``atom_xyz[atom_offsets[i] : atom_offsets[i + 1]]``. -This is the layout that round-trips zero-copy to ``pyarrow.RecordBatch`` -if we ever want a precomputed parquet store. - -Determinism contract (versus exp34's reference parser): - -* ``plddt_per_residue[i]`` is the left-fold mean of every non-hydrogen - atom's ``b_iso`` for residue ``i`` (``-inf`` if no heavy atoms) — - byte-identical Python FP to the legacy ``_residue_plddt``. -* ``cb_or_ca_xyz[i]`` is the first CB seen in residue ``i``, falling - back to the *last* CA seen for non-GLY (matches the legacy - ``cb_or_ca_position`` scan order where ``fallback_ca`` is - overwritten); for GLY it's the *first* CA. ``NaN`` row when neither - atom is present. -* ``global_plddt`` is computed as ``sum(values_list) / N`` over Python - floats — *not* ``np.mean`` — so the FP reduction order matches - exp34's ``ParsedStructure.global_plddt`` property exactly. - -These three invariants are what keeps v2 docs byte-identical to the -exp34 reference (see ``tests/test_byte_identity.py``). -""" - -from __future__ import annotations - -import warnings -from dataclasses import dataclass -from typing import Self - -import fsspec -import numpy as np - -from vocab import AMINO_ACIDS, ATOM_NAMES - - -# -------------------------------------------------------------------------- -# Vocab lookups, hoisted once at module load -# -------------------------------------------------------------------------- - -_CANONICAL_20 = frozenset(AMINO_ACIDS) - -# ATOM_NAMES is small (~37 entries) and fixed. Encoding each atom as an -# index into this tuple lets us store atom_name_id as uint8 and recover -# the string via O(1) tuple indexing in generate.py. -_ATOM_NAMES_TUPLE: tuple[str, ...] = tuple(ATOM_NAMES) -_ATOM_NAME_TO_ID: dict[str, int] = {n: i for i, n in enumerate(_ATOM_NAMES_TUPLE)} - -_NAN3: tuple[float, float, float] = (float("nan"), float("nan"), float("nan")) - - -# -------------------------------------------------------------------------- -# ParsedStructure -# -------------------------------------------------------------------------- - - -@dataclass(frozen=True) -class ParsedStructure: - """One polymer chain in a vectorization-friendly columnar layout. - - See module docstring for the determinism contract that keeps generated - v2 docs byte-identical to the exp34 reference implementation. - """ - - entry_id: str # canonical id, e.g. "AF-A0A090SHW3-F1" (matches afdb-1.6M manifest) - source: str # URI or path, for provenance - - # Per-residue arrays — all length N. - sequence: tuple[str, ...] # 3-letter canonical AA names, "UNK" for non-canonical - plddt_per_residue: np.ndarray # float64[N] - cb_or_ca_xyz: np.ndarray # float64[N, 3], NaN row when neither CB nor CA present - - # Flat CSR-style atom table — atoms for residue i live at - # atom_xyz[atom_offsets[i] : atom_offsets[i+1]] (same slice for atom_name_id). - atom_offsets: np.ndarray # int32[N+1] - atom_name_id: np.ndarray # uint8[T] — index into ``parse._ATOM_NAMES_TUPLE`` - atom_xyz: np.ndarray # float64[T, 3] - - # Per-residue ``(name_ids, xyz)`` slices into the flat tables, materialized - # ONCE in ``from_gemmi``. Numpy slices are views (no copy), so this is - # cheap to build and zero-cost to keep alongside the flat arrays. Used by - # ``atoms_for(i)`` for downstream vectorized per-residue analytics. - per_residue_atoms: tuple[tuple[np.ndarray, np.ndarray], ...] - - # Precomputed once at parse time so generate.py doesn't have to choose - # between FP-fidelity (sum/N over Python floats) and vectorization. - global_plddt: float - - @classmethod - def from_gemmi(cls, structure, *, entry_id: str, source: str, require_single_chain: bool) -> Self: - """Single-pass extraction from a parsed gemmi ``Structure``.""" - structure.setup_entities() - if len(structure) == 0: - raise ValueError(f"{source}: no models in structure") - model = structure[0] - polymer_chains = [ch for ch in model if ch.get_polymer()] - if not polymer_chains: - raise ValueError(f"{source}: no polymer chain") - if require_single_chain and len(polymer_chains) != 1: - raise ValueError( - f"{source}: expected single polymer chain, found {len(polymer_chains)}." - ) - chain = polymer_chains[0] - polymer = chain.get_polymer() - - # Fast-path detection (AFDB-shape): one chain in the model AND every - # residue in that chain is a polymer residue (no waters / ligands). When - # both hold, ``model.all()`` yields exactly the polymer atoms in order - # — see the gemmi maintainer's advice in - # https://github.com/project-gemmi/gemmi/issues/314 — and we can avoid - # the nested-loop overhead. Otherwise fall back to polymer iteration so - # we don't pick up solvent/ligand atoms. - n_polymer = sum(1 for _ in polymer) - fast_path = len(model) == 1 and sum(1 for _ in chain) == n_polymer - - sequence: list[str] = [] - plddt_values: list[float] = [] - cb_or_ca_rows: list[tuple[float, float, float]] = [] - atom_name_id_list: list[int] = [] - atom_xyz_list: list[tuple[float, float, float]] = [] - atom_offsets: list[int] = [0] - - def _flush_residue(name: str, b_values: list[float], - cb: tuple[float, float, float] | None, - ca_first: tuple[float, float, float] | None, - ca_last: tuple[float, float, float] | None) -> None: - # FP-identical to exp34's _residue_plddt (left-fold sum over Python - # floats divided by count; -inf if no heavy atoms). - plddt = sum(b_values) / len(b_values) if b_values else float("-inf") - if name == "GLY": - chosen = ca_first - else: - chosen = cb if cb is not None else ca_last - sequence.append(name) - plddt_values.append(plddt) - cb_or_ca_rows.append(chosen if chosen is not None else _NAN3) - atom_offsets.append(len(atom_name_id_list)) - - if fast_path: - prev_res = None - name = "" - b_values: list[float] = [] - cb: tuple[float, float, float] | None = None - ca_first: tuple[float, float, float] | None = None - ca_last: tuple[float, float, float] | None = None - for cra in model.all(): - if cra.residue is not prev_res: - if prev_res is not None: - _flush_residue(name, b_values, cb, ca_first, ca_last) - prev_res = cra.residue - raw = cra.residue.name.strip() - name = raw if raw in _CANONICAL_20 else "UNK" - b_values = [] - cb = None - ca_first = None - ca_last = None - atom = cra.atom - if atom.is_hydrogen(): - continue - b_values.append(atom.b_iso) - atom_name = atom.name.strip() - name_id = _ATOM_NAME_TO_ID.get(atom_name) - if name_id is None: - continue - x, y, z = atom.pos.tolist() # one nanobind call vs three attr lookups - atom_name_id_list.append(name_id) - atom_xyz_list.append((x, y, z)) - if atom_name == "CB": - if cb is None: - cb = (x, y, z) - elif atom_name == "CA": - if ca_first is None: - ca_first = (x, y, z) - ca_last = (x, y, z) - if prev_res is not None: - _flush_residue(name, b_values, cb, ca_first, ca_last) - else: - for res in polymer: - raw = res.name.strip() - name = raw if raw in _CANONICAL_20 else "UNK" - b_values = [] - cb = None - ca_first = None - ca_last = None - for atom in res: - if atom.is_hydrogen(): - continue - b_values.append(atom.b_iso) - atom_name = atom.name.strip() - name_id = _ATOM_NAME_TO_ID.get(atom_name) - if name_id is None: - continue - x, y, z = atom.pos.tolist() - atom_name_id_list.append(name_id) - atom_xyz_list.append((x, y, z)) - if atom_name == "CB": - if cb is None: - cb = (x, y, z) - elif atom_name == "CA": - if ca_first is None: - ca_first = (x, y, z) - ca_last = (x, y, z) - _flush_residue(name, b_values, cb, ca_first, ca_last) - - if not sequence: - raise ValueError(f"{source}: no residues parsed") - - # global_plddt: sum/N over the Python float list (NOT np.mean) so the - # FP reduction order matches exp34's ParsedStructure.global_plddt - # property — keeps RNG-seeded doc content byte-identical. - global_plddt = sum(plddt_values) / len(plddt_values) - - atom_offsets_arr = np.asarray(atom_offsets, dtype=np.int32) - atom_name_id_arr = ( - np.asarray(atom_name_id_list, dtype=np.uint8) - if atom_name_id_list else np.empty(0, dtype=np.uint8) - ) - atom_xyz_arr = ( - np.asarray(atom_xyz_list, dtype=np.float64).reshape(-1, 3) - if atom_xyz_list else np.empty((0, 3), dtype=np.float64) - ) - - # Materialize per-residue views once. Numpy slices share memory with - # the flat arrays — no copies, just (PyObject + view header) per - # residue. For ~250 residues this is ~25 KB of overhead total. - per_residue_atoms = tuple( - (atom_name_id_arr[atom_offsets[i]:atom_offsets[i + 1]], - atom_xyz_arr[atom_offsets[i]:atom_offsets[i + 1]]) - for i in range(len(sequence)) - ) - - return cls( - entry_id=entry_id, - source=source, - sequence=tuple(sequence), - plddt_per_residue=np.asarray(plddt_values, dtype=np.float64), - cb_or_ca_xyz=np.asarray(cb_or_ca_rows, dtype=np.float64), - atom_offsets=atom_offsets_arr, - atom_name_id=atom_name_id_arr, - atom_xyz=atom_xyz_arr, - per_residue_atoms=per_residue_atoms, - global_plddt=global_plddt, - ) - - @property - def num_residues(self) -> int: - return len(self.sequence) - - def atoms_for(self, i: int) -> tuple[np.ndarray, np.ndarray]: - """Atoms of residue ``i`` (0-based): ``(name_ids[K], xyz[K, 3])``. - - O(1) tuple-index into the precomputed ``per_residue_atoms``. - """ - return self.per_residue_atoms[i] - - -# -------------------------------------------------------------------------- -# Public parsers -# -------------------------------------------------------------------------- - - -def parse_cif_content(data, entry_id: str, *, source: str | None = None, - require_single_chain: bool = True) -> ParsedStructure: - """Parse an mmCIF document held in memory (``str`` or ``bytes``).""" - import gemmi - - structure = gemmi.read_structure_string(data, format=gemmi.CoorFormat.Mmcif) - return ParsedStructure.from_gemmi( - structure, - entry_id=entry_id, - source=source or f"", - require_single_chain=require_single_chain, - ) - - -def parse_cif_from_uri(uri: str, entry_id: str, *, - require_single_chain: bool = True) -> ParsedStructure: - """Fetch ``uri`` (any fsspec URL) and parse the bytes as an mmCIF document. - - Transparent gzip via ``compression='infer'``. The ``entry_id`` arrives - from the manifest (e.g. afdb-1.6M's ``entry_id`` column) and is the - canonical id used to seed generation — it does NOT need to match the - URI basename. - """ - with fsspec.open(uri, "rb", compression="infer") as f: - data = f.read() - return parse_cif_content(data, entry_id=entry_id, source=uri, - require_single_chain=require_single_chain) - - -def try_parse_cif_content(data, entry_id: str, *, source: str | None = None - ) -> ParsedStructure | None: - """``.map``-friendly :func:`parse_cif_content`: ``None`` on failure.""" - try: - return parse_cif_content(data, entry_id=entry_id, source=source) - except ValueError as exc: - warnings.warn(f"skipping {source or entry_id}: {exc}", stacklevel=2) - return None - - -def try_parse_cif_from_uri(uri: str, entry_id: str) -> ParsedStructure | None: - """``.map``-friendly :func:`parse_cif_from_uri`: ``None`` on failure. - - Catches ``OSError`` (gs:// 404 / transient HTTP / DNS) in addition to - ``ValueError`` so a single missing object can't kill a Zephyr worker. - """ - try: - return parse_cif_from_uri(uri, entry_id=entry_id) - except (ValueError, OSError) as exc: - warnings.warn(f"skipping {uri}: {exc}", stacklevel=2) - return None diff --git a/experiments/exp5_data_contacts_and_distances_v2_zephyr/pyproject.toml b/experiments/exp5_data_contacts_and_distances_v2_zephyr/pyproject.toml index 105dc3a..bc5bec2 100644 --- a/experiments/exp5_data_contacts_and_distances_v2_zephyr/pyproject.toml +++ b/experiments/exp5_data_contacts_and_distances_v2_zephyr/pyproject.toml @@ -1,35 +1,30 @@ [project] name = "exp5-data-contacts-and-distances-v2-zephyr" version = "0.1.0" -description = "Generate contacts-and-distances-v2 training documents at scale on the marin Iris cluster via Zephyr." +description = "Generate contacts-and-distances-v2 training documents at scale on the marin Iris cluster via Zephyr. Wraps exp34's local impl." requires-python = ">=3.11,<3.13" dependencies = [ - "marinfold", # vocab (AMINO_ACIDS/ATOM_NAMES) + build_tokenizer for the v2 tokenizer - "gemmi>=0.7", # mmCIF parsing — read_structure_string requires >=0.7.0 - "numpy", # columnar in-memory structure representation - "pyarrow", # parquet read/write + Zephyr-native row dicts - "marin-zephyr", # distributed generation on Iris - "fsspec", # uniform local + cloud file IO - "gcsfs", # gs:// for the AFDB public bucket + marin output buckets - "huggingface_hub", # hf:// for the timodonnell/afdb-1.6M manifest + "marinfold", # build_tokenizer for the `tokenizer` subcommand + "gemmi>=0.6", # mmCIF parsing — version matches exp34's pin + "numpy", # pulled in by exp34's parse + generate + "pyarrow", # parquet read/write + Zephyr-native row dicts + "marin-zephyr", # distributed generation on Iris + "fsspec", # uniform local + cloud file IO for per-row cif fetch + "gcsfs", # gs:// for the AFDB public bucket + marin output buckets + "huggingface_hub", # hf:// for the timodonnell/afdb-1.6M manifest ] -# NOTE on obstore: we tried it (object_store-backed, ~9x throughput vs gcsfs -# for parallel small-object GETs). It works for our marin-us-central1 output -# bucket but fails on the AFDB public bucket with -# "GenericError: Content-Length Header missing from response". Root cause is -# upstream: object_store's HTTP handler strictly requires Content-Length, but -# AFDB cifs are stored with Content-Encoding: gzip and GCS transparently -# gunzips them on the wire, omitting Content-Length. The documented fix -# (Accept-Encoding: gzip) can only be set via client_options.default_headers -# in obstore today, and those headers leak into the OAuth2 token fetch and -# break it. Diagnostic + variants tried are documented in the README. +# Note on the exp34 dep: exp5's ``cli.py`` imports exp34's ``parse`` / +# ``generate`` / ``vocab`` via a ``sys.path`` shim at module load — exp34 is +# ``package = false`` in its own pyproject so we can't path-dep it as an +# installable package. The shim makes its modules visible by their flat +# names. Long-term, v2 should graduate to +# ``marinfold/marinfold/document_structures/contacts_and_distances_v2/`` +# (per Tim's #38 review); then the shim disappears and this becomes a +# regular ``from marinfold... import`` line. [project.optional-dependencies] test = [ "pytest>=8", - # exp34's generate is the byte-identity oracle for v2 doc content. We import - # it through a sys.path shim in tests/ (it's an experiment dir, not a package), - # but its own deps (gemmi, marinfold, numpy) are already covered above. ] [dependency-groups] diff --git a/experiments/exp5_data_contacts_and_distances_v2_zephyr/tests/conftest.py b/experiments/exp5_data_contacts_and_distances_v2_zephyr/tests/conftest.py index 60ece5b..9b21bdf 100644 --- a/experiments/exp5_data_contacts_and_distances_v2_zephyr/tests/conftest.py +++ b/experiments/exp5_data_contacts_and_distances_v2_zephyr/tests/conftest.py @@ -3,18 +3,14 @@ """Test fixtures shared across exp5's test suite. -Two path adjustments: - -* Add the experiment dir itself to ``sys.path`` so ``import parse`` / - ``import generate`` / ``import vocab`` work — experiments aren't - importable packages. -* Add exp34's directory so the byte-identity test can import its - parse + generate as the reference oracle. +Adds the experiment dir to ``sys.path`` so ``import cli`` works — +experiments aren't importable packages. We don't add exp34 here: ``cli.py`` +itself installs the exp34 path shim at module load, so importing ``cli`` +from a test transitively pulls in exp34's ``parse`` / ``generate`` / +``vocab`` correctly. Provides a ``synthetic_cif`` fixture: a realistic-ish polymer mmCIF -text (~50 residues) built by converting a synthetic PDB via gemmi — -identical generation logic to exp34's ``_make_long_pdb`` test helper, -so cross-experiment byte-identity checks are reproducible. +text (~50 residues) built by converting a synthetic PDB via gemmi. """ from __future__ import annotations @@ -25,15 +21,8 @@ import pytest _EXP_DIR = Path(__file__).resolve().parents[1] -_EXP34_DIR = _EXP_DIR.parent / "exp34_document_structures_contacts_and_distances_v2" - -# Order matters: exp5's own modules first so ``import parse`` resolves to -# exp5's parse, not exp34's. The exp34 oracle imports under the -# ``exp34_*`` aliases in test_byte_identity.py via a sys.path swap. -for p in (_EXP_DIR, _EXP34_DIR): - p_str = str(p) - if p_str not in sys.path: - sys.path.insert(0, p_str) +if str(_EXP_DIR) not in sys.path: + sys.path.insert(0, str(_EXP_DIR)) def _make_long_pdb(num_residues: int) -> str: diff --git a/experiments/exp5_data_contacts_and_distances_v2_zephyr/tests/test_byte_identity.py b/experiments/exp5_data_contacts_and_distances_v2_zephyr/tests/test_byte_identity.py deleted file mode 100644 index 65c4471..0000000 --- a/experiments/exp5_data_contacts_and_distances_v2_zephyr/tests/test_byte_identity.py +++ /dev/null @@ -1,104 +0,0 @@ -# Copyright The MarinFold Authors -# SPDX-License-Identifier: Apache-2.0 - -"""Byte-identity test: exp5 generates the same docs as exp34's reference. - -exp5 re-implements parse + generate for performance, with a fresh -columnar ParsedStructure that's not interchangeable with exp34's -tuple-based one. The contract that keeps the docs aligned is that the -RNG stream order is unchanged and the FP arithmetic is reproduced -exactly (see ``parse.py`` and ``generate.py`` docstrings for the -specifics: left-fold pLDDT sum, np.sqrt over float64 = math.sqrt on -the same floats, np.triu_indices preserves the legacy row-major (i, j) -iteration order, etc.). - -This test pins that contract: a synthetic structure through several -distinct entry_ids must produce the same doc bytes whether parsed + -generated by exp34 or by exp5. Any drift will surface here. - -The exp34 reference is imported by temporarily swapping -``sys.path`` — it lives in a sibling experiment dir, not as a -package. -""" - -from __future__ import annotations - -import dataclasses -import hashlib -import importlib -import sys -from pathlib import Path - -import pytest - -import generate as exp5_generate -import parse as exp5_parse - -_EXP34_DIR = Path(__file__).resolve().parents[2] / "exp34_document_structures_contacts_and_distances_v2" - - -def _import_exp34(): - """Import exp34's parse + generate under aliases, isolated from exp5's modules.""" - # Save exp5's modules under their original names, swap exp34 in, import, - # then restore. This way both versions coexist for the duration of the test. - saved = {name: sys.modules[name] for name in ("parse", "generate", "vocab") - if name in sys.modules} - # exp34 is already on sys.path (added in conftest), but its imports - # ("from parse import …" in generate.py) need to resolve to exp34's - # parse, not exp5's. Force re-import in the right path order. - for name in ("parse", "generate", "vocab"): - sys.modules.pop(name, None) - sys.path.insert(0, str(_EXP34_DIR)) - try: - exp34_parse = importlib.import_module("parse") - exp34_generate = importlib.import_module("generate") - return exp34_parse, exp34_generate - finally: - sys.path.remove(str(_EXP34_DIR)) - # Restore exp5's modules so subsequent tests see the right ones. - for name in ("parse", "generate", "vocab"): - sys.modules.pop(name, None) - for name, mod in saved.items(): - sys.modules[name] = mod - - -@pytest.mark.parametrize("entry_id", [ - "AF-A0A090SHW3-F1", - "AF-H0GCG4-F1", - "AF-K7EXM5-F1", - "AF-XYZ123-F1", - "AF-TESTSEED-F2", -]) -def test_doc_byte_identical_to_exp34(synthetic_cif, entry_id, tmp_path): - """Same cif + same entry_id → exp34 and exp5 produce the same doc string. - - The structure is fixed; ``entry_id`` is what seeds the RNG, so varying - it across the parametrize sweep exercises a few different statement - orderings + think-overhead samples + pLDDT placements. - - exp34's ``parse_structure`` reads from a ``Path`` and derives entry_id - from the file stem; we write the synthetic cif to a tmpfile, parse it, - then ``dataclasses.replace`` to swap in our parametrized entry_id — - keeps the cif content identical across both sides so any diff is - parse/generate logic, not source data. - """ - cif_path = tmp_path / f"{entry_id}.cif" - cif_path.write_text(synthetic_cif) - - cfg5 = exp5_generate.GenerationConfig() - ps5 = exp5_parse.parse_cif_content(synthetic_cif, entry_id=entry_id) - doc5 = exp5_generate.generate_one(ps5, context_length=8192, cfg=cfg5) - - exp34_parse, exp34_generate = _import_exp34() - cfg34 = exp34_generate.GenerationConfig() - ps34_raw = exp34_parse.parse_structure(cif_path) - ps34 = dataclasses.replace(ps34_raw, entry_id=entry_id) - doc34 = exp34_generate._generate_one(ps34, context_length=8192, cfg=cfg34) - - assert doc5 is not None, "exp5 returned None" - assert doc34 is not None, "exp34 returned None" - assert doc5 == doc34, ( - f"docs diverge for entry_id={entry_id}\n" - f" exp5 sha1: {hashlib.sha1(doc5.encode()).hexdigest()[:16]} len={len(doc5)}\n" - f" exp34 sha1: {hashlib.sha1(doc34.encode()).hexdigest()[:16]} len={len(doc34)}\n" - ) diff --git a/experiments/exp5_data_contacts_and_distances_v2_zephyr/tests/test_parse.py b/experiments/exp5_data_contacts_and_distances_v2_zephyr/tests/test_parse.py deleted file mode 100644 index 92f05e9..0000000 --- a/experiments/exp5_data_contacts_and_distances_v2_zephyr/tests/test_parse.py +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright The MarinFold Authors -# SPDX-License-Identifier: Apache-2.0 - -"""Sanity tests for exp5's columnar ParsedStructure. - -Not exhaustive — the byte-identity test against exp34 is what catches -correctness regressions on the generation side. These tests just pin -the *shape* of the new representation so a future change to the -columnar layout doesn't silently break downstream code. -""" - -from __future__ import annotations - -import math - -import numpy as np - -import parse - - -def test_parsed_structure_shapes(small_cif): - """Per-residue arrays line up with sequence length; atom CSR offsets are monotonic.""" - ps = parse.parse_cif_content(small_cif, entry_id="AF-TEST-F1") - n = ps.num_residues - assert n == 5 - assert ps.sequence == ("ALA",) * 5 - assert ps.plddt_per_residue.shape == (n,) - assert ps.plddt_per_residue.dtype == np.float64 - assert ps.cb_or_ca_xyz.shape == (n, 3) - assert ps.cb_or_ca_xyz.dtype == np.float64 - assert ps.atom_offsets.shape == (n + 1,) - assert ps.atom_offsets[0] == 0 - assert np.all(np.diff(ps.atom_offsets) >= 0) # monotonic non-decreasing - assert ps.atom_name_id.shape == (int(ps.atom_offsets[-1]),) - assert ps.atom_xyz.shape == (int(ps.atom_offsets[-1]), 3) - - -def test_atoms_for_recovers_per_residue_data(small_cif): - """``atoms_for(i)`` returns the right per-residue atom slice.""" - ps = parse.parse_cif_content(small_cif, entry_id="AF-TEST-F1") - # The synthetic fixture emits N/CA/C/O/CB per residue (5 atoms, all in vocab). - for i in range(ps.num_residues): - name_ids, xyz = ps.atoms_for(i) - assert len(name_ids) == 5, f"residue {i}: expected 5 in-vocab atoms" - assert xyz.shape == (5, 3) - # Convert ids back to names — the order matches the PDB record order - # (N, CA, C, O, CB), which the fast-path single-pass extractor - # preserves. - names = [parse._ATOM_NAMES_TUPLE[int(k)] for k in name_ids] - assert names == ["N", "CA", "C", "O", "CB"] - - -def test_cb_or_ca_falls_back_to_ca_for_gly(small_cif): - """For non-GLY residues with CB present, cb_or_ca_xyz is the CB position.""" - ps = parse.parse_cif_content(small_cif, entry_id="AF-TEST-F1") - # Synthetic fixture is poly-ALA so cb_or_ca_xyz row should be the CB - # coordinate (4.5, 1.5, z + 0.5) for residue i (1-based in PDB, 0-based here). - for i in range(ps.num_residues): - x, y, z = ps.cb_or_ca_xyz[i].tolist() - expected_z = (i + 1) * 3.0 + 0.5 - assert math.isclose(x, 4.5) - assert math.isclose(y, 1.5) - assert math.isclose(z, expected_z) - - -def test_plddt_per_residue_equals_mean_b_factor(small_cif): - """All heavy atoms have b_iso=90 in the fixture → plddt = 90.0 per residue.""" - ps = parse.parse_cif_content(small_cif, entry_id="AF-TEST-F1") - assert np.allclose(ps.plddt_per_residue, 90.0) - assert math.isclose(ps.global_plddt, 90.0) - - -def test_try_parse_returns_none_on_bad_input(): - """``try_parse_cif_content`` warns and returns None on garbage.""" - import warnings - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - out = parse.try_parse_cif_content("not a cif", entry_id="X") - assert out is None diff --git a/experiments/exp5_data_contacts_and_distances_v2_zephyr/vocab.py b/experiments/exp5_data_contacts_and_distances_v2_zephyr/vocab.py deleted file mode 100644 index 898c694..0000000 --- a/experiments/exp5_data_contacts_and_distances_v2_zephyr/vocab.py +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright The MarinFold Authors -# SPDX-License-Identifier: Apache-2.0 - -"""Canonical vocabulary for the contacts-and-distances-v2 format. - -Identical to exp34's vocab.py — v2 reuses every v1 token in its existing -order, then appends two new tokens (```` and -````). Append-only is load-bearing: it keeps every v1 token ID -stable, so a checkpoint pretrained on v1 can be warm-started on v2 simply -by growing the embedding table by 2 rows. - -The v1 vocab is imported directly from the marinfold kind library, so v1 -and v2 can never silently disagree on the position of a shared token. -""" - -from marinfold.document_structures.contacts_and_distances_v1.vocab import ( - AMINO_ACIDS, - ATOM_NAMES, - CONTACT_TYPES, - CONTROL_TOKENS as V1_CONTROL_TOKENS, - DISTANCE_BINS, - DISTANCE_MARKER, - MAX_POSITION, - PLDDT_BINS, - POSITION_TOKENS, - UNK_TOKEN, -) -from marinfold.document_structures.contacts_and_distances_v1.vocab import ( - all_domain_tokens as v1_all_domain_tokens, -) - - -NAME = "contacts-and-distances-v2" -CONTEXT_LENGTH = 8192 - -THINK_TOKEN = "" - -V2_NEW_TOKENS = [ - f"<{NAME}>", - THINK_TOKEN, -] - - -def all_domain_tokens() -> list[str]: - """v2 domain vocabulary = v1 domain vocab ++ [````, ````].""" - out = list(v1_all_domain_tokens()) - out.extend(V2_NEW_TOKENS) - return out - - -__all__ = [ - "AMINO_ACIDS", - "ATOM_NAMES", - "CONTACT_TYPES", - "CONTEXT_LENGTH", - "DISTANCE_BINS", - "DISTANCE_MARKER", - "MAX_POSITION", - "NAME", - "PLDDT_BINS", - "POSITION_TOKENS", - "THINK_TOKEN", - "UNK_TOKEN", - "V1_CONTROL_TOKENS", - "V2_NEW_TOKENS", - "all_domain_tokens", - "v1_all_domain_tokens", -]