diff --git a/.gitignore b/.gitignore index ec5563a..a3fce6f 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,11 @@ logs/ wandb/ *.pt *.onnx +*.pkl +*.pickle +*.npz +src/mjlab_playground/asset_zoo/motions/retargeted/* +!src/mjlab_playground/asset_zoo/motions/retargeted/README.md scripts/ diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..caa9175 --- /dev/null +++ b/NOTICE @@ -0,0 +1,19 @@ +mjlab_playground Booster locomotion extensions +Copyright 2026 66Leslie and contributors + +This product includes software developed by the mjlab_playground contributors: +https://github.com/mujocolab/mjlab_playground + +The Booster T1 velocity task contains adapted and modified work from +KaydenKnapik/BoosterT1mjlab: +https://github.com/KaydenKnapik/BoosterT1mjlab + +Booster K1 asset and configuration work references NishB17/MJLabModified: +https://github.com/NishB17/MJLabModified + +Parts of the competition-compatible Booster K1 MJCF lineage reference +RCSSServerMJ. Its MIT license is reproduced in +third_party_licenses/RCSSServerMJ-MIT.txt. + +Motion datasets and locally generated retargeted files are not included in, and +are not relicensed by, this distribution. diff --git a/README.md b/README.md index 3e23676..bbe5b2c 100644 --- a/README.md +++ b/README.md @@ -1,45 +1,113 @@ -# mjlab playground +# mjlab playground: Booster locomotion tasks + +This fork extends [mujocolab/mjlab_playground](https://github.com/mujocolab/mjlab_playground) with Booster T1/K1 locomotion tasks while keeping the upstream task-registration and configuration style. + +## Demo + +### Booster T1 velocity tracking + +![Booster T1 velocity tracking](docs/media/t1_velocity_tracking.gif) + +The dark-blue arrow is the commanded linear velocity and the cyan arrow is the +measured linear velocity. Dark/light green show commanded/measured yaw motion. +This five-second clip was recorded with MJLab's built-in `play --video` support +using the compatible `2026-04-15_10-04-41/model_10100.pt` checkpoint. The later +`2026-04-27_10-05-04/model_13900.pt` run has a known low-response command region +and is intentionally not used for this demo. + +## Repository layout + +```text +src/mjlab_playground/ +├── amp/ # Shared AMP algorithm, runner, observations, loaders +├── asset_zoo/robots/ # Robot-specific MJCF and constants +│ ├── booster_t1/ +│ └── booster_k1/ +└── tasks/ # Environment/task implementations + ├── common/ # Small helpers shared by task families + ├── getup/ + │ └── config/{go1,k1,t1}/ + ├── velocity/ + │ └── config/t1/ + ├── tracking/ + │ └── config/t1/ + └── target_location_amp/ + └── config/{k1,t1}/ +``` -A collection of tasks built with [mjlab](https://github.com/mujocolab/mjlab), starting with ports from [MuJoCo Playground](https://playground.mujoco.org/). +Task-independent AMP code lives in `amp/`; target-location commands, rewards, termination rules and robot-specific configuration stay in `tasks/target_location_amp/`. This avoids coupling the AMP implementation to a single task and leaves a clear place for future robots. ## Tasks -| Task ID | Robot | Description | Preview | -|---------|-------|-------------|---------| -| **Getup** | | | | -| `Mjlab-Getup-Flat-Unitree-Go1` | Unitree Go1 | Fall recovery on flat terrain | | -| `Mjlab-Getup-Flat-Booster-T1` | Booster T1 | Fall recovery on flat terrain | | - -## Getting Started +| Task ID | Robot | Description | +|---|---|---| +| `Mjlab-Getup-Flat-Unitree-Go1` | Unitree Go1 | Upstream flat-ground get-up | +| `Mjlab-Getup-Flat-Booster-T1` | Booster T1 | Upstream flat-ground get-up | +| `Mjlab-Getup-Flat-Booster-K1` | Booster K1 | Flat-ground get-up | +| `Mjlab-Velocity-Flat-Booster-T1` | Booster T1 | Flat-ground velocity tracking | +| `Mjlab-Tracking-Flat-Booster-T1` | Booster T1 | Reference-motion tracking | +| `Mjlab-TargetLocationAmp-Flat-Booster-K1` | Booster K1 | Target-location locomotion with AMP | +| `Mjlab-TargetLocationAmp-Flat-Booster-T1` | Booster T1 | Target-location locomotion with AMP | + +There is one public Booster velocity task: `Mjlab-Velocity-Flat-Booster-T1`. +Rough-terrain, raw-observation, and yaw-only builders remain implementation +details rather than separately advertised environments. There is currently no +K1 tracking task in this fork. The two additional T1 target-location task IDs +ending in `CompetitionFoot` and `CompetitionCollision` are deployment-oriented +contact/collision variants. + +## Getting started ```bash -git clone https://github.com/mujocolab/mjlab_playground.git && cd mjlab_playground +git clone https://github.com/66Leslie/mjlab_playground.git +cd mjlab_playground uv sync ``` -Train a task: +List registered tasks: + +```bash +uv run list-envs +``` + +Train and play a task: ```bash -uv run train --num_envs 4096 +uv run train Mjlab-Velocity-Flat-Booster-T1 --env.scene.num-envs 4096 +uv run play Mjlab-Velocity-Flat-Booster-T1 ``` -Play back a trained policy: +Record a short clip from a local checkpoint: ```bash -uv run play +uv run play Mjlab-Velocity-Flat-Booster-T1 \ + --checkpoint-file /path/to/model_10100.pt \ + --num-envs 1 --video True --video-length 250 ``` -### Getup training +The tracking and AMP tasks require separately prepared motion data. See [Motion data](docs/motion_data.md) before running them. + +## Motion-data policy + +Retargeted files are generated artifacts, but their redistribution rights still depend on the original motion dataset. Consequently, this repository does **not** include LAFAN1-, AMASS-, or BMLrub-derived `.npz`, `.pkl`, or `.pickle` files. Retarget them locally from data you are licensed to use, then point the task to the local output. The expected paths, environment variables and pickle schema are documented in [docs/motion_data.md](docs/motion_data.md). + +## Sources and attribution + +- Base repository: [mujocolab/mjlab_playground](https://github.com/mujocolab/mjlab_playground), Apache-2.0. +- The T1 velocity task is adapted from [KaydenKnapik/BoosterT1mjlab](https://github.com/KaydenKnapik/BoosterT1mjlab), Apache-2.0, and has been substantially modified for this repository layout and current MJLab APIs. +- K1 MJLab asset/configuration work references [NishB17/MJLabModified](https://github.com/NishB17/MJLabModified), Apache-2.0. The competition-compatible K1 MJCF lineage also references [RCSSServerMJ](https://github.com/YiranWang2004/RCSSServerMJ), MIT. +- The shared AMP implementation was reorganized for this fork. [ccrpRepo/AMP_mjlab](https://github.com/ccrpRepo/AMP_mjlab) was consulted for high-level directory organization only; no source code or motion files were copied from it because the repository does not currently publish a root license. +- Motion retargeting is compatible with [YanjieZe/GMR](https://github.com/YanjieZe/GMR), MIT. GMR's software license does not replace the license of the source motion dataset. + +See [NOTICE](NOTICE) and [THIRD_PARTY.md](THIRD_PARTY.md) for details. -On a single NVIDIA 5090, the Go1 getup task converges in ~2 minutes and T1 in ~8 minutes, but we continue training with a curriculum that progressively tightens action rate, joint velocity, and power penalties to produce smoother, safer policies. +## Checkpoints -

- -

+Checkpoint files are intentionally ignored by Git. Publish reviewed weights as versioned GitHub Release assets instead of committing them to repository history. Each released checkpoint should identify the task ID, repository commit, complete training configuration, motion-data provenance, training command, observation/action contract and weight license. See [docs/checkpoints.md](docs/checkpoints.md). ## Citation -If you use this repository in your research, consider citing mjlab: +If you use this repository in research, consider citing MJLab: ```bibtex @misc{zakka2026mjlablightweightframeworkgpuaccelerated, @@ -55,4 +123,4 @@ If you use this repository in your research, consider citing mjlab: ## License -This repository is released under an [Apache-2.0 License](LICENSE). +Code in this repository is released under the [Apache-2.0 License](LICENSE), except where a bundled third-party component is identified with its own license. Motion datasets and locally generated retargeted files are not covered by this repository's Apache-2.0 license. diff --git a/THIRD_PARTY.md b/THIRD_PARTY.md new file mode 100644 index 0000000..98c2c4e --- /dev/null +++ b/THIRD_PARTY.md @@ -0,0 +1,12 @@ +# Third-party components + +| Component | Use in this repository | License | +|---|---|---| +| [mujocolab/mjlab_playground](https://github.com/mujocolab/mjlab_playground) | Base repository, get-up tasks, package/CLI style | Apache-2.0 | +| [KaydenKnapik/BoosterT1mjlab](https://github.com/KaydenKnapik/BoosterT1mjlab) | Starting point for the T1 velocity task; adapted and modified | Apache-2.0 | +| [NishB17/MJLabModified](https://github.com/NishB17/MJLabModified) | Reference/source lineage for Booster K1 MJLab assets and configuration | Apache-2.0 | +| [RCSSServerMJ](https://github.com/YiranWang2004/RCSSServerMJ) | Competition-compatible Booster K1 MJCF lineage | MIT; bundled text in `third_party_licenses/RCSSServerMJ-MIT.txt` | +| [YanjieZe/GMR](https://github.com/YanjieZe/GMR) | Recommended local motion-retargeting tool; not vendored | MIT | +| [ccrpRepo/AMP_mjlab](https://github.com/ccrpRepo/AMP_mjlab) | Directory organization consulted only; no code or data copied | No root license found as of 2026-08-28 | + +The licenses above apply to software and assets supplied by those projects. They do not grant rights to separately downloaded motion datasets. See [docs/motion_data.md](docs/motion_data.md). diff --git a/docs/checkpoints.md b/docs/checkpoints.md new file mode 100644 index 0000000..0c72eab --- /dev/null +++ b/docs/checkpoints.md @@ -0,0 +1,40 @@ +# Checkpoint releases + +Model checkpoints may be released separately from the source repository after a provenance and reproducibility review. The `.gitignore` excludes `*.pt` and `*.onnx`, so accidental weight commits—and another large-file push failure—are less likely. + +## Release layout + +Use a versioned GitHub Release rather than storing binary weights in Git history: + +```text +-v/ +├── model.pt +├── policy.onnx # optional deployment export +├── env_cfg.yaml +├── agent_cfg.yaml +├── model_card.md +└── SHA256SUMS +``` + +The release tag should point at the exact source commit used for training. + +## Required model-card fields + +Every checkpoint release should state: + +1. Task ID and robot model/version. +2. Source commit and clean/dirty training-tree state. +3. Exact `uv run train ...` command and random seed. +4. Environment and agent configuration, including any command curriculum. +5. Observation names/order, action names/order, control frequency and action scaling. +6. Motion data: dataset, clip IDs, license URLs, retargeting tool/commit and whether the data itself is redistributed. +7. Training duration, hardware and headline evaluation metrics. +8. Known limitations and the tested deployment/runtime contract. +9. License applied to the checkpoint and any attribution requirements. +10. SHA-256 hashes for every binary artifact. + +## Motion-trained weights + +A checkpoint does not contain the original `.npz` or `.pkl` file, but it is still an output of a motion-trained pipeline. Before releasing one, review the source dataset's terms for restrictions on trained models, derived outputs, commercial use and attribution. If the terms are unclear, release the source/configuration first and keep the checkpoint private until permission is confirmed. + +Do not claim that the repository's Apache-2.0 license automatically covers a checkpoint trained from third-party data. State the checkpoint license explicitly in its release notes. diff --git a/docs/media/t1_velocity_tracking.gif b/docs/media/t1_velocity_tracking.gif new file mode 100644 index 0000000..421cf98 Binary files /dev/null and b/docs/media/t1_velocity_tracking.gif differ diff --git a/docs/motion_data.md b/docs/motion_data.md new file mode 100644 index 0000000..5f71ed1 --- /dev/null +++ b/docs/motion_data.md @@ -0,0 +1,91 @@ +# Motion data + +Motion data is deliberately kept outside Git. Both `.npz` tracking files and GMR-style `.pkl`/`.pickle` AMP files are ignored so a local licensed dataset cannot be committed accidentally. + +## Why retargeted files are not committed + +Retargeting changes a motion's robot representation; it does not automatically erase the source dataset's copyright or license conditions. + +- [LAFAN1](https://github.com/ubisoft/ubisoft-laforge-animation-dataset/blob/master/LICENSE) is distributed under CC BY-NC-ND 4.0. Retargeting is reasonably treated as creating adapted material, which that license does not permit users to share. +- [AMASS](https://amass.is.tue.mpg.de/license.html) access is subject to its own terms, and AMASS aggregates datasets with additional source-specific conditions. BMLrub-derived clips should therefore not be published here without explicit redistribution permission. +- [GMR](https://github.com/YanjieZe/GMR) is MIT-licensed software, but its license covers the retargeting code—not the motion inputs or the resulting adapted dataset. + +This is a conservative repository policy, not legal advice. A motion file may be published only when its complete provenance and redistribution rights are documented. + +## Recommended local layout + +Set one root for local retargeted data: + +```bash +export MJLAB_PLAYGROUND_MOTION_ROOT=/absolute/path/to/mjlab_motions +``` + +Arrange locomotion files as follows: + +```text +$MJLAB_PLAYGROUND_MOTION_ROOT/ +└── lafan/ + ├── k1_lafan1_fk_pkl/ + │ ├── walk1_subject5.pkl + │ ├── run1_subject5.pkl + │ └── sprint1_subject4.pkl + └── booster_t1_lafan1_locomotion/ + ├── walk....pkl + ├── run....pkl + └── sprint....pkl +``` + +The K1 AMP loader selects `walk*.pkl`, `run*.pkl`, and `sprint*.pkl`. The T1 loader also accepts names prefixed by `lafan1_`. + +The T1 locomotion directory can be overridden independently: + +```bash +export MJLAB_PLAYGROUND_T1_LOCOMOTION_MOTION_DIR=/absolute/path/to/t1_locomotion_pkls +``` + +## AMP pickle schema + +Each `.pkl`/`.pickle` file must contain at least: + +| Key | Shape/type | Meaning | +|---|---|---| +| `dof_pos` | `[T, num_dof]` float array | Joint positions in the robot's configured joint order | +| `root_pos` | `[T, 3]` float array | Root position | +| `root_rot` | `[T, 4]` float array | Root quaternion in `xyzw` order | +| `local_body_pos` | `[T, B, 3]` float array | GMR forward-kinematics body positions | +| `link_body_list` | sequence of `B` names | Names corresponding to `local_body_pos` | +| `fps` | number | Source frame rate; defaults to 30 if absent | + +For target-location AMP, the configured `expected_dof` is 22 for K1 and 23 for T1. The required key-body names are defined in `src/mjlab_playground/amp/amp_schema.py`. + +## T1 reference tracking + +Reference tracking uses the MJLab `.motion.npz` format expected by `MotionCommandCfg`. Point directly to a locally generated file: + +```bash +export MJLAB_TRACKING_MOTION_FILE=/absolute/path/to/reference.booster_t1_23dof.motion.npz +uv run play Mjlab-Tracking-Flat-Booster-T1 +``` + +Without that variable, the code checks this intentionally untracked package-relative placeholder: + +```text +src/mjlab_playground/asset_zoo/motions/retargeted/ +└── tracking/booster_t1_23dof/ + └── 0026_kicking2_stageii.booster_t1_23dof.motion.npz +``` + +The example filename documents the expected location only; the file is not distributed. + +## Before publishing a motion + +Record all of the following in a data manifest before considering a motion artifact for release: + +1. Original dataset and exact clip identifier. +2. Original dataset version and license URL. +3. Whether derivative/adapted material may be redistributed. +4. Retargeting tool and commit. +5. Robot model and joint order. +6. Any attribution, noncommercial, or share-alike requirements. + +If item 3 is unclear or negative, keep the file local and publish only the conversion instructions. diff --git a/pyproject.toml b/pyproject.toml index 3178cbd..2e81684 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ name = "mjlab-playground" version = "0.1.0" description = "Extra tasks for mjlab" readme = "README.md" -requires-python = ">=3.13" +requires-python = ">=3.13,<3.14" dependencies = [ "mjlab[cu128]", "torch>=2.7.0,<2.10.0", @@ -14,13 +14,11 @@ requires = ["uv_build>=0.8.18,<0.9.0"] build-backend = "uv_build" [tool.uv.sources] -mjlab = { git = "https://github.com/mujocolab/mjlab", rev = "f7fdb163b590e980865e59a5633ff5e1143553bd" } +mjlab = { git = "https://github.com/mujocolab/mjlab", rev = "9298ba5691858dec9f0fa7c53c0ffb453192da67" } warp-lang = { index = "nvidia", marker = "sys_platform != 'darwin'" } torch = [ { index = "pytorch-cu128", marker = "sys_platform != 'darwin'" }, ] -mujoco = { index = "mujoco" } -mujoco-warp = { git = "https://github.com/google-deepmind/mujoco_warp", rev = "875c4caf06d71b12b2036c327e7a07ce08a78b9a" } [[tool.uv.index]] url = "https://pypi.org/simple" @@ -35,15 +33,6 @@ name = "pytorch-cu128" url = "https://download.pytorch.org/whl/cu128" explicit = true -[[tool.uv.index]] -name = "mujoco" -url = "https://py.mujoco.org" -explicit = true - -# The nightly index (py.mujoco.org) only has dev builds. -[tool.uv] -override-dependencies = ["mujoco>=3.6.0.dev0"] - [tool.ruff] indent-width = 2 src = ["src/mjlab_playground"] @@ -54,6 +43,8 @@ select = ["E4", "E7", "E9", "F", "I", "B"] [tool.pyright] pythonVersion = "3.13" +include = ["src"] +exclude = ["tests", "typings"] ignore = ["./typings"] stubPath = "typings" reportWildcardImportFromLibrary = false diff --git a/src/mjlab_playground/__init__.py b/src/mjlab_playground/__init__.py index d6bf697..82537b3 100644 --- a/src/mjlab_playground/__init__.py +++ b/src/mjlab_playground/__init__.py @@ -1,4 +1,9 @@ """A collection of tasks built on mjlab.""" -from mjlab_playground.getup.config.go1 import * # noqa: F401, F403 -from mjlab_playground.getup.config.t1 import * # noqa: F401, F403 +from mjlab_playground.tasks.getup.config.go1 import * # noqa: F401, F403 +from mjlab_playground.tasks.getup.config.k1 import * # noqa: F401, F403 +from mjlab_playground.tasks.getup.config.t1 import * # noqa: F401, F403 +from mjlab_playground.tasks.target_location_amp.config.k1 import * # noqa: F401, F403 +from mjlab_playground.tasks.target_location_amp.config.t1 import * # noqa: F401, F403 +from mjlab_playground.tasks.tracking.config.t1 import * # noqa: F401, F403 +from mjlab_playground.tasks.velocity.config.t1 import * # noqa: F401, F403 diff --git a/src/mjlab_playground/amp/__init__.py b/src/mjlab_playground/amp/__init__.py new file mode 100644 index 0000000..c3e86c7 --- /dev/null +++ b/src/mjlab_playground/amp/__init__.py @@ -0,0 +1 @@ +"""Reusable adversarial motion-prior components.""" diff --git a/src/mjlab_playground/amp/amp_schema.py b/src/mjlab_playground/amp/amp_schema.py new file mode 100644 index 0000000..017b47b --- /dev/null +++ b/src/mjlab_playground/amp/amp_schema.py @@ -0,0 +1,33 @@ +"""Shared AMP observation schema for humanoid motion priors.""" + +from __future__ import annotations + +K1_AMP_KEY_BODY_NAMES: tuple[str, ...] = ( + "left_hand_link", + "right_hand_link", + "left_foot_link", + "right_foot_link", +) + +K1_AMP_NUM_DOFS = 22 +K1_AMP_NUM_KEY_BODIES = len(K1_AMP_KEY_BODY_NAMES) +T1_AMP_NUM_DOFS = 23 +T1_AMP_KEY_BODY_NAMES = K1_AMP_KEY_BODY_NAMES +T1_AMP_NUM_KEY_BODIES = len(T1_AMP_KEY_BODY_NAMES) + + +def amp_obs_dim(num_dofs: int, num_key_bodies: int) -> int: + return ( + int(num_dofs) # joint positions + + int(num_dofs) # joint velocities + + 1 # root height + + 3 # projected gravity in root frame + + 3 # root linear velocity in root frame + + 3 # root angular velocity in root frame + + 3 * int(num_key_bodies) # key body positions in root frame + + 3 * int(num_key_bodies) # key body velocities in root frame + ) + + +K1_AMP_OBS_DIM = amp_obs_dim(K1_AMP_NUM_DOFS, K1_AMP_NUM_KEY_BODIES) +T1_AMP_OBS_DIM = amp_obs_dim(T1_AMP_NUM_DOFS, T1_AMP_NUM_KEY_BODIES) diff --git a/src/mjlab_playground/amp/mdp/__init__.py b/src/mjlab_playground/amp/mdp/__init__.py new file mode 100644 index 0000000..b0acb1d --- /dev/null +++ b/src/mjlab_playground/amp/mdp/__init__.py @@ -0,0 +1,9 @@ +"""MDP helpers shared by adversarial motion-prior tasks.""" + +from mjlab.envs.mdp import * # noqa: F401, F403 + +from .actions import * # noqa: F403 +from .events import * # noqa: F403 +from .observations import * # noqa: F403 +from .rewards import * # noqa: F403 +from .terminations import * # noqa: F403 diff --git a/src/mjlab_playground/amp/mdp/actions.py b/src/mjlab_playground/amp/mdp/actions.py new file mode 100644 index 0000000..b34c8ec --- /dev/null +++ b/src/mjlab_playground/amp/mdp/actions.py @@ -0,0 +1,100 @@ +"""Action terms shared by dynamic AMP tasks.""" + +# pyright: reportIncompatibleVariableOverride=false + +from __future__ import annotations + +from dataclasses import dataclass +from typing import TYPE_CHECKING + +import torch +from mjlab.envs.mdp.actions import JointPositionAction, JointPositionActionCfg + +if TYPE_CHECKING: + from mjlab.envs import ManagerBasedRlEnv + + +@dataclass(kw_only=True) +class DelayedJointPositionActionCfg(JointPositionActionCfg): + """Joint position control with policy-step action application latency.""" + + delay_steps: int = 0 + """Number of controller steps between commanding and applying an action.""" + + startup_hold_steps: int = 0 + """Episode-start steps that hold the neutral pose before policy takeover.""" + + def build(self, env: ManagerBasedRlEnv) -> DelayedJointPositionAction: + return DelayedJointPositionAction(self, env) + + +class DelayedJointPositionAction(JointPositionAction): + """Delay actuator targets without changing ActionManager action history. + + The policy-visible current/previous actions remain the latest commanded raw + actions. Only the processed position target written to MuJoCo is delayed, + matching rcssservermj's parallel observation/action loop. + """ + + cfg: DelayedJointPositionActionCfg + + def __init__( + self, + cfg: DelayedJointPositionActionCfg, + env: ManagerBasedRlEnv, + ) -> None: + if cfg.delay_steps < 0: + raise ValueError(f"delay_steps must be non-negative, got {cfg.delay_steps}.") + if cfg.startup_hold_steps < 0: + raise ValueError( + f"startup_hold_steps must be non-negative, got {cfg.startup_hold_steps}." + ) + super().__init__(cfg=cfg, env=env) + self._delay_steps = int(cfg.delay_steps) + self._startup_hold_steps = int(cfg.startup_hold_steps) + neutral = self._neutral_processed_action() + self._applied_processed_actions = neutral.clone() + self._processed_action_history = [neutral.clone() for _ in range(self._delay_steps)] + + @property + def applied_processed_action(self) -> torch.Tensor: + """Processed target currently written to the simulated actuators.""" + return self._applied_processed_actions + + def process_actions(self, actions: torch.Tensor) -> None: + if self._startup_hold_steps > 0: + actions = actions.clone() + hold_mask = self._env.episode_length_buf < self._startup_hold_steps + actions[hold_mask] = 0.0 + super().process_actions(actions) + if self._delay_steps == 0: + self._applied_processed_actions = self._processed_actions + return + + self._applied_processed_actions = self._processed_action_history.pop(0) + self._processed_action_history.append(self._processed_actions.clone()) + + def apply_actions(self) -> None: + encoder_bias = self._entity.data.encoder_bias[:, self._target_ids] + target = self._applied_processed_actions - encoder_bias + self._entity.set_joint_position_target(target, joint_ids=self._target_ids) + + def reset(self, env_ids: torch.Tensor | slice | None = None) -> None: + if env_ids is None: + env_ids = slice(None) + super().reset(env_ids=env_ids) + neutral = self._neutral_processed_action() + self._processed_actions[env_ids] = neutral[env_ids] + self._applied_processed_actions[env_ids] = neutral[env_ids] + for history_entry in self._processed_action_history: + history_entry[env_ids] = neutral[env_ids] + + def _neutral_processed_action(self) -> torch.Tensor: + neutral = torch.zeros_like(self._processed_actions) * self._scale + self._offset + if self.cfg.clip is not None: + neutral = torch.clamp( + neutral, + min=self._clip[:, :, 0], + max=self._clip[:, :, 1], + ) + return neutral diff --git a/src/mjlab_playground/amp/mdp/events.py b/src/mjlab_playground/amp/mdp/events.py new file mode 100644 index 0000000..82cfbff --- /dev/null +++ b/src/mjlab_playground/amp/mdp/events.py @@ -0,0 +1,63 @@ +"""Reset events shared by locomotion AMP tasks.""" + +from __future__ import annotations + +import torch +from mjlab.utils.lab_api.math import quat_from_euler_xyz + + +def _env_ids(env, env_ids: torch.Tensor | slice | None) -> torch.Tensor: + all_ids = torch.arange(env.num_envs, device=env.device, dtype=torch.int64) + if env_ids is None: + return all_ids + if isinstance(env_ids, slice): + return all_ids[env_ids] + return env_ids + + +def reset_robot_near_origin( + env, + env_ids: torch.Tensor | slice | None, + robot_name: str = "robot", + x_range: tuple[float, float] = (-0.03, 0.03), + y_range: tuple[float, float] = (-0.03, 0.03), + yaw_range: tuple[float, float] = (-0.15, 0.15), + z_offset: float = 0.02, + z_offset_range: tuple[float, float] | None = None, + roll_range: tuple[float, float] = (0.0, 0.0), + pitch_range: tuple[float, float] = (0.0, 0.0), + root_lin_vel_xy_range: tuple[float, float] = (0.0, 0.0), + root_lin_vel_z_range: tuple[float, float] = (0.0, 0.0), + root_ang_vel_range: tuple[float, float] = (0.0, 0.0), + phase_period_s: float | None = None, +) -> None: + """Reset a standing robot near its environment origin with optional noise.""" + del phase_period_s + resolved_ids = _env_ids(env, env_ids) + robot = env.scene[robot_name] + num_resets = len(resolved_ids) + + default_root_state = robot.data.default_root_state[resolved_ids].clone() + positions = env.scene.env_origins[resolved_ids] + default_root_state[:, :3] + positions[:, 0] += torch.empty(num_resets, device=env.device).uniform_(*x_range) + positions[:, 1] += torch.empty(num_resets, device=env.device).uniform_(*y_range) + if z_offset_range is None: + positions[:, 2] += float(z_offset) + else: + positions[:, 2] += torch.empty(num_resets, device=env.device).uniform_( + *z_offset_range + ) + + roll = torch.empty(num_resets, device=env.device).uniform_(*roll_range) + pitch = torch.empty(num_resets, device=env.device).uniform_(*pitch_range) + yaw = torch.empty(num_resets, device=env.device).uniform_(*yaw_range) + quat = quat_from_euler_xyz(roll, pitch, yaw) + robot.write_root_link_pose_to_sim( + torch.cat((positions, quat), dim=-1), env_ids=resolved_ids + ) + + root_velocity = torch.empty(num_resets, 6, device=env.device) + root_velocity[:, :2].uniform_(*root_lin_vel_xy_range) + root_velocity[:, 2].uniform_(*root_lin_vel_z_range) + root_velocity[:, 3:].uniform_(*root_ang_vel_range) + robot.write_root_link_velocity_to_sim(root_velocity, env_ids=resolved_ids) diff --git a/src/mjlab_playground/amp/mdp/observations.py b/src/mjlab_playground/amp/mdp/observations.py new file mode 100644 index 0000000..332662f --- /dev/null +++ b/src/mjlab_playground/amp/mdp/observations.py @@ -0,0 +1,65 @@ +"""Observations shared by adversarial motion-prior tasks.""" + +from __future__ import annotations + +import torch +from mjlab.managers.scene_entity_config import SceneEntityCfg +from mjlab.utils.lab_api.math import quat_apply_inverse + +from mjlab_playground.amp.amp_schema import K1_AMP_KEY_BODY_NAMES + +_ROBOT_CFG = SceneEntityCfg("robot", joint_names=(".*",)) + + +def amp_robot_obs( + env, + robot_cfg: SceneEntityCfg = _ROBOT_CFG, + key_body_names: tuple[str, ...] = K1_AMP_KEY_BODY_NAMES, +) -> torch.Tensor: + """Build the robot-only state consumed by the AMP discriminator.""" + robot = env.scene[robot_cfg.name] + joint_ids = robot_cfg.joint_ids + body_ids = _resolve_key_body_ids(env, robot, key_body_names) + root_quat = robot.data.root_link_quat_w + root_pos = robot.data.root_link_pos_w + root_quat_bodies = root_quat[:, None, :].expand(-1, len(body_ids), -1) + + body_pos_w = robot.data.body_link_pos_w[:, body_ids, :] - root_pos[:, None, :] + body_vel_w = ( + robot.data.body_link_lin_vel_w[:, body_ids, :] + - robot.data.root_link_lin_vel_w[:, None, :] + ) + key_body_pos_b_raw = quat_apply_inverse(root_quat_bodies, body_pos_w) + key_body_vel_b_raw = quat_apply_inverse(root_quat_bodies, body_vel_w) + key_body_vel_b_raw = key_body_vel_b_raw - torch.cross( + robot.data.root_link_ang_vel_b[:, None, :], + key_body_pos_b_raw, + dim=-1, + ) + + return torch.cat( + ( + robot.data.joint_pos[:, joint_ids], + robot.data.joint_vel[:, joint_ids], + root_pos[:, 2:3], + robot.data.projected_gravity_b, + robot.data.root_link_lin_vel_b, + robot.data.root_link_ang_vel_b, + key_body_pos_b_raw.reshape(env.num_envs, -1), + key_body_vel_b_raw.reshape(env.num_envs, -1), + ), + dim=-1, + ) + + +def _resolve_key_body_ids(env, robot, key_body_names: tuple[str, ...]) -> list[int]: + cached_ids = getattr(env, "_amp_key_body_ids", None) + cached_names = getattr(env, "_amp_key_body_names", None) + if cached_ids is not None and cached_names == key_body_names: + return cached_ids + body_ids = robot.find_bodies(key_body_names, preserve_order=True)[0] + if len(body_ids) != len(key_body_names): + raise ValueError(f"Failed to resolve AMP key bodies: {key_body_names}.") + env._amp_key_body_ids = body_ids + env._amp_key_body_names = key_body_names + return body_ids diff --git a/src/mjlab_playground/amp/mdp/rewards.py b/src/mjlab_playground/amp/mdp/rewards.py new file mode 100644 index 0000000..aedd745 --- /dev/null +++ b/src/mjlab_playground/amp/mdp/rewards.py @@ -0,0 +1,47 @@ +"""Reward and metric helpers shared by locomotion AMP tasks.""" + +# pyright: reportOptionalMemberAccess=false + +from __future__ import annotations + +import torch + +from .terminations import fell_over as termination_fell_over + + +def fell_over_penalty( + env, + robot_name: str = "robot", + height_threshold: float = 0.38, + up_z_threshold: float = 0.55, +) -> torch.Tensor: + """Return a fall indicator aligned with the current-step termination.""" + termination_manager = getattr(env, "termination_manager", None) + try: + fell = termination_manager.get_term("fell_over") + except (AttributeError, KeyError): + fell = termination_fell_over( + env, + robot_name=robot_name, + height_threshold=height_threshold, + up_z_threshold=up_z_threshold, + ) + env.extras.setdefault("log", {})["Metrics/robot/fall_fraction"] = fell.float().mean() + if bool(getattr(env.cfg, "scale_rewards_by_dt", True)): + return fell.float() / max(float(env.step_dt), 1.0e-6) + return fell.float() + + +def fell_over_fraction( + env, + robot_name: str = "robot", + height_threshold: float = 0.38, + up_z_threshold: float = 0.55, +) -> torch.Tensor: + """Per-step fall indicator for episode metrics.""" + return termination_fell_over( + env, + robot_name=robot_name, + height_threshold=height_threshold, + up_z_threshold=up_z_threshold, + ).float() diff --git a/src/mjlab_playground/amp/mdp/terminations.py b/src/mjlab_playground/amp/mdp/terminations.py new file mode 100644 index 0000000..cbc50bb --- /dev/null +++ b/src/mjlab_playground/amp/mdp/terminations.py @@ -0,0 +1,18 @@ +"""Termination helpers shared by locomotion AMP tasks.""" + +from __future__ import annotations + +import torch + + +def fell_over( + env, + robot_name: str = "robot", + height_threshold: float = 0.38, + up_z_threshold: float = 0.55, +) -> torch.Tensor: + """Return environments whose robot is too low or too tilted.""" + robot = env.scene[robot_name] + height = robot.data.root_link_pos_w[:, 2] + up_z = -robot.data.projected_gravity_b[:, 2] + return (height < height_threshold) | (up_z < up_z_threshold) diff --git a/src/mjlab_playground/amp/rl/__init__.py b/src/mjlab_playground/amp/rl/__init__.py new file mode 100644 index 0000000..7d6c3fd --- /dev/null +++ b/src/mjlab_playground/amp/rl/__init__.py @@ -0,0 +1,6 @@ +"""Reusable adversarial motion-prior training components.""" + +from .amp_ppo import AmpPPO +from .runner import AmpOnPolicyRunner + +__all__ = ["AmpPPO", "AmpOnPolicyRunner"] diff --git a/src/mjlab_playground/amp/rl/amp_ppo.py b/src/mjlab_playground/amp/rl/amp_ppo.py new file mode 100644 index 0000000..69eba79 --- /dev/null +++ b/src/mjlab_playground/amp/rl/amp_ppo.py @@ -0,0 +1,788 @@ +"""PPO variant with an AMP discriminator update.""" + +# pyright: reportArgumentType=false, reportCallIssue=false, reportIndexIssue=false +# pyright: reportOptionalMemberAccess=false, reportPossiblyUnboundVariable=false + +from __future__ import annotations + +from itertools import chain + +import torch +import torch.nn as nn +from rsl_rl.algorithms.ppo import PPO +from rsl_rl.env import VecEnv +from rsl_rl.extensions import resolve_rnd_config, resolve_symmetry_config +from rsl_rl.models import MLPModel +from rsl_rl.storage import RolloutStorage +from rsl_rl.utils import resolve_callable, resolve_obs_groups, resolve_optimizer +from tensordict import TensorDict + +from .discriminator import AmpDiscriminator +from .motion_loader import RetargetedAmpMotionLoader +from .replay_buffer import AmpTransitionBuffer + + +class AmpPPO(PPO): + """Current RSL-RL-compatible PPO with AMP discriminator training.""" + + def __init__( + self, + actor: MLPModel, + critic: MLPModel, + storage: RolloutStorage, + discriminator: AmpDiscriminator, + expert_loader: RetargetedAmpMotionLoader, + replay_buffer: AmpTransitionBuffer, + stage_discriminators: dict[str, AmpDiscriminator] | None = None, + stage_expert_loaders: dict[str, RetargetedAmpMotionLoader] | None = None, + stage_replay_buffers: dict[str, AmpTransitionBuffer] | None = None, + amp_loss_coef: float = 1.0, + amp_cfg: dict | None = None, + num_learning_epochs: int = 5, + num_mini_batches: int = 4, + clip_param: float = 0.2, + gamma: float = 0.99, + lam: float = 0.95, + value_loss_coef: float = 1.0, + entropy_coef: float = 0.01, + learning_rate: float = 0.001, + discriminator_learning_rate: float | None = None, + discriminator_weight_decay: float = 1.0e-4, + max_grad_norm: float = 1.0, + optimizer: str = "adam", + use_clipped_value_loss: bool = True, + schedule: str = "adaptive", + desired_kl: float = 0.01, + normalize_advantage_per_mini_batch: bool = False, + device: str = "cpu", + rnd_cfg: dict | None = None, + symmetry_cfg: dict | None = None, + multi_gpu_cfg: dict | None = None, + ) -> None: + super().__init__( + actor=actor, + critic=critic, + storage=storage, + num_learning_epochs=num_learning_epochs, + num_mini_batches=num_mini_batches, + clip_param=clip_param, + gamma=gamma, + lam=lam, + value_loss_coef=value_loss_coef, + entropy_coef=entropy_coef, + learning_rate=learning_rate, + max_grad_norm=max_grad_norm, + optimizer=optimizer, + use_clipped_value_loss=use_clipped_value_loss, + schedule=schedule, + desired_kl=desired_kl, + normalize_advantage_per_mini_batch=normalize_advantage_per_mini_batch, + device=device, + rnd_cfg=rnd_cfg, + symmetry_cfg=symmetry_cfg, + multi_gpu_cfg=multi_gpu_cfg, + ) + self.discriminator = discriminator.to(self.device) + self.expert_loader = expert_loader + self.amp_replay = replay_buffer + self.amp_cfg = dict(amp_cfg or {}) + self.stage_discriminators = { + name: module.to(self.device) + for name, module in (stage_discriminators or {}).items() + } + self.stage_expert_loaders = dict(stage_expert_loaders or {}) + self.stage_replay_buffers = dict(stage_replay_buffers or {}) + self.uses_stage_amp = bool(self.stage_discriminators) + self.stage_policy_replay: AmpTransitionBuffer | None = None + if self.uses_stage_amp: + self.stage_policy_replay = AmpTransitionBuffer( + capacity=replay_buffer.capacity, + amp_obs_dim=replay_buffer.amp_obs_dim, + device=device, + ) + self.task_reward_lerp = float(self.discriminator.task_reward_lerp) + self.amp_loss_coef = float(amp_loss_coef) + self.discriminator_learning_rate = ( + float(learning_rate) + if discriminator_learning_rate is None + else float(discriminator_learning_rate) + ) + + self.optimizer = resolve_optimizer(optimizer)( + [ + { + "params": chain(self.actor.parameters(), self.critic.parameters()), + "name": "policy", + "lr": learning_rate, + }, + { + "params": list(self._discriminator_parameters()), + "name": "discriminator", + "lr": self.discriminator_learning_rate, + "weight_decay": float(discriminator_weight_decay), + }, + ], + lr=learning_rate, + ) + + def _discriminator_modules(self) -> list[AmpDiscriminator]: + if self.uses_stage_amp: + return list(self.stage_discriminators.values()) + return [self.discriminator] + + def _discriminator_parameters(self): + return chain.from_iterable( + module.parameters() for module in self._discriminator_modules() + ) + + @staticmethod + def construct_algorithm( + obs: TensorDict, env: VecEnv, cfg: dict, device: str + ) -> "AmpPPO": + alg_class: type[AmpPPO] = resolve_callable(cfg["algorithm"].pop("class_name")) # type: ignore + actor_class: type[MLPModel] = resolve_callable(cfg["actor"].pop("class_name")) # type: ignore + critic_class: type[MLPModel] = resolve_callable(cfg["critic"].pop("class_name")) # type: ignore + + cfg["obs_groups"] = resolve_obs_groups(obs, cfg["obs_groups"], ["actor", "critic"]) + cfg["algorithm"] = resolve_rnd_config(cfg["algorithm"], obs, cfg["obs_groups"], env) + cfg["algorithm"] = resolve_symmetry_config(cfg["algorithm"], env) + + amp_cfg = cfg["algorithm"].pop("amp_cfg") + actor: MLPModel = actor_class( + obs, + cfg["obs_groups"], + "actor", + env.num_actions, + **cfg["actor"], + ).to(device) + print(f"Actor Model: {actor}") + if cfg["algorithm"].pop("share_cnn_encoders", None): + cfg["critic"]["cnns"] = actor.cnns # type: ignore[attr-defined] + critic: MLPModel = critic_class( + obs, cfg["obs_groups"], "critic", 1, **cfg["critic"] + ).to(device) + print(f"Critic Model: {critic}") + + storage = RolloutStorage( + "rl", + env.num_envs, + cfg["num_steps_per_env"], + obs, + [env.num_actions], + device, + ) + + def make_loader(local_cfg: dict) -> RetargetedAmpMotionLoader: + return RetargetedAmpMotionLoader( + tuple(local_cfg.get("motion_files", ())), + device=device, + expected_dof=local_cfg.get("expected_dof", amp_cfg.get("expected_dof", 22)), + motion_groups=local_cfg.get("motion_groups"), + target_fps=local_cfg.get("target_fps", amp_cfg.get("target_fps")), + joint_names=tuple(local_cfg.get("joint_names", amp_cfg.get("joint_names", ()))) + or None, + key_body_names=tuple( + local_cfg.get("key_body_names", amp_cfg.get("key_body_names", ())) + ) + or None, + emphasis_frame_offset_range=local_cfg.get( + "emphasis_frame_offset_range", + amp_cfg.get("emphasis_frame_offset_range"), + ), + emphasis_weight=local_cfg.get( + "emphasis_weight", + amp_cfg.get("emphasis_weight"), + ), + velocity_emphasis_weight=local_cfg.get( + "velocity_emphasis_weight", + amp_cfg.get("velocity_emphasis_weight"), + ), + ) + + def make_discriminator( + loader: RetargetedAmpMotionLoader, + local_cfg: dict, + ) -> AmpDiscriminator: + return AmpDiscriminator( + amp_obs_dim=loader.amp_obs_dim, + hidden_dims=tuple( + local_cfg.get( + "discriminator_hidden_dims", + amp_cfg.get("discriminator_hidden_dims", (512, 256)), + ) + ), + activation=local_cfg.get( + "discriminator_activation", + amp_cfg.get("discriminator_activation", "relu"), + ), + amp_reward_coef=local_cfg.get("reward_coef", amp_cfg.get("reward_coef", 0.3)), + amp_reward_scale=local_cfg.get( + "reward_scale", amp_cfg.get("reward_scale", 1.0) + ), + task_reward_lerp=amp_cfg.get("task_reward_lerp", 0.7), + grad_penalty_coef=local_cfg.get( + "grad_penalty_coef", + amp_cfg.get("grad_penalty_coef", 10.0), + ), + normalize_input=local_cfg.get( + "normalize_input", amp_cfg.get("normalize_input", True) + ), + loss_type=local_cfg.get( + "discriminator_loss_type", + amp_cfg.get("discriminator_loss_type", "bce"), + ), + reward_type=local_cfg.get( + "discriminator_reward_type", + amp_cfg.get("discriminator_reward_type", "softplus"), + ), + expert_label=local_cfg.get( + "discriminator_expert_label", + amp_cfg.get("discriminator_expert_label", 0.9), + ), + policy_label=local_cfg.get( + "discriminator_policy_label", + amp_cfg.get("discriminator_policy_label", 0.1), + ), + reward_clip_max=local_cfg.get( + "discriminator_reward_clip_max", + amp_cfg.get("discriminator_reward_clip_max", 1.5), + ), + ).to(device) + + stage_cfg = dict(amp_cfg.get("stage_amp_cfg", {})) + stage_discriminators: dict[str, AmpDiscriminator] = {} + stage_expert_loaders: dict[str, RetargetedAmpMotionLoader] = {} + stage_replay_buffers: dict[str, AmpTransitionBuffer] = {} + if stage_cfg.get("enabled", False): + reserved_stage_cfg_keys = {"enabled", "stage_names", "stages", "primary_stage"} + if isinstance(stage_cfg.get("stages"), dict): + stage_items = list(stage_cfg["stages"].items()) + else: + stage_names = stage_cfg.get("stage_names") + if stage_names is None: + inferred_names = [ + name for name in stage_cfg.keys() if name not in reserved_stage_cfg_keys + ] + if inferred_names: + stage_names = tuple(inferred_names) + else: + raise ValueError( + "stage_amp_cfg is enabled but no stage definitions were provided." + ) + stage_items = [ + (str(name), stage_cfg.get(str(name), {})) for name in stage_names + ] + + if not stage_items: + raise ValueError("stage_amp_cfg is enabled but resolved to zero AMP stages.") + + for name, raw_local_cfg in stage_items: + local_cfg = dict(raw_local_cfg) + local_cfg.setdefault("motion_groups", stage_cfg.get(f"{name}_motion_groups")) + local_cfg.setdefault("motion_files", stage_cfg.get(f"{name}_motion_files", ())) + loader = make_loader(local_cfg) + stage_expert_loaders[name] = loader + stage_discriminators[name] = make_discriminator(loader, local_cfg) + stage_replay_buffers[name] = AmpTransitionBuffer( + capacity=local_cfg.get( + "replay_buffer_size", amp_cfg.get("replay_buffer_size", 100_000) + ), + amp_obs_dim=loader.amp_obs_dim, + device=device, + ) + primary_stage_name = str(stage_cfg.get("primary_stage", "")) + if primary_stage_name not in stage_discriminators: + primary_stage_name = next(iter(stage_discriminators)) + expert_loader = stage_expert_loaders[primary_stage_name] + discriminator = stage_discriminators[primary_stage_name] + replay_buffer = stage_replay_buffers[primary_stage_name] + for name, loader in stage_expert_loaders.items(): + print( + f"AMP {name} expert transitions: " + f"{loader.num_transitions}, obs dim: {loader.amp_obs_dim}" + ) + else: + expert_loader = make_loader(amp_cfg) + discriminator = make_discriminator(expert_loader, amp_cfg) + replay_buffer = AmpTransitionBuffer( + capacity=amp_cfg.get("replay_buffer_size", 100_000), + amp_obs_dim=expert_loader.amp_obs_dim, + device=device, + ) + print( + "AMP expert transitions: " + f"{expert_loader.num_transitions}, obs dim: {expert_loader.amp_obs_dim}" + ) + + return alg_class( + actor=actor, + critic=critic, + storage=storage, + discriminator=discriminator, + expert_loader=expert_loader, + replay_buffer=replay_buffer, + stage_discriminators=stage_discriminators or None, + stage_expert_loaders=stage_expert_loaders or None, + stage_replay_buffers=stage_replay_buffers or None, + amp_loss_coef=amp_cfg.get("loss_coef", 1.0), + amp_cfg=amp_cfg, + discriminator_learning_rate=amp_cfg.get("discriminator_learning_rate"), + discriminator_weight_decay=amp_cfg.get("discriminator_weight_decay", 1.0e-4), + device=device, + **cfg["algorithm"], + multi_gpu_cfg=cfg["multi_gpu"], + ) + + def train_mode(self) -> None: + super().train_mode() + for discriminator in self._discriminator_modules(): + discriminator.train() + + def eval_mode(self) -> None: + super().eval_mode() + for discriminator in self._discriminator_modules(): + discriminator.eval() + + def predict_amp_reward( + self, + state: torch.Tensor, + next_state: torch.Tensor, + task_reward: torch.Tensor, + stage_weights: dict[str, torch.Tensor] | None = None, + ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, dict[str, torch.Tensor]]: + """Return mixed reward, raw style reward, discriminator score, and stage logs.""" + if not self.uses_stage_amp: + mixed, disc = self.discriminator.predict_reward(state, next_state, task_reward) + amp_weight = 1.0 - self.task_reward_lerp + style_reward = torch.zeros_like(task_reward) + if amp_weight > 0.0: + style_reward = (mixed - self.task_reward_lerp * task_reward) / amp_weight + return mixed, style_reward, disc, {} + + if stage_weights is None: + stage_weights = { + name: torch.full_like(task_reward, 1.0 / len(self.stage_discriminators)) + for name in self.stage_discriminators + } + total_weight = torch.zeros_like(task_reward) + style_reward = torch.zeros_like(task_reward) + disc_pred = torch.zeros_like(task_reward) + logs: dict[str, torch.Tensor] = {} + for name, discriminator in self.stage_discriminators.items(): + weight = stage_weights.get(name) + if weight is None: + weight = torch.zeros_like(task_reward) + weight = weight.to(device=task_reward.device, dtype=task_reward.dtype).view(-1) + stage_style, stage_disc = discriminator.predict_style_reward(state, next_state) + style_reward += weight * stage_style + disc_pred += weight * stage_disc + total_weight += weight + logs[f"{name}_weight"] = weight.detach() + logs[f"{name}_style"] = stage_style.detach() + logs[f"{name}_disc"] = stage_disc.detach() + safe_weight = total_weight.clamp_min(1.0e-6) + style_reward = style_reward / safe_weight + disc_pred = disc_pred / safe_weight + mixed = ( + self.task_reward_lerp * task_reward + (1.0 - self.task_reward_lerp) * style_reward + ) + return mixed, style_reward, disc_pred, logs + + def process_amp_step( + self, + state: torch.Tensor, + next_state: torch.Tensor, + stage_weights: dict[str, torch.Tensor] | None = None, + ) -> None: + if not self.uses_stage_amp: + self.amp_replay.insert(state, next_state) + return + if self.stage_policy_replay is not None: + self.stage_policy_replay.insert(state, next_state) + if stage_weights is None: + return + for name, replay_buffer in self.stage_replay_buffers.items(): + weight = stage_weights.get(name) + if weight is None: + continue + mask = weight.to(device=state.device).view(-1) >= 0.5 + if torch.any(mask): + replay_buffer.insert(state[mask], next_state[mask]) + + def _sample_stage_policy( + self, + name: str, + batch_size: int, + ) -> tuple[torch.Tensor, torch.Tensor, bool]: + replay = self.stage_replay_buffers[name] + if replay.size > 0: + policy_state, policy_next_state = replay.sample(batch_size) + return policy_state, policy_next_state, False + if self.stage_policy_replay is not None and self.stage_policy_replay.size > 0: + policy_state, policy_next_state = self.stage_policy_replay.sample(batch_size) + return policy_state, policy_next_state, True + raise RuntimeError( + f"Stage AMP replay buffer {name!r} is empty and no policy fallback " + "transitions are available. This means no policy AMP transitions were " + "recorded before the discriminator update." + ) + + def update(self) -> dict[str, float]: # noqa: C901 + mean_value_loss = 0.0 + mean_surrogate_loss = 0.0 + mean_entropy = 0.0 + mean_amp_loss = 0.0 + mean_grad_pen = 0.0 + mean_policy_pred = 0.0 + mean_expert_pred = 0.0 + mean_stage_stats = { + name: { + "amp": 0.0, + "policy_pred": 0.0, + "expert_pred": 0.0, + "fallback": 0.0, + } + for name in self.stage_discriminators + } + mean_rnd_loss = 0.0 if self.rnd else None + mean_symmetry_loss = 0.0 if self.symmetry else None + + if self.actor.is_recurrent or self.critic.is_recurrent: + generator = self.storage.recurrent_mini_batch_generator( + self.num_mini_batches, + self.num_learning_epochs, + ) + else: + generator = self.storage.mini_batch_generator( + self.num_mini_batches, + self.num_learning_epochs, + ) + + for batch in generator: + original_batch_size = batch.observations.batch_size[0] + + if self.normalize_advantage_per_mini_batch: + with torch.no_grad(): + batch.advantages = (batch.advantages - batch.advantages.mean()) / ( # type: ignore[union-attr] + batch.advantages.std() + 1e-8 # type: ignore[union-attr] + ) + + if self.symmetry and self.symmetry["use_data_augmentation"]: + data_augmentation_func = self.symmetry["data_augmentation_func"] + batch.observations, batch.actions = data_augmentation_func( + env=self.symmetry["_env"], + obs=batch.observations, + actions=batch.actions, + ) + num_aug = int(batch.observations.batch_size[0] / original_batch_size) + batch.old_actions_log_prob = batch.old_actions_log_prob.repeat(num_aug, 1) + batch.values = batch.values.repeat(num_aug, 1) + batch.advantages = batch.advantages.repeat(num_aug, 1) + batch.returns = batch.returns.repeat(num_aug, 1) + + self.actor( + batch.observations, + masks=batch.masks, + hidden_state=batch.hidden_states[0], + stochastic_output=True, + ) + actions_log_prob = self.actor.get_output_log_prob(batch.actions) # type: ignore[arg-type] + values = self.critic( + batch.observations, + masks=batch.masks, + hidden_state=batch.hidden_states[1], + ) + distribution_params = tuple( + p[:original_batch_size] for p in self.actor.output_distribution_params + ) + entropy = self.actor.output_entropy[:original_batch_size] + + if self.desired_kl is not None and self.schedule == "adaptive": + with torch.inference_mode(): + kl = self.actor.get_kl_divergence( + batch.old_distribution_params, + distribution_params, + ) + kl_mean = torch.mean(kl) + if self.is_multi_gpu: + torch.distributed.all_reduce(kl_mean, op=torch.distributed.ReduceOp.SUM) + kl_mean /= self.gpu_world_size + if self.gpu_global_rank == 0: + if kl_mean > self.desired_kl * 2.0: + self.learning_rate = max(1.0e-5, self.learning_rate / 1.5) + elif kl_mean < self.desired_kl / 2.0 and kl_mean > 0.0: + self.learning_rate = min(1.0e-2, self.learning_rate * 1.5) + if self.is_multi_gpu: + lr_tensor = torch.tensor(self.learning_rate, device=self.device) + torch.distributed.broadcast(lr_tensor, src=0) + self.learning_rate = lr_tensor.item() + for param_group in self.optimizer.param_groups: + if param_group.get("name") == "discriminator": + param_group["lr"] = self.discriminator_learning_rate + else: + param_group["lr"] = self.learning_rate + + ratio = torch.exp(actions_log_prob - torch.squeeze(batch.old_actions_log_prob)) # type: ignore[arg-type] + surrogate = -torch.squeeze(batch.advantages) * ratio # type: ignore[arg-type] + surrogate_clipped = -torch.squeeze(batch.advantages) * torch.clamp( # type: ignore[arg-type] + ratio, + 1.0 - self.clip_param, + 1.0 + self.clip_param, + ) + surrogate_loss = torch.max(surrogate, surrogate_clipped).mean() + + if self.use_clipped_value_loss: + value_clipped = batch.values + (values - batch.values).clamp( # type: ignore[operator] + -self.clip_param, + self.clip_param, + ) + value_losses = (values - batch.returns).pow(2) + value_losses_clipped = (value_clipped - batch.returns).pow(2) + value_loss = torch.max(value_losses, value_losses_clipped).mean() + else: + value_loss = (batch.returns - values).pow(2).mean() + + loss = ( + surrogate_loss + + self.value_loss_coef * value_loss + - self.entropy_coef * entropy.mean() + ) + + if self.symmetry: + if not self.symmetry["use_data_augmentation"]: + data_augmentation_func = self.symmetry["data_augmentation_func"] + batch.observations, _ = data_augmentation_func( + obs=batch.observations, + actions=None, + env=self.symmetry["_env"], + ) + mean_actions = self.actor(batch.observations.detach().clone()) + action_mean_orig = mean_actions[:original_batch_size] + _, actions_mean_symm = data_augmentation_func( + obs=None, + actions=action_mean_orig, + env=self.symmetry["_env"], + ) + mse_loss = torch.nn.MSELoss() + symmetry_loss = mse_loss( + mean_actions[original_batch_size:], + actions_mean_symm.detach()[original_batch_size:], + ) + if self.symmetry["use_mirror_loss"]: + loss += self.symmetry["mirror_loss_coeff"] * symmetry_loss + else: + symmetry_loss = symmetry_loss.detach() + + rnd_loss = ( + self.rnd.compute_loss(batch.observations[:original_batch_size]) # type: ignore[index] + if self.rnd + else None + ) + + mini_batch_size = int(original_batch_size) + if self.uses_stage_amp: + amp_loss = torch.zeros((), dtype=torch.float32, device=self.device) + stage_stats: list[dict[str, torch.Tensor]] = [] + for name, discriminator in self.stage_discriminators.items(): + loader = self.stage_expert_loaders[name] + policy_state, policy_next_state, used_fallback = self._sample_stage_policy( + name, + mini_batch_size, + ) + expert_state, expert_next_state = loader.sample(mini_batch_size) + stage_loss, stats = discriminator.discriminator_loss( + policy_state, + policy_next_state, + expert_state, + expert_next_state, + ) + amp_loss = amp_loss + stage_loss + stage_stats.append(stats) + mean_stage_stats[name]["amp"] += stats["amp"].item() + mean_stage_stats[name]["policy_pred"] += stats["amp_policy_pred"].item() + mean_stage_stats[name]["expert_pred"] += stats["amp_expert_pred"].item() + mean_stage_stats[name]["fallback"] += float(used_fallback) + amp_loss = amp_loss / max(len(stage_stats), 1) + amp_stats = { + key: torch.stack([stats[key] for stats in stage_stats]).mean() + for key in stage_stats[0] + } + else: + if self.amp_replay.size > 0: + policy_state, policy_next_state = self.amp_replay.sample(mini_batch_size) + else: + policy_state, policy_next_state = self.expert_loader.sample(mini_batch_size) + expert_state, expert_next_state = self.expert_loader.sample(mini_batch_size) + amp_loss, amp_stats = self.discriminator.discriminator_loss( + policy_state, + policy_next_state, + expert_state, + expert_next_state, + ) + loss += self.amp_loss_coef * amp_loss + + self.optimizer.zero_grad() + loss.backward() + if self.rnd: + self.rnd.optimizer.zero_grad() + assert rnd_loss is not None + rnd_loss.backward() + + if self.is_multi_gpu: + self.reduce_parameters() + + nn.utils.clip_grad_norm_(self.actor.parameters(), self.max_grad_norm) + nn.utils.clip_grad_norm_(self.critic.parameters(), self.max_grad_norm) + nn.utils.clip_grad_norm_( + list(self._discriminator_parameters()), self.max_grad_norm + ) + self.optimizer.step() + if self.rnd: + self.rnd.optimizer.step() + + mean_value_loss += value_loss.item() + mean_surrogate_loss += surrogate_loss.item() + mean_entropy += entropy.mean().item() + mean_amp_loss += amp_stats["amp"].item() + mean_grad_pen += amp_stats["amp_grad_pen"].item() + mean_policy_pred += amp_stats["amp_policy_pred"].item() + mean_expert_pred += amp_stats["amp_expert_pred"].item() + if mean_rnd_loss is not None: + assert rnd_loss is not None + mean_rnd_loss += rnd_loss.item() + if mean_symmetry_loss is not None: + mean_symmetry_loss += symmetry_loss.item() + + num_updates = self.num_learning_epochs * self.num_mini_batches + loss_dict = { + "value": mean_value_loss / num_updates, + "surrogate": mean_surrogate_loss / num_updates, + "entropy": mean_entropy / num_updates, + "amp": mean_amp_loss / num_updates, + "amp_grad_pen": mean_grad_pen / num_updates, + "amp_policy_pred": mean_policy_pred / num_updates, + "amp_expert_pred": mean_expert_pred / num_updates, + } + for name, stats in mean_stage_stats.items(): + loss_dict[f"amp_{name}"] = stats["amp"] / num_updates + loss_dict[f"amp_{name}_policy_pred"] = stats["policy_pred"] / num_updates + loss_dict[f"amp_{name}_expert_pred"] = stats["expert_pred"] / num_updates + loss_dict[f"amp_{name}_policy_fallback"] = stats["fallback"] / num_updates + if mean_rnd_loss is not None: + loss_dict["rnd"] = mean_rnd_loss / num_updates + if mean_symmetry_loss is not None: + loss_dict["symmetry"] = mean_symmetry_loss / num_updates + + self.storage.clear() + return loss_dict + + def save(self) -> dict: + saved_dict = super().save() + saved_dict["discriminator_state_dict"] = self.discriminator.state_dict() + if self.uses_stage_amp: + saved_dict["stage_discriminator_state_dicts"] = { + name: discriminator.state_dict() + for name, discriminator in self.stage_discriminators.items() + } + return saved_dict + + def load(self, loaded_dict: dict, load_cfg: dict | None, strict: bool) -> bool: + load_disc = True if load_cfg is None else load_cfg.get("discriminator", True) + try: + load_iteration = super().load(loaded_dict, load_cfg, strict) + except (RuntimeError, ValueError) as exc: + wants_optimizer = True if load_cfg is None else load_cfg.get("optimizer", True) + if not wants_optimizer: + raise + retry_cfg = { + "actor": True, + "critic": True, + "optimizer": False, + "iteration": True, + "rnd": True, + } + if load_cfg is not None: + retry_cfg.update(load_cfg) + retry_cfg["optimizer"] = False + print( + "Skipping optimizer state while loading AMP checkpoint because the " + f"current optimizer layout changed: {exc}" + ) + load_iteration = super().load(loaded_dict, retry_cfg, strict) + if load_disc and self.uses_stage_amp: + stage_state_dicts = loaded_dict.get("stage_discriminator_state_dicts") + if isinstance(stage_state_dicts, dict): + for name, state_dict in stage_state_dicts.items(): + if name in self.stage_discriminators: + self.stage_discriminators[name].load_state_dict(state_dict, strict=strict) + elif "discriminator_state_dict" in loaded_dict: + target_name = next(iter(self.stage_discriminators)) + self.stage_discriminators[target_name].load_state_dict( + loaded_dict["discriminator_state_dict"], + strict=strict, + ) + elif ( + load_disc + and (not self.uses_stage_amp) + and "stage_discriminator_state_dicts" in loaded_dict + and not (load_cfg or {}).get("allow_stage_to_single_discriminator", False) + ): + print( + "Skipping discriminator state while loading a stage-AMP checkpoint into " + "a single-discriminator AMP configuration. Set " + "load_cfg['allow_stage_to_single_discriminator']=True to override." + ) + elif load_disc and "discriminator_state_dict" in loaded_dict: + self.discriminator.load_state_dict( + loaded_dict["discriminator_state_dict"], + strict=strict, + ) + return load_iteration + + def broadcast_parameters(self) -> None: + model_params = [ + self.actor.state_dict(), + self.critic.state_dict(), + ( + { + name: module.state_dict() + for name, module in self.stage_discriminators.items() + } + if self.uses_stage_amp + else self.discriminator.state_dict() + ), + ] + if self.rnd: + model_params.append(self.rnd.predictor.state_dict()) + torch.distributed.broadcast_object_list(model_params, src=0) + self.actor.load_state_dict(model_params[0]) + self.critic.load_state_dict(model_params[1]) + if self.uses_stage_amp: + for name, state_dict in model_params[2].items(): + self.stage_discriminators[name].load_state_dict(state_dict) + else: + self.discriminator.load_state_dict(model_params[2]) + if self.rnd: + self.rnd.predictor.load_state_dict(model_params[3]) + + def reduce_parameters(self) -> None: + all_params = chain( + self.actor.parameters(), + self.critic.parameters(), + self._discriminator_parameters(), + ) + if self.rnd: + all_params = chain(all_params, self.rnd.parameters()) + all_params = list(all_params) + grads = [param.grad.view(-1) for param in all_params if param.grad is not None] + all_grads = torch.cat(grads) + torch.distributed.all_reduce(all_grads, op=torch.distributed.ReduceOp.SUM) + all_grads /= self.gpu_world_size + offset = 0 + for param in all_params: + if param.grad is not None: + numel = param.numel() + param.grad.data.copy_( + all_grads[offset : offset + numel].view_as(param.grad.data) + ) + offset += numel diff --git a/src/mjlab_playground/amp/rl/discriminator.py b/src/mjlab_playground/amp/rl/discriminator.py new file mode 100644 index 0000000..315988c --- /dev/null +++ b/src/mjlab_playground/amp/rl/discriminator.py @@ -0,0 +1,166 @@ +"""Discriminator network for adversarial motion priors.""" + +from __future__ import annotations + +import torch +import torch.nn as nn +import torch.nn.functional as F +from rsl_rl.modules import MLP, EmpiricalNormalization + + +class AmpDiscriminator(nn.Module): + """Small AMP discriminator using consecutive robot-only states.""" + + def __init__( + self, + amp_obs_dim: int, + hidden_dims: tuple[int, ...] = (512, 256), + activation: str = "relu", + amp_reward_coef: float = 0.3, + amp_reward_scale: float = 1.0, + task_reward_lerp: float = 0.7, + grad_penalty_coef: float = 10.0, + normalize_input: bool = True, + loss_type: str = "bce", + reward_type: str = "softplus", + expert_label: float = 0.9, + policy_label: float = 0.1, + reward_clip_max: float = 1.5, + ) -> None: + super().__init__() + self.amp_obs_dim = int(amp_obs_dim) + self.input_dim = 2 * self.amp_obs_dim + self.amp_reward_coef = float(amp_reward_coef) + self.amp_reward_scale = float(amp_reward_scale) + self.task_reward_lerp = float(task_reward_lerp) + self.grad_penalty_coef = float(grad_penalty_coef) + self.loss_type = str(loss_type) + self.reward_type = str(reward_type) + self.expert_label = float(expert_label) + self.policy_label = float(policy_label) + self.reward_clip_max = float(reward_clip_max) + self.normalizer = ( + EmpiricalNormalization(self.input_dim) if normalize_input else nn.Identity() + ) + self.net = MLP(self.input_dim, 1, hidden_dims, activation) + + def forward(self, state: torch.Tensor, next_state: torch.Tensor) -> torch.Tensor: + x = torch.cat((state, next_state), dim=-1) + return self.net(self.normalizer(x)) + + @torch.no_grad() + def predict_style_reward( + self, + state: torch.Tensor, + next_state: torch.Tensor, + amp_reward_scale: float | None = None, + ) -> tuple[torch.Tensor, torch.Tensor]: + """Return the AMP/style reward without mixing in task reward.""" + training = self.training + self.eval() + disc = self.forward(state, next_state) + amp_reward = self.amp_reward_coef * self._style_score(disc) + scale = ( + self.amp_reward_scale if amp_reward_scale is None else float(amp_reward_scale) + ) + amp_reward *= scale + if training: + self.train() + return amp_reward.squeeze(-1), disc.squeeze(-1) + + @torch.no_grad() + def predict_reward( + self, + state: torch.Tensor, + next_state: torch.Tensor, + task_reward: torch.Tensor, + amp_reward_scale: float | None = None, + ) -> tuple[torch.Tensor, torch.Tensor]: + amp_reward, disc = self.predict_style_reward( + state, + next_state, + amp_reward_scale=amp_reward_scale, + ) + task = task_reward.view(-1, 1) + mixed = (1.0 - self.task_reward_lerp) * amp_reward.view( + -1, 1 + ) + self.task_reward_lerp * task + return mixed.squeeze(-1), disc + + def discriminator_loss( + self, + policy_state: torch.Tensor, + policy_next_state: torch.Tensor, + expert_state: torch.Tensor, + expert_next_state: torch.Tensor, + ) -> tuple[torch.Tensor, dict[str, torch.Tensor]]: + if isinstance(self.normalizer, EmpiricalNormalization): + with torch.no_grad(): + self.normalizer.update(torch.cat((policy_state, policy_next_state), dim=-1)) + self.normalizer.update(torch.cat((expert_state, expert_next_state), dim=-1)) + + policy_d = self.forward(policy_state, policy_next_state) + expert_d = self.forward(expert_state, expert_next_state) + if self.loss_type == "least_squares": + expert_loss = F.mse_loss(expert_d, torch.ones_like(expert_d)) + policy_loss = F.mse_loss(policy_d, -torch.ones_like(policy_d)) + elif self.loss_type == "bce": + expert_loss = F.binary_cross_entropy_with_logits( + expert_d, + torch.full_like(expert_d, self.expert_label), + ) + policy_loss = F.binary_cross_entropy_with_logits( + policy_d, + torch.full_like(policy_d, self.policy_label), + ) + else: + raise ValueError(f"Unsupported AMP discriminator loss_type: {self.loss_type!r}.") + amp_loss = 0.5 * (expert_loss + policy_loss) + + grad_penalty = self._gradient_penalty(expert_state, expert_next_state) + total = amp_loss + grad_penalty + stats = { + "amp": amp_loss.detach(), + "amp_grad_pen": grad_penalty.detach(), + "amp_policy_pred": policy_d.mean().detach(), + "amp_expert_pred": expert_d.mean().detach(), + "amp_policy_style": self._style_score(policy_d).mean().detach(), + "amp_expert_style": self._style_score(expert_d).mean().detach(), + } + return total, stats + + def _style_score(self, disc: torch.Tensor) -> torch.Tensor: + if self.reward_type == "hardware_lsgan": + return torch.clamp(1.0 - 0.25 * torch.square(disc - 1.0), min=0.0) + if self.reward_type == "legacy_lsgan": + expert_score = torch.clamp(0.5 * (disc + 1.0), min=0.0, max=1.0) + return torch.square(expert_score) + if self.reward_type == "sigmoid": + return torch.sigmoid(disc) + if self.reward_type == "softplus": + clipped = torch.clamp(disc, min=-20.0, max=20.0) + score = F.softplus(clipped) / F.softplus( + torch.ones((), device=disc.device, dtype=disc.dtype) + ) + if self.reward_clip_max > 0.0: + score = torch.clamp(score, max=self.reward_clip_max) + return score + raise ValueError(f"Unsupported AMP reward_type: {self.reward_type!r}.") + + def _gradient_penalty( + self, + expert_state: torch.Tensor, + expert_next_state: torch.Tensor, + ) -> torch.Tensor: + expert_data = torch.cat((expert_state, expert_next_state), dim=-1).detach() + expert_data.requires_grad_(True) + disc = self.net(self.normalizer(expert_data)) + grad = torch.autograd.grad( + outputs=disc, + inputs=expert_data, + grad_outputs=torch.ones_like(disc), + create_graph=True, + retain_graph=True, + only_inputs=True, + )[0] + return self.grad_penalty_coef * torch.square(grad.norm(2, dim=1)).mean() diff --git a/src/mjlab_playground/amp/rl/distributions.py b/src/mjlab_playground/amp/rl/distributions.py new file mode 100644 index 0000000..ffedb83 --- /dev/null +++ b/src/mjlab_playground/amp/rl/distributions.py @@ -0,0 +1,49 @@ +"""Action distributions used by AMP/SMP robot-control tasks.""" + +from __future__ import annotations + +import math + +import torch +from rsl_rl.modules.distribution import GaussianDistribution +from torch.distributions import Normal + + +class ClampedGaussianDistribution(GaussianDistribution): + """Gaussian distribution with a bounded state-independent standard deviation.""" + + def __init__( + self, + output_dim: int, + init_std: float = 0.45, + std_type: str = "log", + min_std: float = 0.05, + max_std: float = 1.0, + ) -> None: + if min_std <= 0.0: + raise ValueError(f"min_std must be positive, got {min_std}.") + if max_std < min_std: + raise ValueError( + f"max_std must be greater than or equal to min_std, got {max_std} < {min_std}." + ) + super().__init__(output_dim=output_dim, init_std=init_std, std_type=std_type) + self.min_std = float(min_std) + self.max_std = float(max_std) + + def update(self, mlp_output: torch.Tensor) -> None: + """Update the Gaussian distribution from MLP output.""" + mean = mlp_output + if self.std_type == "scalar": + std = torch.clamp(self.std_param, self.min_std, self.max_std).expand_as(mean) + elif self.std_type == "log": + log_std = torch.clamp( + self.log_std_param, + math.log(self.min_std), + math.log(self.max_std), + ) + std = torch.exp(log_std).expand_as(mean) + else: + raise ValueError( + f"Unknown standard deviation type: {self.std_type}. Should be 'scalar' or 'log'." + ) + self._distribution = Normal(mean, std) diff --git a/src/mjlab_playground/amp/rl/motion_loader.py b/src/mjlab_playground/amp/rl/motion_loader.py new file mode 100644 index 0000000..1a617b5 --- /dev/null +++ b/src/mjlab_playground/amp/rl/motion_loader.py @@ -0,0 +1,746 @@ +"""Retarget motion loaders for AMP-style priors.""" + +from __future__ import annotations + +import os +import pickle +from pathlib import Path +from typing import Any + +import numpy as np +import torch + +from mjlab_playground.amp.amp_schema import ( + K1_AMP_KEY_BODY_NAMES, + amp_obs_dim, +) +from mjlab_playground.asset_zoo.robots.booster_k1.k1_constants import K1_JOINT_NAMES + +_DEFAULT_EMPHASIS_WEIGHT = 3.0 +_DEFAULT_VELOCITY_EMPHASIS_WEIGHT = 2.0 +_PACKAGED_MOTIONS_ROOT = ( + Path(__file__).resolve().parents[2] / "asset_zoo" / "motions" / "retargeted" +) +_ASSET_MOTIONS_ROOT = Path( + os.getenv("MJLAB_PLAYGROUND_MOTION_ROOT", str(_PACKAGED_MOTIONS_ROOT)) +).expanduser() + + +def _first_existing_dir(*candidates: Path) -> Path: + for candidate in candidates: + if candidate.exists(): + return candidate + return candidates[0] + + +def _k1_lafan_fk_root() -> Path: + return _ASSET_MOTIONS_ROOT / "lafan" / "k1_lafan1_fk_pkl" + + +def _t1_gmr_motion_root() -> Path: + return Path( + os.getenv( + "MJLAB_PLAYGROUND_T1_TRACKING_MOTION_DIR", + str(_ASSET_MOTIONS_ROOT / "tracking" / "booster_t1_23dof"), + ) + ).expanduser() + + +def _t1_lafan_fk_root() -> Path: + return _first_existing_dir( + Path( + os.getenv( + "MJLAB_PLAYGROUND_T1_LOCOMOTION_MOTION_DIR", + str(_ASSET_MOTIONS_ROOT / "lafan" / "booster_t1_lafan1_locomotion"), + ) + ).expanduser(), + _t1_gmr_motion_root(), + ) + + +def default_k1_locomotion_motion_files() -> tuple[str, ...]: + root = _k1_lafan_fk_root() + patterns = ("walk*.pkl", "run*.pkl", "sprint*.pkl") + files = [] + for pattern in patterns: + files.extend(sorted(root.glob(pattern))) + return tuple(str(path) for path in files) + + +def default_k1_locomotion_support_motion_files() -> dict[str, str]: + root = _k1_lafan_fk_root() + return { + "walk": str(root / "walk1_subject5.pkl"), + "run": str(root / "run1_subject5.pkl"), + "sprint": str(root / "sprint1_subject4.pkl"), + } + + +def default_t1_locomotion_motion_files() -> tuple[str, ...]: + root = _t1_lafan_fk_root() + patterns = ( + "walk*.pkl", + "run*.pkl", + "sprint*.pkl", + "lafan1_walk*.pkl", + "lafan1_run*.pkl", + "lafan1_sprint*.pkl", + ) + files = [] + for pattern in patterns: + files.extend(sorted(root.glob(pattern))) + return tuple(str(path) for path in files) + + +def default_k1_locomotion_amp_motion_groups() -> tuple[dict[str, Any], ...]: + return ( + { + "name": "loco", + "files": default_k1_locomotion_motion_files(), + "weight": 1.0, + }, + ) + + +def default_t1_locomotion_amp_motion_groups() -> tuple[dict[str, Any], ...]: + return ( + { + "name": "loco", + "files": default_t1_locomotion_motion_files(), + "weight": 1.0, + }, + ) + + +class RetargetedAmpMotionLoader: + """Samples consecutive humanoid AMP transitions from retarget files. + + The discriminator observes a robot-only state: + ``joint_pos, joint_vel, root height, projected gravity, root velocity, key-body + positions, key-body velocities``. This follows a common humanoid AMP + convention and gives the prior visibility into end-effector and support-foot + geometry. Key body positions come from GMR-exported + ``local_body_pos`` fields, not an ad hoc loader-side FK pass. + """ + + def __init__( + self, + motion_files: tuple[str, ...] | list[str] = (), + device: str | torch.device = "cpu", + expected_dof: int = 22, + motion_groups: Any | None = None, + target_fps: float | None = None, + joint_names: tuple[str, ...] | None = K1_JOINT_NAMES, + key_body_names: tuple[str, ...] | None = K1_AMP_KEY_BODY_NAMES, + emphasis_frame_offset_range: tuple[int, int] | list[int] | None = None, + emphasis_weight: float | None = None, + velocity_emphasis_weight: float | None = None, + ) -> None: + self.device = torch.device(device) + self.expected_dof = int(expected_dof) + self.joint_names = tuple(joint_names or K1_JOINT_NAMES) + self.target_fps = None if target_fps is None else max(float(target_fps), 1.0e-6) + if len(self.joint_names) != self.expected_dof: + raise ValueError( + "AMP joint order has " + f"{len(self.joint_names)} names, expected {self.expected_dof}." + ) + self.key_body_names = tuple(key_body_names or K1_AMP_KEY_BODY_NAMES) + self.expected_amp_obs_dim = amp_obs_dim( + self.expected_dof, + len(self.key_body_names), + ) + + groups = self._normalize_groups( + motion_files=motion_files, + motion_groups=motion_groups, + emphasis_frame_offset_range=emphasis_frame_offset_range, + emphasis_weight=emphasis_weight, + velocity_emphasis_weight=velocity_emphasis_weight, + ) + if not groups: + raise ValueError("No AMP motion files were provided.") + + loaded_groups = [] + for group in groups: + states = [] + next_states = [] + transition_weights = [] + for motion_file in group["files"]: + file_states, file_weights = self._load_file( + Path(motion_file), + emphasis_frame_offset_range=group.get("emphasis_frame_offset_range"), + emphasis_weight=group.get("emphasis_weight", 0.0), + velocity_emphasis_weight=group.get("velocity_emphasis_weight", 0.0), + ) + if file_states.shape[0] < 2: + continue + states.append(file_states[:-1]) + next_states.append(file_states[1:]) + transition_weights.append(file_weights) + if not states: + continue + sample_weights = np.concatenate(transition_weights, axis=0).astype(np.float32) + if not np.isfinite(sample_weights).all() or np.sum(sample_weights) <= 0.0: + sample_weights = np.ones_like(sample_weights, dtype=np.float32) + loaded_groups.append( + { + "name": group["name"], + "weight": float(group["weight"]), + "state": torch.tensor( + np.concatenate(states, axis=0), + dtype=torch.float32, + device=self.device, + ), + "next_state": torch.tensor( + np.concatenate(next_states, axis=0), + dtype=torch.float32, + device=self.device, + ), + "sample_weights": torch.tensor( + sample_weights, + dtype=torch.float32, + device=self.device, + ), + } + ) + + if not loaded_groups: + raise ValueError("No AMP transitions were loaded from the provided motion files.") + + self._groups = loaded_groups + self.group_names = tuple(group["name"] for group in loaded_groups) + self.group_counts = { + group["name"]: int(group["state"].shape[0]) for group in loaded_groups + } + self.group_sample_weight_sums = { + group["name"]: float(group["sample_weights"].sum().item()) + for group in loaded_groups + } + self.group_weights = torch.tensor( + [max(float(group["weight"]), 0.0) for group in loaded_groups], + dtype=torch.float32, + device=self.device, + ) + if torch.sum(self.group_weights) <= 0.0: + self.group_weights[:] = 1.0 + self.group_weights = self.group_weights / torch.sum(self.group_weights) + self.state = torch.cat([group["state"] for group in loaded_groups], dim=0) + self.next_state = torch.cat([group["next_state"] for group in loaded_groups], dim=0) + self.sample_weights = torch.cat( + [group["sample_weights"] for group in loaded_groups], dim=0 + ) + self.amp_obs_dim = int(self.state.shape[1]) + + @property + def num_transitions(self) -> int: + return int(self.state.shape[0]) + + def sample(self, batch_size: int) -> tuple[torch.Tensor, torch.Tensor]: + if len(self._groups) == 1: + group = self._groups[0] + idx = torch.multinomial( + group["sample_weights"], + batch_size, + replacement=True, + ) + return group["state"][idx], group["next_state"][idx] + + group_ids = torch.multinomial(self.group_weights, batch_size, replacement=True) + state = torch.empty(batch_size, self.amp_obs_dim, device=self.device) + next_state = torch.empty_like(state) + for group_id, group in enumerate(self._groups): + mask = group_ids == group_id + num = int(mask.sum().item()) + if num == 0: + continue + idx = torch.multinomial(group["sample_weights"], num, replacement=True) + state[mask] = group["state"][idx] + next_state[mask] = group["next_state"][idx] + return state, next_state + + def _normalize_groups( + self, + *, + motion_files: tuple[str, ...] | list[str], + motion_groups: Any | None, + emphasis_frame_offset_range: tuple[int, int] | list[int] | None, + emphasis_weight: float | None, + velocity_emphasis_weight: float | None, + ) -> list[dict[str, Any]]: + if motion_groups is None: + files = tuple(str(path) for path in motion_files) + default_window = _normalize_frame_offset_range( + emphasis_frame_offset_range, + field_name="emphasis_frame_offset_range", + ) + default_emphasis_weight = ( + _DEFAULT_EMPHASIS_WEIGHT + if emphasis_weight is None and default_window is not None + else float(emphasis_weight or 0.0) + ) + default_velocity_weight = ( + _DEFAULT_VELOCITY_EMPHASIS_WEIGHT + if velocity_emphasis_weight is None and default_window is not None + else float(velocity_emphasis_weight or 0.0) + ) + return ( + [ + { + "name": "default", + "files": files, + "weight": 1.0, + "emphasis_frame_offset_range": default_window, + "emphasis_weight": default_emphasis_weight, + "velocity_emphasis_weight": default_velocity_weight, + } + ] + if files + else [] + ) + + if isinstance(motion_groups, dict): + iterable = [ + {"name": name, **value} + if isinstance(value, dict) + else {"name": name, "files": value} + for name, value in motion_groups.items() + ] + else: + iterable = list(motion_groups) + + groups = [] + for i, group in enumerate(iterable): + if not isinstance(group, dict): + raise TypeError(f"AMP motion group {i} must be a dict, got {type(group)!r}.") + files = tuple(str(path) for path in group.get("files", ())) + if not files: + continue + emphasis_window = _normalize_frame_offset_range( + group.get("emphasis_frame_offset_range"), + field_name="emphasis_frame_offset_range", + ) + emphasis_weight = float( + group.get( + "emphasis_weight", + _DEFAULT_EMPHASIS_WEIGHT if emphasis_window is not None else 0.0, + ) + ) + velocity_weight = float( + group.get( + "velocity_emphasis_weight", + _DEFAULT_VELOCITY_EMPHASIS_WEIGHT if emphasis_window is not None else 0.0, + ) + ) + groups.append( + { + "name": str(group.get("name", f"group_{i}")), + "files": files, + "weight": float(group.get("weight", 1.0)), + "emphasis_frame_offset_range": emphasis_window, + "emphasis_weight": emphasis_weight, + "velocity_emphasis_weight": velocity_weight, + } + ) + return groups + + def _load_file( + self, + path: Path, + emphasis_frame_offset_range: tuple[int, int] | None = None, + emphasis_weight: float = 0.0, + velocity_emphasis_weight: float = 0.0, + ) -> tuple[np.ndarray, np.ndarray]: + if not path.exists(): + raise FileNotFoundError(f"AMP motion file not found: {path}") + if path.suffix.lower() == ".csv": + return self._load_gmr_fk_pkl_for_csv( + path, + emphasis_frame_offset_range=emphasis_frame_offset_range, + emphasis_weight=emphasis_weight, + velocity_emphasis_weight=velocity_emphasis_weight, + ) + if path.suffix.lower() not in {".pkl", ".pickle"}: + raise ValueError(f"Unsupported AMP motion file type: {path}") + with path.open("rb") as f: + data = pickle.load(f) + joint_pos = np.asarray(data["dof_pos"], dtype=np.float32) + fps = float(data.get("fps", 30.0)) + root_pos = np.asarray(data["root_pos"], dtype=np.float32) + root_quat_wxyz = _xyzw_to_wxyz(np.asarray(data["root_rot"], dtype=np.float32)) + body_names, body_pos = self._body_positions_from_pickle(data, path=path) + root_pos, root_quat_wxyz, body_pos, joint_pos, fps = _resample_state_parts( + root_pos, + root_quat_wxyz, + body_pos, + joint_pos, + src_fps=fps, + target_fps=self.target_fps, + ) + key_body_pos = self._key_body_pos_from_body_positions( + body_names, + body_pos, + joint_pos=joint_pos, + path=path, + ) + emphasis_frame = None + if emphasis_frame_offset_range is not None or velocity_emphasis_weight > 0.0: + emphasis_frame = self._infer_emphasis_frame( + body_names, + body_pos, + root_quat_wxyz=root_quat_wxyz, + path=path, + ) + transition_weights = self._transition_sample_weights( + body_names, + body_pos, + num_frames=joint_pos.shape[0], + fps=fps, + emphasis_frame=emphasis_frame, + emphasis_frame_offset_range=emphasis_frame_offset_range, + emphasis_weight=emphasis_weight, + velocity_emphasis_weight=velocity_emphasis_weight, + path=path, + ) + return ( + self._amp_state_obs( + joint_pos, + root_pos=root_pos, + root_quat_wxyz=root_quat_wxyz, + key_body_pos_b=key_body_pos, + fps=fps, + path=path, + ), + transition_weights, + ) + + def _load_gmr_fk_pkl_for_csv( + self, + path: Path, + emphasis_frame_offset_range: tuple[int, int] | None = None, + emphasis_weight: float = 0.0, + velocity_emphasis_weight: float = 0.0, + ) -> tuple[np.ndarray, np.ndarray]: + candidates = ( + path.with_suffix(".pkl"), + path.parent.parent / f"{path.parent.name}_fk_pkl" / f"{path.stem}.pkl", + path.parent.parent / "k1_lafan1_fk_pkl" / f"{path.stem}.pkl", + ) + for candidate in candidates: + if candidate.exists(): + return self._load_file( + candidate, + emphasis_frame_offset_range=emphasis_frame_offset_range, + emphasis_weight=emphasis_weight, + velocity_emphasis_weight=velocity_emphasis_weight, + ) + raise ValueError( + f"{path} is a root/joint CSV without GMR key-body data. AMP key-body " + "observations require a converted pkl with local_body_pos/link_body_list. " + f"Checked: {', '.join(str(candidate) for candidate in candidates)}." + ) + + def _amp_state_obs( + self, + joint_pos: np.ndarray, + *, + root_pos: np.ndarray, + root_quat_wxyz: np.ndarray, + key_body_pos_b: np.ndarray, + fps: float, + path: Path, + ) -> np.ndarray: + if joint_pos.ndim != 2 or joint_pos.shape[1] != self.expected_dof: + raise ValueError( + f"{path} has dof_pos shape {joint_pos.shape}, expected [T, {self.expected_dof}]." + ) + if root_pos.shape != (joint_pos.shape[0], 3): + raise ValueError(f"{path} has root_pos shape {root_pos.shape}, expected [T, 3].") + if root_quat_wxyz.shape != (joint_pos.shape[0], 4): + raise ValueError( + f"{path} has root_rot shape {root_quat_wxyz.shape}, expected [T, 4]." + ) + if key_body_pos_b.shape != (joint_pos.shape[0], len(self.key_body_names), 3): + raise ValueError( + f"{path} has key-body position shape {key_body_pos_b.shape}, " + f"expected [T, {len(self.key_body_names)}, 3]." + ) + dt = 1.0 / max(fps, 1.0e-6) + joint_vel = np.gradient(joint_pos, dt, axis=0).astype(np.float32) + root_quat_wxyz = _normalize_quat_wxyz(root_quat_wxyz) + root_lin_vel_w = np.gradient(root_pos, dt, axis=0).astype(np.float32) + root_lin_vel_b = _quat_apply_inverse_wxyz(root_quat_wxyz, root_lin_vel_w) + root_ang_vel_b = _quat_to_body_ang_vel_wxyz(root_quat_wxyz, dt) + projected_gravity_b = _quat_apply_inverse_wxyz( + root_quat_wxyz, + np.tile(np.array([[0.0, 0.0, -1.0]], dtype=np.float32), (joint_pos.shape[0], 1)), + ) + key_body_vel_b = np.gradient(key_body_pos_b, dt, axis=0).astype(np.float32) + obs = np.concatenate( + ( + joint_pos.astype(np.float32), + joint_vel, + root_pos[:, 2:3].astype(np.float32), + projected_gravity_b.astype(np.float32), + root_lin_vel_b.astype(np.float32), + root_ang_vel_b.astype(np.float32), + key_body_pos_b.reshape(joint_pos.shape[0], -1).astype(np.float32), + key_body_vel_b.reshape(joint_pos.shape[0], -1).astype(np.float32), + ), + axis=1, + ) + if obs.shape[1] != self.expected_amp_obs_dim: + raise ValueError( + f"{path} produced AMP obs dim {obs.shape[1]}, " + f"expected {self.expected_amp_obs_dim}." + ) + return np.nan_to_num(obs, nan=0.0, posinf=0.0, neginf=0.0) + + def _body_positions_from_pickle( + self, + data: dict[str, Any], + *, + path: Path, + ) -> tuple[list[str], np.ndarray]: + if data.get("local_body_pos") is None or "link_body_list" not in data: + raise ValueError( + f"{path} does not contain GMR local_body_pos/link_body_list. " + "Regenerate this motion with GMR KinematicsModel.forward_kinematics before AMP training." + ) + body_names = list(data["link_body_list"]) + body_pos = np.asarray(data["local_body_pos"], dtype=np.float32) + if body_pos.ndim != 3 or body_pos.shape[2] != 3: + raise ValueError( + f"{path} has local_body_pos shape {body_pos.shape}, expected [T, B, 3]." + ) + return body_names, body_pos + + def _key_body_pos_from_body_positions( + self, + body_names: list[str], + body_pos: np.ndarray, + *, + joint_pos: np.ndarray, + path: Path, + ) -> np.ndarray: + ids = [] + for name in self.key_body_names: + try: + ids.append(body_names.index(name)) + except ValueError as exc: + raise ValueError(f"{path} does not contain key body {name!r}.") from exc + key_body_pos = body_pos[:, ids, :] + if key_body_pos.shape[0] != joint_pos.shape[0]: + raise ValueError( + f"{path} has local_body_pos length {key_body_pos.shape[0]}, " + f"expected {joint_pos.shape[0]}." + ) + return key_body_pos + + def _infer_emphasis_frame( + self, + body_names: list[str], + body_pos: np.ndarray, + *, + root_quat_wxyz: np.ndarray | None = None, + path: Path, + ) -> int | None: + try: + right_foot_id = body_names.index("right_foot_link") + except ValueError as exc: + raise ValueError( + f"{path} does not contain right_foot_link in link_body_list." + ) from exc + right_foot_pos = body_pos[:, right_foot_id, :] + if root_quat_wxyz is not None: + root_roll_pitch = _roll_pitch_quat_wxyz(root_quat_wxyz) + right_foot_pos = _quat_apply_wxyz(root_roll_pitch, right_foot_pos) + return int(np.argmax(right_foot_pos[:, 0])) + + def _transition_sample_weights( + self, + body_names: list[str], + body_pos: np.ndarray, + *, + num_frames: int, + fps: float, + emphasis_frame: int | None, + emphasis_frame_offset_range: tuple[int, int] | None, + emphasis_weight: float, + velocity_emphasis_weight: float, + path: Path, + ) -> np.ndarray: + transition_weights = np.ones(max(num_frames - 1, 0), dtype=np.float32) + if transition_weights.size == 0: + return transition_weights + if emphasis_frame_offset_range is None and velocity_emphasis_weight <= 0.0: + return transition_weights + try: + right_foot_id = body_names.index("right_foot_link") + except ValueError as exc: + raise ValueError( + f"{path} does not contain right_foot_link in link_body_list." + ) from exc + if emphasis_frame is None: + emphasis_frame = int(np.argmax(body_pos[:, right_foot_id, 0])) + if emphasis_frame_offset_range is not None and emphasis_weight > 0.0: + offset_min, offset_max = emphasis_frame_offset_range + start = max(0, emphasis_frame + offset_min) + stop = min(num_frames - 1, emphasis_frame + offset_max) + transition_ids = np.arange(num_frames - 1, dtype=np.int64) + in_window = (transition_ids >= start) & (transition_ids <= stop) + transition_weights[in_window] += float(emphasis_weight) + + if velocity_emphasis_weight > 0.0: + dt = 1.0 / max(float(fps), 1.0e-6) + right_foot_vel = np.gradient(body_pos[:, right_foot_id, :], dt, axis=0) + right_foot_speed = np.linalg.norm(right_foot_vel, axis=1) + transition_speed = 0.5 * (right_foot_speed[:-1] + right_foot_speed[1:]) + speed_scale = float(np.percentile(transition_speed, 90.0)) + if speed_scale > 1.0e-6: + transition_weights += float(velocity_emphasis_weight) * np.clip( + transition_speed / speed_scale, + 0.0, + 1.0, + ).astype(np.float32) + return np.clip(transition_weights, 1.0e-6, None) + + +def _xyzw_to_wxyz(quat_xyzw: np.ndarray) -> np.ndarray: + if quat_xyzw.ndim != 2 or quat_xyzw.shape[1] != 4: + raise ValueError(f"Expected quaternion shape [T, 4], got {quat_xyzw.shape}.") + return _normalize_quat_wxyz(quat_xyzw[:, [3, 0, 1, 2]]) + + +def _normalize_frame_offset_range( + value: tuple[int, int] | list[int] | None, + *, + field_name: str, +) -> tuple[int, int] | None: + if value is None: + return None + if len(value) != 2: + raise ValueError(f"{field_name} must have two values, got {value!r}.") + start, stop = int(value[0]), int(value[1]) + if start > stop: + raise ValueError(f"{field_name} start must be <= stop, got {(start, stop)!r}.") + return start, stop + + +def _resample_array( + array: np.ndarray, + *, + src_fps: float, + target_fps: float | None, +) -> np.ndarray: + array = np.asarray(array, dtype=np.float32) + if target_fps is None or np.isclose(float(target_fps), float(src_fps)): + return array + if array.shape[0] <= 1: + return array + src_fps = max(float(src_fps), 1.0e-6) + target_fps = max(float(target_fps), 1.0e-6) + duration = (array.shape[0] - 1) / src_fps + target_frames = max(int(round(duration * target_fps)) + 1, 2) + src_t = np.arange(array.shape[0], dtype=np.float32) / src_fps + target_t = np.linspace(0.0, duration, target_frames, dtype=np.float32) + flat = array.reshape(array.shape[0], -1) + resampled = np.empty((target_frames, flat.shape[1]), dtype=np.float32) + for dim in range(flat.shape[1]): + resampled[:, dim] = np.interp(target_t, src_t, flat[:, dim]).astype(np.float32) + return resampled.reshape((target_frames, *array.shape[1:])) + + +def _resample_state_parts( + root_pos: np.ndarray, + root_quat_wxyz: np.ndarray, + body_pos: np.ndarray, + joint_pos: np.ndarray, + *, + src_fps: float, + target_fps: float | None, +) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, float]: + fps = float(target_fps) if target_fps is not None else float(src_fps) + root_pos = _resample_array(root_pos, src_fps=src_fps, target_fps=target_fps) + root_quat = _resample_array(root_quat_wxyz, src_fps=src_fps, target_fps=target_fps) + root_quat = _normalize_quat_wxyz(root_quat) + body_pos = _resample_array(body_pos, src_fps=src_fps, target_fps=target_fps) + joint_pos = _resample_array(joint_pos, src_fps=src_fps, target_fps=target_fps) + return root_pos, root_quat, body_pos, joint_pos, fps + + +def _normalize_quat_wxyz(quat: np.ndarray) -> np.ndarray: + quat = np.asarray(quat, dtype=np.float32) + norm = np.linalg.norm(quat, axis=-1, keepdims=True) + return quat / np.clip(norm, 1.0e-6, None) + + +def _quat_apply_inverse_wxyz(quat: np.ndarray, vec: np.ndarray) -> np.ndarray: + quat = _normalize_quat_wxyz(quat) + vec = np.asarray(vec, dtype=np.float32) + if quat.ndim == vec.ndim - 1: + quat = quat[:, None, :] + u = quat[..., 1:] + s = quat[..., :1] + return vec - 2.0 * s * np.cross(u, vec) + 2.0 * np.cross(u, np.cross(u, vec)) + + +def _quat_apply_wxyz(quat: np.ndarray, vec: np.ndarray) -> np.ndarray: + quat = _normalize_quat_wxyz(quat) + vec = np.asarray(vec, dtype=np.float32) + if quat.ndim == vec.ndim - 1: + quat = quat[:, None, :] + u = quat[..., 1:] + s = quat[..., :1] + return ( + 2.0 * np.sum(u * vec, axis=-1, keepdims=True) * u + + (s * s - np.sum(u * u, axis=-1, keepdims=True)) * vec + + 2.0 * s * np.cross(u, vec) + ).astype(np.float32) + + +def _roll_pitch_quat_wxyz(quat: np.ndarray) -> np.ndarray: + quat = _normalize_quat_wxyz(quat) + w, x, y, z = np.moveaxis(quat, -1, 0) + roll = np.arctan2(2.0 * (w * x + y * z), 1.0 - 2.0 * (x * x + y * y)) + pitch = np.arcsin(np.clip(2.0 * (w * y - z * x), -1.0, 1.0)) + half_roll = 0.5 * roll + half_pitch = 0.5 * pitch + cr = np.cos(half_roll) + sr = np.sin(half_roll) + cp = np.cos(half_pitch) + sp = np.sin(half_pitch) + return _normalize_quat_wxyz( + np.stack( + ( + cr * cp, + sr * cp, + cr * sp, + -sr * sp, + ), + axis=-1, + ).astype(np.float32) + ) + + +def _quat_mul_wxyz(a: np.ndarray, b: np.ndarray) -> np.ndarray: + aw, ax, ay, az = np.moveaxis(a, -1, 0) + bw, bx, by, bz = np.moveaxis(b, -1, 0) + return np.stack( + ( + aw * bw - ax * bx - ay * by - az * bz, + aw * bx + ax * bw + ay * bz - az * by, + aw * by - ax * bz + ay * bw + az * bx, + aw * bz + ax * by - ay * bx + az * bw, + ), + axis=-1, + ).astype(np.float32) + + +def _quat_to_body_ang_vel_wxyz(quat: np.ndarray, dt: float) -> np.ndarray: + quat = _normalize_quat_wxyz(quat) + qdot = np.gradient(quat, dt, axis=0).astype(np.float32) + quat_inv = quat.copy() + quat_inv[:, 1:] *= -1.0 + omega_quat = _quat_mul_wxyz(quat_inv, qdot) + return (2.0 * omega_quat[:, 1:]).astype(np.float32) diff --git a/src/mjlab_playground/amp/rl/replay_buffer.py b/src/mjlab_playground/amp/rl/replay_buffer.py new file mode 100644 index 0000000..839c5ab --- /dev/null +++ b/src/mjlab_playground/amp/rl/replay_buffer.py @@ -0,0 +1,57 @@ +"""Simple transition replay buffers for AMP state pairs.""" + +from __future__ import annotations + +import torch + + +class AmpTransitionBuffer: + """Circular replay buffer storing AMP state transitions.""" + + def __init__( + self, + capacity: int, + amp_obs_dim: int, + device: str | torch.device, + ) -> None: + self.capacity = int(capacity) + self.amp_obs_dim = int(amp_obs_dim) + self.device = torch.device(device) + self.state = torch.zeros(self.capacity, self.amp_obs_dim, device=self.device) + self.next_state = torch.zeros_like(self.state) + self._pos = 0 + self._size = 0 + + @property + def size(self) -> int: + return self._size + + def insert(self, state: torch.Tensor, next_state: torch.Tensor) -> None: + state = state.detach().to(self.device) + next_state = next_state.detach().to(self.device) + num = state.shape[0] + if num >= self.capacity: + self.state[:] = state[-self.capacity :] + self.next_state[:] = next_state[-self.capacity :] + self._pos = 0 + self._size = self.capacity + return + + end = self._pos + num + if end <= self.capacity: + self.state[self._pos : end] = state + self.next_state[self._pos : end] = next_state + else: + first = self.capacity - self._pos + self.state[self._pos :] = state[:first] + self.next_state[self._pos :] = next_state[:first] + self.state[: end % self.capacity] = state[first:] + self.next_state[: end % self.capacity] = next_state[first:] + self._pos = end % self.capacity + self._size = min(self.capacity, self._size + num) + + def sample(self, batch_size: int) -> tuple[torch.Tensor, torch.Tensor]: + if self._size <= 0: + raise RuntimeError("Cannot sample from an empty AMP replay buffer.") + idx = torch.randint(self._size, (batch_size,), device=self.device) + return self.state[idx], self.next_state[idx] diff --git a/src/mjlab_playground/amp/rl/runner.py b/src/mjlab_playground/amp/rl/runner.py new file mode 100644 index 0000000..386300c --- /dev/null +++ b/src/mjlab_playground/amp/rl/runner.py @@ -0,0 +1,379 @@ +"""On-policy runner with adversarial motion-prior rewards.""" + +# pyright: reportArgumentType=false, reportAttributeAccessIssue=false +# pyright: reportCallIssue=false, reportOptionalMemberAccess=false + +from __future__ import annotations + +import os +import time + +import torch +from mjlab.rl import MjlabOnPolicyRunner +from rsl_rl.utils import check_nan + +from mjlab_playground.amp.mdp.observations import amp_robot_obs + + +class AmpOnPolicyRunner(MjlabOnPolicyRunner): + """Runner that computes AMP observations around env.step().""" + + _VERBOSE_LOG_VALUES = {"1", "true", "yes", "on"} + + def _amp_obs(self) -> torch.Tensor: + return amp_robot_obs(self.env.unwrapped).to(self.device) + + def _amp_cfg(self) -> dict: + return dict( + getattr(self.alg, "amp_cfg", {}) or self.cfg["algorithm"].get("amp_cfg", {}) + ) + + def _log_namespace(self) -> str: + return str(self._amp_cfg().get("log_namespace", "amp")) + + def _verbose_log_enabled(self) -> bool: + return ( + os.environ.get("MJLAB_AMP_VERBOSE_LOG", "").lower() in self._VERBOSE_LOG_VALUES + ) + + def _stage_weights(self, valid: torch.Tensor) -> dict[str, torch.Tensor] | None: + if not getattr(self.alg, "uses_stage_amp", False): + return None + env = self.env.unwrapped + stage_weight_map = getattr(env, "_amp_stage_weight_map", None) + if isinstance(stage_weight_map, dict) and stage_weight_map: + resolved: dict[str, torch.Tensor] = {} + for name, weight in stage_weight_map.items(): + resolved[name] = weight.to(self.device).view(-1)[valid.view(-1)] + return resolved + + raise RuntimeError( + "Stage AMP is enabled but env._amp_stage_weight_map is not available." + ) + + def _task_reward_rate(self, task_rewards: torch.Tensor) -> tuple[torch.Tensor, float]: + env = self.env.unwrapped + scale = 1.0 + if getattr(env.cfg, "scale_rewards_by_dt", True): + scale = max(float(getattr(env, "step_dt", 1.0)), 1.0e-6) + return task_rewards / scale, scale + + def _style_reward_ceiling( + self, + task_rewards: torch.Tensor, + stage_weights: dict[str, torch.Tensor] | None, + ) -> torch.Tensor: + def discriminator_ceiling(discriminator) -> float: + return float(discriminator.amp_reward_coef) * float( + discriminator.amp_reward_scale + ) + + if not getattr(self.alg, "uses_stage_amp", False): + discriminator = getattr(self.alg, "discriminator", None) + ceiling = ( + discriminator_ceiling(discriminator) if discriminator is not None else 0.0 + ) + return torch.full_like(task_rewards, ceiling) + + total_weight = torch.zeros_like(task_rewards) + ceiling = torch.zeros_like(task_rewards) + for name, discriminator in self.alg.stage_discriminators.items(): + if stage_weights is None: + weight = torch.full_like(task_rewards, 1.0 / len(self.alg.stage_discriminators)) + else: + weight = stage_weights.get(name, torch.zeros_like(task_rewards)) + weight = weight.to(device=task_rewards.device, dtype=task_rewards.dtype).view( + -1 + ) + ceiling += weight * discriminator_ceiling(discriminator) + total_weight += weight + return ceiling / total_weight.clamp_min(1.0e-6) + + def _style_reward_gate( + self, + style_rewards: torch.Tensor, + dones: torch.Tensor, + ) -> tuple[torch.Tensor, torch.Tensor | None]: + gate_cfg = self._amp_cfg().get("style_reward_gate") + if not gate_cfg: + return style_rewards, None + + env = self.env.unwrapped + entity_name = str(gate_cfg.get("entity_name", "robot")) + asset = env.scene[entity_name] + healthy = torch.ones_like(style_rewards, dtype=torch.bool, device=self.device) + + if gate_cfg.get("zero_on_done", True): + healthy = healthy & (~dones.to(self.device).bool().view(-1)) + if gate_cfg.get("min_root_height") is not None: + root_height = asset.data.root_link_pos_w[:, 2].to(self.device).view(-1) + healthy = healthy & (root_height >= float(gate_cfg["min_root_height"])) + if gate_cfg.get("min_up_z") is not None: + up_z = (-asset.data.projected_gravity_b[:, 2]).to(self.device).view(-1) + healthy = healthy & (up_z >= float(gate_cfg["min_up_z"])) + + gated_style = torch.where(healthy, style_rewards, torch.zeros_like(style_rewards)) + return gated_style, healthy.float() + + def _apply_amp_reward_schedule(self) -> tuple[float, float]: + amp_cfg = dict(getattr(self.alg, "amp_cfg", {})) + if not amp_cfg: + amp_cfg = self.cfg["algorithm"].get("amp_cfg", {}) + schedule = amp_cfg.get("reward_mix_schedule", ()) + + task_reward_lerp = float(getattr(self.alg, "task_reward_lerp", 0.0)) + main_discriminator = getattr(self.alg, "discriminator", None) + reward_coef = ( + float(getattr(main_discriminator, "amp_reward_coef", 0.0)) + if main_discriminator is not None + else 0.0 + ) + + for stage in schedule: + if self.env.unwrapped.common_step_counter < int(stage["step"]): + continue + if "task_reward_lerp" in stage: + task_reward_lerp = float(stage["task_reward_lerp"]) + if "reward_coef" in stage: + reward_coef = float(stage["reward_coef"]) + + self.alg.task_reward_lerp = task_reward_lerp + discriminators = [] + if main_discriminator is not None: + discriminators.append(main_discriminator) + discriminators.extend(getattr(self.alg, "stage_discriminators", {}).values()) + + seen: set[int] = set() + for discriminator in discriminators: + disc_id = id(discriminator) + if disc_id in seen: + continue + seen.add(disc_id) + discriminator.task_reward_lerp = task_reward_lerp + discriminator.amp_reward_coef = reward_coef + + return task_reward_lerp, reward_coef + + def _disable_auto_reset_for_terminal_amp(self) -> None: + """Let the runner capture terminal AMP transitions before resetting envs.""" + self.env.unwrapped.cfg.auto_reset = False + + def _reset_done_envs(self, dones: torch.Tensor) -> torch.Tensor | None: + done_ids = dones.to(self.env.device).bool().nonzero(as_tuple=False).squeeze(-1) + if done_ids.numel() == 0: + return None + self.env.unwrapped.reset(env_ids=done_ids) + return self.env.get_observations().to(self.device) + + def _learning_start_iteration(self) -> int: + start_it = self.current_learning_iteration + if getattr(self, "_resume_next_iteration", False): + start_it += 1 + self._resume_next_iteration = False + return start_it + + def learn( + self, num_learning_iterations: int, init_at_random_ep_len: bool = False + ) -> None: + if self._amp_cfg().get("disable_init_at_random_ep_len", False): + init_at_random_ep_len = False + if init_at_random_ep_len: + self.env.episode_length_buf = torch.randint_like( + self.env.episode_length_buf, + high=int(self.env.max_episode_length), + ) + + obs = self.env.get_observations().to(self.device) + self.alg.train_mode() + + if self.is_distributed: + print(f"Synchronizing parameters for rank {self.gpu_global_rank}...") + self.alg.broadcast_parameters() + + self._disable_auto_reset_for_terminal_amp() + self.logger.init_logging_writer() + verbose_log = self._verbose_log_enabled() + log_namespace = self._log_namespace() + + start_it = self._learning_start_iteration() + total_it = start_it + num_learning_iterations + for it in range(start_it, total_it): + start = time.time() + with torch.inference_mode(): + for _ in range(self.cfg["num_steps_per_env"]): + amp_obs = self._amp_obs() + actions = self.alg.act(obs) + obs, task_rewards, dones, extras = self.env.step(actions.to(self.env.device)) + if self.cfg.get("check_for_nan", True): + check_nan(obs, task_rewards, dones) + next_amp_obs = self._amp_obs() + + obs = obs.to(self.device) + task_rewards = task_rewards.to(self.device) + dones = dones.to(self.device) + + valid = torch.ones_like(dones, dtype=torch.bool) + task_reward_weight, amp_reward_coef = self._apply_amp_reward_schedule() + amp_reward_weight = 1.0 - task_reward_weight + stage_weights = self._stage_weights(valid) + amp_rewards, amp_prior_rewards, amp_disc, amp_stage_logs = ( + self.alg.predict_amp_reward( + amp_obs, + next_amp_obs, + task_rewards, + stage_weights=stage_weights, + ) + ) + amp_prior_rewards, style_gate = self._style_reward_gate( + amp_prior_rewards, dones + ) + amp_rewards = ( + task_reward_weight * task_rewards + amp_reward_weight * amp_prior_rewards + ) + self.alg.process_amp_step( + amp_obs, + next_amp_obs, + stage_weights=stage_weights, + ) + + extras.setdefault("log", {})[f"Rewards/{log_namespace}/summary/task"] = ( + task_rewards.mean() + ) + task_component_mean = task_reward_weight * task_rewards.mean() + amp_component_mean = amp_reward_weight * amp_prior_rewards.mean() + extras["log"][f"Rewards/{log_namespace}/summary/style_prior"] = ( + amp_prior_rewards.mean() + ) + extras["log"][f"Rewards/{log_namespace}/summary/train_mixed"] = ( + amp_rewards.mean() + ) + extras["log"][f"Rewards/{log_namespace}/summary/style_to_task_abs"] = ( + torch.abs(amp_component_mean) + / torch.abs(task_component_mean).clamp_min(1.0e-6) + ) + if style_gate is not None: + extras["log"][f"Metrics/{log_namespace}/style_reward_gate_fraction"] = ( + style_gate.mean() + ) + extras["log"][f"Curriculum/{log_namespace}/task_reward_lerp"] = ( + torch.as_tensor( + task_reward_weight, + dtype=task_rewards.dtype, + device=task_rewards.device, + ) + ) + extras["log"][f"Curriculum/{log_namespace}/reward_coef"] = torch.as_tensor( + amp_reward_coef, + dtype=task_rewards.dtype, + device=task_rewards.device, + ) + if getattr(self.alg, "uses_stage_amp", False): + for name, replay in self.alg.stage_replay_buffers.items(): + extras["log"][f"Metrics/{log_namespace}/amp/{name}_replay_size"] = ( + torch.as_tensor( + replay.size, + dtype=task_rewards.dtype, + device=task_rewards.device, + ) + ) + else: + replay = getattr(self.alg, "amp_replay", None) + if replay is not None: + extras["log"][f"Metrics/{log_namespace}/amp/replay_size"] = ( + torch.as_tensor( + replay.size, + dtype=task_rewards.dtype, + device=task_rewards.device, + ) + ) + if verbose_log: + task_reward_rate, task_dt_scale = self._task_reward_rate(task_rewards) + style_ceiling = self._style_reward_ceiling(task_rewards, stage_weights) + amp_ceiling_component_mean = amp_reward_weight * style_ceiling.mean() + extras["log"][ + f"RewardsDebug/{log_namespace}/summary/task_rate_unscaled" + ] = task_reward_rate.mean() + extras["log"][f"RewardsDebug/{log_namespace}/components/task"] = ( + task_component_mean + ) + extras["log"][f"RewardsDebug/{log_namespace}/components/style"] = ( + amp_component_mean + ) + extras["log"][f"RewardsDebug/{log_namespace}/components/style_ceiling"] = ( + amp_ceiling_component_mean + ) + extras["log"][ + f"RewardsDebug/{log_namespace}/ratios/style_ceiling_to_task_abs" + ] = torch.abs(amp_ceiling_component_mean) / torch.abs( + task_component_mean + ).clamp_min(1.0e-6) + extras["log"][ + f"MetricsDebug/{log_namespace}/reward_scale/task_dt_scale" + ] = torch.as_tensor( + task_dt_scale, + dtype=task_rewards.dtype, + device=task_rewards.device, + ) + extras["log"][ + f"MetricsDebug/{log_namespace}/reward_scale/style_fraction_of_ceiling" + ] = amp_prior_rewards.mean() / style_ceiling.mean().clamp_min(1.0e-6) + extras["log"][f"RewardsDebug/{log_namespace}/amp/scale"] = torch.as_tensor( + style_ceiling.mean(), + dtype=task_rewards.dtype, + device=task_rewards.device, + ) + extras["log"][f"MetricsDebug/{log_namespace}/amp/disc_pred_mean"] = ( + amp_disc.mean() + ) + extras["log"][f"MetricsDebug/{log_namespace}/amp/done_transition_ratio"] = ( + dones.float().mean() + ) + for key, value in amp_stage_logs.items(): + extras["log"][f"MetricsDebug/{log_namespace}/amp/{key}"] = value.mean() + + step_extras = dict(extras) + if "log" in extras: + step_extras["log"] = dict(extras["log"]) + obs_after_reset = self._reset_done_envs(dones) + if obs_after_reset is not None: + obs = obs_after_reset + + self.alg.process_env_step(obs, amp_rewards, dones, step_extras) + intrinsic_rewards = ( + self.alg.intrinsic_rewards if self.cfg["algorithm"]["rnd_cfg"] else None + ) + self.logger.process_env_step( + amp_rewards, dones, step_extras, intrinsic_rewards + ) + + stop = time.time() + collect_time = stop - start + start = stop + self.alg.compute_returns(obs) + + loss_dict = self.alg.update() + + stop = time.time() + learn_time = stop - start + self.current_learning_iteration = it + self.logger.log( + it=it, + start_it=start_it, + total_it=total_it, + collect_time=collect_time, + learn_time=learn_time, + loss_dict=loss_dict, + learning_rate=self.alg.learning_rate, + action_std=self.alg.get_policy().output_std, + rnd_weight=self.alg.rnd.weight if self.cfg["algorithm"]["rnd_cfg"] else None, + print_minimal=not verbose_log, + ) + + if self.logger.writer is not None and it % self.cfg["save_interval"] == 0: + self.save(os.path.join(self.logger.log_dir, f"model_{it}.pt")) # type: ignore[arg-type] + + if self.logger.writer is not None: + self.save( + os.path.join(self.logger.log_dir, f"model_{self.current_learning_iteration}.pt") + ) # type: ignore[arg-type] + self.logger.stop_logging_writer() diff --git a/src/mjlab_playground/amp/rl/runner_cfg.py b/src/mjlab_playground/amp/rl/runner_cfg.py new file mode 100644 index 0000000..d5a6040 --- /dev/null +++ b/src/mjlab_playground/amp/rl/runner_cfg.py @@ -0,0 +1,22 @@ +"""Shared runner and algorithm configuration for AMP tasks.""" + +# pyright: reportIncompatibleVariableOverride=false + +from __future__ import annotations + +from dataclasses import dataclass, field + +from mjlab.rl import RslRlOnPolicyRunnerCfg, RslRlPpoAlgorithmCfg + + +@dataclass +class AmpPpoAlgorithmCfg(RslRlPpoAlgorithmCfg): + class_name: str = "mjlab_playground.amp.rl.amp_ppo:AmpPPO" + amp_cfg: dict = field(default_factory=dict) + symmetry_cfg: dict | None = None + + +@dataclass +class AmpOnPolicyRunnerCfg(RslRlOnPolicyRunnerCfg): + class_name: str = "mjlab_playground.amp.rl.runner:AmpOnPolicyRunner" + algorithm: AmpPpoAlgorithmCfg = field(default_factory=AmpPpoAlgorithmCfg) diff --git a/src/mjlab_playground/asset_zoo/motions/retargeted/README.md b/src/mjlab_playground/asset_zoo/motions/retargeted/README.md new file mode 100644 index 0000000..7add7ca --- /dev/null +++ b/src/mjlab_playground/asset_zoo/motions/retargeted/README.md @@ -0,0 +1,5 @@ +# Local retargeted motions + +Keep locally generated motion artifacts below this directory only when a package-relative path is convenient. Git ignores the contents of this directory except for this README. + +Prefer setting `MJLAB_PLAYGROUND_MOTION_ROOT` or `MJLAB_TRACKING_MOTION_FILE` to a data directory outside the source tree. See the repository's `docs/motion_data.md` for the expected layout, schema, and redistribution policy. diff --git a/src/mjlab_playground/asset_zoo/robots/booster_k1/__init__.py b/src/mjlab_playground/asset_zoo/robots/booster_k1/__init__.py new file mode 100644 index 0000000..e63d5e7 --- /dev/null +++ b/src/mjlab_playground/asset_zoo/robots/booster_k1/__init__.py @@ -0,0 +1 @@ +"""Booster K1 humanoid.""" diff --git a/src/mjlab_playground/asset_zoo/robots/booster_k1/k1_constants.py b/src/mjlab_playground/asset_zoo/robots/booster_k1/k1_constants.py new file mode 100644 index 0000000..46539b5 --- /dev/null +++ b/src/mjlab_playground/asset_zoo/robots/booster_k1/k1_constants.py @@ -0,0 +1,213 @@ +"""Booster K1 constants.""" + +import os +from pathlib import Path + +import mujoco +from mjlab.actuator import BuiltinPositionActuatorCfg +from mjlab.entity import EntityArticulationInfoCfg, EntityCfg + +## +# MJCF and assets. +## + +_PACKAGED_K1_XML = Path(__file__).parent / "xmls" / "k1" / "robot.xml" + + +def _resolve_k1_xml() -> Path: + candidates = ( + Path(os.environ["MJLAB_K1_XML"]) if "MJLAB_K1_XML" in os.environ else None, + _PACKAGED_K1_XML, + ) + for path in candidates: + if path is not None and path.exists(): + return path + checked = ", ".join(str(path) for path in candidates if path is not None) + raise FileNotFoundError( + "K1 MJCF robot.xml was not found. Set MJLAB_K1_XML or sync " + f"the packaged K1 xmls directory. Checked: {checked}" + ) + + +def get_spec() -> mujoco.MjSpec: + xml_path = _resolve_k1_xml() + xml = xml_path.read_text() + xml = xml.replace('meshdir="k1/meshes/"', 'meshdir=""') + meshes_dir = xml_path.parent / "meshes" + assets = { + path.name: path.read_bytes() for path in meshes_dir.glob("*") if path.is_file() + } + spec = mujoco.MjSpec.from_string(xml, assets=assets) + while spec.actuators: + spec.delete(spec.actuators[0]) + return spec + + +## +# Actuator config. +## + + +K1_JOINT_NAMES: tuple[str, ...] = ( + "AAHead_yaw", + "Head_pitch", + "ALeft_Shoulder_Pitch", + "Left_Shoulder_Roll", + "Left_Elbow_Pitch", + "Left_Elbow_Yaw", + "ARight_Shoulder_Pitch", + "Right_Shoulder_Roll", + "Right_Elbow_Pitch", + "Right_Elbow_Yaw", + "Left_Hip_Pitch", + "Left_Hip_Roll", + "Left_Hip_Yaw", + "Left_Knee_Pitch", + "Left_Ankle_Pitch", + "Left_Ankle_Roll", + "Right_Hip_Pitch", + "Right_Hip_Roll", + "Right_Hip_Yaw", + "Right_Knee_Pitch", + "Right_Ankle_Pitch", + "Right_Ankle_Roll", +) + + +def _kp(armature: float, natural_freq: float) -> float: + return armature * (2 * 3.1415926535 * natural_freq) ** 2 + + +def _kv(armature: float, natural_freq: float, damping_ratio: float) -> float: + return 2.0 * damping_ratio * armature * (2 * 3.1415926535 * natural_freq) + + +_NF = 4.0 + +K1_ACTUATOR_HEAD = BuiltinPositionActuatorCfg( + target_names_expr=("AAHead_yaw", "Head_pitch"), + stiffness=_kp(0.001, _NF), + damping=_kv(0.001, _NF, 2.0), + effort_limit=6.0, + armature=0.001, +) + +K1_ACTUATOR_ARM = BuiltinPositionActuatorCfg( + target_names_expr=( + "ALeft_Shoulder_Pitch", + "Left_Shoulder_Roll", + "Left_Elbow_Pitch", + "Left_Elbow_Yaw", + "ARight_Shoulder_Pitch", + "Right_Shoulder_Roll", + "Right_Elbow_Pitch", + "Right_Elbow_Yaw", + ), + stiffness=_kp(0.001, _NF), + damping=_kv(0.001, _NF, 2.0), + effort_limit=14.0, + armature=0.001, +) + +K1_ACTUATOR_HIP_PITCH = BuiltinPositionActuatorCfg( + target_names_expr=(".*_Hip_Pitch",), + stiffness=_kp(0.0478125, _NF), + damping=_kv(0.0478125, _NF, 1.5), + effort_limit=68.0, + armature=0.0478125, +) + +K1_ACTUATOR_HIP_ROLL = BuiltinPositionActuatorCfg( + target_names_expr=(".*_Hip_Roll",), + stiffness=_kp(0.0339552, _NF), + damping=_kv(0.0339552, _NF, 1.5), + effort_limit=76.0, + armature=0.0339552, +) + +K1_ACTUATOR_HIP_YAW = BuiltinPositionActuatorCfg( + target_names_expr=(".*_Hip_Yaw",), + stiffness=_kp(0.0282528, _NF), + damping=_kv(0.0282528, _NF, 1.5), + effort_limit=38.3, + armature=0.0282528, +) + +K1_ACTUATOR_KNEE = BuiltinPositionActuatorCfg( + target_names_expr=(".*_Knee_Pitch",), + stiffness=_kp(0.095625, _NF), + damping=_kv(0.095625, _NF, 1.0), + effort_limit=112.0, + armature=0.095625, +) + +_ANKLE_ARMATURE = 0.0282528 * 2.0 + +K1_ACTUATOR_ANKLE_PITCH = BuiltinPositionActuatorCfg( + target_names_expr=(".*_Ankle_Pitch",), + stiffness=_kp(_ANKLE_ARMATURE, _NF), + damping=_kv(_ANKLE_ARMATURE, _NF, 1.5), + effort_limit=38.3, + armature=_ANKLE_ARMATURE, +) + +K1_ACTUATOR_ANKLE_ROLL = BuiltinPositionActuatorCfg( + target_names_expr=(".*_Ankle_Roll",), + stiffness=_kp(_ANKLE_ARMATURE, _NF), + damping=_kv(_ANKLE_ARMATURE, _NF, 1.5), + effort_limit=38.3, + armature=_ANKLE_ARMATURE, +) + +## +# Keyframes. +## + +HOME_KEYFRAME = EntityCfg.InitialStateCfg( + pos=(0.0, 0.0, 0.543), + joint_pos={ + "Left_Shoulder_Roll": -1.3, + "Left_Elbow_Yaw": -0.4, + "Right_Shoulder_Roll": 1.3, + "Right_Elbow_Yaw": 0.4, + ".*_Hip_Pitch": -0.2, + ".*_Knee_Pitch": 0.4, + ".*_Ankle_Pitch": -0.2, + }, + joint_vel={".*": 0.0}, +) + +## +# Final config. +## + +K1_ARTICULATION = EntityArticulationInfoCfg( + actuators=( + K1_ACTUATOR_HEAD, + K1_ACTUATOR_ARM, + K1_ACTUATOR_HIP_PITCH, + K1_ACTUATOR_HIP_ROLL, + K1_ACTUATOR_HIP_YAW, + K1_ACTUATOR_KNEE, + K1_ACTUATOR_ANKLE_PITCH, + K1_ACTUATOR_ANKLE_ROLL, + ), + soft_joint_pos_limit_factor=0.9, +) + + +def get_k1_robot_cfg() -> EntityCfg: + """Get a fresh K1 robot configuration instance.""" + return EntityCfg( + init_state=HOME_KEYFRAME, + spec_fn=get_spec, + articulation=K1_ARTICULATION, + ) + + +K1_ACTION_SCALE: dict[str, float] = {} +for a in K1_ARTICULATION.actuators: + assert isinstance(a, BuiltinPositionActuatorCfg) + assert a.effort_limit is not None + for n in a.target_names_expr: + K1_ACTION_SCALE[n] = 0.25 * a.effort_limit / a.stiffness diff --git a/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Ankle_Cross.STL b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Ankle_Cross.STL new file mode 100644 index 0000000..6807ae4 Binary files /dev/null and b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Ankle_Cross.STL differ diff --git a/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Head_1.STL b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Head_1.STL new file mode 100644 index 0000000..9b363d0 Binary files /dev/null and b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Head_1.STL differ diff --git a/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Head_2.STL b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Head_2.STL new file mode 100644 index 0000000..c22b6af Binary files /dev/null and b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Head_2.STL differ diff --git a/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Head_2_ZED.STL b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Head_2_ZED.STL new file mode 100644 index 0000000..fc001e3 Binary files /dev/null and b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Head_2_ZED.STL differ diff --git a/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Hip_Roll.STL b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Hip_Roll.STL new file mode 100644 index 0000000..4624783 Binary files /dev/null and b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Hip_Roll.STL differ diff --git a/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/K1logo.STL b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/K1logo.STL new file mode 100644 index 0000000..168ef8f Binary files /dev/null and b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/K1logo.STL differ diff --git a/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Left_Arm_1.STL b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Left_Arm_1.STL new file mode 100644 index 0000000..5032fb2 Binary files /dev/null and b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Left_Arm_1.STL differ diff --git a/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Left_Arm_2.STL b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Left_Arm_2.STL new file mode 100644 index 0000000..202e54c Binary files /dev/null and b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Left_Arm_2.STL differ diff --git a/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Left_Arm_3.STL b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Left_Arm_3.STL new file mode 100644 index 0000000..cc77fec Binary files /dev/null and b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Left_Arm_3.STL differ diff --git a/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Left_Arm_4.STL b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Left_Arm_4.STL new file mode 100644 index 0000000..d361bde Binary files /dev/null and b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Left_Arm_4.STL differ diff --git a/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Left_Foot.STL b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Left_Foot.STL new file mode 100644 index 0000000..242827b Binary files /dev/null and b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Left_Foot.STL differ diff --git a/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Left_Hip_Pitch.STL b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Left_Hip_Pitch.STL new file mode 100644 index 0000000..cd2827c Binary files /dev/null and b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Left_Hip_Pitch.STL differ diff --git a/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Left_Hip_Yaw.STL b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Left_Hip_Yaw.STL new file mode 100644 index 0000000..9f24efb Binary files /dev/null and b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Left_Hip_Yaw.STL differ diff --git a/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Left_Shank.STL b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Left_Shank.STL new file mode 100644 index 0000000..da6dc7c Binary files /dev/null and b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Left_Shank.STL differ diff --git a/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Right_Arm_1.STL b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Right_Arm_1.STL new file mode 100644 index 0000000..60480d9 Binary files /dev/null and b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Right_Arm_1.STL differ diff --git a/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Right_Arm_2.STL b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Right_Arm_2.STL new file mode 100644 index 0000000..9996b42 Binary files /dev/null and b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Right_Arm_2.STL differ diff --git a/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Right_Arm_3.STL b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Right_Arm_3.STL new file mode 100644 index 0000000..56208e2 Binary files /dev/null and b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Right_Arm_3.STL differ diff --git a/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Right_Arm_4.STL b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Right_Arm_4.STL new file mode 100644 index 0000000..40bc43b Binary files /dev/null and b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Right_Arm_4.STL differ diff --git a/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Right_Foot.STL b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Right_Foot.STL new file mode 100644 index 0000000..f5855c7 Binary files /dev/null and b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Right_Foot.STL differ diff --git a/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Right_Hip_Pitch.STL b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Right_Hip_Pitch.STL new file mode 100644 index 0000000..889cd10 Binary files /dev/null and b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Right_Hip_Pitch.STL differ diff --git a/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Right_Hip_Yaw.STL b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Right_Hip_Yaw.STL new file mode 100644 index 0000000..d2e0b9f Binary files /dev/null and b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Right_Hip_Yaw.STL differ diff --git a/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Right_Shank.STL b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Right_Shank.STL new file mode 100644 index 0000000..7d752ad Binary files /dev/null and b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Right_Shank.STL differ diff --git a/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Trunk.STL b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Trunk.STL new file mode 100644 index 0000000..ec286f0 Binary files /dev/null and b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/meshes/Trunk.STL differ diff --git a/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/robot.xml b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/robot.xml new file mode 100644 index 0000000..6db43b0 --- /dev/null +++ b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/k1/robot.xml @@ -0,0 +1,420 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/motphys-ground.png b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/motphys-ground.png new file mode 100644 index 0000000..04967dc Binary files /dev/null and b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/motphys-ground.png differ diff --git a/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/scene_getup.xml b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/scene_getup.xml new file mode 100644 index 0000000..ad6c306 --- /dev/null +++ b/src/mjlab_playground/asset_zoo/robots/booster_k1/xmls/scene_getup.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + diff --git a/src/mjlab_playground/asset_zoo/robots/booster_t1/t1_constants.py b/src/mjlab_playground/asset_zoo/robots/booster_t1/t1_constants.py index 6a3bc54..c13bd01 100644 --- a/src/mjlab_playground/asset_zoo/robots/booster_t1/t1_constants.py +++ b/src/mjlab_playground/asset_zoo/robots/booster_t1/t1_constants.py @@ -1,5 +1,16 @@ -"""Booster T1 constants.""" +"""Booster T1 constants. +This profile keeps the mjlab position-actuator control law but aligns the +actuated joint limits/passive terms with the active competition-side T1: +- joint armature: 0.01 +- joint viscous damping: 0.01 for the competition-collision profile +- joint frictionloss: 0.1 +- actuator effort limits from the competition robot joint classes +""" + +# pyright: reportAttributeAccessIssue=false + +from copy import deepcopy from pathlib import Path import mujoco @@ -36,35 +47,70 @@ def get_spec() -> mujoco.MjSpec: ARM_ACTUATOR = ElectricActuator( reflected_inertia=reflected_inertia(21.8e-6, 36), velocity_limit=_rpm(89), - effort_limit=36.0, + effort_limit=18.0, ) WAIST_HIP_ROLL_YAW_ACTUATOR = ElectricActuator( reflected_inertia=reflected_inertia(76.5e-6, 25), velocity_limit=_rpm(70), - effort_limit=40.0, + effort_limit=30.0, ) HIP_PITCH_ACTUATOR = ElectricActuator( reflected_inertia=reflected_inertia(161.7e-6, 18), velocity_limit=_rpm(157), - effort_limit=55.0, + effort_limit=45.0, ) KNEE_ACTUATOR = ElectricActuator( reflected_inertia=reflected_inertia(196.3e-6, 18), velocity_limit=_rpm(140), - effort_limit=65.0, + effort_limit=60.0, +) + +ANKLE_PITCH_ACTUATOR = ElectricActuator( + reflected_inertia=reflected_inertia(26.2e-6, 36), + velocity_limit=_rpm(117), + effort_limit=20.0, ) -ANKLE_ACTUATOR = ElectricActuator( +ANKLE_ROLL_ACTUATOR = ElectricActuator( reflected_inertia=reflected_inertia(26.2e-6, 36), velocity_limit=_rpm(117), - effort_limit=50.0, + effort_limit=15.0, ) NATURAL_FREQ = 5.0 * 2.0 * 3.14159265 # 5 Hz DAMPING_RATIO = 2.0 +COMPETITION_JOINT_ARMATURE = 0.01 +COMPETITION_JOINT_FRICTIONLOSS = 0.1 +COMPETITION_JOINT_VISCOUS_DAMPING = 0.01 + +T1_JOINT_NAMES: tuple[str, ...] = ( + "AAHead_yaw", + "Head_pitch", + "Left_Shoulder_Pitch", + "Left_Shoulder_Roll", + "Left_Elbow_Pitch", + "Left_Elbow_Yaw", + "Right_Shoulder_Pitch", + "Right_Shoulder_Roll", + "Right_Elbow_Pitch", + "Right_Elbow_Yaw", + "Waist", + "Left_Hip_Pitch", + "Left_Hip_Roll", + "Left_Hip_Yaw", + "Left_Knee_Pitch", + "Left_Ankle_Pitch", + "Left_Ankle_Roll", + "Right_Hip_Pitch", + "Right_Hip_Roll", + "Right_Hip_Yaw", + "Right_Knee_Pitch", + "Right_Ankle_Pitch", + "Right_Ankle_Roll", +) def _kp(act: ElectricActuator) -> float: @@ -80,7 +126,8 @@ def _kv(act: ElectricActuator) -> float: stiffness=_kp(NECK_ACTUATOR), damping=_kv(NECK_ACTUATOR), effort_limit=NECK_ACTUATOR.effort_limit, - armature=NECK_ACTUATOR.reflected_inertia, + armature=COMPETITION_JOINT_ARMATURE, + frictionloss=COMPETITION_JOINT_FRICTIONLOSS, ) T1_ACTUATOR_ARM = BuiltinPositionActuatorCfg( @@ -93,7 +140,8 @@ def _kv(act: ElectricActuator) -> float: stiffness=_kp(ARM_ACTUATOR), damping=_kv(ARM_ACTUATOR), effort_limit=ARM_ACTUATOR.effort_limit, - armature=ARM_ACTUATOR.reflected_inertia, + armature=COMPETITION_JOINT_ARMATURE, + frictionloss=COMPETITION_JOINT_FRICTIONLOSS, ) T1_ACTUATOR_WAIST_HIP_ROLL_YAW = BuiltinPositionActuatorCfg( @@ -101,7 +149,8 @@ def _kv(act: ElectricActuator) -> float: stiffness=_kp(WAIST_HIP_ROLL_YAW_ACTUATOR), damping=_kv(WAIST_HIP_ROLL_YAW_ACTUATOR), effort_limit=WAIST_HIP_ROLL_YAW_ACTUATOR.effort_limit, - armature=WAIST_HIP_ROLL_YAW_ACTUATOR.reflected_inertia, + armature=COMPETITION_JOINT_ARMATURE, + frictionloss=COMPETITION_JOINT_FRICTIONLOSS, ) T1_ACTUATOR_HIP_PITCH = BuiltinPositionActuatorCfg( @@ -109,7 +158,8 @@ def _kv(act: ElectricActuator) -> float: stiffness=_kp(HIP_PITCH_ACTUATOR), damping=_kv(HIP_PITCH_ACTUATOR), effort_limit=HIP_PITCH_ACTUATOR.effort_limit, - armature=HIP_PITCH_ACTUATOR.reflected_inertia, + armature=COMPETITION_JOINT_ARMATURE, + frictionloss=COMPETITION_JOINT_FRICTIONLOSS, ) T1_ACTUATOR_KNEE = BuiltinPositionActuatorCfg( @@ -117,15 +167,26 @@ def _kv(act: ElectricActuator) -> float: stiffness=_kp(KNEE_ACTUATOR), damping=_kv(KNEE_ACTUATOR), effort_limit=KNEE_ACTUATOR.effort_limit, - armature=KNEE_ACTUATOR.reflected_inertia, + armature=COMPETITION_JOINT_ARMATURE, + frictionloss=COMPETITION_JOINT_FRICTIONLOSS, +) + +T1_ACTUATOR_ANKLE_PITCH = BuiltinPositionActuatorCfg( + target_names_expr=(".*_Ankle_Pitch",), + stiffness=_kp(ANKLE_PITCH_ACTUATOR), + damping=_kv(ANKLE_PITCH_ACTUATOR), + effort_limit=ANKLE_PITCH_ACTUATOR.effort_limit, + armature=COMPETITION_JOINT_ARMATURE, + frictionloss=COMPETITION_JOINT_FRICTIONLOSS, ) -T1_ACTUATOR_ANKLE = BuiltinPositionActuatorCfg( - target_names_expr=(".*_Ankle_Pitch", ".*_Ankle_Roll"), - stiffness=_kp(ANKLE_ACTUATOR), - damping=_kv(ANKLE_ACTUATOR), - effort_limit=ANKLE_ACTUATOR.effort_limit, - armature=ANKLE_ACTUATOR.reflected_inertia, +T1_ACTUATOR_ANKLE_ROLL = BuiltinPositionActuatorCfg( + target_names_expr=(".*_Ankle_Roll",), + stiffness=_kp(ANKLE_ROLL_ACTUATOR), + damping=_kv(ANKLE_ROLL_ACTUATOR), + effort_limit=ANKLE_ROLL_ACTUATOR.effort_limit, + armature=COMPETITION_JOINT_ARMATURE, + frictionloss=COMPETITION_JOINT_FRICTIONLOSS, ) ## @@ -151,6 +212,26 @@ def _kv(act: ElectricActuator) -> float: ## _foot_regex = r"^(left|right)_foot\d+_collision$" +_competition_foot_regex = r"^(left|right)_foot$" +_competition_foot_friction = (1.0, 0.01, 0.005) +_competition_foot_solref = (0.02, 1.0) +_competition_foot_solimp = (0.015, 1.0, 0.015, 0.5, 2.0) +_competition_collision_regex = ( + r"^(torso|head|left_forearm|left_hand|right_forearm|right_hand|" + r"left_thigh|left_calf|left_foot|right_thigh|right_calf|right_foot)$" +) +_competition_collision_friction = (1.0, 0.01, 0.005) +_competition_collision_solref = (0.02, 1.0) +_competition_collision_solimp = (0.9, 0.95, 0.001, 0.5, 2.0) +_competition_collision_rgba = (0.0, 1.0, 0.0, 0.0) +_competition_foot_inertia = (0.00268212, 0.002385, 0.000726885) +_competition_foot_iquat = (0.0, 0.6799274021193834, 0.0, 0.7332794336725845) +_competition_contact_excludes = ( + ("Hip_Roll_Left", "Shank_Left"), + ("Shank_Left", "left_foot_link"), + ("Hip_Roll_Right", "Shank_Right"), + ("Shank_Right", "right_foot_link"), +) FULL_COLLISION = CollisionCfg( geom_names_expr=(".*_collision",), @@ -160,6 +241,30 @@ def _kv(act: ElectricActuator) -> float: priority=1, ) +COMPETITION_FOOT_COLLISION = CollisionCfg( + geom_names_expr=(".*_collision", _competition_foot_regex), + solref={_competition_foot_regex: _competition_foot_solref, ".*_collision": (0.01, 1)}, + solimp={_competition_foot_regex: _competition_foot_solimp}, + condim={_competition_foot_regex: 3, ".*_collision": 3}, + friction={ + _competition_foot_regex: _competition_foot_friction, + ".*_collision": (0.6,), + }, + priority=1, +) + +COMPETITION_COLLISION = CollisionCfg( + geom_names_expr=(_competition_collision_regex,), + solref=_competition_collision_solref, + solimp={ + _competition_foot_regex: _competition_foot_solimp, + _competition_collision_regex: _competition_collision_solimp, + }, + condim=3, + friction=_competition_collision_friction, + priority=1, +) + ## # Final config. ## @@ -171,7 +276,8 @@ def _kv(act: ElectricActuator) -> float: T1_ACTUATOR_WAIST_HIP_ROLL_YAW, T1_ACTUATOR_HIP_PITCH, T1_ACTUATOR_KNEE, - T1_ACTUATOR_ANKLE, + T1_ACTUATOR_ANKLE_PITCH, + T1_ACTUATOR_ANKLE_ROLL, ), soft_joint_pos_limit_factor=0.9, ) @@ -187,6 +293,248 @@ def get_t1_robot_cfg() -> EntityCfg: ) +def get_competition_foot_spec() -> mujoco.MjSpec: + """Get the T1 spec with competition-style box foot collision geoms. + + This keeps the training-side T1 meshes, inertials, joints, and actuators, but + replaces the four capsule foot collision geoms per foot with one box collision + geom per foot matching the competition T1 foot shape. + """ + spec = get_spec() + capsule_foot_names = { + f"{side}_foot{idx}_collision" for side in ("left", "right") for idx in range(1, 5) + } + for geom in tuple(spec.geoms): + if geom.name in capsule_foot_names: + spec.delete(geom) + + for side in ("left", "right"): + body = spec.body(f"{side}_foot_link") + body.add_geom( + name=f"{side}_foot", + type=mujoco.mjtGeom.mjGEOM_BOX, + pos=(0.01, 0.0, -0.015), + size=(0.1115, 0.05, 0.015), + contype=1, + conaffinity=1, + condim=3, + priority=1, + friction=_competition_foot_friction, + solref=_competition_foot_solref, + solimp=_competition_foot_solimp, + rgba=(0.0, 1.0, 0.0, 0.5), + ) + return spec + + +_T1_TRAINING_COLLISION_GEOM_NAMES = { + "trunk_collision", + "head_collision", + "left_shoulder_collision", + "left_upper_arm_collision", + "left_elbow_collision", + "left_lower_arm_collision", + "right_shoulder_collision", + "right_upper_arm_collision", + "right_elbow_collision", + "right_lower_arm_collision", + "waist_collision", + "left_thigh_collision", + "left_shin_collision", + "left_knee_collision", + "right_thigh_collision", + "right_shin_collision", + "right_knee_collision", + *{f"{side}_foot{idx}_collision" for side in ("left", "right") for idx in range(1, 5)}, +} + + +def _delete_named_geoms(spec: mujoco.MjSpec, geom_names: set[str]) -> None: + for geom in tuple(spec.geoms): + if geom.name in geom_names: + spec.delete(geom) + + +def _add_competition_collision_geom( + spec: mujoco.MjSpec, + *, + body_name: str, + geom_name: str, + geom_type: mujoco.mjtGeom, + pos: tuple[float, float, float], + size: tuple[float, float, float], + quat: tuple[float, float, float, float] = (1.0, 0.0, 0.0, 0.0), + solimp: tuple[float, float, float, float, float] = _competition_collision_solimp, +) -> None: + spec.body(body_name).add_geom( + name=geom_name, + type=geom_type, + pos=pos, + size=size, + quat=quat, + contype=1, + conaffinity=1, + condim=3, + group=3, + priority=1, + friction=_competition_collision_friction, + solref=_competition_collision_solref, + solimp=solimp, + rgba=_competition_collision_rgba, + ) + + +def get_competition_collision_spec() -> mujoco.MjSpec: + """Get the T1 spec with competition-style collision geoms. + + The source remains the training T1 MJCF so body/joint names used by the + mjlab task stay stable. Only collision geoms and the foot inertial frame are + aligned to the active competition T1 XML. + """ + spec = get_spec() + _delete_named_geoms(spec, _T1_TRAINING_COLLISION_GEOM_NAMES) + + # The competition server excludes different non-adjacent self-contact pairs + # than the original training MJCF. These pairs materially change leg motion. + for exclude in tuple(spec.excludes): + spec.delete(exclude) + for body_name_1, body_name_2 in _competition_contact_excludes: + spec.add_exclude(bodyname1=body_name_1, bodyname2=body_name_2) + + for side in ("left", "right"): + foot_body = spec.body(f"{side}_foot_link") + foot_body.inertia = _competition_foot_inertia + foot_body.iquat = _competition_foot_iquat + + _add_competition_collision_geom( + spec, + body_name="Trunk", + geom_name="torso", + geom_type=mujoco.mjtGeom.mjGEOM_BOX, + pos=(0.06, 0.0, 0.12), + size=(0.075, 0.1, 0.15), + ) + _add_competition_collision_geom( + spec, + body_name="H2", + geom_name="head", + geom_type=mujoco.mjtGeom.mjGEOM_SPHERE, + pos=(0.01, 0.0, 0.11), + size=(0.08, 0.0, 0.0), + ) + _add_competition_collision_geom( + spec, + body_name="AL3", + geom_name="left_forearm", + geom_type=mujoco.mjtGeom.mjGEOM_CAPSULE, + pos=(0.0, 0.05, 0.0), + size=(0.03, 0.075, 0.0), + quat=(0.70710678, 0.70710678, 0.0, 0.0), + ) + _add_competition_collision_geom( + spec, + body_name="left_hand_link", + geom_name="left_hand", + geom_type=mujoco.mjtGeom.mjGEOM_CAPSULE, + pos=(0.0, 0.13, 0.0), + size=(0.03, 0.0875, 0.0), + quat=(0.70710678, 0.70710678, 0.0, 0.0), + ) + _add_competition_collision_geom( + spec, + body_name="AR3", + geom_name="right_forearm", + geom_type=mujoco.mjtGeom.mjGEOM_CAPSULE, + pos=(0.0, -0.05, 0.0), + size=(0.03, 0.075, 0.0), + quat=(0.70710678, 0.70710678, 0.0, 0.0), + ) + _add_competition_collision_geom( + spec, + body_name="right_hand_link", + geom_name="right_hand", + geom_type=mujoco.mjtGeom.mjGEOM_CAPSULE, + pos=(0.0, -0.13, 0.0), + size=(0.03, 0.0875, 0.0), + quat=(0.70710678, 0.70710678, 0.0, 0.0), + ) + _add_competition_collision_geom( + spec, + body_name="Hip_Roll_Left", + geom_name="left_thigh", + geom_type=mujoco.mjtGeom.mjGEOM_CAPSULE, + pos=(0.0, 0.0, -0.08), + size=(0.05, 0.08, 0.0), + ) + _add_competition_collision_geom( + spec, + body_name="Shank_Left", + geom_name="left_calf", + geom_type=mujoco.mjtGeom.mjGEOM_CAPSULE, + pos=(0.0, 0.0, -0.12), + size=(0.05, 0.075, 0.0), + ) + _add_competition_collision_geom( + spec, + body_name="left_foot_link", + geom_name="left_foot", + geom_type=mujoco.mjtGeom.mjGEOM_BOX, + pos=(0.01, 0.0, -0.015), + size=(0.1115, 0.05, 0.015), + solimp=_competition_foot_solimp, + ) + _add_competition_collision_geom( + spec, + body_name="Hip_Roll_Right", + geom_name="right_thigh", + geom_type=mujoco.mjtGeom.mjGEOM_CAPSULE, + pos=(0.0, 0.0, -0.08), + size=(0.05, 0.08, 0.0), + ) + _add_competition_collision_geom( + spec, + body_name="Shank_Right", + geom_name="right_calf", + geom_type=mujoco.mjtGeom.mjGEOM_CAPSULE, + pos=(0.0, 0.0, -0.12), + size=(0.05, 0.075, 0.0), + ) + _add_competition_collision_geom( + spec, + body_name="right_foot_link", + geom_name="right_foot", + geom_type=mujoco.mjtGeom.mjGEOM_BOX, + pos=(0.01, 0.0, -0.015), + size=(0.1115, 0.05, 0.015), + solimp=_competition_foot_solimp, + ) + return spec + + +def get_t1_competition_foot_robot_cfg() -> EntityCfg: + """Get a fresh T1 robot config with competition-style box foot contacts.""" + return EntityCfg( + init_state=HOME_KEYFRAME, + collisions=(COMPETITION_FOOT_COLLISION,), + spec_fn=get_competition_foot_spec, + articulation=T1_ARTICULATION, + ) + + +def get_t1_competition_collision_robot_cfg() -> EntityCfg: + """Get a fresh T1 robot config with competition-style collision contacts.""" + articulation = deepcopy(T1_ARTICULATION) + for actuator in articulation.actuators: + assert isinstance(actuator, BuiltinPositionActuatorCfg) + actuator.viscous_damping = COMPETITION_JOINT_VISCOUS_DAMPING + return EntityCfg( + init_state=HOME_KEYFRAME, + collisions=(COMPETITION_COLLISION,), + spec_fn=get_competition_collision_spec, + articulation=articulation, + ) + + T1_ACTION_SCALE: dict[str, float] = {} for a in T1_ARTICULATION.actuators: assert isinstance(a, BuiltinPositionActuatorCfg) diff --git a/src/mjlab_playground/tasks/__init__.py b/src/mjlab_playground/tasks/__init__.py new file mode 100644 index 0000000..171452e --- /dev/null +++ b/src/mjlab_playground/tasks/__init__.py @@ -0,0 +1 @@ +"""Task definitions and registrations built on mjlab.""" diff --git a/src/mjlab_playground/tasks/common/__init__.py b/src/mjlab_playground/tasks/common/__init__.py new file mode 100644 index 0000000..ffc31bb --- /dev/null +++ b/src/mjlab_playground/tasks/common/__init__.py @@ -0,0 +1,5 @@ +"""Helpers shared by multiple playground tasks.""" + +from .events import reset_joints_by_offset + +__all__ = ["reset_joints_by_offset"] diff --git a/src/mjlab_playground/tasks/common/events.py b/src/mjlab_playground/tasks/common/events.py new file mode 100644 index 0000000..e2e602e --- /dev/null +++ b/src/mjlab_playground/tasks/common/events.py @@ -0,0 +1,58 @@ +"""Reset events shared by locomotion tasks.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +import torch +from mjlab.entity import Entity +from mjlab.managers.scene_entity_config import SceneEntityCfg +from mjlab.utils.lab_api.math import sample_uniform + +if TYPE_CHECKING: + from mjlab.envs import ManagerBasedRlEnv + +_DEFAULT_ASSET_CFG = SceneEntityCfg("robot") + + +def reset_joints_by_offset( + env: ManagerBasedRlEnv, + env_ids: torch.Tensor | None, + position_range: tuple[float, float], + velocity_range: tuple[float, float], + asset_cfg: SceneEntityCfg = _DEFAULT_ASSET_CFG, +) -> None: + """Reset joints around their defaults for invariant or per-world limits.""" + if env_ids is None: + env_ids = torch.arange(env.num_envs, device=env.device, dtype=torch.long) + + asset: Entity = env.scene[asset_cfg.name] + default_joint_pos = asset.data.default_joint_pos + default_joint_vel = asset.data.default_joint_vel + soft_joint_pos_limits = asset.data.soft_joint_pos_limits + assert default_joint_pos is not None + assert default_joint_vel is not None + assert soft_joint_pos_limits is not None + + joint_pos = default_joint_pos[env_ids][:, asset_cfg.joint_ids].clone() + joint_pos += sample_uniform(*position_range, joint_pos.shape, env.device) + limits = ( + soft_joint_pos_limits.expand(len(env_ids), -1, -1) + if soft_joint_pos_limits.shape[0] == 1 + else soft_joint_pos_limits[env_ids] + ) + limits = limits[:, asset_cfg.joint_ids] + joint_pos.clamp_(limits[..., 0], limits[..., 1]) + + joint_vel = default_joint_vel[env_ids][:, asset_cfg.joint_ids].clone() + joint_vel += sample_uniform(*velocity_range, joint_vel.shape, env.device) + + joint_ids = asset_cfg.joint_ids + if isinstance(joint_ids, list): + joint_ids = torch.tensor(joint_ids, device=env.device) + asset.write_joint_state_to_sim( + joint_pos.view(len(env_ids), -1), + joint_vel.view(len(env_ids), -1), + env_ids=env_ids, + joint_ids=joint_ids, + ) diff --git a/src/mjlab_playground/getup/__init__.py b/src/mjlab_playground/tasks/getup/__init__.py similarity index 100% rename from src/mjlab_playground/getup/__init__.py rename to src/mjlab_playground/tasks/getup/__init__.py diff --git a/src/mjlab_playground/getup/config/__init__.py b/src/mjlab_playground/tasks/getup/config/__init__.py similarity index 100% rename from src/mjlab_playground/getup/config/__init__.py rename to src/mjlab_playground/tasks/getup/config/__init__.py diff --git a/src/mjlab_playground/getup/config/go1/__init__.py b/src/mjlab_playground/tasks/getup/config/go1/__init__.py similarity index 100% rename from src/mjlab_playground/getup/config/go1/__init__.py rename to src/mjlab_playground/tasks/getup/config/go1/__init__.py diff --git a/src/mjlab_playground/getup/config/go1/env_cfgs.py b/src/mjlab_playground/tasks/getup/config/go1/env_cfgs.py similarity index 96% rename from src/mjlab_playground/getup/config/go1/env_cfgs.py rename to src/mjlab_playground/tasks/getup/config/go1/env_cfgs.py index af9d524..bd3988a 100644 --- a/src/mjlab_playground/getup/config/go1/env_cfgs.py +++ b/src/mjlab_playground/tasks/getup/config/go1/env_cfgs.py @@ -9,8 +9,8 @@ from mjlab.sensor import ContactMatch, ContactSensorCfg from mjlab.utils.spec_config import CollisionCfg -from mjlab_playground.getup import mdp -from mjlab_playground.getup.getup_env_cfg import make_getup_env_cfg +from mjlab_playground.tasks.getup import mdp +from mjlab_playground.tasks.getup.getup_env_cfg import make_getup_env_cfg _TORSO_HEIGHT = 0.275 diff --git a/src/mjlab_playground/getup/config/go1/rl_cfg.py b/src/mjlab_playground/tasks/getup/config/go1/rl_cfg.py similarity index 100% rename from src/mjlab_playground/getup/config/go1/rl_cfg.py rename to src/mjlab_playground/tasks/getup/config/go1/rl_cfg.py diff --git a/src/mjlab_playground/tasks/getup/config/k1/__init__.py b/src/mjlab_playground/tasks/getup/config/k1/__init__.py new file mode 100644 index 0000000..d714032 --- /dev/null +++ b/src/mjlab_playground/tasks/getup/config/k1/__init__.py @@ -0,0 +1,11 @@ +from mjlab.tasks.registry import register_mjlab_task + +from .env_cfgs import booster_k1_getup_env_cfg +from .rl_cfg import booster_k1_getup_ppo_runner_cfg + +register_mjlab_task( + task_id="Mjlab-Getup-Flat-Booster-K1", + env_cfg=booster_k1_getup_env_cfg(), + play_env_cfg=booster_k1_getup_env_cfg(play=True), + rl_cfg=booster_k1_getup_ppo_runner_cfg(), +) diff --git a/src/mjlab_playground/tasks/getup/config/k1/env_cfgs.py b/src/mjlab_playground/tasks/getup/config/k1/env_cfgs.py new file mode 100644 index 0000000..c1d2751 --- /dev/null +++ b/src/mjlab_playground/tasks/getup/config/k1/env_cfgs.py @@ -0,0 +1,134 @@ +"""Booster K1 getup environment configuration.""" + +from mjlab.envs import ManagerBasedRlEnvCfg +from mjlab.managers.curriculum_manager import CurriculumTermCfg +from mjlab.managers.reward_manager import RewardTermCfg +from mjlab.managers.scene_entity_config import SceneEntityCfg +from mjlab.sensor import ContactMatch, ContactSensorCfg + +from mjlab_playground.asset_zoo.robots.booster_k1.k1_constants import get_k1_robot_cfg +from mjlab_playground.tasks.getup import mdp +from mjlab_playground.tasks.getup.getup_env_cfg import make_getup_env_cfg +from mjlab_playground.tasks.getup.mdp.actions import ( + SettleRelativeJointPositionActionCfg, +) + +# Derived from the K1 standing/getup calibration in the MotrixLab scene. +_TORSO_HEIGHT = 0.543 +_HIP_HEIGHT = 0.466 + + +def booster_k1_getup_env_cfg(play: bool = False) -> ManagerBasedRlEnvCfg: + """Create Booster K1 getup task configuration.""" + cfg = make_getup_env_cfg() + + cfg.scene.entities = {"robot": get_k1_robot_cfg()} + cfg.episode_length_s = 8.0 + + cfg.observations["actor"].terms["base_ang_vel"].func = mdp.base_ang_vel + cfg.observations["actor"].terms["base_ang_vel"].params = {} + cfg.observations["critic"].terms["base_ang_vel"].func = mdp.base_ang_vel + cfg.observations["critic"].terms["base_ang_vel"].params = {} + cfg.observations["critic"].terms["base_lin_vel"].func = mdp.base_lin_vel + cfg.observations["critic"].terms["base_lin_vel"].params = {} + + self_collision_cfg = ContactSensorCfg( + name="self_collision", + primary=ContactMatch(mode="subtree", pattern="torso", entity="robot"), + secondary=ContactMatch(mode="subtree", pattern="torso", entity="robot"), + fields=("found", "force"), + reduce="none", + num_slots=1, + history_length=4, + ) + cfg.scene.sensors = (cfg.scene.sensors or ()) + (self_collision_cfg,) + + cfg.rewards["self_collisions"] = RewardTermCfg( + func=mdp.self_collision_cost, + weight=-0.1, + params={"sensor_name": self_collision_cfg.name}, + ) + + cfg.rewards["torso_height"].params["desired_height"] = _TORSO_HEIGHT + cfg.rewards["torso_height"].params["asset_cfg"] = SceneEntityCfg( + "robot", body_names=("torso",) + ) + cfg.rewards["left_hip_height"] = RewardTermCfg( + func=mdp.height_reward, + weight=0.5, + params={ + "desired_height": _HIP_HEIGHT, + "asset_cfg": SceneEntityCfg("robot", body_names=("Left_Hip_Pitch",)), + }, + ) + cfg.rewards["right_hip_height"] = RewardTermCfg( + func=mdp.height_reward, + weight=0.5, + params={ + "desired_height": _HIP_HEIGHT, + "asset_cfg": SceneEntityCfg("robot", body_names=("Right_Hip_Pitch",)), + }, + ) + cfg.metrics["getup_success"].params["desired_height"] = _TORSO_HEIGHT + + cfg.rewards["posture"].params["std"] = { + r".*_Hip_Roll": 0.08, + r".*_Hip_Yaw": 0.08, + r".*_Hip_Pitch": 0.12, + r".*_Knee_Pitch": 0.15, + r".*_Ankle_Pitch": 0.2, + r".*_Ankle_Roll": 0.2, + r"(AAHead_yaw|Head_pitch)": 0.15, + r"(.*_Shoulder.*|.*_Elbow.*)": 0.5, + } + + cfg.viewer.body_name = "torso" + + cfg.events["base_com"].params["asset_cfg"] = SceneEntityCfg( + "robot", body_names=("torso",) + ) + + cfg.events["reset_fallen_or_standing"].params["fall_height"] = 0.65 + + assert isinstance(cfg.actions["joint_pos"], SettleRelativeJointPositionActionCfg) + cfg.actions["joint_pos"].settle_steps = 50 + cfg.terminations["energy"].params["settle_steps"] = 50 + cfg.terminations["energy"].params["threshold"] = float("inf") + cfg.rewards["action_rate_l2"].weight = -0.005 + cfg.rewards["joint_vel_l2"].weight = 0.0 + + # K1 has no waist joint, so supine recovery needs larger full-body swings for + # longer than T1. Keep the getup reward simple, but delay regularization. + cfg.curriculum = { + "action_rate_weight": CurriculumTermCfg( + func=mdp.reward_curriculum, + params={ + "reward_name": "action_rate_l2", + "stages": [ + {"step": 0, "weight": -0.005}, + {"step": 1500 * 24, "weight": -0.01}, + {"step": 2500 * 24, "weight": -0.03}, + {"step": 3500 * 24, "weight": -0.05}, + {"step": 4500 * 24, "weight": -0.08}, + ], + }, + ), + "joint_vel_weight": CurriculumTermCfg( + func=mdp.reward_curriculum, + params={ + "reward_name": "joint_vel_l2", + "stages": [ + {"step": 0, "weight": 0.0}, + {"step": 2500 * 24, "weight": -0.002}, + {"step": 3500 * 24, "weight": -0.005}, + {"step": 4500 * 24, "weight": -0.008}, + ], + }, + ), + } + + if play: + cfg.observations["actor"].enable_corruption = False + cfg.events["reset_fallen_or_standing"].params["fall_probability"] = 1.0 + + return cfg diff --git a/src/mjlab_playground/tasks/getup/config/k1/rl_cfg.py b/src/mjlab_playground/tasks/getup/config/k1/rl_cfg.py new file mode 100644 index 0000000..a580d8a --- /dev/null +++ b/src/mjlab_playground/tasks/getup/config/k1/rl_cfg.py @@ -0,0 +1,47 @@ +"""RL configuration for Booster K1 getup task.""" + +from mjlab.rl import ( + RslRlModelCfg, + RslRlOnPolicyRunnerCfg, + RslRlPpoAlgorithmCfg, +) + + +def booster_k1_getup_ppo_runner_cfg() -> RslRlOnPolicyRunnerCfg: + """Create RL runner configuration for Booster K1 getup task.""" + return RslRlOnPolicyRunnerCfg( + actor=RslRlModelCfg( + hidden_dims=(512, 256, 128), + activation="elu", + obs_normalization=False, + distribution_cfg={ + "class_name": "GaussianDistribution", + "init_std": 1.0, + "std_type": "log", + }, + ), + critic=RslRlModelCfg( + hidden_dims=(512, 256, 128), + activation="elu", + obs_normalization=True, + ), + algorithm=RslRlPpoAlgorithmCfg( + value_loss_coef=1.0, + use_clipped_value_loss=True, + clip_param=0.2, + entropy_coef=0.01, + num_learning_epochs=5, + num_mini_batches=4, + learning_rate=1.0e-3, + schedule="adaptive", + gamma=0.99, + lam=0.95, + desired_kl=0.01, + max_grad_norm=1.0, + ), + experiment_name="k1_getup", + wandb_project="mjlab_playground", + save_interval=50, + num_steps_per_env=24, + max_iterations=3_000, + ) diff --git a/src/mjlab_playground/getup/config/t1/__init__.py b/src/mjlab_playground/tasks/getup/config/t1/__init__.py similarity index 100% rename from src/mjlab_playground/getup/config/t1/__init__.py rename to src/mjlab_playground/tasks/getup/config/t1/__init__.py diff --git a/src/mjlab_playground/getup/config/t1/env_cfgs.py b/src/mjlab_playground/tasks/getup/config/t1/env_cfgs.py similarity index 96% rename from src/mjlab_playground/getup/config/t1/env_cfgs.py rename to src/mjlab_playground/tasks/getup/config/t1/env_cfgs.py index 87343ed..6594c8d 100644 --- a/src/mjlab_playground/getup/config/t1/env_cfgs.py +++ b/src/mjlab_playground/tasks/getup/config/t1/env_cfgs.py @@ -9,9 +9,11 @@ from mjlab.sensor import ContactMatch, ContactSensorCfg from mjlab_playground.asset_zoo.robots.booster_t1.t1_constants import get_t1_robot_cfg -from mjlab_playground.getup import mdp -from mjlab_playground.getup.getup_env_cfg import make_getup_env_cfg -from mjlab_playground.getup.mdp.actions import SettleRelativeJointPositionActionCfg +from mjlab_playground.tasks.getup import mdp +from mjlab_playground.tasks.getup.getup_env_cfg import make_getup_env_cfg +from mjlab_playground.tasks.getup.mdp.actions import ( + SettleRelativeJointPositionActionCfg, +) # Derived from home keyframe. _TORSO_HEIGHT = 0.67 diff --git a/src/mjlab_playground/getup/config/t1/rl_cfg.py b/src/mjlab_playground/tasks/getup/config/t1/rl_cfg.py similarity index 100% rename from src/mjlab_playground/getup/config/t1/rl_cfg.py rename to src/mjlab_playground/tasks/getup/config/t1/rl_cfg.py diff --git a/src/mjlab_playground/getup/getup_env_cfg.py b/src/mjlab_playground/tasks/getup/getup_env_cfg.py similarity index 97% rename from src/mjlab_playground/getup/getup_env_cfg.py rename to src/mjlab_playground/tasks/getup/getup_env_cfg.py index 3265463..0fd5d1b 100644 --- a/src/mjlab_playground/getup/getup_env_cfg.py +++ b/src/mjlab_playground/tasks/getup/getup_env_cfg.py @@ -25,8 +25,10 @@ from mjlab.utils.noise import UniformNoiseCfg as Unoise from mjlab.viewer import ViewerConfig -from mjlab_playground.getup import mdp -from mjlab_playground.getup.mdp.actions import SettleRelativeJointPositionActionCfg +from mjlab_playground.tasks.getup import mdp +from mjlab_playground.tasks.getup.mdp.actions import ( + SettleRelativeJointPositionActionCfg, +) def make_getup_env_cfg() -> ManagerBasedRlEnvCfg: diff --git a/src/mjlab_playground/getup/mdp/__init__.py b/src/mjlab_playground/tasks/getup/mdp/__init__.py similarity index 100% rename from src/mjlab_playground/getup/mdp/__init__.py rename to src/mjlab_playground/tasks/getup/mdp/__init__.py diff --git a/src/mjlab_playground/getup/mdp/actions.py b/src/mjlab_playground/tasks/getup/mdp/actions.py similarity index 100% rename from src/mjlab_playground/getup/mdp/actions.py rename to src/mjlab_playground/tasks/getup/mdp/actions.py diff --git a/src/mjlab_playground/getup/mdp/events.py b/src/mjlab_playground/tasks/getup/mdp/events.py similarity index 93% rename from src/mjlab_playground/getup/mdp/events.py rename to src/mjlab_playground/tasks/getup/mdp/events.py index c2eb4a3..fd42f69 100644 --- a/src/mjlab_playground/getup/mdp/events.py +++ b/src/mjlab_playground/tasks/getup/mdp/events.py @@ -91,7 +91,13 @@ def reset_fallen_or_standing( asset.write_root_link_velocity_to_sim(velocities, env_ids=env_ids) # Joint state. - joint_limits = soft_joint_pos_limits[env_ids] + # MJLab stores invariant limits once, while per-world variants may expose + # one row per environment. Support both layouts. + joint_limits = ( + soft_joint_pos_limits.expand(n, -1, -1) + if soft_joint_pos_limits.shape[0] == 1 + else soft_joint_pos_limits[env_ids] + ) random_joint_pos = sample_uniform( joint_limits[..., 0], joint_limits[..., 1], joint_limits[..., 0].shape, env.device ) diff --git a/src/mjlab_playground/getup/mdp/rewards.py b/src/mjlab_playground/tasks/getup/mdp/rewards.py similarity index 100% rename from src/mjlab_playground/getup/mdp/rewards.py rename to src/mjlab_playground/tasks/getup/mdp/rewards.py diff --git a/src/mjlab_playground/getup/mdp/terminations.py b/src/mjlab_playground/tasks/getup/mdp/terminations.py similarity index 100% rename from src/mjlab_playground/getup/mdp/terminations.py rename to src/mjlab_playground/tasks/getup/mdp/terminations.py diff --git a/src/mjlab_playground/tasks/target_location_amp/__init__.py b/src/mjlab_playground/tasks/target_location_amp/__init__.py new file mode 100644 index 0000000..2279123 --- /dev/null +++ b/src/mjlab_playground/tasks/target_location_amp/__init__.py @@ -0,0 +1 @@ +"""Target-location locomotion AMP tasks.""" diff --git a/src/mjlab_playground/tasks/target_location_amp/config/__init__.py b/src/mjlab_playground/tasks/target_location_amp/config/__init__.py new file mode 100644 index 0000000..43c4884 --- /dev/null +++ b/src/mjlab_playground/tasks/target_location_amp/config/__init__.py @@ -0,0 +1 @@ +"""Target-location locomotion AMP configurations.""" diff --git a/src/mjlab_playground/tasks/target_location_amp/config/k1/__init__.py b/src/mjlab_playground/tasks/target_location_amp/config/k1/__init__.py new file mode 100644 index 0000000..7eaa1c0 --- /dev/null +++ b/src/mjlab_playground/tasks/target_location_amp/config/k1/__init__.py @@ -0,0 +1,16 @@ +"""Booster K1 target-location AMP task registration.""" + +from mjlab.tasks.registry import register_mjlab_task + +from mjlab_playground.amp.rl import AmpOnPolicyRunner + +from .env_cfgs import booster_k1_target_location_amp_flat_env_cfg +from .rl_cfg import booster_k1_target_location_amp_runner_cfg + +register_mjlab_task( + task_id="Mjlab-TargetLocationAmp-Flat-Booster-K1", + env_cfg=booster_k1_target_location_amp_flat_env_cfg(), + play_env_cfg=booster_k1_target_location_amp_flat_env_cfg(play=True), + rl_cfg=booster_k1_target_location_amp_runner_cfg(), + runner_cls=AmpOnPolicyRunner, +) diff --git a/src/mjlab_playground/tasks/target_location_amp/config/k1/env_cfgs.py b/src/mjlab_playground/tasks/target_location_amp/config/k1/env_cfgs.py new file mode 100644 index 0000000..ccf615e --- /dev/null +++ b/src/mjlab_playground/tasks/target_location_amp/config/k1/env_cfgs.py @@ -0,0 +1,172 @@ +"""Booster K1 target-location AMP environment configuration.""" + +from __future__ import annotations + +import math +from copy import deepcopy + +from mjlab.envs import ManagerBasedRlEnvCfg +from mjlab.envs.mdp.actions import RelativeJointPositionActionCfg +from mjlab.managers.curriculum_manager import CurriculumTermCfg +from mjlab.managers.scene_entity_config import SceneEntityCfg +from mjlab.managers.termination_manager import TerminationTermCfg + +from mjlab_playground.asset_zoo.robots.booster_k1.k1_constants import ( + get_k1_robot_cfg, +) +from mjlab_playground.tasks.target_location_amp import mdp as target_mdp +from mjlab_playground.tasks.target_location_amp.target_location_amp_env_cfg import ( + make_target_location_amp_env_cfg, +) + +_K1_TARGET_POSITION_TOLERANCE = 0.16 +_K1_TARGET_YAW_TOLERANCE = 0.20 +_K1_TARGET_STABLE_HOLD_STEPS = 25 +_K1_TARGET_MAX_LIN_SPEED = 0.35 +_K1_TARGET_MAX_YAW_RATE = 0.60 +_K1_TARGET_MIN_UP_Z = 0.75 +_K1_EPISODE_LENGTH_S = 6.0 +_K1_FIXED_TARGET_RESAMPLING_TIME_RANGE = (1.0e9, 1.0e9) + +_K1_TARGET_LOCO_ACTION_SCALE = { + "AAHead_yaw": 0.5, + "Head_pitch": 0.5, + "ALeft_Shoulder_Pitch": 1.0, + "Left_Shoulder_Roll": 1.0, + "Left_Elbow_Pitch": 1.0, + "Left_Elbow_Yaw": 1.0, + "ARight_Shoulder_Pitch": 1.0, + "Right_Shoulder_Roll": 1.0, + "Right_Elbow_Pitch": 1.0, + "Right_Elbow_Yaw": 1.0, + ".*_Hip_Pitch": 0.9, + ".*_Hip_Roll": 0.8, + ".*_Hip_Yaw": 0.8, + ".*_Knee_Pitch": 1.0, + ".*_Ankle_Pitch": 0.6, + ".*_Ankle_Roll": 0.45, +} + +_K1_TARGET_LOCO_GOAL_CURRICULUM_STAGES = [ + { + "step": 0, + "target_radius": (0.4, 1.2), + "target_yaw": (-0.25, 0.25), + "target_angle": (-0.50, 0.50), + "resampling_time_range": None, + "resample_on_near_goal": False, + "resample_position_tolerance": _K1_TARGET_POSITION_TOLERANCE, + "resample_yaw_tolerance": _K1_TARGET_YAW_TOLERANCE, + }, + { + "step": 600 * 24, + "target_radius": (0.4, 2.4), + "target_yaw": (-0.75, 0.75), + "target_angle": (-1.25, 1.25), + "resampling_time_range": None, + "resample_on_near_goal": False, + "resample_position_tolerance": _K1_TARGET_POSITION_TOLERANCE, + "resample_yaw_tolerance": _K1_TARGET_YAW_TOLERANCE, + }, + { + "step": 1_600 * 24, + "target_radius": (0.4, 4.0), + "target_yaw": (-1.50, 1.50), + "target_angle": (-2.20, 2.20), + "resampling_time_range": None, + "resample_on_near_goal": False, + "resample_position_tolerance": _K1_TARGET_POSITION_TOLERANCE, + "resample_yaw_tolerance": _K1_TARGET_YAW_TOLERANCE, + }, + { + "step": 3_500 * 24, + "target_radius": (0.4, 6.0), + "target_yaw": (-math.pi, math.pi), + "target_angle": (-math.pi, math.pi), + "resampling_time_range": None, + "resample_on_near_goal": False, + "resample_position_tolerance": _K1_TARGET_POSITION_TOLERANCE, + "resample_yaw_tolerance": _K1_TARGET_YAW_TOLERANCE, + }, +] + + +def _apply_k1_joint_action_setup(cfg: ManagerBasedRlEnvCfg) -> None: + joint_action = cfg.actions["joint_pos"] + assert isinstance(joint_action, RelativeJointPositionActionCfg) + joint_action.scale = _K1_TARGET_LOCO_ACTION_SCALE + joint_action.clip = None + + +def _robot_asset_cfg() -> SceneEntityCfg: + return SceneEntityCfg("robot", joint_names=(".*",)) + + +def _configure_k1_goal_reached_termination(cfg: ManagerBasedRlEnvCfg) -> None: + cfg.terminations["goal_reached"] = TerminationTermCfg( + func=target_mdp.goal_pose_reached, + params={ + "command_name": "goal_pose", + "position_tolerance": _K1_TARGET_POSITION_TOLERANCE, + "yaw_tolerance": _K1_TARGET_YAW_TOLERANCE, + "hold_steps": _K1_TARGET_STABLE_HOLD_STEPS, + "entity_name": "robot", + "max_lin_speed": _K1_TARGET_MAX_LIN_SPEED, + "max_yaw_rate": _K1_TARGET_MAX_YAW_RATE, + "min_up_z": _K1_TARGET_MIN_UP_Z, + }, + ) + + +def _configure_k1_goal_pose_command(cfg: ManagerBasedRlEnvCfg) -> None: + goal_pose_cmd = cfg.commands["goal_pose"] + assert isinstance(goal_pose_cmd, target_mdp.GoalPoseCommandCfg) + stage0 = _K1_TARGET_LOCO_GOAL_CURRICULUM_STAGES[0] + goal_pose_cmd.ranges.target_radius = stage0["target_radius"] + goal_pose_cmd.ranges.target_yaw = stage0["target_yaw"] + goal_pose_cmd.ranges.target_angle = stage0["target_angle"] + goal_pose_cmd.resampling_time_range = _K1_FIXED_TARGET_RESAMPLING_TIME_RANGE + goal_pose_cmd.resample_on_near_goal = stage0["resample_on_near_goal"] + goal_pose_cmd.resample_position_tolerance = stage0["resample_position_tolerance"] + goal_pose_cmd.resample_yaw_tolerance = stage0["resample_yaw_tolerance"] + + +def booster_k1_target_location_amp_flat_env_cfg( + play: bool = False, +) -> ManagerBasedRlEnvCfg: + """Create the K1 target-location locomotion AMP task.""" + cfg = make_target_location_amp_env_cfg(play=play) + cfg.scene.entities = { + "robot": deepcopy(get_k1_robot_cfg()), + } + cfg.scene.num_envs = 1 if play else 1024 + cfg.episode_length_s = _K1_EPISODE_LENGTH_S + cfg.viewer.body_name = "torso" + _apply_k1_joint_action_setup(cfg) + _configure_k1_goal_pose_command(cfg) + cfg.events["reset_robot_joints"].params["asset_cfg"] = _robot_asset_cfg() + cfg.observations["critic"].terms["amp_robot"].params["robot_cfg"] = _robot_asset_cfg() + + if not play: + _configure_k1_goal_reached_termination(cfg) + cfg.curriculum["goal_pose"] = CurriculumTermCfg( + func=target_mdp.goal_pose_ranges, + params={ + "command_name": "goal_pose", + "stages": _K1_TARGET_LOCO_GOAL_CURRICULUM_STAGES, + }, + ) + + if play: + cfg.observations["actor"].enable_corruption = False + goal_pose_cmd = cfg.commands["goal_pose"] + assert isinstance(goal_pose_cmd, target_mdp.GoalPoseCommandCfg) + goal_pose_cmd.ranges.target_radius = (0.4, 6.0) + goal_pose_cmd.ranges.target_yaw = (-math.pi, math.pi) + goal_pose_cmd.ranges.target_angle = (-math.pi, math.pi) + goal_pose_cmd.resampling_time_range = _K1_FIXED_TARGET_RESAMPLING_TIME_RANGE + goal_pose_cmd.resample_on_near_goal = False + goal_pose_cmd.resample_position_tolerance = _K1_TARGET_POSITION_TOLERANCE + goal_pose_cmd.resample_yaw_tolerance = _K1_TARGET_YAW_TOLERANCE + + return cfg diff --git a/src/mjlab_playground/tasks/target_location_amp/config/k1/rl_cfg.py b/src/mjlab_playground/tasks/target_location_amp/config/k1/rl_cfg.py new file mode 100644 index 0000000..46a3eb0 --- /dev/null +++ b/src/mjlab_playground/tasks/target_location_amp/config/k1/rl_cfg.py @@ -0,0 +1,80 @@ +"""RL configuration for Booster K1 target-location AMP locomotion.""" + +from __future__ import annotations + +from mjlab.rl import RslRlModelCfg + +from mjlab_playground.amp.rl.motion_loader import ( + default_k1_locomotion_amp_motion_groups, +) +from mjlab_playground.amp.rl.runner_cfg import ( + AmpOnPolicyRunnerCfg, + AmpPpoAlgorithmCfg, +) + +_STABLE_ACTION_DISTRIBUTION = { + "class_name": "mjlab_playground.amp.rl.distributions:ClampedGaussianDistribution", + "init_std": 0.45, + "std_type": "log", + "min_std": 0.05, + "max_std": 1.0, +} + + +def booster_k1_target_location_amp_runner_cfg() -> AmpOnPolicyRunnerCfg: + """Create RL runner configuration for K1 target-location locomotion AMP.""" + return AmpOnPolicyRunnerCfg( + actor=RslRlModelCfg( + hidden_dims=(512, 256, 128), + activation="elu", + obs_normalization=True, + distribution_cfg=dict(_STABLE_ACTION_DISTRIBUTION), + ), + critic=RslRlModelCfg( + hidden_dims=(512, 256, 128), + activation="elu", + obs_normalization=True, + ), + algorithm=AmpPpoAlgorithmCfg( + value_loss_coef=1.0, + use_clipped_value_loss=True, + clip_param=0.2, + entropy_coef=0.001, + num_learning_epochs=5, + num_mini_batches=4, + learning_rate=1.0e-3, + schedule="adaptive", + gamma=0.98, + lam=0.95, + desired_kl=0.01, + max_grad_norm=1.0, + amp_cfg={ + "motion_groups": default_k1_locomotion_amp_motion_groups(), + "target_fps": 50.0, + "expected_dof": 22, + "reward_coef": 0.6, + "reward_scale": 1.0, + "task_reward_lerp": 0.45, + "loss_coef": 1.0, + "grad_penalty_coef": 10.0, + "discriminator_learning_rate": 3.0e-4, + "replay_buffer_size": 100_000, + "discriminator_hidden_dims": (512, 256), + "discriminator_activation": "relu", + "normalize_input": True, + "discriminator_loss_type": "least_squares", + "discriminator_reward_type": "hardware_lsgan", + "discriminator_expert_label": 0.9, + "discriminator_policy_label": 0.1, + "discriminator_reward_clip_max": 1.5, + }, + symmetry_cfg=None, + ), + experiment_name="k1_target_location_amp", + run_name="k1_target_location_amp", + wandb_project="mjlab_playground", + save_interval=50, + num_steps_per_env=24, + max_iterations=10_000, + clip_actions=1.2, + ) diff --git a/src/mjlab_playground/tasks/target_location_amp/config/t1/__init__.py b/src/mjlab_playground/tasks/target_location_amp/config/t1/__init__.py new file mode 100644 index 0000000..64e5f2c --- /dev/null +++ b/src/mjlab_playground/tasks/target_location_amp/config/t1/__init__.py @@ -0,0 +1,42 @@ +"""Booster T1 target-location AMP task registration.""" + +from mjlab.tasks.registry import register_mjlab_task + +from mjlab_playground.amp.rl import AmpOnPolicyRunner + +from .env_cfgs import ( + booster_t1_target_location_amp_competition_collision_flat_env_cfg, + booster_t1_target_location_amp_competition_foot_flat_env_cfg, + booster_t1_target_location_amp_flat_env_cfg, +) +from .rl_cfg import ( + booster_t1_target_location_amp_competition_collision_runner_cfg, + booster_t1_target_location_amp_competition_foot_runner_cfg, + booster_t1_target_location_amp_runner_cfg, +) + +register_mjlab_task( + task_id="Mjlab-TargetLocationAmp-Flat-Booster-T1", + env_cfg=booster_t1_target_location_amp_flat_env_cfg(), + play_env_cfg=booster_t1_target_location_amp_flat_env_cfg(play=True), + rl_cfg=booster_t1_target_location_amp_runner_cfg(), + runner_cls=AmpOnPolicyRunner, +) + +register_mjlab_task( + task_id="Mjlab-TargetLocationAmp-CompetitionFoot-Flat-Booster-T1", + env_cfg=booster_t1_target_location_amp_competition_foot_flat_env_cfg(), + play_env_cfg=booster_t1_target_location_amp_competition_foot_flat_env_cfg(play=True), + rl_cfg=booster_t1_target_location_amp_competition_foot_runner_cfg(), + runner_cls=AmpOnPolicyRunner, +) + +register_mjlab_task( + task_id="Mjlab-TargetLocationAmp-CompetitionCollision-Flat-Booster-T1", + env_cfg=booster_t1_target_location_amp_competition_collision_flat_env_cfg(), + play_env_cfg=booster_t1_target_location_amp_competition_collision_flat_env_cfg( + play=True + ), + rl_cfg=booster_t1_target_location_amp_competition_collision_runner_cfg(), + runner_cls=AmpOnPolicyRunner, +) diff --git a/src/mjlab_playground/tasks/target_location_amp/config/t1/env_cfgs.py b/src/mjlab_playground/tasks/target_location_amp/config/t1/env_cfgs.py new file mode 100644 index 0000000..802dfa7 --- /dev/null +++ b/src/mjlab_playground/tasks/target_location_amp/config/t1/env_cfgs.py @@ -0,0 +1,589 @@ +"""Booster T1 target-location AMP environment configuration.""" + +from __future__ import annotations + +import math +from copy import deepcopy + +from mjlab.envs import ManagerBasedRlEnvCfg +from mjlab.envs import mdp as env_mdp +from mjlab.envs.mdp import dr +from mjlab.envs.mdp.actions import JointPositionActionCfg +from mjlab.managers.curriculum_manager import CurriculumTermCfg +from mjlab.managers.event_manager import EventTermCfg +from mjlab.managers.reward_manager import RewardTermCfg +from mjlab.managers.scene_entity_config import SceneEntityCfg + +from mjlab_playground.amp.mdp.actions import DelayedJointPositionActionCfg +from mjlab_playground.asset_zoo.robots.booster_t1.t1_constants import ( + T1_ACTION_SCALE, + get_t1_competition_collision_robot_cfg, + get_t1_competition_foot_robot_cfg, + get_t1_robot_cfg, +) +from mjlab_playground.tasks.target_location_amp import mdp as target_mdp +from mjlab_playground.tasks.target_location_amp.target_location_amp_env_cfg import ( + make_target_location_amp_env_cfg, +) + +_T1_TARGET_POSITION_TOLERANCE = 0.22 +_T1_TARGET_YAW_TOLERANCE = 0.30 +_T1_NEAR_GOAL_DISTANCE = 0.85 +_T1_REWARD_GATE_MIN_ROOT_HEIGHT = 0.58 +_T1_REWARD_GATE_MIN_UP_Z = 0.80 +_T1_FALL_HEIGHT_THRESHOLD = 0.50 +_T1_FALL_UP_Z_THRESHOLD = 0.70 +_T1_EPISODE_LENGTH_S = 20.0 +_T1_TARGET_STABLE_HOLD_STEPS = 25 +_T1_TARGET_MAX_LIN_SPEED = 0.35 +_T1_TARGET_MAX_YAW_RATE = 0.60 +_T1_TARGET_MIN_UP_Z = 0.75 +_T1_RL_STEPS_PER_ITERATION = 24 +_T1_COMPETITION_ACTION_DELAY_STEPS = 1 +_T1_FINE_TUNE_ITERATION = 70_500 +_T1_FINE_TARGET_POSITION_TOLERANCE = 0.08 +_T1_FINE_TARGET_YAW_TOLERANCE = math.radians(6.0) +_T1_FINE_NEAR_GOAL_DISTANCE = 0.45 +_T1_FINE_TARGET_STABLE_HOLD_STEPS = 25 +_T1_FINE_TARGET_MAX_LIN_SPEED = 0.25 +_T1_FINE_TARGET_MAX_YAW_RATE = 0.35 +_T1_FINE_TARGET_MIN_UP_Z = 0.80 +_T1_FINE_NEAR_GOAL_STABILITY_WEIGHT = 0.50 +_T1_FINE_GOAL_POSITION_BONUS_WEIGHT = 0.50 +_T1_FINE_GOAL_POSE_BONUS_WEIGHT = 1.00 +_T1_FINE_JOINT_TARGET_LIMITS_WEIGHT = -0.03 +_T1_FOOT_FRICTION_GEOMS = r"^(left|right)_foot[1-4]_collision$" +_T1_COMPETITION_FOOT_FRICTION_GEOMS = r"^(left|right)_foot$" +_T1_FINE_TARGET_COMMAND_BUCKETS = [ + { + "radius_range": (0.0, 0.0), + "yaw_range": (0.0, 0.0), + "angle_range": (0.0, 0.0), + "probability": 0.1, + }, + { + "radius_range": (0.0, 0.0), + "yaw_range": (-math.pi, math.pi), + "angle_range": (0.0, 0.0), + "probability": 0.1, + }, + { + "radius_range": (0.0, 2.0), + "yaw_range": None, + "angle_range": None, + "probability": 0.3, + }, + { + "radius_range": (5.0, 6.0), + "yaw_range": None, + "angle_range": None, + "probability": 0.4, + }, + { + "radius_range": (2.0, 4.0), + "yaw_range": None, + "angle_range": None, + "probability": 0.1, + }, +] +_T1_TARGET_LOCO_GOAL_CURRICULUM_STAGES = [ + { + "step": 0, + "target_radius": (0.3, 0.8), + "target_yaw": (-0.2, 0.2), + "target_angle": (-0.4, 0.4), + "resampling_time_range": (4.0, 8.0), + "resample_on_near_goal": True, + "resample_on_goal_success": False, + "resample_position_tolerance": _T1_TARGET_POSITION_TOLERANCE, + "resample_yaw_tolerance": _T1_TARGET_YAW_TOLERANCE, + }, + { + "step": 2_000 * _T1_RL_STEPS_PER_ITERATION, + "target_radius": (0.3, 1.5), + "target_yaw": (-0.5, 0.5), + "target_angle": (-1.0, 1.0), + "resampling_time_range": (4.0, 8.0), + "resample_on_near_goal": True, + "resample_on_goal_success": False, + "resample_position_tolerance": _T1_TARGET_POSITION_TOLERANCE, + "resample_yaw_tolerance": _T1_TARGET_YAW_TOLERANCE, + }, + { + "step": 4_000 * _T1_RL_STEPS_PER_ITERATION, + "target_radius": (0.5, 3.0), + "target_yaw": (-1.0, 1.0), + "target_angle": (-math.pi, math.pi), + "resampling_time_range": (4.0, 8.0), + "resample_on_near_goal": True, + "resample_on_goal_success": False, + "resample_position_tolerance": _T1_TARGET_POSITION_TOLERANCE, + "resample_yaw_tolerance": _T1_TARGET_YAW_TOLERANCE, + }, + { + "step": 7_000 * _T1_RL_STEPS_PER_ITERATION, + "target_radius": (0.5, 5.0), + "target_yaw": (-math.pi, math.pi), + "target_angle": (-math.pi, math.pi), + "resampling_time_range": (4.0, 8.0), + "resample_on_near_goal": True, + "resample_on_goal_success": False, + "resample_position_tolerance": _T1_TARGET_POSITION_TOLERANCE, + "resample_yaw_tolerance": _T1_TARGET_YAW_TOLERANCE, + }, + { + "step": 24_000 * _T1_RL_STEPS_PER_ITERATION, + "target_radius": (1.0, 7.0), + "target_yaw": (-math.pi, math.pi), + "target_angle": (-math.pi, math.pi), + "resampling_time_range": (5.0, 10.0), + "resample_on_near_goal": True, + "resample_on_goal_success": False, + "resample_position_tolerance": _T1_TARGET_POSITION_TOLERANCE, + "resample_yaw_tolerance": _T1_TARGET_YAW_TOLERANCE, + }, + { + "step": 27_000 * _T1_RL_STEPS_PER_ITERATION, + "target_radius": (1.0, 10.0), + "target_yaw": (-math.pi, math.pi), + "target_angle": (-math.pi, math.pi), + "resampling_time_range": (2.0, 12.0), + "resample_on_near_goal": True, + "resample_on_goal_success": False, + "resample_position_tolerance": _T1_TARGET_POSITION_TOLERANCE, + "resample_yaw_tolerance": _T1_TARGET_YAW_TOLERANCE, + }, + { + "step": _T1_FINE_TUNE_ITERATION * _T1_RL_STEPS_PER_ITERATION, + "target_radius": (0.0, 6.0), + "target_command_buckets": _T1_FINE_TARGET_COMMAND_BUCKETS, + "target_yaw": (-math.pi, math.pi), + "target_angle": (-math.pi, math.pi), + "resampling_time_range": (1.0, 4.0), + "resample_on_near_goal": False, + "resample_on_goal_success": True, + "resample_position_tolerance": _T1_FINE_TARGET_POSITION_TOLERANCE, + "resample_yaw_tolerance": _T1_FINE_TARGET_YAW_TOLERANCE, + "goal_position_tolerance": _T1_FINE_TARGET_POSITION_TOLERANCE, + "goal_yaw_tolerance": _T1_FINE_TARGET_YAW_TOLERANCE, + "goal_hold_steps": _T1_FINE_TARGET_STABLE_HOLD_STEPS, + "goal_max_lin_speed": _T1_FINE_TARGET_MAX_LIN_SPEED, + "goal_max_yaw_rate": _T1_FINE_TARGET_MAX_YAW_RATE, + "goal_min_up_z": _T1_FINE_TARGET_MIN_UP_Z, + "near_goal_heading_distance": _T1_FINE_NEAR_GOAL_DISTANCE, + "near_goal_stability_weight": _T1_FINE_NEAR_GOAL_STABILITY_WEIGHT, + "near_goal_stability_distance": _T1_FINE_NEAR_GOAL_DISTANCE, + "near_goal_stability_lin_speed_std": 0.30, + "near_goal_stability_yaw_rate_std": 0.45, + "near_goal_stability_min_up_z": _T1_FINE_TARGET_MIN_UP_Z, + "goal_pose_bonus_weight": _T1_FINE_GOAL_POSE_BONUS_WEIGHT, + "joint_target_limits_weight": _T1_FINE_JOINT_TARGET_LIMITS_WEIGHT, + }, +] + + +def _robot_asset_cfg() -> SceneEntityCfg: + return SceneEntityCfg("robot", joint_names=(".*",)) + + +def _apply_t1_joint_action_setup( + cfg: ManagerBasedRlEnvCfg, + *, + action_delay_steps: int = 0, +) -> None: + action_cfg_type = ( + DelayedJointPositionActionCfg if action_delay_steps > 0 else JointPositionActionCfg + ) + action_cfg_kwargs = {} + if action_delay_steps > 0: + action_cfg_kwargs["delay_steps"] = action_delay_steps + cfg.actions["joint_pos"] = action_cfg_type( + entity_name="robot", + actuator_names=(".*",), + scale=T1_ACTION_SCALE, + clip=None, + use_default_offset=True, + **action_cfg_kwargs, + ) + + +def _configure_t1_domain_randomization( + cfg: ManagerBasedRlEnvCfg, + *, + foot_geom_names: str = _T1_FOOT_FRICTION_GEOMS, +) -> None: + cfg.events["push_robot"] = EventTermCfg( + func=env_mdp.push_by_setting_velocity, + mode="interval", + interval_range_s=(1.0, 3.0), + params={ + "velocity_range": { + "x": (-0.5, 0.5), + "y": (-0.5, 0.5), + "z": (-0.4, 0.4), + "roll": (-0.52, 0.52), + "pitch": (-0.52, 0.52), + "yaw": (-0.78, 0.78), + }, + }, + ) + cfg.events["foot_friction"] = EventTermCfg( + mode="startup", + func=dr.geom_friction, + params={ + "asset_cfg": SceneEntityCfg( + "robot", + geom_names=foot_geom_names, + ), + "operation": "abs", + "ranges": (0.3, 1.2), + "shared_random": True, + }, + ) + cfg.events["encoder_bias"] = EventTermCfg( + mode="startup", + func=dr.encoder_bias, + params={ + "asset_cfg": SceneEntityCfg("robot"), + "bias_range": (-0.015, 0.015), + }, + ) + cfg.events["base_com"] = EventTermCfg( + mode="startup", + func=dr.body_com_offset, + params={ + "asset_cfg": SceneEntityCfg("robot", body_names=("Trunk",)), + "operation": "add", + "ranges": { + 0: (-0.025, 0.025), + 1: (-0.025, 0.025), + 2: (-0.03, 0.03), + }, + }, + ) + + +def _configure_t1_goal_rewards(cfg: ManagerBasedRlEnvCfg) -> None: + cfg.rewards["near_goal_stability"].weight = 0.0 + cfg.rewards["near_goal_stability"].params["near_distance"] = 0.75 + cfg.rewards["near_goal_stability"].params["position_tolerance"] = ( + _T1_FINE_TARGET_POSITION_TOLERANCE + ) + cfg.rewards["near_goal_stability"].params["yaw_tolerance"] = ( + _T1_FINE_TARGET_YAW_TOLERANCE + ) + cfg.rewards["near_goal_stability"].params["lin_speed_std"] = 0.45 + cfg.rewards["near_goal_stability"].params["yaw_rate_std"] = 0.75 + cfg.rewards["near_goal_stability"].params["min_up_z"] = 0.75 + + cfg.rewards["position_progress"].params["position_tolerance"] = ( + _T1_TARGET_POSITION_TOLERANCE + ) + cfg.rewards["position_progress"].params["yaw_tolerance"] = _T1_TARGET_YAW_TOLERANCE + cfg.rewards["position_progress"].params["entity_name"] = "robot" + cfg.rewards["position_progress"].params["min_root_height"] = ( + _T1_REWARD_GATE_MIN_ROOT_HEIGHT + ) + cfg.rewards["position_progress"].params["min_up_z"] = _T1_REWARD_GATE_MIN_UP_Z + cfg.rewards["near_goal_heading_alignment"].weight = 0.70 + cfg.rewards["near_goal_heading_alignment"].params["near_distance"] = ( + _T1_NEAR_GOAL_DISTANCE + ) + cfg.rewards["near_goal_heading_alignment"].params["entity_name"] = "robot" + cfg.rewards["near_goal_heading_alignment"].params["min_root_height"] = ( + _T1_REWARD_GATE_MIN_ROOT_HEIGHT + ) + cfg.rewards["near_goal_heading_alignment"].params["min_up_z"] = ( + _T1_REWARD_GATE_MIN_UP_Z + ) + cfg.rewards["yaw_progress"].params["position_tolerance"] = ( + _T1_TARGET_POSITION_TOLERANCE + ) + cfg.rewards["yaw_progress"].params["yaw_tolerance"] = _T1_TARGET_YAW_TOLERANCE + cfg.rewards["yaw_progress"].params["near_distance"] = _T1_NEAR_GOAL_DISTANCE + cfg.rewards["yaw_progress"].params["entity_name"] = "robot" + cfg.rewards["yaw_progress"].params["min_root_height"] = ( + _T1_REWARD_GATE_MIN_ROOT_HEIGHT + ) + cfg.rewards["yaw_progress"].params["min_up_z"] = _T1_REWARD_GATE_MIN_UP_Z + cfg.rewards["goal_position_bonus"].params["position_tolerance"] = ( + _T1_TARGET_POSITION_TOLERANCE + ) + cfg.rewards["goal_position_bonus"].weight = _T1_FINE_GOAL_POSITION_BONUS_WEIGHT + cfg.rewards["goal_position_bonus"].params["yaw_soft_tolerance"] = ( + _T1_TARGET_YAW_TOLERANCE + ) + cfg.rewards["goal_position_bonus"].params["entity_name"] = "robot" + cfg.rewards["goal_position_bonus"].params["min_root_height"] = ( + _T1_REWARD_GATE_MIN_ROOT_HEIGHT + ) + cfg.rewards["goal_position_bonus"].params["min_up_z"] = _T1_REWARD_GATE_MIN_UP_Z + cfg.rewards["goal_pose_bonus"].weight = 0.0 + cfg.rewards["goal_pose_bonus"].params["position_tolerance"] = ( + _T1_TARGET_POSITION_TOLERANCE + ) + cfg.rewards["goal_pose_bonus"].params["yaw_tolerance"] = _T1_TARGET_YAW_TOLERANCE + cfg.rewards["goal_pose_bonus"].params["hold_steps"] = _T1_TARGET_STABLE_HOLD_STEPS + cfg.rewards["goal_pose_bonus"].params["entity_name"] = "robot" + cfg.rewards["goal_pose_bonus"].params["max_lin_speed"] = _T1_TARGET_MAX_LIN_SPEED + cfg.rewards["goal_pose_bonus"].params["max_yaw_rate"] = _T1_TARGET_MAX_YAW_RATE + cfg.rewards["goal_pose_bonus"].params["min_up_z"] = _T1_TARGET_MIN_UP_Z + cfg.rewards["goal_time_efficiency"].params["position_tolerance"] = ( + _T1_TARGET_POSITION_TOLERANCE + ) + cfg.rewards["goal_time_efficiency"].params["yaw_tolerance"] = _T1_TARGET_YAW_TOLERANCE + cfg.rewards["goal_time_efficiency"].params["hold_steps"] = ( + _T1_TARGET_STABLE_HOLD_STEPS + ) + cfg.rewards["goal_time_efficiency"].params["entity_name"] = "robot" + cfg.rewards["goal_time_efficiency"].params["max_lin_speed"] = _T1_TARGET_MAX_LIN_SPEED + cfg.rewards["goal_time_efficiency"].params["max_yaw_rate"] = _T1_TARGET_MAX_YAW_RATE + cfg.rewards["goal_time_efficiency"].params["min_up_z"] = _T1_TARGET_MIN_UP_Z + cfg.rewards["fall_penalty"].weight = -0.2 + cfg.rewards["fall_penalty"].params["height_threshold"] = _T1_FALL_HEIGHT_THRESHOLD + cfg.rewards["fall_penalty"].params["up_z_threshold"] = _T1_FALL_UP_Z_THRESHOLD + + +def _apply_t1_goal_stage_to_reward_cfg(cfg: ManagerBasedRlEnvCfg, stage: dict) -> None: + if stage.get("goal_position_tolerance") is not None: + position_tolerance = stage["goal_position_tolerance"] + cfg.rewards["position_progress"].params["position_tolerance"] = position_tolerance + cfg.rewards["yaw_progress"].params["position_tolerance"] = position_tolerance + cfg.rewards["goal_position_bonus"].params["position_tolerance"] = position_tolerance + cfg.rewards["goal_pose_bonus"].params["position_tolerance"] = position_tolerance + cfg.rewards["goal_time_efficiency"].params["position_tolerance"] = ( + position_tolerance + ) + + if stage.get("goal_yaw_tolerance") is not None: + yaw_tolerance = stage["goal_yaw_tolerance"] + cfg.rewards["position_progress"].params["yaw_tolerance"] = yaw_tolerance + cfg.rewards["yaw_progress"].params["yaw_tolerance"] = yaw_tolerance + cfg.rewards["goal_position_bonus"].params["yaw_soft_tolerance"] = yaw_tolerance + cfg.rewards["goal_pose_bonus"].params["yaw_tolerance"] = yaw_tolerance + cfg.rewards["goal_time_efficiency"].params["yaw_tolerance"] = yaw_tolerance + + if stage.get("goal_hold_steps") is not None: + hold_steps = stage["goal_hold_steps"] + cfg.rewards["goal_pose_bonus"].params["hold_steps"] = hold_steps + cfg.rewards["goal_time_efficiency"].params["hold_steps"] = hold_steps + + if stage.get("goal_max_lin_speed") is not None: + max_lin_speed = stage["goal_max_lin_speed"] + cfg.rewards["goal_pose_bonus"].params["max_lin_speed"] = max_lin_speed + cfg.rewards["goal_time_efficiency"].params["max_lin_speed"] = max_lin_speed + + if stage.get("goal_max_yaw_rate") is not None: + max_yaw_rate = stage["goal_max_yaw_rate"] + cfg.rewards["goal_pose_bonus"].params["max_yaw_rate"] = max_yaw_rate + cfg.rewards["goal_time_efficiency"].params["max_yaw_rate"] = max_yaw_rate + + if stage.get("goal_min_up_z") is not None: + min_up_z = stage["goal_min_up_z"] + cfg.rewards["goal_pose_bonus"].params["min_up_z"] = min_up_z + cfg.rewards["goal_time_efficiency"].params["min_up_z"] = min_up_z + + if stage.get("near_goal_heading_distance") is not None: + near_distance = stage["near_goal_heading_distance"] + cfg.rewards["near_goal_heading_alignment"].params["near_distance"] = near_distance + cfg.rewards["yaw_progress"].params["near_distance"] = near_distance + + if stage.get("near_goal_stability_weight") is not None: + cfg.rewards["near_goal_stability"].weight = stage["near_goal_stability_weight"] + if stage.get("near_goal_stability_distance") is not None: + cfg.rewards["near_goal_stability"].params["near_distance"] = stage[ + "near_goal_stability_distance" + ] + if stage.get("near_goal_stability_lin_speed_std") is not None: + cfg.rewards["near_goal_stability"].params["lin_speed_std"] = stage[ + "near_goal_stability_lin_speed_std" + ] + if stage.get("near_goal_stability_yaw_rate_std") is not None: + cfg.rewards["near_goal_stability"].params["yaw_rate_std"] = stage[ + "near_goal_stability_yaw_rate_std" + ] + if stage.get("near_goal_stability_min_up_z") is not None: + cfg.rewards["near_goal_stability"].params["min_up_z"] = stage[ + "near_goal_stability_min_up_z" + ] + + if stage.get("goal_pose_bonus_weight") is not None: + cfg.rewards["goal_pose_bonus"].weight = stage["goal_pose_bonus_weight"] + + if stage.get("joint_target_limits_weight") is not None: + cfg.rewards["joint_target_limits"].weight = stage["joint_target_limits_weight"] + + +def _apply_t1_goal_stage_to_command_cfg( + goal_pose_cmd: target_mdp.GoalPoseCommandCfg, stage: dict +) -> None: + goal_pose_cmd.ranges.target_radius = stage["target_radius"] + goal_pose_cmd.ranges.target_yaw = stage["target_yaw"] + goal_pose_cmd.ranges.target_angle = stage["target_angle"] + command_buckets = stage.get("target_command_buckets") + goal_pose_cmd.ranges.target_command_buckets = ( + tuple( + target_mdp.GoalPoseCommandCfg.CommandBucket( + radius_range=tuple(bucket["radius_range"]), + probability=float(bucket["probability"]), + yaw_range=( + tuple(bucket["yaw_range"]) if bucket["yaw_range"] is not None else None + ), + angle_range=( + tuple(bucket["angle_range"]) if bucket["angle_range"] is not None else None + ), + ) + for bucket in command_buckets + ) + if command_buckets is not None + else None + ) + radius_buckets = stage.get("target_radius_buckets") + goal_pose_cmd.ranges.target_radius_buckets = ( + tuple( + target_mdp.GoalPoseCommandCfg.RadiusBucket( + radius_range=tuple(bucket["radius_range"]), + probability=float(bucket["probability"]), + ) + for bucket in radius_buckets + ) + if radius_buckets is not None + else None + ) + goal_pose_cmd.resampling_time_range = stage["resampling_time_range"] + goal_pose_cmd.resample_on_near_goal = stage["resample_on_near_goal"] + goal_pose_cmd.resample_on_goal_success = stage["resample_on_goal_success"] + goal_pose_cmd.resample_position_tolerance = stage["resample_position_tolerance"] + goal_pose_cmd.resample_yaw_tolerance = stage["resample_yaw_tolerance"] + + +def _configure_t1_goal_pose_command(cfg: ManagerBasedRlEnvCfg) -> None: + goal_pose_cmd = cfg.commands["goal_pose"] + assert isinstance(goal_pose_cmd, target_mdp.GoalPoseCommandCfg) + stage0 = _T1_TARGET_LOCO_GOAL_CURRICULUM_STAGES[0] + _apply_t1_goal_stage_to_command_cfg(goal_pose_cmd, stage0) + + +def _configure_t1_competition_server_physics(cfg: ManagerBasedRlEnvCfg) -> None: + cfg.sim.mujoco.integrator = "euler" + cfg.sim.mujoco.cone = "pyramidal" + cfg.sim.mujoco.impratio = 1.0 + cfg.sim.mujoco.iterations = 100 + cfg.sim.mujoco.ls_iterations = 50 + cfg.sim.mujoco.ccd_iterations = 35 + + +def _t1_play_stage() -> dict: + return _T1_TARGET_LOCO_GOAL_CURRICULUM_STAGES[-1] + + +def booster_t1_target_location_amp_flat_env_cfg( + play: bool = False, + competition_foot: bool = False, + competition_collision: bool = False, +) -> ManagerBasedRlEnvCfg: + """Create the T1 target-location locomotion AMP task.""" + if competition_collision and competition_foot: + raise ValueError( + "competition_collision and competition_foot are mutually exclusive." + ) + + cfg = make_target_location_amp_env_cfg(play=play) + robot_cfg = get_t1_robot_cfg() + if competition_foot: + robot_cfg = get_t1_competition_foot_robot_cfg() + if competition_collision: + robot_cfg = get_t1_competition_collision_robot_cfg() + if competition_foot or competition_collision: + _configure_t1_competition_server_physics(cfg) + cfg.scene.entities = { + "robot": deepcopy(robot_cfg), + } + cfg.scene.num_envs = 1 if play else 1024 + cfg.episode_length_s = _T1_EPISODE_LENGTH_S + cfg.viewer.body_name = "Trunk" + _apply_t1_joint_action_setup( + cfg, + action_delay_steps=( + _T1_COMPETITION_ACTION_DELAY_STEPS if competition_collision else 0 + ), + ) + _configure_t1_goal_rewards(cfg) + _configure_t1_goal_pose_command(cfg) + cfg.events["reset_robot_joints"].params["asset_cfg"] = _robot_asset_cfg() + if not play: + _configure_t1_domain_randomization( + cfg, + foot_geom_names=( + _T1_COMPETITION_FOOT_FRICTION_GEOMS + if competition_foot or competition_collision + else _T1_FOOT_FRICTION_GEOMS + ), + ) + cfg.observations["critic"].terms["amp_robot"].params["robot_cfg"] = _robot_asset_cfg() + cfg.terminations["fell_over"].params["height_threshold"] = _T1_FALL_HEIGHT_THRESHOLD + cfg.terminations["fell_over"].params["up_z_threshold"] = _T1_FALL_UP_Z_THRESHOLD + + cfg.rewards["action_rate_l2"] = RewardTermCfg( + func=target_mdp.action_rate_l2, + weight=-0.01, + ) + cfg.rewards["joint_pos_limits"] = RewardTermCfg( + func=target_mdp.joint_pos_limits, + weight=-0.2, + params={"asset_cfg": _robot_asset_cfg()}, + ) + cfg.rewards["joint_target_limits"] = RewardTermCfg( + func=target_mdp.joint_target_pos_limits, + weight=0.0, + params={ + "asset_cfg": _robot_asset_cfg(), + "action_name": "joint_pos", + }, + ) + + if not play: + cfg.curriculum["goal_pose"] = CurriculumTermCfg( + func=target_mdp.goal_pose_ranges, + params={ + "command_name": "goal_pose", + "stages": _T1_TARGET_LOCO_GOAL_CURRICULUM_STAGES, + }, + ) + + if play: + cfg.observations["actor"].enable_corruption = False + goal_pose_cmd = cfg.commands["goal_pose"] + assert isinstance(goal_pose_cmd, target_mdp.GoalPoseCommandCfg) + play_stage = _t1_play_stage() + _apply_t1_goal_stage_to_command_cfg(goal_pose_cmd, play_stage) + _apply_t1_goal_stage_to_reward_cfg(cfg, play_stage) + cfg.curriculum["goal_pose"] = CurriculumTermCfg( + func=target_mdp.goal_pose_ranges, + params={ + "command_name": "goal_pose", + "stages": _T1_TARGET_LOCO_GOAL_CURRICULUM_STAGES, + }, + ) + + return cfg + + +def booster_t1_target_location_amp_competition_foot_flat_env_cfg( + play: bool = False, +) -> ManagerBasedRlEnvCfg: + """Create the T1 target-location AMP task with competition-style foot contacts.""" + return booster_t1_target_location_amp_flat_env_cfg( + play=play, + competition_foot=True, + ) + + +def booster_t1_target_location_amp_competition_collision_flat_env_cfg( + play: bool = False, +) -> ManagerBasedRlEnvCfg: + """Create the T1 target-location AMP task with competition-style collision contacts.""" + return booster_t1_target_location_amp_flat_env_cfg( + play=play, + competition_collision=True, + ) diff --git a/src/mjlab_playground/tasks/target_location_amp/config/t1/rl_cfg.py b/src/mjlab_playground/tasks/target_location_amp/config/t1/rl_cfg.py new file mode 100644 index 0000000..1efa7be --- /dev/null +++ b/src/mjlab_playground/tasks/target_location_amp/config/t1/rl_cfg.py @@ -0,0 +1,105 @@ +"""RL configuration for Booster T1 target-location AMP locomotion.""" + +from __future__ import annotations + +from mjlab.rl import RslRlModelCfg + +from mjlab_playground.amp.rl.motion_loader import ( + default_t1_locomotion_amp_motion_groups, +) +from mjlab_playground.amp.rl.runner_cfg import ( + AmpOnPolicyRunnerCfg, + AmpPpoAlgorithmCfg, +) +from mjlab_playground.asset_zoo.robots.booster_t1.t1_constants import T1_JOINT_NAMES + +_T1_ACTION_DISTRIBUTION = { + "class_name": "GaussianDistribution", + "init_std": 1.0, + "std_type": "log", +} + + +def booster_t1_target_location_amp_runner_cfg() -> AmpOnPolicyRunnerCfg: + """Create RL runner configuration for T1 target-location locomotion AMP.""" + return AmpOnPolicyRunnerCfg( + actor=RslRlModelCfg( + hidden_dims=(512, 256, 128), + activation="elu", + obs_normalization=True, + distribution_cfg=dict(_T1_ACTION_DISTRIBUTION), + ), + critic=RslRlModelCfg( + hidden_dims=(512, 256, 128), + activation="elu", + obs_normalization=True, + ), + algorithm=AmpPpoAlgorithmCfg( + value_loss_coef=1.0, + use_clipped_value_loss=True, + clip_param=0.2, + entropy_coef=0.0005, + num_learning_epochs=5, + num_mini_batches=4, + learning_rate=1.0e-3, + schedule="adaptive", + gamma=0.98, + lam=0.95, + desired_kl=0.01, + max_grad_norm=1.0, + amp_cfg={ + "motion_groups": default_t1_locomotion_amp_motion_groups(), + "target_fps": 50.0, + "expected_dof": 23, + "joint_names": T1_JOINT_NAMES, + "reward_coef": 0.6, + "reward_scale": 1.0, + "task_reward_lerp": 0.5, + "style_reward_gate": { + "entity_name": "robot", + "min_root_height": 0.58, + "min_up_z": 0.80, + "zero_on_done": True, + }, + "loss_coef": 1.0, + "grad_penalty_coef": 10.0, + "discriminator_learning_rate": 3.0e-4, + "replay_buffer_size": 100_000, + "discriminator_hidden_dims": (512, 256), + "discriminator_activation": "relu", + "normalize_input": True, + "discriminator_loss_type": "least_squares", + "discriminator_reward_type": "hardware_lsgan", + "discriminator_expert_label": 0.9, + "discriminator_policy_label": 0.1, + "discriminator_reward_clip_max": 1.5, + }, + symmetry_cfg=None, + ), + experiment_name="t1_target_location_amp", + run_name="t1_target_location_amp", + wandb_project="mjlab_playground", + num_steps_per_env=24, + max_iterations=10_000, + clip_actions=None, + ) + + +def booster_t1_target_location_amp_competition_foot_runner_cfg() -> ( + AmpOnPolicyRunnerCfg +): + """Create RL runner config for T1 target-location AMP with box foot contacts.""" + cfg = booster_t1_target_location_amp_runner_cfg() + cfg.experiment_name = "t1_target_location_amp_competition_foot" + cfg.run_name = "t1_target_location_amp_competition_foot" + return cfg + + +def booster_t1_target_location_amp_competition_collision_runner_cfg() -> ( + AmpOnPolicyRunnerCfg +): + """Create RL runner config for T1 target-location AMP with competition collisions.""" + cfg = booster_t1_target_location_amp_runner_cfg() + cfg.experiment_name = "t1_target_location_amp_competition_collision" + cfg.run_name = "t1_target_location_amp_competition_collision" + return cfg diff --git a/src/mjlab_playground/tasks/target_location_amp/mdp/__init__.py b/src/mjlab_playground/tasks/target_location_amp/mdp/__init__.py new file mode 100644 index 0000000..be79dbd --- /dev/null +++ b/src/mjlab_playground/tasks/target_location_amp/mdp/__init__.py @@ -0,0 +1,63 @@ +"""MDP helpers specific to target-location AMP locomotion.""" + +from .commands import GoalPoseCommand, GoalPoseCommandCfg +from .curriculums import GoalStage, goal_pose_ranges +from .observations import ( + final_yaw_error_features, + goal_distance, + goal_local_position, + heading_to_target_features, +) +from .rewards import ( + action_rate_l2, + alive_reward, + body_ang_vel_xy_l2, + distance_error_penalty, + goal_pose_terminal_bonus, + goal_position_bonus, + goal_time_efficiency_bonus, + joint_pos_limits, + joint_target_pos_limits, + near_goal_heading_alignment_reward, + near_goal_stability_reward, + position_progress_reward, + root_height_reward, + upright_orientation_reward, + yaw_progress_reward, +) +from .state import ( + reset_goal_pose_state, + update_goal_pose_state, + update_goal_pose_success_hold_state, +) +from .terminations import goal_pose_reached + +__all__ = [ + "GoalPoseCommand", + "GoalPoseCommandCfg", + "GoalStage", + "action_rate_l2", + "alive_reward", + "body_ang_vel_xy_l2", + "distance_error_penalty", + "final_yaw_error_features", + "goal_distance", + "goal_local_position", + "goal_pose_reached", + "goal_pose_ranges", + "goal_pose_terminal_bonus", + "goal_position_bonus", + "goal_time_efficiency_bonus", + "heading_to_target_features", + "joint_pos_limits", + "joint_target_pos_limits", + "near_goal_stability_reward", + "near_goal_heading_alignment_reward", + "position_progress_reward", + "reset_goal_pose_state", + "root_height_reward", + "upright_orientation_reward", + "update_goal_pose_state", + "update_goal_pose_success_hold_state", + "yaw_progress_reward", +] diff --git a/src/mjlab_playground/tasks/target_location_amp/mdp/commands.py b/src/mjlab_playground/tasks/target_location_amp/mdp/commands.py new file mode 100644 index 0000000..f088351 --- /dev/null +++ b/src/mjlab_playground/tasks/target_location_amp/mdp/commands.py @@ -0,0 +1,394 @@ +"""Goal-pose command local to the target-location AMP task.""" + +from __future__ import annotations + +import math +from dataclasses import dataclass, field + +import numpy as np +import torch +from mjlab.entity import Entity +from mjlab.managers import CommandTerm, CommandTermCfg +from mjlab.utils.lab_api.math import wrap_to_pi + +from .rewards import goal_stability_mask +from .state import reset_goal_pose_state, update_goal_pose_success_hold_state + + +class GoalPoseCommand(CommandTerm): + cfg: "GoalPoseCommandCfg" + + def __init__(self, cfg: "GoalPoseCommandCfg", env): + super().__init__(cfg, env) + self.robot: Entity = env.scene[cfg.entity_name] + self.target_pos_w = torch.zeros(self.num_envs, 2, device=self.device) + self.target_yaw_w = torch.zeros(self.num_envs, device=self.device) + self.initial_distance_to_goal = torch.zeros(self.num_envs, device=self.device) + self._target_offset_b = torch.zeros(self.num_envs, 2, device=self.device) + self._target_yaw_offset = torch.zeros(self.num_envs, device=self.device) + self._target_needs_anchor = torch.ones( + self.num_envs, dtype=torch.bool, device=self.device + ) + self.local_pos_error = torch.zeros(self.num_envs, 2, device=self.device) + self.distance_to_goal = torch.zeros(self.num_envs, device=self.device) + self.heading_to_target = torch.zeros(self.num_envs, device=self.device) + self.final_heading_error = torch.zeros(self.num_envs, device=self.device) + self._resample_on_next_step = torch.zeros( + self.num_envs, dtype=torch.bool, device=self.device + ) + self.metrics["goal_distance"] = torch.zeros(self.num_envs, device=self.device) + self.metrics["goal_final_yaw_abs"] = torch.zeros(self.num_envs, device=self.device) + + @property + def command(self) -> torch.Tensor: + return torch.cat( + ( + self.local_pos_error, + self.final_heading_error.unsqueeze(-1), + ), + dim=-1, + ) + + def _update_metrics(self) -> None: + self.metrics["goal_distance"] = self.distance_to_goal + self.metrics["goal_final_yaw_abs"] = torch.abs(self.final_heading_error) + + def _sample_bucket_ids( + self, + probabilities: list[float], + num_samples: int, + error_message: str, + ) -> torch.Tensor: + probs = torch.tensor( + [max(float(probability), 0.0) for probability in probabilities], + dtype=torch.float32, + device=self.device, + ) + total_probability = float(probs.sum().item()) + if total_probability <= 0.0: + raise ValueError(error_message) + probs = probs / total_probability + return torch.multinomial(probs, num_samples, replacement=True) + + def _sample_goal_components( + self, env_ids: torch.Tensor + ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]: + command_buckets = self.cfg.ranges.target_command_buckets + if command_buckets: + bucket_ids = self._sample_bucket_ids( + [bucket.probability for bucket in command_buckets], + len(env_ids), + "target_command_buckets must contain positive probability mass.", + ) + radius_min = torch.tensor( + [float(bucket.radius_range[0]) for bucket in command_buckets], + dtype=torch.float32, + device=self.device, + )[bucket_ids] + radius_max = torch.tensor( + [float(bucket.radius_range[1]) for bucket in command_buckets], + dtype=torch.float32, + device=self.device, + )[bucket_ids] + angle_min = torch.tensor( + [ + float( + bucket.angle_range[0] + if bucket.angle_range is not None + else self.cfg.ranges.target_angle[0] + ) + for bucket in command_buckets + ], + dtype=torch.float32, + device=self.device, + )[bucket_ids] + angle_max = torch.tensor( + [ + float( + bucket.angle_range[1] + if bucket.angle_range is not None + else self.cfg.ranges.target_angle[1] + ) + for bucket in command_buckets + ], + dtype=torch.float32, + device=self.device, + )[bucket_ids] + yaw_min = torch.tensor( + [ + float( + bucket.yaw_range[0] + if bucket.yaw_range is not None + else self.cfg.ranges.target_yaw[0] + ) + for bucket in command_buckets + ], + dtype=torch.float32, + device=self.device, + )[bucket_ids] + yaw_max = torch.tensor( + [ + float( + bucket.yaw_range[1] + if bucket.yaw_range is not None + else self.cfg.ranges.target_yaw[1] + ) + for bucket in command_buckets + ], + dtype=torch.float32, + device=self.device, + )[bucket_ids] + target_radius = radius_min + torch.rand(len(env_ids), device=self.device) * ( + radius_max - radius_min + ) + target_angle = angle_min + torch.rand(len(env_ids), device=self.device) * ( + angle_max - angle_min + ) + yaw_offset = yaw_min + torch.rand(len(env_ids), device=self.device) * ( + yaw_max - yaw_min + ) + return target_radius, target_angle, yaw_offset + + target_radius = self._sample_target_radius(env_ids) + target_angle = torch.empty(len(env_ids), device=self.device).uniform_( + *self.cfg.ranges.target_angle + ) + yaw_offset = torch.empty(len(env_ids), device=self.device).uniform_( + *self.cfg.ranges.target_yaw + ) + return target_radius, target_angle, yaw_offset + + def _sample_target_radius(self, env_ids: torch.Tensor) -> torch.Tensor: + buckets = self.cfg.ranges.target_radius_buckets + if not buckets: + return torch.empty(len(env_ids), device=self.device).uniform_( + *self.cfg.ranges.target_radius + ) + + bucket_ids = self._sample_bucket_ids( + [bucket.probability for bucket in buckets], + len(env_ids), + "target_radius_buckets must contain positive probability mass.", + ) + radius_min = torch.tensor( + [float(bucket.radius_range[0]) for bucket in buckets], + dtype=torch.float32, + device=self.device, + )[bucket_ids] + radius_max = torch.tensor( + [float(bucket.radius_range[1]) for bucket in buckets], + dtype=torch.float32, + device=self.device, + )[bucket_ids] + return radius_min + torch.rand(len(env_ids), device=self.device) * ( + radius_max - radius_min + ) + + def _resample_command(self, env_ids: torch.Tensor) -> None: + if len(env_ids) == 0: + return + + target_radius, target_angle, yaw_offset = self._sample_goal_components(env_ids) + target_x = target_radius * torch.cos(target_angle) + target_y = target_radius * torch.sin(target_angle) + self.initial_distance_to_goal[env_ids] = target_radius + self._target_offset_b[env_ids, 0] = target_x + self._target_offset_b[env_ids, 1] = target_y + self._target_yaw_offset[env_ids] = yaw_offset + self._target_needs_anchor[env_ids] = True + self._resample_on_next_step[env_ids] = False + reset_goal_pose_state(self._env, env_ids) + + def _anchor_pending_targets(self) -> None: + heading = self.robot.data.heading_w + root_pos_w = self.robot.data.root_link_pos_w[:, :2] + if torch.any(self._target_needs_anchor): + env_ids = self._target_needs_anchor.nonzero(as_tuple=False).flatten() + cos_h = torch.cos(heading[env_ids]) + sin_h = torch.sin(heading[env_ids]) + target_offset_b = self._target_offset_b[env_ids] + delta_w = torch.stack( + ( + cos_h * target_offset_b[:, 0] - sin_h * target_offset_b[:, 1], + sin_h * target_offset_b[:, 0] + cos_h * target_offset_b[:, 1], + ), + dim=-1, + ) + self.target_pos_w[env_ids] = root_pos_w[env_ids] + delta_w + self.target_yaw_w[env_ids] = wrap_to_pi( + heading[env_ids] + self._target_yaw_offset[env_ids] + ) + self._target_needs_anchor[env_ids] = False + + def _refresh_local_errors(self) -> None: + root_pos_w = self.robot.data.root_link_pos_w[:, :2] + heading = self.robot.data.heading_w + delta_w = self.target_pos_w - root_pos_w + cos_h = torch.cos(heading) + sin_h = torch.sin(heading) + self.local_pos_error[:, 0] = cos_h * delta_w[:, 0] + sin_h * delta_w[:, 1] + self.local_pos_error[:, 1] = -sin_h * delta_w[:, 0] + cos_h * delta_w[:, 1] + self.distance_to_goal = torch.norm(self.local_pos_error, dim=-1) + self.heading_to_target = torch.atan2( + self.local_pos_error[:, 1], self.local_pos_error[:, 0] + ) + self.final_heading_error = wrap_to_pi(self.target_yaw_w - heading) + + def _queue_near_goal_resample(self) -> None: + if not self.cfg.resample_on_near_goal: + return + reached_position = self.distance_to_goal <= self.cfg.resample_position_tolerance + reached_yaw = torch.abs(self.final_heading_error) <= self.cfg.resample_yaw_tolerance + self._resample_on_next_step |= reached_position & reached_yaw + + def _queue_goal_success_resample(self) -> None: + if not self.cfg.resample_on_goal_success: + return + + reward_manager = getattr(self._env, "reward_manager", None) + if reward_manager is None: + return + try: + goal_pose_bonus_cfg = reward_manager.get_term_cfg("goal_pose_bonus") + except (KeyError, ValueError, AttributeError): + return + params = getattr(goal_pose_bonus_cfg, "params", None) + if not isinstance(params, dict): + return + + position_tolerance = float( + params.get("position_tolerance", self.cfg.resample_position_tolerance) + ) + yaw_tolerance = float(params.get("yaw_tolerance", self.cfg.resample_yaw_tolerance)) + hold_steps = int(params.get("hold_steps", 1)) + entity_name = str(params.get("entity_name", self.cfg.entity_name)) + max_lin_speed = params.get("max_lin_speed") + max_yaw_rate = params.get("max_yaw_rate") + min_up_z = params.get("min_up_z") + + reached_now = (self.distance_to_goal <= position_tolerance) & ( + torch.abs(self.final_heading_error) <= yaw_tolerance + ) + if max_lin_speed is not None or max_yaw_rate is not None or min_up_z is not None: + stable, _ = goal_stability_mask( + self._env, + entity_name=entity_name, + max_lin_speed=(float(max_lin_speed) if max_lin_speed is not None else None), + max_yaw_rate=(float(max_yaw_rate) if max_yaw_rate is not None else None), + min_up_z=float(min_up_z) if min_up_z is not None else None, + ) + reached_now = reached_now & stable + hold = update_goal_pose_success_hold_state( + self._env, + success_now=reached_now, + ) + reached = hold >= hold_steps + self._resample_on_next_step |= reached + + def _update_command(self) -> None: + if torch.any(self._resample_on_next_step): + env_ids = self._resample_on_next_step.nonzero(as_tuple=False).flatten() + self._resample(env_ids) + self._anchor_pending_targets() + self._refresh_local_errors() + self._queue_near_goal_resample() + self._queue_goal_success_resample() + + def _debug_vis_impl(self, visualizer) -> None: + env_indices = visualizer.get_env_indices(self.num_envs) + if not env_indices: + return + + root_pos_ws = self.robot.data.root_link_pos_w.cpu().numpy() + target_pos_ws = self.target_pos_w.cpu().numpy() + target_yaws = self.target_yaw_w.cpu().numpy() + + z_offset = self.cfg.viz.z_offset + sphere_radius = self.cfg.viz.sphere_radius + heading_scale = self.cfg.viz.heading_scale + link_radius = self.cfg.viz.link_radius + + for env_idx in env_indices: + root_pos_w = root_pos_ws[env_idx] + target_pos_w = target_pos_ws[env_idx] + + if np.linalg.norm(root_pos_w) < 1.0e-6: + continue + + target_point = np.array( + [target_pos_w[0], target_pos_w[1], z_offset], dtype=np.float32 + ) + robot_point = np.array([root_pos_w[0], root_pos_w[1], z_offset], dtype=np.float32) + target_yaw = float(target_yaws[env_idx]) + + visualizer.add_sphere( + target_point, + radius=sphere_radius, + color=(1.0, 0.55, 0.1, 0.9), + label=f"goal_target_{env_idx}", + ) + visualizer.add_cylinder( + robot_point, + target_point, + radius=link_radius, + color=(1.0, 0.7, 0.2, 0.35), + label=f"goal_link_{env_idx}", + ) + + heading_end = np.array( + [ + target_pos_w[0] + heading_scale * np.cos(target_yaw), + target_pos_w[1] + heading_scale * np.sin(target_yaw), + z_offset, + ], + dtype=np.float32, + ) + visualizer.add_arrow( + target_point, + heading_end, + color=(1.0, 0.2, 0.2, 0.95), + width=link_radius * 1.5, + label=f"goal_heading_{env_idx}", + ) + + +@dataclass(kw_only=True) +class GoalPoseCommandCfg(CommandTermCfg): + @dataclass + class RadiusBucket: + radius_range: tuple[float, float] + probability: float + + @dataclass + class CommandBucket: + radius_range: tuple[float, float] + probability: float + yaw_range: tuple[float, float] | None = None + angle_range: tuple[float, float] | None = None + + entity_name: str + resample_on_near_goal: bool = False + resample_on_goal_success: bool = False + resample_position_tolerance: float = 0.0 + resample_yaw_tolerance: float = 0.0 + + @dataclass + class Ranges: + target_radius: tuple[float, float] + target_yaw: tuple[float, float] + target_angle: tuple[float, float] = (-math.pi, math.pi) + target_radius_buckets: tuple["GoalPoseCommandCfg.RadiusBucket", ...] | None = None + target_command_buckets: tuple["GoalPoseCommandCfg.CommandBucket", ...] | None = None + + @dataclass + class VizCfg: + z_offset: float = 0.08 + sphere_radius: float = 0.07 + heading_scale: float = 0.28 + link_radius: float = 0.012 + + ranges: Ranges + viz: VizCfg = field(default_factory=VizCfg) + + def build(self, env) -> GoalPoseCommand: + return GoalPoseCommand(self, env) diff --git a/src/mjlab_playground/tasks/target_location_amp/mdp/curriculums.py b/src/mjlab_playground/tasks/target_location_amp/mdp/curriculums.py new file mode 100644 index 0000000..82fc117 --- /dev/null +++ b/src/mjlab_playground/tasks/target_location_amp/mdp/curriculums.py @@ -0,0 +1,280 @@ +"""Curriculum helpers local to the target-location AMP task.""" + +# pyright: reportArgumentType=false, reportAttributeAccessIssue=false +# pyright: reportOptionalIterable=false + +from __future__ import annotations + +import math +from typing import TypedDict, cast + +import torch + +from .commands import GoalPoseCommandCfg + + +class GoalRadiusBucketStage(TypedDict): + radius_range: tuple[float, float] + probability: float + + +class GoalCommandBucketStage(TypedDict): + radius_range: tuple[float, float] + probability: float + yaw_range: tuple[float, float] | None + angle_range: tuple[float, float] | None + + +class GoalStage(TypedDict): + step: int + target_radius: tuple[float, float] | None + target_radius_buckets: list[GoalRadiusBucketStage] | None + target_command_buckets: list[GoalCommandBucketStage] | None + target_yaw: tuple[float, float] | None + target_angle: tuple[float, float] | None + resampling_time_range: tuple[float, float] | None + resample_on_near_goal: bool | None + resample_on_goal_success: bool | None + resample_position_tolerance: float | None + resample_yaw_tolerance: float | None + goal_position_tolerance: float | None + goal_yaw_tolerance: float | None + goal_hold_steps: int | None + goal_max_lin_speed: float | None + goal_max_yaw_rate: float | None + goal_min_up_z: float | None + near_goal_heading_distance: float | None + near_goal_stability_weight: float | None + near_goal_stability_distance: float | None + near_goal_stability_lin_speed_std: float | None + near_goal_stability_yaw_rate_std: float | None + near_goal_stability_min_up_z: float | None + goal_pose_bonus_weight: float | None + joint_target_limits_weight: float | None + + +def _apply_goal_reward_stage(env, stage: GoalStage) -> None: + reward_manager = env.reward_manager + + if stage.get("goal_position_tolerance") is not None: + position_tolerance = float(stage["goal_position_tolerance"]) + reward_manager.get_term_cfg("position_progress").params["position_tolerance"] = ( + position_tolerance + ) + reward_manager.get_term_cfg("yaw_progress").params["position_tolerance"] = ( + position_tolerance + ) + reward_manager.get_term_cfg("goal_position_bonus").params["position_tolerance"] = ( + position_tolerance + ) + reward_manager.get_term_cfg("goal_pose_bonus").params["position_tolerance"] = ( + position_tolerance + ) + reward_manager.get_term_cfg("goal_time_efficiency").params["position_tolerance"] = ( + position_tolerance + ) + + if stage.get("goal_yaw_tolerance") is not None: + yaw_tolerance = float(stage["goal_yaw_tolerance"]) + reward_manager.get_term_cfg("position_progress").params["yaw_tolerance"] = ( + yaw_tolerance + ) + reward_manager.get_term_cfg("yaw_progress").params["yaw_tolerance"] = yaw_tolerance + reward_manager.get_term_cfg("goal_position_bonus").params["yaw_soft_tolerance"] = ( + yaw_tolerance + ) + reward_manager.get_term_cfg("goal_pose_bonus").params["yaw_tolerance"] = ( + yaw_tolerance + ) + reward_manager.get_term_cfg("goal_time_efficiency").params["yaw_tolerance"] = ( + yaw_tolerance + ) + + if stage.get("goal_hold_steps") is not None: + hold_steps = int(stage["goal_hold_steps"]) + reward_manager.get_term_cfg("goal_pose_bonus").params["hold_steps"] = hold_steps + reward_manager.get_term_cfg("goal_time_efficiency").params["hold_steps"] = ( + hold_steps + ) + + if stage.get("goal_max_lin_speed") is not None: + max_lin_speed = float(stage["goal_max_lin_speed"]) + reward_manager.get_term_cfg("goal_pose_bonus").params["max_lin_speed"] = ( + max_lin_speed + ) + reward_manager.get_term_cfg("goal_time_efficiency").params["max_lin_speed"] = ( + max_lin_speed + ) + + if stage.get("goal_max_yaw_rate") is not None: + max_yaw_rate = float(stage["goal_max_yaw_rate"]) + reward_manager.get_term_cfg("goal_pose_bonus").params["max_yaw_rate"] = max_yaw_rate + reward_manager.get_term_cfg("goal_time_efficiency").params["max_yaw_rate"] = ( + max_yaw_rate + ) + + if stage.get("goal_min_up_z") is not None: + min_up_z = float(stage["goal_min_up_z"]) + reward_manager.get_term_cfg("goal_pose_bonus").params["min_up_z"] = min_up_z + reward_manager.get_term_cfg("goal_time_efficiency").params["min_up_z"] = min_up_z + + if stage.get("near_goal_heading_distance") is not None: + near_distance = float(stage["near_goal_heading_distance"]) + reward_manager.get_term_cfg("near_goal_heading_alignment").params[ + "near_distance" + ] = near_distance + reward_manager.get_term_cfg("yaw_progress").params["near_distance"] = near_distance + + if stage.get("near_goal_stability_weight") is not None: + reward_manager.get_term_cfg("near_goal_stability").weight = float( + stage["near_goal_stability_weight"] + ) + + if stage.get("near_goal_stability_distance") is not None: + reward_manager.get_term_cfg("near_goal_stability").params["near_distance"] = float( + stage["near_goal_stability_distance"] + ) + + if stage.get("near_goal_stability_lin_speed_std") is not None: + reward_manager.get_term_cfg("near_goal_stability").params["lin_speed_std"] = float( + stage["near_goal_stability_lin_speed_std"] + ) + + if stage.get("near_goal_stability_yaw_rate_std") is not None: + reward_manager.get_term_cfg("near_goal_stability").params["yaw_rate_std"] = float( + stage["near_goal_stability_yaw_rate_std"] + ) + + if stage.get("near_goal_stability_min_up_z") is not None: + reward_manager.get_term_cfg("near_goal_stability").params["min_up_z"] = float( + stage["near_goal_stability_min_up_z"] + ) + + if stage.get("goal_pose_bonus_weight") is not None: + reward_manager.get_term_cfg("goal_pose_bonus").weight = float( + stage["goal_pose_bonus_weight"] + ) + + if stage.get("joint_target_limits_weight") is not None: + reward_manager.get_term_cfg("joint_target_limits").weight = float( + stage["joint_target_limits_weight"] + ) + + +def _radius_range_metrics( + cfg: GoalPoseCommandCfg, +) -> tuple[float, float, torch.Tensor]: + if cfg.ranges.target_command_buckets: + radius_min = min( + float(bucket.radius_range[0]) for bucket in cfg.ranges.target_command_buckets + ) + radius_max = max( + float(bucket.radius_range[1]) for bucket in cfg.ranges.target_command_buckets + ) + return ( + radius_min, + radius_max, + torch.tensor(float(len(cfg.ranges.target_command_buckets))), + ) + + if not cfg.ranges.target_radius_buckets: + return ( + float(cfg.ranges.target_radius[0]), + float(cfg.ranges.target_radius[1]), + torch.tensor(0.0), + ) + + radius_min = min( + float(bucket.radius_range[0]) for bucket in cfg.ranges.target_radius_buckets + ) + radius_max = max( + float(bucket.radius_range[1]) for bucket in cfg.ranges.target_radius_buckets + ) + return ( + radius_min, + radius_max, + torch.tensor(float(len(cfg.ranges.target_radius_buckets))), + ) + + +def goal_pose_ranges( + env, + env_ids: torch.Tensor, + command_name: str, + stages: list[GoalStage], +) -> dict[str, torch.Tensor]: + del env_ids + command_term = env.command_manager.get_term(command_name) + cfg = cast(GoalPoseCommandCfg, command_term.cfg) + reward_manager = env.reward_manager + for stage in stages: + if env.common_step_counter > stage["step"]: + if stage.get("target_radius") is not None: + cfg.ranges.target_radius = stage["target_radius"] + if stage.get("target_radius_buckets") is not None: + cfg.ranges.target_radius_buckets = tuple( + GoalPoseCommandCfg.RadiusBucket( + radius_range=tuple(bucket["radius_range"]), + probability=float(bucket["probability"]), + ) + for bucket in stage["target_radius_buckets"] + ) + if stage.get("target_command_buckets") is not None: + cfg.ranges.target_command_buckets = tuple( + GoalPoseCommandCfg.CommandBucket( + radius_range=tuple(bucket["radius_range"]), + probability=float(bucket["probability"]), + yaw_range=( + tuple(bucket["yaw_range"]) if bucket["yaw_range"] is not None else None + ), + angle_range=( + tuple(bucket["angle_range"]) + if bucket["angle_range"] is not None + else None + ), + ) + for bucket in stage["target_command_buckets"] + ) + if stage.get("target_yaw") is not None: + cfg.ranges.target_yaw = stage["target_yaw"] + if stage.get("target_angle") is not None: + cfg.ranges.target_angle = stage["target_angle"] + if stage.get("resampling_time_range") is not None: + cfg.resampling_time_range = stage["resampling_time_range"] + if stage.get("resample_on_near_goal") is not None: + cfg.resample_on_near_goal = stage["resample_on_near_goal"] + if stage.get("resample_on_goal_success") is not None: + cfg.resample_on_goal_success = stage["resample_on_goal_success"] + if stage.get("resample_position_tolerance") is not None: + cfg.resample_position_tolerance = stage["resample_position_tolerance"] + if stage.get("resample_yaw_tolerance") is not None: + cfg.resample_yaw_tolerance = stage["resample_yaw_tolerance"] + _apply_goal_reward_stage(env, stage) + + radius_min, radius_max, bucket_count = _radius_range_metrics(cfg) + return { + "target_radius_min": torch.tensor(radius_min), + "target_radius_max": torch.tensor(radius_max), + "target_radius_bucket_count": bucket_count, + "target_yaw_min": torch.tensor(cfg.ranges.target_yaw[0]), + "target_yaw_max": torch.tensor(cfg.ranges.target_yaw[1]), + "target_angle_min": torch.tensor(cfg.ranges.target_angle[0]), + "target_angle_max": torch.tensor(cfg.ranges.target_angle[1]), + "resampling_time_min": torch.tensor(cfg.resampling_time_range[0]), + "resampling_time_max": torch.tensor(cfg.resampling_time_range[1]), + "resample_on_near_goal": torch.tensor(float(cfg.resample_on_near_goal)), + "resample_on_goal_success": torch.tensor(float(cfg.resample_on_goal_success)), + "resample_position_tolerance": torch.tensor(cfg.resample_position_tolerance), + "resample_yaw_tolerance": torch.tensor(cfg.resample_yaw_tolerance), + "reward_goal_position_tolerance": torch.tensor( + reward_manager.get_term_cfg("goal_pose_bonus").params["position_tolerance"] + ), + "reward_goal_yaw_tolerance_deg": torch.tensor( + math.degrees( + reward_manager.get_term_cfg("goal_pose_bonus").params["yaw_tolerance"] + ) + ), + "reward_goal_hold_steps": torch.tensor( + reward_manager.get_term_cfg("goal_pose_bonus").params["hold_steps"] + ), + } diff --git a/src/mjlab_playground/tasks/target_location_amp/mdp/observations.py b/src/mjlab_playground/tasks/target_location_amp/mdp/observations.py new file mode 100644 index 0000000..6def890 --- /dev/null +++ b/src/mjlab_playground/tasks/target_location_amp/mdp/observations.py @@ -0,0 +1,27 @@ +"""Goal-pose observations for target-location AMP locomotion.""" + +from __future__ import annotations + +import torch + + +def goal_local_position(env, command_name: str = "goal_pose") -> torch.Tensor: + command_term = env.command_manager.get_term(command_name) + return command_term.local_pos_error + + +def heading_to_target_features(env, command_name: str = "goal_pose") -> torch.Tensor: + command_term = env.command_manager.get_term(command_name) + angle = command_term.heading_to_target + return torch.stack((torch.sin(angle), torch.cos(angle)), dim=-1) + + +def final_yaw_error_features(env, command_name: str = "goal_pose") -> torch.Tensor: + command_term = env.command_manager.get_term(command_name) + angle = command_term.final_heading_error + return torch.stack((torch.sin(angle), torch.cos(angle)), dim=-1) + + +def goal_distance(env, command_name: str = "goal_pose") -> torch.Tensor: + command_term = env.command_manager.get_term(command_name) + return command_term.distance_to_goal.unsqueeze(-1) diff --git a/src/mjlab_playground/tasks/target_location_amp/mdp/rewards.py b/src/mjlab_playground/tasks/target_location_amp/mdp/rewards.py new file mode 100644 index 0000000..c0b7784 --- /dev/null +++ b/src/mjlab_playground/tasks/target_location_amp/mdp/rewards.py @@ -0,0 +1,558 @@ +"""Rewards local to the target-location AMP locomotion task.""" + +from __future__ import annotations + +import torch +from mjlab.managers.scene_entity_config import SceneEntityCfg + +from .state import update_goal_pose_state + +_DEFAULT_ASSET_CFG = SceneEntityCfg("robot") + + +def _log_metric(env, key: str, value: torch.Tensor) -> None: + extras = getattr(env, "extras", None) + if not isinstance(extras, dict): + return + log = extras.get("log") + if isinstance(log, dict): + log[key] = value + + +def _log_goal_pose_metrics( + env, + command_term, + *, + position_tolerance: float, + yaw_tolerance: float, + hold_steps: torch.Tensor, +) -> None: + yaw_abs = torch.abs(command_term.final_heading_error) + position_ok = command_term.distance_to_goal <= position_tolerance + yaw_ok = yaw_abs <= yaw_tolerance + pose_ok = position_ok & yaw_ok + _log_metric( + env, + "Metrics/target_location/reward_goal_distance", + command_term.distance_to_goal.mean(), + ) + _log_metric(env, "Metrics/target_location/reward_final_yaw_abs", yaw_abs.mean()) + _log_metric( + env, "Metrics/target_location/position_ok_fraction", position_ok.float().mean() + ) + _log_metric(env, "Metrics/target_location/yaw_ok_fraction", yaw_ok.float().mean()) + _log_metric(env, "Metrics/target_location/pose_ok_fraction", pose_ok.float().mean()) + _log_metric(env, "Metrics/target_location/hold_steps_mean", hold_steps.float().mean()) + + +def _state(env, command_name: str, position_tolerance: float, yaw_tolerance: float): + command_term = env.command_manager.get_term(command_name) + state = update_goal_pose_state( + env, + distance=command_term.distance_to_goal, + final_yaw_error=command_term.final_heading_error, + position_tolerance=position_tolerance, + yaw_tolerance=yaw_tolerance, + ) + _log_goal_pose_metrics( + env, + command_term, + position_tolerance=position_tolerance, + yaw_tolerance=yaw_tolerance, + hold_steps=state[2], + ) + return state + + +def _healthy_reward_gate( + env, + reward: torch.Tensor, + *, + entity_name: str = "robot", + min_root_height: float | None = None, + min_up_z: float | None = None, +) -> torch.Tensor: + if min_root_height is None and min_up_z is None: + return reward + + asset = env.scene[entity_name] + stable = torch.ones_like(reward, dtype=torch.bool) + if min_root_height is not None: + stable = stable & (asset.data.root_link_pos_w[:, 2] >= float(min_root_height)) + if min_up_z is not None: + up_z = -asset.data.projected_gravity_b[:, 2] + stable = stable & (up_z >= float(min_up_z)) + _log_metric(env, "Metrics/target_location/reward_gate_up_z", up_z.mean()) + _log_metric( + env, "Metrics/target_location/reward_gate_fraction", stable.float().mean() + ) + # Keep negative shaping when unstable, but suppress positive reward hacking. + return torch.where(stable, reward, torch.minimum(reward, torch.zeros_like(reward))) + + +def _apply_training_decay( + env, + reward: torch.Tensor, + *, + start_step: int = 0, + decay_steps: int = 0, + final_scale: float = 1.0, + log_name: str | None = None, +) -> torch.Tensor: + final = float(final_scale) + if start_step <= 0 and decay_steps <= 0 and final >= 1.0: + return reward + + step = int(getattr(env, "common_step_counter", 0)) + start = int(start_step) + decay = int(decay_steps) + if step < start: + scale = 1.0 + elif decay <= 0: + scale = final + else: + progress = min(1.0, (step - start) / float(decay)) + scale = 1.0 - (1.0 - final) * progress + + scale_tensor = torch.as_tensor(scale, dtype=reward.dtype, device=reward.device) + if log_name is not None: + _log_metric(env, f"Curriculum/target_location/{log_name}_scale", scale_tensor) + return reward * scale_tensor + + +def alive_reward( + env, + decay_start_step: int = 0, + decay_steps: int = 0, + final_scale: float = 1.0, +) -> torch.Tensor: + """Dense survival reward so early PPO gets a clear non-fall signal.""" + reward = torch.ones(env.num_envs, device=env.device) + return _apply_training_decay( + env, + reward, + start_step=decay_start_step, + decay_steps=decay_steps, + final_scale=final_scale, + log_name="alive", + ) + + +def upright_orientation_reward( + env, + std: float = 0.5, + decay_start_step: int = 0, + decay_steps: int = 0, + final_scale: float = 1.0, + asset_cfg: SceneEntityCfg = _DEFAULT_ASSET_CFG, +) -> torch.Tensor: + """Reward keeping the root upright from projected gravity.""" + asset = env.scene[asset_cfg.name] + gravity_xy = asset.data.projected_gravity_b[:, :2] + error = torch.sum(torch.square(gravity_xy), dim=-1) + reward = torch.exp(-error / max(float(std) ** 2, 1.0e-6)) + reward = _apply_training_decay( + env, + reward, + start_step=decay_start_step, + decay_steps=decay_steps, + final_scale=final_scale, + log_name="upright", + ) + _log_metric(env, "Metrics/target_location/upright_reward", reward.mean()) + return reward + + +def root_height_reward( + env, + target_height: float = 0.65, + std: float = 0.2, + decay_start_step: int = 0, + decay_steps: int = 0, + final_scale: float = 1.0, + asset_cfg: SceneEntityCfg = _DEFAULT_ASSET_CFG, +) -> torch.Tensor: + """Reward staying near the nominal root height.""" + asset = env.scene[asset_cfg.name] + height = asset.data.root_link_pos_w[:, 2] + error = torch.square(height - float(target_height)) + reward = torch.exp(-error / max(float(std) ** 2, 1.0e-6)) + reward = _apply_training_decay( + env, + reward, + start_step=decay_start_step, + decay_steps=decay_steps, + final_scale=final_scale, + log_name="root_height", + ) + _log_metric(env, "Metrics/target_location/root_height_reward", reward.mean()) + return reward + + +def body_ang_vel_xy_l2( + env, + asset_cfg: SceneEntityCfg = _DEFAULT_ASSET_CFG, +) -> torch.Tensor: + """Penalize roll/pitch angular velocity of the root.""" + asset = env.scene[asset_cfg.name] + return torch.sum(torch.square(asset.data.root_link_ang_vel_b[:, :2]), dim=-1) + + +def action_rate_l2(env) -> torch.Tensor: + """Penalize raw policy action changes.""" + return torch.sum( + torch.square(env.action_manager.action - env.action_manager.prev_action), + dim=-1, + ) + + +def joint_pos_limits( + env, + asset_cfg: SceneEntityCfg = _DEFAULT_ASSET_CFG, +) -> torch.Tensor: + """Penalize excursions beyond soft joint position limits.""" + asset = env.scene[asset_cfg.name] + soft_limits = asset.data.soft_joint_pos_limits + joint_pos = asset.data.joint_pos[:, asset_cfg.joint_ids] + lower_limits = soft_limits[:, asset_cfg.joint_ids, 0] + upper_limits = soft_limits[:, asset_cfg.joint_ids, 1] + out_of_limits = -(joint_pos - lower_limits).clip(max=0.0) + out_of_limits += (joint_pos - upper_limits).clip(min=0.0) + return torch.sum(out_of_limits, dim=-1) + + +def joint_target_pos_limits( + env, + asset_cfg: SceneEntityCfg = _DEFAULT_ASSET_CFG, + action_name: str = "joint_pos", + normalize_by_range: bool = False, +) -> torch.Tensor: + """Penalize commanded position targets beyond soft joint limits.""" + asset = env.scene[asset_cfg.name] + term = env.action_manager.get_term(action_name) + target_ids = term.target_ids + soft_limits = asset.data.soft_joint_pos_limits[:, target_ids] + lower_limits = soft_limits[:, :, 0] + upper_limits = soft_limits[:, :, 1] + + processed_actions = term.raw_action * term.scale + term.offset + if term.__class__.__name__ == "RelativeJointPositionAction": + joint_pos = asset.data.joint_pos[:, target_ids] + target_pos = joint_pos + processed_actions + else: + target_pos = processed_actions + + out_of_limits = -(target_pos - lower_limits).clip(max=0.0) + out_of_limits += (target_pos - upper_limits).clip(min=0.0) + if normalize_by_range: + limit_range = (upper_limits - lower_limits).clamp_min(1.0e-6) + out_of_limits = out_of_limits / limit_range + penalty = torch.sum(out_of_limits, dim=-1) + metric_name = ( + "Metrics/target_location/target_excess_normalized" + if normalize_by_range + else "Metrics/target_location/target_excess" + ) + _log_metric(env, metric_name, penalty.mean()) + return penalty + + +def position_progress_reward( + env, + command_name: str = "goal_pose", + position_tolerance: float = 0.1, + yaw_tolerance: float = 0.1, + max_speed: float = 2.5, + entity_name: str = "robot", + min_root_height: float | None = None, + min_up_z: float | None = None, +) -> torch.Tensor: + distance_progress, _, _ = _state(env, command_name, position_tolerance, yaw_tolerance) + reward = distance_progress / max(float(env.step_dt) * max(max_speed, 1.0e-6), 1.0e-6) + return _healthy_reward_gate( + env, + reward, + entity_name=entity_name, + min_root_height=min_root_height, + min_up_z=min_up_z, + ) + + +def distance_error_penalty( + env, + command_name: str = "goal_pose", + min_distance_scale: float = 0.5, +) -> torch.Tensor: + command_term = env.command_manager.get_term(command_name) + scale = torch.clamp( + command_term.initial_distance_to_goal, + min=min_distance_scale, + ) + distance_error = command_term.distance_to_goal / scale + _log_metric(env, "Metrics/target_location/distance_error_norm", distance_error.mean()) + return distance_error + + +def yaw_progress_reward( + env, + command_name: str = "goal_pose", + position_tolerance: float = 0.1, + yaw_tolerance: float = 0.1, + near_distance: float = 0.3, + entity_name: str = "robot", + min_root_height: float | None = None, + min_up_z: float | None = None, +) -> torch.Tensor: + _, yaw_progress, _ = _state(env, command_name, position_tolerance, yaw_tolerance) + command_term = env.command_manager.get_term(command_name) + near = (command_term.distance_to_goal <= near_distance).float() + reward = yaw_progress * near + return _healthy_reward_gate( + env, + reward, + entity_name=entity_name, + min_root_height=min_root_height, + min_up_z=min_up_z, + ) + + +def goal_position_bonus( + env, + command_name: str = "goal_pose", + position_tolerance: float = 0.1, + yaw_soft_tolerance: float | None = None, + entity_name: str = "robot", + min_root_height: float | None = None, + min_up_z: float | None = None, +) -> torch.Tensor: + command_term = env.command_manager.get_term(command_name) + position_ok = command_term.distance_to_goal <= position_tolerance + if yaw_soft_tolerance is None: + reward = position_ok.float() + else: + yaw_ok = torch.abs(command_term.final_heading_error) <= yaw_soft_tolerance + reward = (position_ok & yaw_ok).float() + return _healthy_reward_gate( + env, + reward, + entity_name=entity_name, + min_root_height=min_root_height, + min_up_z=min_up_z, + ) + + +def near_goal_heading_alignment_reward( + env, + command_name: str = "goal_pose", + near_distance: float = 0.5, + entity_name: str = "robot", + min_root_height: float | None = None, + min_up_z: float | None = None, +) -> torch.Tensor: + command_term = env.command_manager.get_term(command_name) + near_weight = torch.exp( + -command_term.distance_to_goal / max(float(near_distance), 1.0e-6) + ) + reward = near_weight * torch.cos(command_term.final_heading_error) + _log_metric(env, "Metrics/target_location/near_heading_alignment", reward.mean()) + return _healthy_reward_gate( + env, + reward, + entity_name=entity_name, + min_root_height=min_root_height, + min_up_z=min_up_z, + ) + + +def goal_stability_mask( + env, + *, + entity_name: str = "robot", + max_lin_speed: float | None = None, + max_yaw_rate: float | None = None, + min_up_z: float | None = None, +) -> tuple[torch.Tensor, dict[str, torch.Tensor]]: + asset = env.scene[entity_name] + device = asset.data.root_link_pos_w.device + stable = torch.ones( + asset.data.root_link_pos_w.shape[0], dtype=torch.bool, device=device + ) + metrics: dict[str, torch.Tensor] = {} + + if max_lin_speed is not None: + lin_speed = torch.linalg.vector_norm(asset.data.root_link_lin_vel_b[:, :2], dim=1) + metrics["lin_speed"] = lin_speed + stable = stable & (lin_speed <= float(max_lin_speed)) + + if max_yaw_rate is not None: + yaw_rate = torch.abs(asset.data.root_link_ang_vel_b[:, 2]) + metrics["yaw_rate"] = yaw_rate + stable = stable & (yaw_rate <= float(max_yaw_rate)) + + if min_up_z is not None: + up_z = -asset.data.projected_gravity_b[:, 2] + metrics["up_z"] = up_z + stable = stable & (up_z >= float(min_up_z)) + + metrics["stable"] = stable.float() + return stable, metrics + + +def _stability_ok( + env, + *, + entity_name: str, + max_lin_speed: float | None, + max_yaw_rate: float | None, + min_up_z: float | None, +) -> torch.Tensor | None: + if max_lin_speed is None and max_yaw_rate is None and min_up_z is None: + return None + + stable, metrics = goal_stability_mask( + env, + entity_name=entity_name, + max_lin_speed=max_lin_speed, + max_yaw_rate=max_yaw_rate, + min_up_z=min_up_z, + ) + if "lin_speed" in metrics: + _log_metric( + env, + "Metrics/target_location/stability_lin_speed", + metrics["lin_speed"].mean(), + ) + if "yaw_rate" in metrics: + _log_metric( + env, + "Metrics/target_location/stability_yaw_rate", + metrics["yaw_rate"].mean(), + ) + if "up_z" in metrics: + _log_metric(env, "Metrics/target_location/stability_up_z", metrics["up_z"].mean()) + _log_metric( + env, "Metrics/target_location/stability_ok_fraction", stable.float().mean() + ) + return stable + + +def goal_pose_terminal_bonus( + env, + command_name: str = "goal_pose", + position_tolerance: float = 0.1, + yaw_tolerance: float = 0.1, + hold_steps: int = 10, + entity_name: str = "robot", + max_lin_speed: float | None = None, + max_yaw_rate: float | None = None, + min_up_z: float | None = None, +) -> torch.Tensor: + from .state import update_goal_pose_success_hold_state + + command_term = env.command_manager.get_term(command_name) + stable = _stability_ok( + env, + entity_name=entity_name, + max_lin_speed=max_lin_speed, + max_yaw_rate=max_yaw_rate, + min_up_z=min_up_z, + ) + success_now = (command_term.distance_to_goal <= float(position_tolerance)) & ( + torch.abs(command_term.final_heading_error) <= float(yaw_tolerance) + ) + if stable is not None: + success_now = success_now & stable + hold = update_goal_pose_success_hold_state(env, success_now=success_now) + just_reached = hold == hold_steps + _log_metric( + env, + "Metrics/target_location/terminal_success_fraction", + just_reached.float().mean(), + ) + return just_reached.float() + + +def goal_time_efficiency_bonus( + env, + command_name: str = "goal_pose", + position_tolerance: float = 0.1, + yaw_tolerance: float = 0.1, + hold_steps: int = 10, + max_speed: float = 2.5, + entity_name: str = "robot", + max_lin_speed: float | None = None, + max_yaw_rate: float | None = None, + min_up_z: float | None = None, +) -> torch.Tensor: + from .state import update_goal_pose_success_hold_state + + command_term = env.command_manager.get_term(command_name) + stable = _stability_ok( + env, + entity_name=entity_name, + max_lin_speed=max_lin_speed, + max_yaw_rate=max_yaw_rate, + min_up_z=min_up_z, + ) + success_now = (command_term.distance_to_goal <= float(position_tolerance)) & ( + torch.abs(command_term.final_heading_error) <= float(yaw_tolerance) + ) + if stable is not None: + success_now = success_now & stable + hold = update_goal_pose_success_hold_state(env, success_now=success_now) + just_reached = hold == hold_steps + + optimal_time = command_term.initial_distance_to_goal / max(max_speed, 1.0e-6) + actual_steps = env.episode_length_buf.float() - float(max(hold_steps - 1, 0)) + actual_steps = torch.clamp(actual_steps, min=1.0) + actual_time = actual_steps * float(env.step_dt) + + efficiency = torch.clamp(optimal_time / actual_time, min=0.0, max=1.0) + bonus = torch.where(just_reached, efficiency, torch.zeros_like(efficiency)) + _log_metric( + env, + "Metrics/target_location/time_efficiency_on_success", + torch.where(just_reached, efficiency, torch.zeros_like(efficiency)).mean(), + ) + return bonus + + +def near_goal_stability_reward( + env, + command_name: str = "goal_pose", + entity_name: str = "robot", + near_distance: float = 0.5, + position_tolerance: float | None = None, + yaw_tolerance: float | None = None, + lin_speed_std: float = 0.35, + yaw_rate_std: float = 0.6, + min_up_z: float = 0.75, +) -> torch.Tensor: + command_term = env.command_manager.get_term(command_name) + asset = env.scene[entity_name] + lin_speed = torch.linalg.vector_norm(asset.data.root_link_lin_vel_b[:, :2], dim=1) + yaw_rate = torch.abs(asset.data.root_link_ang_vel_b[:, 2]) + up_z = -asset.data.projected_gravity_b[:, 2] + + near_weight = torch.exp( + -command_term.distance_to_goal / max(float(near_distance), 1.0e-6) + ) + lin_score = torch.exp(-torch.square(lin_speed / max(float(lin_speed_std), 1.0e-6))) + yaw_score = torch.exp(-torch.square(yaw_rate / max(float(yaw_rate_std), 1.0e-6))) + upright_score = torch.clamp( + (up_z - float(min_up_z)) / max(1.0 - float(min_up_z), 1.0e-6), + min=0.0, + max=1.0, + ) + reward = near_weight * lin_score * yaw_score * upright_score + if position_tolerance is not None: + reward = ( + reward * (command_term.distance_to_goal <= float(position_tolerance)).float() + ) + if yaw_tolerance is not None: + reward = ( + reward + * (torch.abs(command_term.final_heading_error) <= float(yaw_tolerance)).float() + ) + _log_metric(env, "Metrics/target_location/near_stability_reward", reward.mean()) + return reward diff --git a/src/mjlab_playground/tasks/target_location_amp/mdp/state.py b/src/mjlab_playground/tasks/target_location_amp/mdp/state.py new file mode 100644 index 0000000..dfc918a --- /dev/null +++ b/src/mjlab_playground/tasks/target_location_amp/mdp/state.py @@ -0,0 +1,149 @@ +"""Goal-pose progress state for target-location AMP locomotion.""" + +from __future__ import annotations + +import torch + + +def ensure_goal_pose_state(env) -> None: + if not hasattr(env, "_goal_pose_prev_distance"): + env._goal_pose_prev_distance = torch.zeros( + env.num_envs, dtype=torch.float32, device=env.device + ) + if not hasattr(env, "_goal_pose_prev_yaw_abs"): + env._goal_pose_prev_yaw_abs = torch.zeros( + env.num_envs, dtype=torch.float32, device=env.device + ) + if not hasattr(env, "_goal_pose_distance_progress"): + env._goal_pose_distance_progress = torch.zeros( + env.num_envs, dtype=torch.float32, device=env.device + ) + if not hasattr(env, "_goal_pose_yaw_progress"): + env._goal_pose_yaw_progress = torch.zeros( + env.num_envs, dtype=torch.float32, device=env.device + ) + if not hasattr(env, "_goal_pose_hold_steps"): + env._goal_pose_hold_steps = torch.zeros( + env.num_envs, dtype=torch.long, device=env.device + ) + if not hasattr(env, "_goal_pose_success_hold_steps"): + env._goal_pose_success_hold_steps = torch.zeros( + env.num_envs, dtype=torch.long, device=env.device + ) + if not hasattr(env, "_goal_pose_success_hold_last_step"): + env._goal_pose_success_hold_last_step = torch.full( + (env.num_envs,), -1, dtype=torch.long, device=env.device + ) + if not hasattr(env, "_goal_pose_state_last_step"): + env._goal_pose_state_last_step = torch.full( + (env.num_envs,), -1, dtype=torch.long, device=env.device + ) + if not hasattr(env, "_goal_pose_just_resampled"): + env._goal_pose_just_resampled = torch.zeros( + env.num_envs, dtype=torch.bool, device=env.device + ) + + +def reset_goal_pose_state(env, env_ids: torch.Tensor) -> None: + ensure_goal_pose_state(env) + env._goal_pose_prev_distance[env_ids] = 0.0 + env._goal_pose_prev_yaw_abs[env_ids] = 0.0 + env._goal_pose_distance_progress[env_ids] = 0.0 + env._goal_pose_yaw_progress[env_ids] = 0.0 + env._goal_pose_hold_steps[env_ids] = 0 + env._goal_pose_success_hold_steps[env_ids] = 0 + env._goal_pose_success_hold_last_step[env_ids] = -1 + env._goal_pose_state_last_step[env_ids] = -1 + env._goal_pose_just_resampled[env_ids] = True + + +def update_goal_pose_state( + env, + *, + distance: torch.Tensor, + final_yaw_error: torch.Tensor, + position_tolerance: float, + yaw_tolerance: float, +) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]: + ensure_goal_pose_state(env) + + current_step = env.episode_length_buf + step_changed = current_step != env._goal_pose_state_last_step + if torch.any(step_changed): + yaw_abs = torch.abs(final_yaw_error) + skip_mask = (current_step <= 1) | env._goal_pose_just_resampled + + distance_progress = env._goal_pose_prev_distance - distance + yaw_progress = env._goal_pose_prev_yaw_abs - yaw_abs + distance_progress = torch.where( + skip_mask, + torch.zeros_like(distance_progress), + distance_progress, + ) + yaw_progress = torch.where( + skip_mask, + torch.zeros_like(yaw_progress), + yaw_progress, + ) + + position_ok = distance <= position_tolerance + yaw_ok = yaw_abs <= yaw_tolerance + goal_ok = position_ok & yaw_ok + next_hold_steps = torch.where( + goal_ok, + env._goal_pose_hold_steps + 1, + torch.zeros_like(env._goal_pose_hold_steps), + ) + + env._goal_pose_distance_progress = torch.where( + step_changed, distance_progress, env._goal_pose_distance_progress + ) + env._goal_pose_yaw_progress = torch.where( + step_changed, yaw_progress, env._goal_pose_yaw_progress + ) + env._goal_pose_hold_steps = torch.where( + step_changed, next_hold_steps, env._goal_pose_hold_steps + ) + env._goal_pose_prev_distance = torch.where( + step_changed, distance, env._goal_pose_prev_distance + ) + env._goal_pose_prev_yaw_abs = torch.where( + step_changed, yaw_abs, env._goal_pose_prev_yaw_abs + ) + env._goal_pose_state_last_step = torch.where( + step_changed, current_step, env._goal_pose_state_last_step + ) + env._goal_pose_just_resampled[step_changed] = False + + return ( + env._goal_pose_distance_progress, + env._goal_pose_yaw_progress, + env._goal_pose_hold_steps, + ) + + +def update_goal_pose_success_hold_state( + env, + *, + success_now: torch.Tensor, +) -> torch.Tensor: + """Count consecutive controller steps satisfying the full success contract.""" + ensure_goal_pose_state(env) + current_step = env.episode_length_buf + step_changed = current_step != env._goal_pose_success_hold_last_step + next_hold_steps = torch.where( + success_now, + env._goal_pose_success_hold_steps + 1, + torch.zeros_like(env._goal_pose_success_hold_steps), + ) + env._goal_pose_success_hold_steps = torch.where( + step_changed, + next_hold_steps, + env._goal_pose_success_hold_steps, + ) + env._goal_pose_success_hold_last_step = torch.where( + step_changed, + current_step, + env._goal_pose_success_hold_last_step, + ) + return env._goal_pose_success_hold_steps diff --git a/src/mjlab_playground/tasks/target_location_amp/mdp/terminations.py b/src/mjlab_playground/tasks/target_location_amp/mdp/terminations.py new file mode 100644 index 0000000..2812af6 --- /dev/null +++ b/src/mjlab_playground/tasks/target_location_amp/mdp/terminations.py @@ -0,0 +1,34 @@ +"""Terminations local to the target-location AMP locomotion task.""" + +from __future__ import annotations + +from .rewards import goal_stability_mask +from .state import update_goal_pose_success_hold_state + + +def goal_pose_reached( + env, + command_name: str = "goal_pose", + position_tolerance: float = 0.1, + yaw_tolerance: float = 0.2, + hold_steps: int = 10, + entity_name: str = "robot", + max_lin_speed: float | None = None, + max_yaw_rate: float | None = None, + min_up_z: float | None = None, +): + command_term = env.command_manager.get_term(command_name) + success_now = (command_term.distance_to_goal <= float(position_tolerance)) & ( + abs(command_term.final_heading_error) <= float(yaw_tolerance) + ) + if max_lin_speed is not None or max_yaw_rate is not None or min_up_z is not None: + stable, _ = goal_stability_mask( + env, + entity_name=entity_name, + max_lin_speed=max_lin_speed, + max_yaw_rate=max_yaw_rate, + min_up_z=min_up_z, + ) + success_now = success_now & stable + hold = update_goal_pose_success_hold_state(env, success_now=success_now) + return hold >= hold_steps diff --git a/src/mjlab_playground/tasks/target_location_amp/target_location_amp_env_cfg.py b/src/mjlab_playground/tasks/target_location_amp/target_location_amp_env_cfg.py new file mode 100644 index 0000000..ccd35da --- /dev/null +++ b/src/mjlab_playground/tasks/target_location_amp/target_location_amp_env_cfg.py @@ -0,0 +1,279 @@ +"""Goal-conditioned K1 locomotion AMP environment configuration.""" + +from __future__ import annotations + +import math + +from mjlab.envs import ManagerBasedRlEnvCfg +from mjlab.envs.mdp.actions import RelativeJointPositionActionCfg +from mjlab.managers.action_manager import ActionTermCfg +from mjlab.managers.event_manager import EventTermCfg +from mjlab.managers.metrics_manager import MetricsTermCfg +from mjlab.managers.observation_manager import ObservationGroupCfg, ObservationTermCfg +from mjlab.managers.reward_manager import RewardTermCfg +from mjlab.managers.termination_manager import TerminationTermCfg +from mjlab.scene import SceneCfg +from mjlab.sim import MujocoCfg, SimulationCfg +from mjlab.terrains import TerrainEntityCfg +from mjlab.utils.noise import UniformNoiseCfg as Unoise +from mjlab.viewer import ViewerConfig + +from mjlab_playground.amp import mdp as amp_mdp +from mjlab_playground.tasks.common import reset_joints_by_offset +from mjlab_playground.tasks.target_location_amp import mdp as target_mdp + +_TARGET_POSITION_TOLERANCE = 0.16 +_TARGET_YAW_TOLERANCE = 0.20 +_TARGET_STABLE_HOLD_STEPS = 25 +_TARGET_MAX_LIN_SPEED = 0.35 +_TARGET_MAX_YAW_RATE = 0.60 +_TARGET_MIN_UP_Z = 0.75 + + +def make_target_location_amp_env_cfg(play: bool = False) -> ManagerBasedRlEnvCfg: + actor_terms = { + "base_ang_vel": ObservationTermCfg( + func=amp_mdp.base_ang_vel, + noise=Unoise(n_min=-0.15, n_max=0.15), + ), + "projected_gravity": ObservationTermCfg( + func=amp_mdp.projected_gravity, + noise=Unoise(n_min=-0.04, n_max=0.04), + ), + "joint_pos": ObservationTermCfg( + func=amp_mdp.joint_pos_rel, + noise=Unoise(n_min=-0.015, n_max=0.015), + ), + "joint_vel": ObservationTermCfg( + func=amp_mdp.joint_vel_rel, + noise=Unoise(n_min=-0.5, n_max=0.5), + ), + "actions": ObservationTermCfg(func=amp_mdp.last_action), + "goal_local_pos": ObservationTermCfg( + func=target_mdp.goal_local_position, + noise=Unoise(n_min=-0.01, n_max=0.01), + ), + "heading_to_target": ObservationTermCfg( + func=target_mdp.heading_to_target_features, + noise=Unoise(n_min=-0.01, n_max=0.01), + ), + "final_yaw_error": ObservationTermCfg( + func=target_mdp.final_yaw_error_features, + noise=Unoise(n_min=-0.01, n_max=0.01), + ), + "goal_distance": ObservationTermCfg( + func=target_mdp.goal_distance, + scale=0.25, + ), + } + + critic_terms = { + **actor_terms, + "base_lin_vel": ObservationTermCfg(func=amp_mdp.base_lin_vel), + "amp_robot": ObservationTermCfg(func=amp_mdp.amp_robot_obs), + } + + actions: dict[str, ActionTermCfg] = { + "joint_pos": RelativeJointPositionActionCfg( + entity_name="robot", + actuator_names=(".*",), + scale=0.35, + ) + } + + events = { + "reset_robot": EventTermCfg( + func=amp_mdp.reset_robot_near_origin, + mode="reset", + params={ + "robot_name": "robot", + "x_range": (-0.04, 0.04), + "y_range": (-0.04, 0.04), + "yaw_range": (-0.20, 0.20), + }, + ), + "reset_robot_joints": EventTermCfg( + func=reset_joints_by_offset, + mode="reset", + params={ + "position_range": (-0.01, 0.01), + "velocity_range": (-0.02, 0.02), + }, + ), + } + + rewards = { + "distance_error": RewardTermCfg( + func=target_mdp.distance_error_penalty, + weight=0.0, + params={ + "command_name": "goal_pose", + "min_distance_scale": 0.4, + }, + ), + "position_progress": RewardTermCfg( + func=target_mdp.position_progress_reward, + weight=1.75, + params={ + "command_name": "goal_pose", + "position_tolerance": _TARGET_POSITION_TOLERANCE, + "yaw_tolerance": _TARGET_YAW_TOLERANCE, + "max_speed": 1.8, + }, + ), + "near_goal_heading_alignment": RewardTermCfg( + func=target_mdp.near_goal_heading_alignment_reward, + weight=0.35, + params={ + "command_name": "goal_pose", + "near_distance": 0.75, + }, + ), + "near_goal_stability": RewardTermCfg( + func=target_mdp.near_goal_stability_reward, + weight=0.0, + params={ + "command_name": "goal_pose", + "entity_name": "robot", + "near_distance": 0.60, + "lin_speed_std": 0.35, + "yaw_rate_std": 0.60, + "min_up_z": _TARGET_MIN_UP_Z, + }, + ), + "yaw_progress": RewardTermCfg( + func=target_mdp.yaw_progress_reward, + weight=0.25, + params={ + "command_name": "goal_pose", + "position_tolerance": _TARGET_POSITION_TOLERANCE, + "yaw_tolerance": _TARGET_YAW_TOLERANCE, + "near_distance": 0.75, + }, + ), + "goal_position_bonus": RewardTermCfg( + func=target_mdp.goal_position_bonus, + weight=0.05, + params={ + "command_name": "goal_pose", + "position_tolerance": _TARGET_POSITION_TOLERANCE, + "yaw_soft_tolerance": _TARGET_YAW_TOLERANCE, + }, + ), + "goal_pose_bonus": RewardTermCfg( + func=target_mdp.goal_pose_terminal_bonus, + weight=0.2, + params={ + "command_name": "goal_pose", + "position_tolerance": _TARGET_POSITION_TOLERANCE, + "yaw_tolerance": _TARGET_YAW_TOLERANCE, + "hold_steps": _TARGET_STABLE_HOLD_STEPS, + "entity_name": "robot", + "max_lin_speed": _TARGET_MAX_LIN_SPEED, + "max_yaw_rate": _TARGET_MAX_YAW_RATE, + "min_up_z": _TARGET_MIN_UP_Z, + }, + ), + "goal_time_efficiency": RewardTermCfg( + func=target_mdp.goal_time_efficiency_bonus, + weight=0.0, + params={ + "command_name": "goal_pose", + "position_tolerance": _TARGET_POSITION_TOLERANCE, + "yaw_tolerance": _TARGET_YAW_TOLERANCE, + "hold_steps": _TARGET_STABLE_HOLD_STEPS, + "max_speed": 1.8, + "entity_name": "robot", + "max_lin_speed": _TARGET_MAX_LIN_SPEED, + "max_yaw_rate": _TARGET_MAX_YAW_RATE, + "min_up_z": _TARGET_MIN_UP_Z, + }, + ), + "fall_penalty": RewardTermCfg( + func=amp_mdp.fell_over_penalty, + weight=-0.1, + params={ + "robot_name": "robot", + }, + ), + } + + terminations = { + "time_out": TerminationTermCfg(func=amp_mdp.time_out, time_out=True), + "fell_over": TerminationTermCfg(func=amp_mdp.fell_over), + } + + cfg = ManagerBasedRlEnvCfg( + scene=SceneCfg( + terrain=TerrainEntityCfg(terrain_type="plane"), + num_envs=1, + extent=3.0, + ), + observations={ + "actor": ObservationGroupCfg( + terms=actor_terms, + concatenate_terms=True, + enable_corruption=not play, + ), + "critic": ObservationGroupCfg( + terms=critic_terms, + concatenate_terms=True, + enable_corruption=False, + ), + }, + actions=actions, + commands={ + "goal_pose": target_mdp.GoalPoseCommandCfg( + entity_name="robot", + resampling_time_range=(6.0, 10.0), + debug_vis=play, + ranges=target_mdp.GoalPoseCommandCfg.Ranges( + target_radius=(0.4, 1.2), + target_yaw=(-0.25, 0.25), + target_angle=(-0.50, 0.50), + ), + ) + }, + events=events, + rewards=rewards, + terminations=terminations, + curriculum={}, + metrics={ + "fell_over_fraction": MetricsTermCfg( + func=amp_mdp.fell_over_fraction, + params={"robot_name": "robot"}, + ), + }, + viewer=ViewerConfig( + origin_type=ViewerConfig.OriginType.ASSET_BODY, + entity_name="robot", + body_name="torso", + distance=2.5, + elevation=-10.0, + azimuth=90.0, + width=640, + height=480, + ), + sim=SimulationCfg( + nconmax=80, + njmax=400, + mujoco=MujocoCfg( + timestep=0.005, + iterations=10, + ls_iterations=20, + ccd_iterations=80, + cone="elliptic", + impratio=5.0, + ), + ), + decimation=4, + episode_length_s=20.0, + ) + cfg.scale_rewards_by_dt = False + if play: + goal_pose_cmd = cfg.commands["goal_pose"] + assert isinstance(goal_pose_cmd, target_mdp.GoalPoseCommandCfg) + goal_pose_cmd.ranges.target_radius = (0.4, 10.0) + goal_pose_cmd.ranges.target_yaw = (-math.pi, math.pi) + goal_pose_cmd.ranges.target_angle = (-math.pi, math.pi) + return cfg diff --git a/src/mjlab_playground/tasks/tracking/__init__.py b/src/mjlab_playground/tasks/tracking/__init__.py new file mode 100644 index 0000000..d5b1973 --- /dev/null +++ b/src/mjlab_playground/tasks/tracking/__init__.py @@ -0,0 +1 @@ +"""Tracking tasks for mjlab_playground.""" diff --git a/src/mjlab_playground/tasks/tracking/config/__init__.py b/src/mjlab_playground/tasks/tracking/config/__init__.py new file mode 100644 index 0000000..e51a57b --- /dev/null +++ b/src/mjlab_playground/tasks/tracking/config/__init__.py @@ -0,0 +1 @@ +"""Configuration modules for tracking tasks.""" diff --git a/src/mjlab_playground/tasks/tracking/config/t1/__init__.py b/src/mjlab_playground/tasks/tracking/config/t1/__init__.py new file mode 100644 index 0000000..1b2c814 --- /dev/null +++ b/src/mjlab_playground/tasks/tracking/config/t1/__init__.py @@ -0,0 +1,13 @@ +from mjlab.tasks.registry import register_mjlab_task +from mjlab.tasks.tracking.rl import MotionTrackingOnPolicyRunner + +from .env_cfgs import booster_t1_flat_tracking_env_cfg +from .rl_cfg import booster_t1_tracking_ppo_runner_cfg + +register_mjlab_task( + task_id="Mjlab-Tracking-Flat-Booster-T1", + env_cfg=booster_t1_flat_tracking_env_cfg(), + play_env_cfg=booster_t1_flat_tracking_env_cfg(play=True), + rl_cfg=booster_t1_tracking_ppo_runner_cfg(), + runner_cls=MotionTrackingOnPolicyRunner, +) diff --git a/src/mjlab_playground/tasks/tracking/config/t1/env_cfgs.py b/src/mjlab_playground/tasks/tracking/config/t1/env_cfgs.py new file mode 100644 index 0000000..c2d75c2 --- /dev/null +++ b/src/mjlab_playground/tasks/tracking/config/t1/env_cfgs.py @@ -0,0 +1,210 @@ +"""Booster T1 tracking environment configuration.""" + +from __future__ import annotations + +import copy +import math +import os +from pathlib import Path + +import torch +from mjlab.entity import Entity +from mjlab.envs import ManagerBasedRlEnv, ManagerBasedRlEnvCfg +from mjlab.envs.mdp.actions import JointPositionActionCfg +from mjlab.managers import EventTermCfg, TerminationTermCfg +from mjlab.managers.observation_manager import ObservationGroupCfg +from mjlab.sensor import ContactMatch, ContactSensorCfg +from mjlab.tasks.tracking.mdp import MotionCommandCfg +from mjlab.tasks.tracking.tracking_env_cfg import make_tracking_env_cfg + +from mjlab_playground.asset_zoo.robots.booster_t1.t1_constants import ( + T1_ACTION_SCALE, + get_t1_robot_cfg, +) +from mjlab_playground.tasks.tracking.mdp.commands import ( + TrackingMixedResetMotionCommandCfg, +) +from mjlab_playground.tasks.tracking.mdp.events import ( + TRACKING_HOME_RESET_MASK_KEY, + sample_tracking_reset_source, +) + +_MAX_ANG_VEL = 500 * math.pi / 180.0 # [rad/s] +_HOME_RESET_PROBABILITY_TRAIN = 0.35 +_HOME_RESET_PROBABILITY_PLAY = 1.0 +_HOME_JOINT_VEL_RANGE = (0.0, 0.0) +_HOME_HOLD_STEPS_TRAIN = 0 +_HOME_HOLD_STEPS_PLAY = 20 + +_T1_TRACKING_BODY_NAMES = ( + # MotionCommand reset treats body_names[0] as the floating base body. + "Trunk", + "Waist", + "Hip_Roll_Left", + "Shank_Left", + "left_foot_link", + "Hip_Roll_Right", + "Shank_Right", + "right_foot_link", + "AL1", + "AL3", + "left_hand_link", + "AR1", + "AR3", + "right_hand_link", +) + + +def _resolve_default_tracking_motion_file() -> str: + asset_motion_root = ( + Path(__file__).resolve().parents[3] + / "asset_zoo" + / "motions" + / "retargeted" + / "tracking" + / "booster_t1_23dof" + ) + candidates = [ + os.getenv("MJLAB_TRACKING_MOTION_FILE", ""), + str(asset_motion_root / "0026_kicking2_stageii.booster_t1_23dof.motion.npz"), + ] + for candidate in candidates: + if candidate and Path(candidate).exists(): + return candidate + return candidates[1] + + +_TRACKING_MOTION_FILE = _resolve_default_tracking_motion_file() + + +def base_ang_vel_exceed( + env: ManagerBasedRlEnv, + threshold: float, +) -> torch.Tensor: + asset: Entity = env.scene["robot"] + ang_vel = asset.data.root_link_ang_vel_b + return torch.any(ang_vel.abs() > threshold, dim=-1) + + +def booster_t1_flat_tracking_env_cfg( + has_state_estimation: bool = False, + play: bool = False, +) -> ManagerBasedRlEnvCfg: + """Create Booster T1 flat tracking configuration.""" + if _T1_TRACKING_BODY_NAMES[0] != "Trunk": + raise ValueError( + "Tracking body_names must start with 'Trunk' for Booster T1. " + "MotionCommand reset uses body_names[0] as root state." + ) + + cfg = make_tracking_env_cfg() + cfg.scene.entities = {"robot": get_t1_robot_cfg()} + + self_collision_cfg = ContactSensorCfg( + name="self_collision", + primary=ContactMatch(mode="subtree", pattern="Trunk", entity="robot"), + secondary=ContactMatch(mode="subtree", pattern="Trunk", entity="robot"), + fields=("found", "force"), + reduce="none", + num_slots=1, + history_length=4, + ) + cfg.scene.sensors = (self_collision_cfg,) + + joint_pos_action = cfg.actions["joint_pos"] + assert isinstance(joint_pos_action, JointPositionActionCfg) + joint_pos_action.scale = T1_ACTION_SCALE + + motion_cmd_base = cfg.commands["motion"] + assert isinstance(motion_cmd_base, MotionCommandCfg) + motion_cmd = TrackingMixedResetMotionCommandCfg( + entity_name=motion_cmd_base.entity_name, + resampling_time_range=motion_cmd_base.resampling_time_range, + debug_vis=motion_cmd_base.debug_vis, + pose_range=dict(motion_cmd_base.pose_range), + velocity_range=dict(motion_cmd_base.velocity_range), + joint_position_range=motion_cmd_base.joint_position_range, + adaptive_kernel_size=motion_cmd_base.adaptive_kernel_size, + adaptive_lambda=motion_cmd_base.adaptive_lambda, + adaptive_uniform_ratio=motion_cmd_base.adaptive_uniform_ratio, + adaptive_alpha=motion_cmd_base.adaptive_alpha, + sampling_mode=motion_cmd_base.sampling_mode, + motion_file=motion_cmd_base.motion_file, + anchor_body_name=motion_cmd_base.anchor_body_name, + body_names=motion_cmd_base.body_names, + viz=copy.deepcopy(motion_cmd_base.viz), + home_start_frame=0, + home_pose_range={}, + home_velocity_range={}, + home_joint_position_range=(0.0, 0.0), + home_joint_velocity_range=_HOME_JOINT_VEL_RANGE, + home_root_position_mode="motion_xyz", + home_root_orientation_mode="motion_yaw", + home_canonicalize_motion_yaw=False, + home_initial_hold_steps=_HOME_HOLD_STEPS_TRAIN, + ) + cfg.commands["motion"] = motion_cmd + motion_cmd.motion_file = _TRACKING_MOTION_FILE + motion_cmd.anchor_body_name = "Trunk" + motion_cmd.body_names = _T1_TRACKING_BODY_NAMES + + cfg.events["tracking_reset_source"] = EventTermCfg( + func=sample_tracking_reset_source, + mode="reset", + params={ + "home_probability": _HOME_RESET_PROBABILITY_TRAIN, + "mask_key": TRACKING_HOME_RESET_MASK_KEY, + }, + ) + + cfg.events["foot_friction"].params[ + "asset_cfg" + ].geom_names = r"^(left|right)_foot[1-4]_collision$" + cfg.events["base_com"].params["asset_cfg"].body_names = ("Trunk",) + + cfg.terminations["ee_body_pos"].params["body_names"] = ( + "left_foot_link", + "right_foot_link", + "left_hand_link", + "right_hand_link", + ) + cfg.terminations["base_ang_vel_exceed"] = TerminationTermCfg( + func=base_ang_vel_exceed, + params={"threshold": _MAX_ANG_VEL}, + ) + cfg.viewer.body_name = "Trunk" + + # Remove state-estimation terms for pure proprio + motion command tracking. + if not has_state_estimation: + new_actor_terms = { + k: v + for k, v in cfg.observations["actor"].terms.items() + if k not in ["motion_anchor_pos_b", "base_lin_vel"] + } + cfg.observations["actor"] = ObservationGroupCfg( + terms=new_actor_terms, + concatenate_terms=True, + enable_corruption=True, + ) + + if play: + cfg.episode_length_s = int(1e9) + cfg.observations["actor"].enable_corruption = False + cfg.events.pop("push_robot", None) + cfg.events["tracking_reset_source"].params["home_probability"] = ( + _HOME_RESET_PROBABILITY_PLAY + ) + + # Disable RSI randomization in play mode. + motion_cmd.pose_range = {} + motion_cmd.velocity_range = {} + motion_cmd.sampling_mode = "start" + # Match competition kickoff intuition: stand/home orientation, no forced + # leaning root pitch/roll from motion frame, and give a short warmup window + # before advancing the reference timeline. + motion_cmd.home_root_position_mode = "motion_xy_home_z" + motion_cmd.home_root_orientation_mode = "home" + motion_cmd.home_canonicalize_motion_yaw = True + motion_cmd.home_initial_hold_steps = _HOME_HOLD_STEPS_PLAY + + return cfg diff --git a/src/mjlab_playground/tasks/tracking/config/t1/rl_cfg.py b/src/mjlab_playground/tasks/tracking/config/t1/rl_cfg.py new file mode 100644 index 0000000..cabf050 --- /dev/null +++ b/src/mjlab_playground/tasks/tracking/config/t1/rl_cfg.py @@ -0,0 +1,13 @@ +"""RL configuration for Booster T1 tracking.""" + +from mjlab.rl import RslRlOnPolicyRunnerCfg +from mjlab.tasks.tracking.config.g1.rl_cfg import unitree_g1_tracking_ppo_runner_cfg + + +def booster_t1_tracking_ppo_runner_cfg() -> RslRlOnPolicyRunnerCfg: + """Create RL runner configuration for Booster T1 tracking task.""" + cfg = unitree_g1_tracking_ppo_runner_cfg() + cfg.experiment_name = "t1_tracking" + cfg.run_name = "t1_tracking" + cfg.wandb_project = "mjlab_playground" + return cfg diff --git a/src/mjlab_playground/tasks/tracking/mdp/__init__.py b/src/mjlab_playground/tasks/tracking/mdp/__init__.py new file mode 100644 index 0000000..240bbad --- /dev/null +++ b/src/mjlab_playground/tasks/tracking/mdp/__init__.py @@ -0,0 +1,4 @@ +from mjlab.tasks.tracking.mdp import * # noqa: F401, F403 + +from .commands import * # noqa: F401, F403 +from .events import * # noqa: F401, F403 diff --git a/src/mjlab_playground/tasks/tracking/mdp/commands.py b/src/mjlab_playground/tasks/tracking/mdp/commands.py new file mode 100644 index 0000000..a8aedb6 --- /dev/null +++ b/src/mjlab_playground/tasks/tracking/mdp/commands.py @@ -0,0 +1,329 @@ +"""Tracking command term extensions for mixed reset sources.""" + +# pyright: reportIncompatibleVariableOverride=false, reportOptionalMemberAccess=false + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Literal + +import torch +from mjlab.tasks.tracking.mdp.commands import MotionCommand, MotionCommandCfg +from mjlab.utils.lab_api.math import ( + quat_apply, + quat_from_euler_xyz, + quat_inv, + quat_mul, + sample_uniform, + yaw_quat, +) + +from .events import TRACKING_HOME_RESET_MASK_KEY + + +class TrackingMixedResetMotionCommand(MotionCommand): + """Motion command with optional home-pose reset on a subset of environments.""" + + cfg: "TrackingMixedResetMotionCommandCfg" + _hold_time_step_counter: torch.Tensor + + def __init__(self, cfg: "TrackingMixedResetMotionCommandCfg", env): + super().__init__(cfg, env) + if self.cfg.home_canonicalize_motion_yaw: + self._canonicalize_motion_yaw_to_home() + + def _canonicalize_motion_yaw_to_home(self) -> None: + """Rotate full motion trajectory so start-frame yaw matches home yaw. + + This removes large global heading offsets in motion files while preserving + relative body kinematics and root pitch/roll profile. + """ + default_root_state = self.robot.data.default_root_state + assert default_root_state is not None + max_frame = int(self.motion.time_step_total) - 1 + start_frame = int(max(0, min(max_frame, self.cfg.home_start_frame))) + + home_quat = default_root_state[0, 3:7].unsqueeze(0) + source_quat = self.motion.body_quat_w[start_frame, 0].unsqueeze(0) + home_yaw = yaw_quat(home_quat) + source_yaw = yaw_quat(source_quat) + delta = quat_mul(home_yaw, quat_inv(source_yaw))[0] + + delta_quat = delta.view(1, 1, 4).expand( + self.motion.body_quat_w.shape[0], self.motion.body_quat_w.shape[1], 4 + ) + start_anchor_pos = self.motion.body_pos_w[start_frame, 0].view(1, 1, 3) + + self.motion.body_pos_w = ( + quat_apply(delta_quat, self.motion.body_pos_w - start_anchor_pos) + + start_anchor_pos + ) + self.motion.body_quat_w = quat_mul(delta_quat, self.motion.body_quat_w) + self.motion.body_lin_vel_w = quat_apply(delta_quat, self.motion.body_lin_vel_w) + self.motion.body_ang_vel_w = quat_apply(delta_quat, self.motion.body_ang_vel_w) + + def apply_gui_reset(self, env_ids: torch.Tensor) -> bool: + """Optionally keep base scrubber behavior for interactive frame jumps. + + In play/debug for mixed-reset tracking we usually want viewer reset to preserve + the configured reset-source policy (home vs motion), so this defaults to False. + """ + if self.cfg.gui_reset_to_frame: + return super().apply_gui_reset(env_ids) + return False + + def _resample_command(self, env_ids: torch.Tensor): + if env_ids.numel() == 0: + return + + # Guard against invalid indices and prefer CPU-side split logic to avoid + # opaque CUDA device-side assert when advanced indexing receives bad ids. + env_ids_cpu = env_ids.detach().to(device="cpu", dtype=torch.long) + min_id = int(env_ids_cpu.min().item()) + max_id = int(env_ids_cpu.max().item()) + if min_id < 0 or max_id >= self.num_envs: + raise RuntimeError( + f"Invalid env_ids in tracking resample: min={min_id}, max={max_id}, " + f"num_envs={self.num_envs}" + ) + + reset_mask = self._env.extras.get(self.cfg.home_reset_mask_key) + if ( + isinstance(reset_mask, torch.Tensor) + and reset_mask.dtype == torch.bool + and reset_mask.shape == (self.num_envs,) + ): + reset_mask_cpu = reset_mask.detach().to(device="cpu") + home_select = reset_mask_cpu[env_ids_cpu] + home_env_ids = env_ids_cpu[home_select].to(device=env_ids.device) + motion_env_ids = env_ids_cpu[~home_select].to(device=env_ids.device) + else: + home_env_ids = env_ids.new_empty((0,), dtype=env_ids.dtype) + motion_env_ids = env_ids + + if len(home_env_ids) > 0: + self._resample_home_command(home_env_ids) + + # Important: MotionCommand._resample_command internally reads self.body_pos_w, + # which indexes with self.time_steps for all envs. Therefore, any env with an + # out-of-range timestep must be repaired before calling into the base method. + # Home envs are repaired above; now motion envs can be safely resampled. + if len(motion_env_ids) > 0: + super()._resample_command(motion_env_ids) + + def _write_reference_state_to_sim( + self, + env_ids: torch.Tensor, + root_pos: torch.Tensor, + root_ori: torch.Tensor, + root_lin_vel: torch.Tensor, + root_ang_vel: torch.Tensor, + joint_pos: torch.Tensor, + joint_vel: torch.Tensor, + ) -> None: + """Write reference state with invariant or per-world joint limits.""" + soft_limits = self.robot.data.soft_joint_pos_limits + assert soft_limits is not None + limits = ( + soft_limits.expand(len(env_ids), -1, -1) + if soft_limits.shape[0] == 1 + else soft_limits[env_ids] + ) + joint_pos = torch.clip(joint_pos, limits[..., 0], limits[..., 1]) + self.robot.write_joint_state_to_sim(joint_pos, joint_vel, env_ids=env_ids) + + root_state = torch.cat([root_pos, root_ori, root_lin_vel, root_ang_vel], dim=-1) + self.robot.write_root_state_to_sim(root_state, env_ids=env_ids) + self.robot.reset(env_ids=env_ids) + + def _resample_home_command(self, env_ids: torch.Tensor) -> None: + # Keep command time index fixed at the configured frame so the policy always + # starts from the same reference timing when using home reset. + max_frame = int(self.motion.time_step_total) - 1 + start_frame = int(max(0, min(max_frame, self.cfg.home_start_frame))) + self.time_steps[env_ids] = start_frame + + default_root_state = self.robot.data.default_root_state + default_joint_pos = self.robot.data.default_joint_pos + default_joint_vel = self.robot.data.default_joint_vel + assert default_root_state is not None + assert default_joint_pos is not None + assert default_joint_vel is not None + + root_pos = default_root_state[env_ids, 0:3].clone() + root_ori = default_root_state[env_ids, 3:7].clone() + root_lin_vel = default_root_state[env_ids, 7:10].clone() + root_ang_vel = default_root_state[env_ids, 10:13].clone() + + pose_ranges = torch.tensor( + [ + self.cfg.home_pose_range.get(key, (0.0, 0.0)) + for key in ["x", "y", "z", "roll", "pitch", "yaw"] + ], + device=self.device, + dtype=torch.float32, + ) + pose_noise = sample_uniform( + pose_ranges[:, 0], + pose_ranges[:, 1], + (len(env_ids), 6), + device=self.device, + ) + if self.cfg.home_root_position_mode == "motion_xyz": + reference_pos = ( + self.motion.body_pos_w[start_frame, 0].unsqueeze(0).expand(len(env_ids), -1) + ) + root_pos = reference_pos.clone() + elif self.cfg.home_root_position_mode == "motion_xy_home_z": + reference_pos = ( + self.motion.body_pos_w[start_frame, 0].unsqueeze(0).expand(len(env_ids), -1) + ) + root_pos[:, :2] = reference_pos[:, :2] + elif self.cfg.home_root_position_mode == "home": + pass + else: + raise ValueError( + f"Unsupported home_root_position_mode: {self.cfg.home_root_position_mode}" + ) + root_pos += pose_noise[:, :3] + self._env.scene.env_origins[env_ids] + if self.cfg.home_root_orientation_mode == "motion": + reference_ori = ( + self.motion.body_quat_w[start_frame, 0].unsqueeze(0).expand(len(env_ids), -1) + ) + root_ori = reference_ori.clone() + elif self.cfg.home_root_orientation_mode == "motion_yaw": + reference_ori = ( + self.motion.body_quat_w[start_frame, 0].unsqueeze(0).expand(len(env_ids), -1) + ) + root_ori = yaw_quat(reference_ori) + elif self.cfg.home_root_orientation_mode == "home": + pass + else: + raise ValueError( + f"Unsupported home_root_orientation_mode: {self.cfg.home_root_orientation_mode}" + ) + ori_delta = quat_from_euler_xyz( + pose_noise[:, 3], + pose_noise[:, 4], + pose_noise[:, 5], + ) + root_ori = quat_mul(ori_delta, root_ori) + + vel_ranges = torch.tensor( + [ + self.cfg.home_velocity_range.get(key, (0.0, 0.0)) + for key in ["x", "y", "z", "roll", "pitch", "yaw"] + ], + device=self.device, + dtype=torch.float32, + ) + vel_noise = sample_uniform( + vel_ranges[:, 0], + vel_ranges[:, 1], + (len(env_ids), 6), + device=self.device, + ) + root_lin_vel += vel_noise[:, :3] + root_ang_vel += vel_noise[:, 3:] + + joint_pos = default_joint_pos[env_ids].clone() + joint_pos += sample_uniform( + lower=self.cfg.home_joint_position_range[0], + upper=self.cfg.home_joint_position_range[1], + size=joint_pos.shape, + device=joint_pos.device, # type: ignore[arg-type] + ) + + joint_vel = default_joint_vel[env_ids].clone() + joint_vel += sample_uniform( + lower=self.cfg.home_joint_velocity_range[0], + upper=self.cfg.home_joint_velocity_range[1], + size=joint_vel.shape, + device=joint_vel.device, # type: ignore[arg-type] + ) + + self._write_reference_state_to_sim( + env_ids, + root_pos, + root_ori, + root_lin_vel, + root_ang_vel, + joint_pos, + joint_vel, + ) + + # ManagerBasedRlEnv.reset() calls command_manager.compute(dt=0) once after + # resampling. Hold the just-reset home frame for that first compute, and + # optionally for extra warmup steps so policy can transition from stand/home. + hold_steps = int(max(0, self.cfg.home_initial_hold_steps)) + if self.cfg.hold_home_frame_for_first_compute: + hold_steps += 1 + if hold_steps > 0: + if not hasattr(self, "_hold_time_step_counter"): + self._hold_time_step_counter = torch.zeros( + self.num_envs, dtype=torch.long, device=self.device + ) + self._hold_time_step_counter[env_ids] = hold_steps + + # Mark home resets with deterministic sampling metrics. + self.metrics["sampling_entropy"][env_ids] = 0.0 + self.metrics["sampling_top1_prob"][env_ids] = 1.0 + self.metrics["sampling_top1_bin"][env_ids] = float(start_frame) / max( + int(self.bin_count), 1 + ) + + def _update_command(self): + hold_counter = getattr(self, "_hold_time_step_counter", None) + if ( + isinstance(hold_counter, torch.Tensor) + and hold_counter.dtype == torch.long + and hold_counter.shape == (self.num_envs,) + ): + increment_ids = torch.where(hold_counter <= 0)[0] + if increment_ids.numel() > 0: + self.time_steps[increment_ids] += 1 + held_ids = torch.where(hold_counter > 0)[0] + if held_ids.numel() > 0: + hold_counter[held_ids] -= 1 + else: + self.time_steps += 1 + + env_ids = torch.where(self.time_steps >= self.motion.time_step_total)[0] + if env_ids.numel() > 0: + self._resample_command(env_ids) + + self.update_relative_body_poses() + + if self.cfg.sampling_mode == "adaptive": + self.bin_failed_count = ( + self.cfg.adaptive_alpha * self._current_bin_failed + + (1 - self.cfg.adaptive_alpha) * self.bin_failed_count + ) + self._current_bin_failed.zero_() + + +@dataclass(kw_only=True) +class TrackingMixedResetMotionCommandCfg(MotionCommandCfg): + """Configuration for mixed motion/home reset in tracking.""" + + home_reset_mask_key: str = TRACKING_HOME_RESET_MASK_KEY + home_start_frame: int = 0 + home_pose_range: dict[str, tuple[float, float]] | None = None + home_velocity_range: dict[str, tuple[float, float]] | None = None + home_joint_position_range: tuple[float, float] = (0.0, 0.0) + home_joint_velocity_range: tuple[float, float] = (0.0, 0.0) + home_root_position_mode: Literal["motion_xyz", "motion_xy_home_z", "home"] = ( + "motion_xyz" + ) + home_root_orientation_mode: Literal["motion", "motion_yaw", "home"] = "motion" + home_canonicalize_motion_yaw: bool = False + gui_reset_to_frame: bool = False + hold_home_frame_for_first_compute: bool = True + home_initial_hold_steps: int = 0 + + def build(self, env) -> TrackingMixedResetMotionCommand: + if self.home_pose_range is None: + self.home_pose_range = {} + if self.home_velocity_range is None: + self.home_velocity_range = {} + return TrackingMixedResetMotionCommand(self, env) diff --git a/src/mjlab_playground/tasks/tracking/mdp/events.py b/src/mjlab_playground/tasks/tracking/mdp/events.py new file mode 100644 index 0000000..4a7207e --- /dev/null +++ b/src/mjlab_playground/tasks/tracking/mdp/events.py @@ -0,0 +1,47 @@ +"""Reset event helpers for tracking tasks.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +import torch + +if TYPE_CHECKING: + from mjlab.envs import ManagerBasedRlEnv + + +TRACKING_HOME_RESET_MASK_KEY = "tracking_home_reset_mask" + + +def sample_tracking_reset_source( + env: ManagerBasedRlEnv, + env_ids: torch.Tensor | None, + home_probability: float, + mask_key: str = TRACKING_HOME_RESET_MASK_KEY, +) -> None: + """Sample which envs should use the home-pose reset path. + + This event does not write simulation state directly. It records a boolean mask in + ``env.extras``. The tracking motion command consumes this mask during command + resampling and applies either: + - the default motion-frame reset path, or + - the home-pose reset path. + """ + if env_ids is None: + env_ids = torch.arange(env.num_envs, device=env.device, dtype=torch.int64) + + p = float(max(0.0, min(1.0, home_probability))) + + mask = env.extras.get(mask_key) + if not isinstance(mask, torch.Tensor) or mask.shape != (env.num_envs,): + mask = torch.zeros(env.num_envs, device=env.device, dtype=torch.bool) + env.extras[mask_key] = mask + + if p <= 0.0: + mask[env_ids] = False + return + if p >= 1.0: + mask[env_ids] = True + return + + mask[env_ids] = torch.rand(len(env_ids), device=env.device) < p diff --git a/src/mjlab_playground/tasks/velocity/__init__.py b/src/mjlab_playground/tasks/velocity/__init__.py new file mode 100644 index 0000000..d8c0c15 --- /dev/null +++ b/src/mjlab_playground/tasks/velocity/__init__.py @@ -0,0 +1,4 @@ +# Adapted from KaydenKnapik/BoosterT1mjlab (Apache-2.0); modified for this fork. +"""Velocity tracking environments for legged robots.""" + +from . import config as config diff --git a/src/mjlab_playground/tasks/velocity/config/__init__.py b/src/mjlab_playground/tasks/velocity/config/__init__.py new file mode 100644 index 0000000..a8552b1 --- /dev/null +++ b/src/mjlab_playground/tasks/velocity/config/__init__.py @@ -0,0 +1,2 @@ +# Adapted from KaydenKnapik/BoosterT1mjlab (Apache-2.0); modified for this fork. +from . import t1 as t1 diff --git a/src/mjlab_playground/tasks/velocity/config/t1/__init__.py b/src/mjlab_playground/tasks/velocity/config/t1/__init__.py new file mode 100644 index 0000000..ced7034 --- /dev/null +++ b/src/mjlab_playground/tasks/velocity/config/t1/__init__.py @@ -0,0 +1,14 @@ +# Adapted from KaydenKnapik/BoosterT1mjlab (Apache-2.0); modified for this fork. +from mjlab.tasks.registry import register_mjlab_task +from mjlab.tasks.velocity.rl import VelocityOnPolicyRunner + +from .env_cfgs import booster_t1_flat_env_cfg +from .rl_cfg import booster_t1_ppo_runner_cfg + +register_mjlab_task( + task_id="Mjlab-Velocity-Flat-Booster-T1", + env_cfg=booster_t1_flat_env_cfg(), + play_env_cfg=booster_t1_flat_env_cfg(play=True), + rl_cfg=booster_t1_ppo_runner_cfg(), + runner_cls=VelocityOnPolicyRunner, +) diff --git a/src/mjlab_playground/tasks/velocity/config/t1/env_cfgs.py b/src/mjlab_playground/tasks/velocity/config/t1/env_cfgs.py new file mode 100644 index 0000000..2debd17 --- /dev/null +++ b/src/mjlab_playground/tasks/velocity/config/t1/env_cfgs.py @@ -0,0 +1,300 @@ +# Adapted from KaydenKnapik/BoosterT1mjlab (Apache-2.0); modified for this fork. +"""Booster T1 velocity environment configurations.""" + +from mjlab.envs import ManagerBasedRlEnvCfg +from mjlab.envs import mdp as envs_mdp +from mjlab.envs.mdp.actions import JointPositionActionCfg +from mjlab.managers.event_manager import EventTermCfg +from mjlab.managers.reward_manager import RewardTermCfg +from mjlab.sensor import ( + ContactMatch, + ContactSensorCfg, + ObjRef, + RayCastSensorCfg, + RingPatternCfg, + TerrainHeightSensorCfg, +) +from mjlab.tasks.velocity import mdp + +from mjlab_playground.asset_zoo.robots.booster_t1.t1_constants import ( + T1_ACTION_SCALE, + get_t1_robot_cfg, +) +from mjlab_playground.tasks.velocity.mdp import UniformVelocityCommandCfg +from mjlab_playground.tasks.velocity.velocity_env_cfg import make_velocity_env_cfg + + +def booster_t1_rough_env_cfg(play: bool = False) -> ManagerBasedRlEnvCfg: + """Create Booster T1 rough terrain velocity configuration.""" + cfg = make_velocity_env_cfg() + + cfg.sim.mujoco.ccd_iterations = 500 + cfg.sim.contact_sensor_maxmatch = 500 + cfg.sim.nconmax = 70 + + cfg.scene.entities = {"robot": get_t1_robot_cfg()} + + # Set raycast sensor frame to T1 pelvis. + for sensor in cfg.scene.sensors or (): + if sensor.name == "terrain_scan": + assert isinstance(sensor, RayCastSensorCfg) + assert isinstance(sensor.frame, ObjRef) + sensor.frame.name = "Trunk" + + site_names = ("left_foot", "right_foot") + geom_names = tuple( + f"{side}_foot{i}_collision" for side in ("left", "right") for i in range(1, 5) + ) + + # Wire foot height scan to per-foot sites. + for sensor in cfg.scene.sensors or (): + if sensor.name == "foot_height_scan": + assert isinstance(sensor, TerrainHeightSensorCfg) + sensor.frame = tuple( + ObjRef(type="site", name=s, entity="robot") for s in site_names + ) + sensor.pattern = RingPatternCfg.single_ring(radius=0.03, num_samples=6) + + feet_ground_cfg = ContactSensorCfg( + name="feet_ground_contact", + primary=ContactMatch( + mode="subtree", + pattern=r"^(left_foot_link|right_foot_link)$", + entity="robot", + ), + secondary=ContactMatch(mode="body", pattern="terrain"), + fields=("found", "force"), + reduce="netforce", + num_slots=1, + track_air_time=True, + ) + self_collision_cfg = ContactSensorCfg( + name="self_collision", + primary=ContactMatch(mode="subtree", pattern="Trunk", entity="robot"), + secondary=ContactMatch(mode="subtree", pattern="Trunk", entity="robot"), + fields=("found", "force"), + reduce="none", + num_slots=1, + history_length=4, + ) + cfg.scene.sensors = (cfg.scene.sensors or ()) + ( + feet_ground_cfg, + self_collision_cfg, + ) + + if cfg.scene.terrain is not None and cfg.scene.terrain.terrain_generator is not None: + cfg.scene.terrain.terrain_generator.curriculum = True + + joint_pos_action = cfg.actions["joint_pos"] + assert isinstance(joint_pos_action, JointPositionActionCfg) + joint_pos_action.scale = T1_ACTION_SCALE + + cfg.viewer.body_name = "Trunk" + + twist_cmd = cfg.commands["twist"] + assert isinstance(twist_cmd, UniformVelocityCommandCfg) + twist_cmd.viz.z_offset = 1.15 + + cfg.events["foot_friction"].params["asset_cfg"].geom_names = geom_names + cfg.events["base_com"].params["asset_cfg"].body_names = ("Trunk",) + + # Rationale for std values: + # - Knees/hip_pitch get the loosest std to allow natural leg bending during stride. + # - Hip roll/yaw stay tighter to prevent excessive lateral sway and keep gait stable. + # - Ankle roll is very tight for balance; ankle pitch looser for foot clearance. + # - Waist roll/pitch stay tight to keep the torso upright and stable. + # - Shoulders/elbows get moderate freedom for natural arm swing during walking. + # - Wrists are loose (0.3) since they don't affect balance much. + # Running values are ~1.5-2x walking values to accommodate larger motion range. + cfg.rewards["pose"].params["std_standing"] = {".*": 0.05} + cfg.rewards["pose"].params["std_walking"] = { + # Head. + r"AAHead_yaw": 0.1, + r"Head_pitch": 0.1, + # Lower body. + r".*Hip_Pitch.*": 0.3, + r".*Hip_Roll.*": 0.15, + r".*Hip_Yaw.*": 0.15, + r".*Knee_Pitch.*": 0.35, + r".*Ankle_Pitch.*": 0.25, + r".*Ankle_Roll.*": 0.1, + # Waist. + r"Waist": 0.2, + # Arms. + r".*Shoulder_Pitch.*": 0.15, + r".*Shoulder_Roll.*": 0.15, + r".*Elbow_Pitch.*": 0.15, + r".*Elbow_Yaw.*": 0.1, + } + cfg.rewards["pose"].params["std_running"] = { + # Head. + r"AAHead_yaw": 0.15, + r"Head_pitch": 0.15, + # Lower body. + r".*Hip_Pitch.*": 0.5, + r".*Hip_Roll.*": 0.2, + r".*Hip_Yaw.*": 0.2, + r".*Knee_Pitch.*": 0.6, + r".*Ankle_Pitch.*": 0.35, + r".*Ankle_Roll.*": 0.15, + # Waist. + r"Waist": 0.3, + # Arms. + r".*Shoulder_Pitch.*": 0.5, + r".*Shoulder_Roll.*": 0.2, + r".*Elbow_Pitch.*": 0.35, + r".*Elbow_Yaw.*": 0.15, + } + + cfg.rewards["upright"].params["asset_cfg"].body_names = ("Trunk",) + cfg.rewards["body_ang_vel"].params["asset_cfg"].body_names = ("Trunk",) + + for reward_name in ["foot_clearance", "foot_slip"]: + cfg.rewards[reward_name].params["asset_cfg"].site_names = site_names + + cfg.rewards["body_ang_vel"].weight = -0.05 + cfg.rewards["angular_momentum"].weight = -0.05 + # cfg.rewards["angular_momentum"].weight = -0.02 + cfg.rewards["air_time"].weight = 0.02 + + cfg.rewards["self_collisions"] = RewardTermCfg( + func=mdp.self_collision_cost, + weight=-1.0, + params={"sensor_name": self_collision_cfg.name, "force_threshold": 10.0}, + ) + + # Apply play mode overrides. + if play: + # Effectively infinite episode length. + cfg.episode_length_s = int(1e9) + + cfg.observations["actor"].enable_corruption = False + cfg.events.pop("push_robot", None) + cfg.terminations.pop("out_of_terrain_bounds", None) + cfg.curriculum = {} + cfg.events["randomize_terrain"] = EventTermCfg( + func=envs_mdp.randomize_terrain, + mode="reset", + params={}, + ) + + if cfg.scene.terrain is not None: + if cfg.scene.terrain.terrain_generator is not None: + cfg.scene.terrain.terrain_generator.curriculum = False + cfg.scene.terrain.terrain_generator.num_cols = 5 + cfg.scene.terrain.terrain_generator.num_rows = 5 + cfg.scene.terrain.terrain_generator.border_width = 10.0 + + return cfg + + +def booster_t1_flat_env_cfg(play: bool = False) -> ManagerBasedRlEnvCfg: + """Create Booster T1 flat terrain velocity configuration.""" + cfg = booster_t1_rough_env_cfg(play=play) + + cfg.sim.njmax = 300 + cfg.sim.mujoco.ccd_iterations = 50 + cfg.sim.contact_sensor_maxmatch = 64 + cfg.sim.nconmax = None + + # Switch to flat terrain. + assert cfg.scene.terrain is not None + cfg.scene.terrain.terrain_type = "plane" + cfg.scene.terrain.terrain_generator = None + + # Remove raycast sensor and height scan (no terrain to scan). + cfg.scene.sensors = tuple( + s for s in (cfg.scene.sensors or ()) if s.name != "terrain_scan" + ) + del cfg.observations["actor"].terms["height_scan"] + del cfg.observations["critic"].terms["height_scan"] + + cfg.terminations.pop("out_of_terrain_bounds", None) + + # Disable terrain curriculum (not present in play mode since rough clears all). + cfg.curriculum.pop("terrain_levels", None) + + if play: + twist_cmd = cfg.commands["twist"] + assert isinstance(twist_cmd, UniformVelocityCommandCfg) + # Resample visibly different commands during short play/video sessions. + twist_cmd.resampling_time_range = (1.0, 1.5) + twist_cmd.rel_standing_envs = 0.0 + twist_cmd.rel_lateral_envs = 0.0 + twist_cmd.rel_heading_envs = 1.0 + twist_cmd.rel_forward_envs = 0.0 + twist_cmd.ranges.lin_vel_x = (2.0, 3.0) + twist_cmd.ranges.lin_vel_y = (0.0, 0.0) + twist_cmd.ranges.ang_vel_z = (-0.7, 0.7) + twist_cmd.ranges.heading = (-0.5, 0.5) + + return cfg + + +def booster_t1_flat_velocity_yaw_env_cfg(play: bool = False) -> ManagerBasedRlEnvCfg: + """Create a flat x-plus-heading velocity task for small-command play tests.""" + cfg = booster_t1_flat_env_cfg(play=play) + + twist_cmd = cfg.commands["twist"] + assert isinstance(twist_cmd, UniformVelocityCommandCfg) + twist_cmd.rel_standing_envs = 0.1 + twist_cmd.rel_lateral_envs = 0.0 + twist_cmd.rel_heading_envs = 1.0 + twist_cmd.rel_forward_envs = 0.0 + twist_cmd.heading_command = True + twist_cmd.heading_control_stiffness = 0.75 + twist_cmd.ranges.lin_vel_x = (-1.0, 1.5) + twist_cmd.ranges.lin_vel_y = (0.0, 0.0) + twist_cmd.ranges.ang_vel_z = (-0.7, 0.7) + twist_cmd.ranges.heading = (-0.7, 0.7) + + return cfg + + +def booster_t1_flat_velocity_raw_env_cfg(play: bool = False) -> ManagerBasedRlEnvCfg: + """Create a flat raw-command velocity task compatible with old 81-D checkpoints.""" + cfg = booster_t1_flat_env_cfg(play=play) + + cfg.observations["actor"].terms["command"].func = envs_mdp.generated_commands + cfg.observations["critic"].terms["command"].func = envs_mdp.generated_commands + + cfg.rewards["track_angular_velocity"] = RewardTermCfg( + func=mdp.track_angular_velocity, + weight=2.0, + params={"command_name": "twist", "std": 0.7071067811865476}, + ) + cfg.rewards.pop("track_heading", None) + + twist_cmd = cfg.commands["twist"] + assert isinstance(twist_cmd, UniformVelocityCommandCfg) + twist_cmd.rel_standing_envs = 0.1 + twist_cmd.rel_lateral_envs = 0.0 + twist_cmd.rel_heading_envs = 0.3 + twist_cmd.rel_forward_envs = 0.2 + twist_cmd.heading_command = True + twist_cmd.heading_control_stiffness = 0.5 + twist_cmd.ranges.lin_vel_x = (-1.0, 1.0) + twist_cmd.ranges.lin_vel_y = (-1.0, 1.0) + twist_cmd.ranges.ang_vel_z = (-0.5, 0.5) + twist_cmd.ranges.heading = (-3.141592653589793, 3.141592653589793) + + command_vel = cfg.curriculum.get("command_vel") + if command_vel is not None: + command_vel.params["velocity_stages"] = [ + { + "step": 0, + "lin_vel_x": (-1.0, 1.0), + "lin_vel_y": (-1.0, 1.0), + "ang_vel_z": (-0.5, 0.5), + "heading": None, + }, + { + "step": 120000, + "lin_vel_x": (-1.5, 1.5), + "lin_vel_y": (-1.0, 1.0), + "ang_vel_z": (-1.0, 1.0), + "heading": None, + }, + ] + + return cfg diff --git a/src/mjlab_playground/tasks/velocity/config/t1/rl_cfg.py b/src/mjlab_playground/tasks/velocity/config/t1/rl_cfg.py new file mode 100644 index 0000000..5835ec8 --- /dev/null +++ b/src/mjlab_playground/tasks/velocity/config/t1/rl_cfg.py @@ -0,0 +1,47 @@ +# Adapted from KaydenKnapik/BoosterT1mjlab (Apache-2.0); modified for this fork. +"""RL configuration for Booster T1 velocity task.""" + +from mjlab.rl import ( + RslRlModelCfg, + RslRlOnPolicyRunnerCfg, + RslRlPpoAlgorithmCfg, +) + + +def booster_t1_ppo_runner_cfg() -> RslRlOnPolicyRunnerCfg: + """Create RL runner configuration for Booster T1 velocity task.""" + return RslRlOnPolicyRunnerCfg( + actor=RslRlModelCfg( + hidden_dims=(512, 256, 128), + activation="elu", + obs_normalization=True, + distribution_cfg={ + "class_name": "GaussianDistribution", + "init_std": 1.0, + "std_type": "scalar", + }, + ), + critic=RslRlModelCfg( + hidden_dims=(512, 256, 128), + activation="elu", + obs_normalization=True, + ), + algorithm=RslRlPpoAlgorithmCfg( + value_loss_coef=1.0, + use_clipped_value_loss=True, + clip_param=0.2, + entropy_coef=0.01, + num_learning_epochs=5, + num_mini_batches=4, + learning_rate=1.0e-3, + schedule="adaptive", + gamma=0.99, + lam=0.95, + desired_kl=0.01, + max_grad_norm=1.0, + ), + experiment_name="t1_velocity", + save_interval=50, + num_steps_per_env=24, + max_iterations=30_000, + ) diff --git a/src/mjlab_playground/tasks/velocity/mdp/__init__.py b/src/mjlab_playground/tasks/velocity/mdp/__init__.py new file mode 100644 index 0000000..969c830 --- /dev/null +++ b/src/mjlab_playground/tasks/velocity/mdp/__init__.py @@ -0,0 +1,8 @@ +# Adapted from KaydenKnapik/BoosterT1mjlab (Apache-2.0); modified for this fork. +from mjlab.envs.mdp import * # noqa: F401, F403 + +from .curriculums import * # noqa: F403 +from .observations import * # noqa: F403 +from .rewards import * # noqa: F403 +from .terminations import * # noqa: F403 +from .velocity_command import * # noqa: F403 diff --git a/src/mjlab_playground/tasks/velocity/mdp/curriculums.py b/src/mjlab_playground/tasks/velocity/mdp/curriculums.py new file mode 100644 index 0000000..1ae1353 --- /dev/null +++ b/src/mjlab_playground/tasks/velocity/mdp/curriculums.py @@ -0,0 +1,114 @@ +# Adapted from KaydenKnapik/BoosterT1mjlab (Apache-2.0); modified for this fork. +from __future__ import annotations + +from typing import TYPE_CHECKING, TypedDict, cast + +import torch +from mjlab.entity import Entity +from mjlab.managers.scene_entity_config import SceneEntityCfg + +from .velocity_command import UniformVelocityCommandCfg + +if TYPE_CHECKING: + from mjlab.envs import ManagerBasedRlEnv + +_DEFAULT_SCENE_CFG = SceneEntityCfg("robot") + + +class VelocityStage(TypedDict): + step: int + lin_vel_x: tuple[float, float] | None + lin_vel_y: tuple[float, float] | None + ang_vel_z: tuple[float, float] | None + heading: tuple[float, float] | None + + +class RewardWeightStage(TypedDict): + step: int + weight: float + + +def terrain_levels_vel( + env: ManagerBasedRlEnv, + env_ids: torch.Tensor, + command_name: str, + asset_cfg: SceneEntityCfg = _DEFAULT_SCENE_CFG, +) -> torch.Tensor: + asset: Entity = env.scene[asset_cfg.name] + + terrain = env.scene.terrain + assert terrain is not None + terrain_generator = terrain.cfg.terrain_generator + assert terrain_generator is not None + + command = env.command_manager.get_command(command_name) + assert command is not None + + # Compute the distance the robot walked. + distance = torch.norm( + asset.data.root_link_pos_w[env_ids, :2] - env.scene.env_origins[env_ids, :2], dim=1 + ) + + # Robots that walked far enough progress to harder terrains. + move_up = distance > terrain_generator.size[0] / 2 + + # Robots that walked less than half of their required distance go to simpler + # terrains. + move_down = ( + distance < torch.norm(command[env_ids, :2], dim=1) * env.max_episode_length_s * 0.5 + ) + move_down *= ~move_up + + # Update terrain levels. + terrain.update_env_origins(env_ids, move_up, move_down) + + return torch.mean(terrain.terrain_levels.float()) + + +def commands_vel( + env: ManagerBasedRlEnv, + env_ids: torch.Tensor, + command_name: str, + velocity_stages: list[VelocityStage], +) -> dict[str, torch.Tensor]: + del env_ids # Unused. + command_term = env.command_manager.get_term(command_name) + assert command_term is not None + cfg = cast(UniformVelocityCommandCfg, command_term.cfg) + for stage in velocity_stages: + if env.common_step_counter > stage["step"]: + if "lin_vel_x" in stage and stage["lin_vel_x"] is not None: + cfg.ranges.lin_vel_x = stage["lin_vel_x"] + if "lin_vel_y" in stage and stage["lin_vel_y"] is not None: + cfg.ranges.lin_vel_y = stage["lin_vel_y"] + if "ang_vel_z" in stage and stage["ang_vel_z"] is not None: + cfg.ranges.ang_vel_z = stage["ang_vel_z"] + if "heading" in stage and stage["heading"] is not None: + cfg.ranges.heading = stage["heading"] + return { + "lin_vel_x_min": torch.tensor(cfg.ranges.lin_vel_x[0]), + "lin_vel_x_max": torch.tensor(cfg.ranges.lin_vel_x[1]), + "lin_vel_y_min": torch.tensor(cfg.ranges.lin_vel_y[0]), + "lin_vel_y_max": torch.tensor(cfg.ranges.lin_vel_y[1]), + "heading_min": torch.tensor( + cfg.ranges.heading[0] if cfg.ranges.heading is not None else 0.0 + ), + "heading_max": torch.tensor( + cfg.ranges.heading[1] if cfg.ranges.heading is not None else 0.0 + ), + } + + +def reward_weight( + env: ManagerBasedRlEnv, + env_ids: torch.Tensor, + reward_name: str, + weight_stages: list[RewardWeightStage], +) -> torch.Tensor: + """Update a reward term's weight based on training step stages.""" + del env_ids # Unused. + reward_term_cfg = env.reward_manager.get_term_cfg(reward_name) + for stage in weight_stages: + if env.common_step_counter > stage["step"]: + reward_term_cfg.weight = stage["weight"] + return torch.tensor([reward_term_cfg.weight]) diff --git a/src/mjlab_playground/tasks/velocity/mdp/observations.py b/src/mjlab_playground/tasks/velocity/mdp/observations.py new file mode 100644 index 0000000..d071d55 --- /dev/null +++ b/src/mjlab_playground/tasks/velocity/mdp/observations.py @@ -0,0 +1,86 @@ +# Adapted from KaydenKnapik/BoosterT1mjlab (Apache-2.0); modified for this fork. +from __future__ import annotations + +from typing import TYPE_CHECKING + +import torch +from mjlab.entity import Entity +from mjlab.managers.scene_entity_config import SceneEntityCfg +from mjlab.sensor import ContactSensor +from mjlab.utils.lab_api.math import wrap_to_pi + +if TYPE_CHECKING: + from mjlab.envs import ManagerBasedRlEnv + +_DEFAULT_ASSET_CFG = SceneEntityCfg("robot") + + +def generated_commands(env: ManagerBasedRlEnv, command_name: str) -> torch.Tensor: + """Expose command axes plus heading error encoding. + + The velocity task mixes three mutually exclusive command modes: stand, + x+heading, and lateral-y without heading. Both linear command axes are + exposed directly and heading stays in sin/cos form for wraparound safety. + """ + command = env.command_manager.get_command(command_name) + assert command is not None + command_term = env.command_manager.get_term(command_name) + heading_target = getattr(command_term, "heading_target", None) + assert heading_target is not None, ( + f"Command term '{command_name}' does not expose a heading target. " + "Enable heading_command for this task." + ) + + robot = getattr(command_term, "robot", None) + if robot is None: + robot = env.scene["robot"] + heading_error = wrap_to_pi(heading_target - robot.data.heading_w) + is_heading_env = getattr(command_term, "is_heading_env", None) + if is_heading_env is not None: + heading_error = torch.where( + is_heading_env, + heading_error, + torch.zeros_like(heading_error), + ) + is_standing_env = getattr(command_term, "is_standing_env", None) + if is_standing_env is not None: + heading_error = torch.where( + is_standing_env, + torch.zeros_like(heading_error), + heading_error, + ) + + return torch.stack( + (command[:, 0], command[:, 1], torch.sin(heading_error), torch.cos(heading_error)), + dim=1, + ) + + +def foot_height( + env: ManagerBasedRlEnv, asset_cfg: SceneEntityCfg = _DEFAULT_ASSET_CFG +) -> torch.Tensor: + asset: Entity = env.scene[asset_cfg.name] + return asset.data.site_pos_w[:, asset_cfg.site_ids, 2] # (num_envs, num_sites) + + +def foot_air_time(env: ManagerBasedRlEnv, sensor_name: str) -> torch.Tensor: + sensor: ContactSensor = env.scene[sensor_name] + sensor_data = sensor.data + current_air_time = sensor_data.current_air_time + assert current_air_time is not None + return current_air_time + + +def foot_contact(env: ManagerBasedRlEnv, sensor_name: str) -> torch.Tensor: + sensor: ContactSensor = env.scene[sensor_name] + sensor_data = sensor.data + assert sensor_data.found is not None + return (sensor_data.found > 0).float() + + +def foot_contact_forces(env: ManagerBasedRlEnv, sensor_name: str) -> torch.Tensor: + sensor: ContactSensor = env.scene[sensor_name] + sensor_data = sensor.data + assert sensor_data.force is not None + forces_flat = sensor_data.force.flatten(start_dim=1) # [B, N*3] + return torch.sign(forces_flat) * torch.log1p(torch.abs(forces_flat)) diff --git a/src/mjlab_playground/tasks/velocity/mdp/rewards.py b/src/mjlab_playground/tasks/velocity/mdp/rewards.py new file mode 100644 index 0000000..66b956f --- /dev/null +++ b/src/mjlab_playground/tasks/velocity/mdp/rewards.py @@ -0,0 +1,410 @@ +# Adapted from KaydenKnapik/BoosterT1mjlab (Apache-2.0); modified for this fork. +from __future__ import annotations + +from typing import TYPE_CHECKING + +import torch +from mjlab.entity import Entity +from mjlab.managers.reward_manager import RewardTermCfg +from mjlab.managers.scene_entity_config import SceneEntityCfg +from mjlab.sensor import BuiltinSensor, ContactSensor +from mjlab.utils.lab_api.math import quat_apply_inverse, wrap_to_pi +from mjlab.utils.lab_api.string import ( + resolve_matching_names_values, +) + +if TYPE_CHECKING: + from mjlab.envs import ManagerBasedRlEnv + + +_DEFAULT_ASSET_CFG = SceneEntityCfg("robot") + + +def _is_forward_command_active( + command: torch.Tensor, command_threshold: float +) -> torch.Tensor: + """Gate rewards using only the x-command magnitude.""" + return (torch.abs(command[:, 0]) > command_threshold).float() + + +def track_linear_velocity( + env: ManagerBasedRlEnv, + std: float, + command_name: str, + asset_cfg: SceneEntityCfg = _DEFAULT_ASSET_CFG, +) -> torch.Tensor: + """Reward for tracking the commanded base linear velocity. + + The commanded z velocity is assumed to be zero. + """ + asset: Entity = env.scene[asset_cfg.name] + command = env.command_manager.get_command(command_name) + assert command is not None, f"Command '{command_name}' not found." + actual = asset.data.root_link_lin_vel_b + xy_error = torch.sum(torch.square(command[:, :2] - actual[:, :2]), dim=1) + z_error = torch.square(actual[:, 2]) + lin_vel_error = xy_error + z_error + return torch.exp(-lin_vel_error / std**2) + + +def track_angular_velocity( + env: ManagerBasedRlEnv, + std: float, + command_name: str, + asset_cfg: SceneEntityCfg = _DEFAULT_ASSET_CFG, +) -> torch.Tensor: + """Reward heading error for heading-controlled envs, angular velocity for others. + + The commanded xy angular velocities are assumed to be zero. + """ + asset: Entity = env.scene[asset_cfg.name] + command = env.command_manager.get_command(command_name) + assert command is not None, f"Command '{command_name}' not found." + actual = asset.data.root_link_ang_vel_b + z_error = torch.square(command[:, 2] - actual[:, 2]) + xy_error = torch.sum(torch.square(actual[:, :2]), dim=1) + ang_vel_error = z_error + xy_error + return torch.exp(-ang_vel_error / std**2) + + +def track_heading_command( + env: ManagerBasedRlEnv, + std: float, + command_name: str, + asset_cfg: SceneEntityCfg = _DEFAULT_ASSET_CFG, +) -> torch.Tensor: + """Reward tracking of heading targets instead of yaw-rate commands.""" + asset: Entity = env.scene[asset_cfg.name] + command_term = env.command_manager.get_term(command_name) + heading_target = getattr(command_term, "heading_target", None) + assert heading_target is not None, ( + f"Command term '{command_name}' does not expose a heading target. " + "Enable heading_command for this task." + ) + + heading_error = wrap_to_pi(heading_target - asset.data.heading_w) + env.extras["log"]["Metrics/heading_error_mean"] = torch.mean(torch.abs(heading_error)) + reward = torch.exp(-torch.square(heading_error) / std**2) + env.extras["log"]["Metrics/heading_reward_mean"] = torch.mean(reward) + + active_mask = torch.ones_like(heading_error, dtype=torch.bool) + is_heading_env = getattr(command_term, "is_heading_env", None) + if is_heading_env is not None: + active_mask &= is_heading_env + is_standing_env = getattr(command_term, "is_standing_env", None) + if is_standing_env is not None: + active_mask &= ~is_standing_env + + return torch.where(active_mask, reward, torch.ones_like(reward)) + + +def flat_orientation( + env: ManagerBasedRlEnv, + std: float, + asset_cfg: SceneEntityCfg = _DEFAULT_ASSET_CFG, +) -> torch.Tensor: + """Reward flat base orientation (robot being upright). + + If asset_cfg has body_ids specified, computes the projected gravity + for that specific body. Otherwise, uses the root link projected gravity. + """ + asset: Entity = env.scene[asset_cfg.name] + + # If body_ids are specified, compute projected gravity for that body. + if asset_cfg.body_ids: + body_quat_w = asset.data.body_link_quat_w[:, asset_cfg.body_ids, :] # [B, N, 4] + body_quat_w = body_quat_w.squeeze(1) # [B, 4] + gravity_w = asset.data.gravity_vec_w # [3] + projected_gravity_b = quat_apply_inverse(body_quat_w, gravity_w) # [B, 3] + xy_squared = torch.sum(torch.square(projected_gravity_b[:, :2]), dim=1) + else: + # Use root link projected gravity. + xy_squared = torch.sum(torch.square(asset.data.projected_gravity_b[:, :2]), dim=1) + return torch.exp(-xy_squared / std**2) + + +def self_collision_cost(env: ManagerBasedRlEnv, sensor_name: str) -> torch.Tensor: + """Penalize self-collisions. + + Returns the number of self-collisions detected by the specified contact sensor. + """ + sensor: ContactSensor = env.scene[sensor_name] + assert sensor.data.found is not None + return sensor.data.found.squeeze(-1) + + +def body_angular_velocity_penalty( + env: ManagerBasedRlEnv, + asset_cfg: SceneEntityCfg = _DEFAULT_ASSET_CFG, +) -> torch.Tensor: + """Penalize excessive body angular velocities.""" + asset: Entity = env.scene[asset_cfg.name] + ang_vel = asset.data.body_link_ang_vel_w[:, asset_cfg.body_ids, :] + ang_vel = ang_vel.squeeze(1) + ang_vel_xy = ang_vel[:, :2] # Don't penalize z-angular velocity. + return torch.sum(torch.square(ang_vel_xy), dim=1) + + +def body_lateral_velocity_penalty( + env: ManagerBasedRlEnv, + asset_cfg: SceneEntityCfg = _DEFAULT_ASSET_CFG, +) -> torch.Tensor: + """Penalize side-to-side base motion in the robot body frame.""" + asset: Entity = env.scene[asset_cfg.name] + del asset_cfg + return torch.square(asset.data.root_link_lin_vel_b[:, 1]) + + +def angular_momentum_penalty( + env: ManagerBasedRlEnv, + sensor_name: str, +) -> torch.Tensor: + """Penalize whole-body angular momentum to encourage natural arm swing.""" + angmom_sensor: BuiltinSensor = env.scene[sensor_name] + angmom = angmom_sensor.data + angmom_magnitude_sq = torch.sum(torch.square(angmom), dim=-1) + angmom_magnitude = torch.sqrt(angmom_magnitude_sq) + env.extras["log"]["Metrics/angular_momentum_mean"] = torch.mean(angmom_magnitude) + return angmom_magnitude_sq + + +def feet_air_time( + env: ManagerBasedRlEnv, + sensor_name: str, + threshold_min: float = 0.05, + threshold_max: float = 0.5, + command_name: str | None = None, + command_threshold: float = 0.5, +) -> torch.Tensor: + """Reward feet air time.""" + sensor: ContactSensor = env.scene[sensor_name] + sensor_data = sensor.data + current_air_time = sensor_data.current_air_time + assert current_air_time is not None + in_range = (current_air_time > threshold_min) & (current_air_time < threshold_max) + reward = torch.sum(in_range.float(), dim=1) + in_air = current_air_time > 0 + num_in_air = torch.sum(in_air.float()) + mean_air_time = torch.sum(current_air_time * in_air.float()) / torch.clamp( + num_in_air, min=1 + ) + env.extras["log"]["Metrics/air_time_mean"] = mean_air_time + if command_name is not None: + command = env.command_manager.get_command(command_name) + if command is not None: + linear_norm = torch.norm(command[:, :2], dim=1) + angular_norm = torch.abs(command[:, 2]) + total_command = linear_norm + angular_norm + scale = (total_command > command_threshold).float() + reward *= scale + return reward + + +def feet_clearance( + env: ManagerBasedRlEnv, + target_height: float, + command_name: str | None = None, + command_threshold: float = 0.01, + asset_cfg: SceneEntityCfg = _DEFAULT_ASSET_CFG, +) -> torch.Tensor: + """Penalize deviation from target clearance height, weighted by foot velocity.""" + asset: Entity = env.scene[asset_cfg.name] + foot_z = asset.data.site_pos_w[:, asset_cfg.site_ids, 2] # [B, N] + foot_vel_xy = asset.data.site_lin_vel_w[:, asset_cfg.site_ids, :2] # [B, N, 2] + vel_norm = torch.norm(foot_vel_xy, dim=-1) # [B, N] + delta = torch.abs(foot_z - target_height) # [B, N] + cost = torch.sum(delta * vel_norm, dim=1) # [B] + if command_name is not None: + command = env.command_manager.get_command(command_name) + if command is not None: + cost = cost * _is_forward_command_active(command, command_threshold) + return cost + + +class feet_swing_height: + """Penalize deviation from target swing height, evaluated at landing.""" + + def __init__(self, cfg: RewardTermCfg, env: ManagerBasedRlEnv): + self.sensor_name = cfg.params["sensor_name"] + self.site_names = cfg.params["asset_cfg"].site_names + self.peak_heights = torch.zeros( + (env.num_envs, len(self.site_names)), device=env.device, dtype=torch.float32 + ) + self.step_dt = env.step_dt + + def __call__( + self, + env: ManagerBasedRlEnv, + sensor_name: str, + target_height: float, + command_name: str, + command_threshold: float, + asset_cfg: SceneEntityCfg, + ) -> torch.Tensor: + asset: Entity = env.scene[asset_cfg.name] + contact_sensor: ContactSensor = env.scene[sensor_name] + command = env.command_manager.get_command(command_name) + assert command is not None + foot_heights = asset.data.site_pos_w[:, asset_cfg.site_ids, 2] + in_air = contact_sensor.data.found == 0 + self.peak_heights = torch.where( + in_air, + torch.maximum(self.peak_heights, foot_heights), + self.peak_heights, + ) + first_contact = contact_sensor.compute_first_contact(dt=self.step_dt) + active = _is_forward_command_active(command, command_threshold) + error = self.peak_heights / target_height - 1.0 + cost = torch.sum(torch.square(error) * first_contact.float(), dim=1) * active + num_landings = torch.sum(first_contact.float()) + peak_heights_at_landing = self.peak_heights * first_contact.float() + mean_peak_height = torch.sum(peak_heights_at_landing) / torch.clamp( + num_landings, min=1 + ) + env.extras["log"]["Metrics/peak_height_mean"] = mean_peak_height + self.peak_heights = torch.where( + first_contact, + torch.zeros_like(self.peak_heights), + self.peak_heights, + ) + return cost + + +def feet_slip( + env: ManagerBasedRlEnv, + sensor_name: str, + command_name: str, + command_threshold: float = 0.01, + asset_cfg: SceneEntityCfg = _DEFAULT_ASSET_CFG, +) -> torch.Tensor: + """Penalize foot sliding (xy velocity while in contact).""" + asset: Entity = env.scene[asset_cfg.name] + contact_sensor: ContactSensor = env.scene[sensor_name] + command = env.command_manager.get_command(command_name) + assert command is not None + active = _is_forward_command_active(command, command_threshold) + assert contact_sensor.data.found is not None + in_contact = (contact_sensor.data.found > 0).float() # [B, N] + foot_vel_xy = asset.data.site_lin_vel_w[:, asset_cfg.site_ids, :2] # [B, N, 2] + vel_xy_norm = torch.norm(foot_vel_xy, dim=-1) # [B, N] + vel_xy_norm_sq = torch.square(vel_xy_norm) # [B, N] + cost = torch.sum(vel_xy_norm_sq * in_contact, dim=1) * active + num_in_contact = torch.sum(in_contact) + mean_slip_vel = torch.sum(vel_xy_norm * in_contact) / torch.clamp( + num_in_contact, min=1 + ) + env.extras["log"]["Metrics/slip_velocity_mean"] = mean_slip_vel + return cost + + +def soft_landing( + env: ManagerBasedRlEnv, + sensor_name: str, + command_name: str | None = None, + command_threshold: float = 0.05, +) -> torch.Tensor: + """Penalize high impact forces at landing to encourage soft footfalls.""" + contact_sensor: ContactSensor = env.scene[sensor_name] + sensor_data = contact_sensor.data + assert sensor_data.force is not None + forces = sensor_data.force # [B, N, 3] + force_magnitude = torch.norm(forces, dim=-1) # [B, N] + first_contact = contact_sensor.compute_first_contact(dt=env.step_dt) # [B, N] + landing_impact = force_magnitude * first_contact.float() # [B, N] + cost = torch.sum(landing_impact, dim=1) # [B] + num_landings = torch.sum(first_contact.float()) + mean_landing_force = torch.sum(landing_impact) / torch.clamp(num_landings, min=1) + env.extras["log"]["Metrics/landing_force_mean"] = mean_landing_force + if command_name is not None: + command = env.command_manager.get_command(command_name) + if command is not None: + linear_norm = torch.norm(command[:, :2], dim=1) + angular_norm = torch.abs(command[:, 2]) + total_command = linear_norm + angular_norm + active = (total_command > command_threshold).float() + cost = cost * active + return cost + + +class variable_posture: + """Penalize deviation from default pose with speed-dependent tolerance. + + Uses per-joint standard deviations to control how much each joint can deviate + from default pose. Smaller std = stricter (less deviation allowed), larger + std = more forgiving. The reward is: exp(-mean(error² / std²)) + + Three speed regimes (based on linear + angular command velocity): + - std_standing (speed < walking_threshold): Tight tolerance for holding pose. + - std_walking (walking_threshold <= speed < running_threshold): Moderate. + - std_running (speed >= running_threshold): Loose tolerance for large motion. + + Tune std values per joint based on how much motion that joint needs at each + speed. Map joint name patterns to std values, e.g. {".*knee.*": 0.35}. + """ + + def __init__(self, cfg: RewardTermCfg, env: ManagerBasedRlEnv): + asset: Entity = env.scene[cfg.params["asset_cfg"].name] + default_joint_pos = asset.data.default_joint_pos + assert default_joint_pos is not None + self.default_joint_pos = default_joint_pos + + _, joint_names = asset.find_joints(cfg.params["asset_cfg"].joint_names) + + _, _, std_standing = resolve_matching_names_values( + data=cfg.params["std_standing"], + list_of_strings=joint_names, + ) + self.std_standing = torch.tensor( + std_standing, device=env.device, dtype=torch.float32 + ) + + _, _, std_walking = resolve_matching_names_values( + data=cfg.params["std_walking"], + list_of_strings=joint_names, + ) + self.std_walking = torch.tensor(std_walking, device=env.device, dtype=torch.float32) + + _, _, std_running = resolve_matching_names_values( + data=cfg.params["std_running"], + list_of_strings=joint_names, + ) + self.std_running = torch.tensor(std_running, device=env.device, dtype=torch.float32) + + def __call__( + self, + env: ManagerBasedRlEnv, + std_standing, + std_walking, + std_running, + asset_cfg: SceneEntityCfg, + command_name: str, + walking_threshold: float = 0.5, + running_threshold: float = 1.5, + ) -> torch.Tensor: + del std_standing, std_walking, std_running # Unused. + + asset: Entity = env.scene[asset_cfg.name] + command = env.command_manager.get_command(command_name) + assert command is not None + + linear_speed = torch.norm(command[:, :2], dim=1) + angular_speed = torch.abs(command[:, 2]) + total_speed = linear_speed + angular_speed + + standing_mask = (total_speed < walking_threshold).float() + walking_mask = ( + (total_speed >= walking_threshold) & (total_speed < running_threshold) + ).float() + running_mask = (total_speed >= running_threshold).float() + + std = ( + self.std_standing * standing_mask.unsqueeze(1) + + self.std_walking * walking_mask.unsqueeze(1) + + self.std_running * running_mask.unsqueeze(1) + ) + + current_joint_pos = asset.data.joint_pos[:, asset_cfg.joint_ids] + desired_joint_pos = self.default_joint_pos[:, asset_cfg.joint_ids] + error_squared = torch.square(current_joint_pos - desired_joint_pos) + + return torch.exp(-torch.mean(error_squared / (std**2), dim=1)) diff --git a/src/mjlab_playground/tasks/velocity/mdp/terminations.py b/src/mjlab_playground/tasks/velocity/mdp/terminations.py new file mode 100644 index 0000000..d8757c5 --- /dev/null +++ b/src/mjlab_playground/tasks/velocity/mdp/terminations.py @@ -0,0 +1,16 @@ +# Adapted from KaydenKnapik/BoosterT1mjlab (Apache-2.0); modified for this fork. +from __future__ import annotations + +from typing import TYPE_CHECKING + +import torch +from mjlab.sensor import ContactSensor + +if TYPE_CHECKING: + from mjlab.envs import ManagerBasedRlEnv + + +def illegal_contact(env: ManagerBasedRlEnv, sensor_name: str) -> torch.Tensor: + sensor: ContactSensor = env.scene[sensor_name] + assert sensor.data.found is not None + return torch.any(sensor.data.found, dim=-1) diff --git a/src/mjlab_playground/tasks/velocity/mdp/velocity_command.py b/src/mjlab_playground/tasks/velocity/mdp/velocity_command.py new file mode 100644 index 0000000..6f3afbf --- /dev/null +++ b/src/mjlab_playground/tasks/velocity/mdp/velocity_command.py @@ -0,0 +1,393 @@ +# Adapted from KaydenKnapik/BoosterT1mjlab (Apache-2.0); modified for this fork. +from __future__ import annotations + +from collections.abc import Callable +from dataclasses import dataclass, field +from typing import TYPE_CHECKING, Any + +import numpy as np +import torch +from mjlab.entity import Entity +from mjlab.managers.command_manager import CommandTerm, CommandTermCfg +from mjlab.utils.lab_api.math import ( + matrix_from_quat, + quat_apply, + wrap_to_pi, +) + +if TYPE_CHECKING: + import viser + from mjlab.envs.manager_based_rl_env import ManagerBasedRlEnv + from mjlab.viewer.debug_visualizer import DebugVisualizer + + +class UniformVelocityCommand(CommandTerm): + cfg: UniformVelocityCommandCfg + + def __init__(self, cfg: UniformVelocityCommandCfg, env: ManagerBasedRlEnv): + super().__init__(cfg, env) + + if self.cfg.heading_command and self.cfg.ranges.heading is None: + raise ValueError("heading_command=True but ranges.heading is set to None.") + if self.cfg.ranges.heading and not self.cfg.heading_command: + raise ValueError("ranges.heading is set but heading_command=False.") + + self.robot: Entity = env.scene[cfg.entity_name] + + self.vel_command_b = torch.zeros(self.num_envs, 3, device=self.device) + self.vel_command_w = torch.zeros(self.num_envs, 3, device=self.device) + self.heading_target = torch.zeros(self.num_envs, device=self.device) + self.heading_error = torch.zeros(self.num_envs, device=self.device) + self.is_heading_env = torch.zeros( + self.num_envs, dtype=torch.bool, device=self.device + ) + self.is_standing_env = torch.zeros_like(self.is_heading_env) + self.is_lateral_env = torch.zeros_like(self.is_heading_env) + self.is_world_env = torch.zeros_like(self.is_heading_env) + self.is_forward_env = torch.zeros_like(self.is_heading_env) + + self.metrics["error_vel_xy"] = torch.zeros(self.num_envs, device=self.device) + self.metrics["error_heading"] = torch.zeros(self.num_envs, device=self.device) + self._joystick_enabled: viser.GuiCheckboxHandle | None = None + self._joystick_sliders: list[viser.GuiSliderHandle] = [] + self._joystick_get_env_idx: Callable[[], int] | None = None + + @property + def command(self) -> torch.Tensor: + return self.vel_command_b + + def _update_metrics(self) -> None: + max_command_time = self.cfg.resampling_time_range[1] + max_command_step = max_command_time / self._env.step_dt + self.metrics["error_vel_xy"] += ( + torch.norm( + self.vel_command_b[:, :2] - self.robot.data.root_link_lin_vel_b[:, :2], dim=-1 + ) + / max_command_step + ) + if self.cfg.heading_command: + self.metrics["error_heading"] += ( + torch.abs(wrap_to_pi(self.heading_target - self.robot.data.heading_w)) + / max_command_step + ) + + def _resample_command(self, env_ids: torch.Tensor) -> None: + # Modes are mutually exclusive: + # - stand: zero command + # - x + heading: x only, y = 0, heading active + # - lateral: y only, x = 0, heading inactive + num_envs = len(env_ids) + if num_envs == 0: + return + + self.vel_command_b[env_ids] = 0.0 + self.vel_command_w[env_ids] = 0.0 + self.is_heading_env[env_ids] = False + self.is_standing_env[env_ids] = False + self.is_lateral_env[env_ids] = False + self.is_world_env[env_ids] = False + self.is_forward_env[env_ids] = False + self.heading_target[env_ids] = self.robot.data.heading_w[env_ids] + + mode_sample = torch.rand(num_envs, device=self.device) + standing_mask = mode_sample < self.cfg.rel_standing_envs + lateral_mask = (mode_sample >= self.cfg.rel_standing_envs) & ( + mode_sample < self.cfg.rel_standing_envs + self.cfg.rel_lateral_envs + ) + x_heading_mask = ~(standing_mask | lateral_mask) + + standing_env_ids = env_ids[standing_mask] + lateral_env_ids = env_ids[lateral_mask] + x_heading_env_ids = env_ids[x_heading_mask] + + self.is_standing_env[standing_env_ids] = True + self.is_lateral_env[lateral_env_ids] = True + self.is_world_env[env_ids] = ( + torch.rand(num_envs, device=self.device) <= self.cfg.rel_world_envs + ) + + if len(x_heading_env_ids) > 0: + self.vel_command_b[x_heading_env_ids, 0] = torch.empty( + len(x_heading_env_ids), device=self.device + ).uniform_(*self.cfg.ranges.lin_vel_x) + self.vel_command_b[x_heading_env_ids, 2] = torch.empty( + len(x_heading_env_ids), device=self.device + ).uniform_(*self.cfg.ranges.ang_vel_z) + if self.cfg.heading_command: + assert self.cfg.ranges.heading is not None + self.heading_target[x_heading_env_ids] = torch.empty( + len(x_heading_env_ids), device=self.device + ).uniform_(*self.cfg.ranges.heading) + self.is_heading_env[x_heading_env_ids] = ( + torch.rand(len(x_heading_env_ids), device=self.device) + <= self.cfg.rel_heading_envs + ) + + if len(lateral_env_ids) > 0: + self.vel_command_b[lateral_env_ids, 1] = torch.empty( + len(lateral_env_ids), device=self.device + ).uniform_(*self.cfg.ranges.lin_vel_y) + + self.vel_command_w[env_ids] = self.vel_command_b[env_ids] + + fwd_candidates = x_heading_env_ids + if len(fwd_candidates) > 0: + self.is_forward_env[fwd_candidates] = ( + torch.rand(len(fwd_candidates), device=self.device) <= self.cfg.rel_forward_envs + ) + fwd_ids = fwd_candidates[self.is_forward_env[fwd_candidates]] + if len(fwd_ids) > 0: + self.vel_command_b[fwd_ids, 0] = ( + self.vel_command_b[fwd_ids, 0].abs().clamp(min=0.3) + ) + self.vel_command_b[fwd_ids, 1] = 0.0 + self.vel_command_b[fwd_ids, 2] = 0.0 + self.vel_command_w[fwd_ids] = self.vel_command_b[fwd_ids] + + init_vel_env_ids = env_ids[ + torch.rand(num_envs, device=self.device) < self.cfg.init_velocity_prob + ] + if len(init_vel_env_ids) > 0: + root_pos = self.robot.data.root_link_pos_w[init_vel_env_ids] + root_quat = self.robot.data.root_link_quat_w[init_vel_env_ids] + lin_vel_b = self.robot.data.root_link_lin_vel_b[init_vel_env_ids] + lin_vel_b[:, :2] = self.vel_command_b[init_vel_env_ids, :2] + root_lin_vel_w = quat_apply(root_quat, lin_vel_b) + root_ang_vel_b = self.robot.data.root_link_ang_vel_b[init_vel_env_ids] + root_ang_vel_b[:, 2] = self.vel_command_b[init_vel_env_ids, 2] + root_state = torch.cat( + [root_pos, root_quat, root_lin_vel_w, root_ang_vel_b], dim=-1 + ) + self.robot.write_root_state_to_sim(root_state, init_vel_env_ids) + + def _update_command(self) -> None: + if self.cfg.heading_command: + self.heading_error = wrap_to_pi(self.heading_target - self.robot.data.heading_w) + env_ids = self.is_heading_env.nonzero(as_tuple=False).flatten() + self.vel_command_b[env_ids, 2] = torch.clip( + self.cfg.heading_control_stiffness * self.heading_error[env_ids], + min=self.cfg.ranges.ang_vel_z[0], + max=self.cfg.ranges.ang_vel_z[1], + ) + if self.is_world_env.any(): + w_ids = self.is_world_env.nonzero(as_tuple=False).flatten() + heading = self.robot.data.heading_w[w_ids] + cos_h = torch.cos(heading) + sin_h = torch.sin(heading) + vx_w = self.vel_command_w[w_ids, 0] + vy_w = self.vel_command_w[w_ids, 1] + self.vel_command_b[w_ids, 0] = cos_h * vx_w + sin_h * vy_w + self.vel_command_b[w_ids, 1] = -sin_h * vx_w + cos_h * vy_w + standing_env_ids = self.is_standing_env.nonzero(as_tuple=False).flatten() + self.vel_command_b[standing_env_ids, :] = 0.0 + self.vel_command_w[standing_env_ids, :] = 0.0 + + # GUI. + + def create_gui( + self, + name: str, + server: viser.ViserServer, + get_env_idx: Callable[[], int], + on_change: Callable[[], None] | None = None, + request_action: Callable[[str, Any], None] | None = None, + ) -> None: + """Create velocity controls for the Viser Commands panel.""" + del on_change, request_action + + from viser import Icon + + ranges = self.cfg.ranges + axes = [ + ("lin_vel_x", max(abs(v) for v in ranges.lin_vel_x)), + ("lin_vel_y", max(abs(v) for v in ranges.lin_vel_y)), + ] + if self.cfg.heading_command: + assert ranges.heading is not None + axes.append(("heading_error", max(abs(v) for v in ranges.heading))) + else: + axes.append(("ang_vel_z", max(abs(v) for v in ranges.ang_vel_z))) + + sliders: list[viser.GuiSliderHandle] = [] + with server.gui.add_folder(name.capitalize()): + enabled = server.gui.add_checkbox("Enable", initial_value=False) + for label, max_val in axes: + safe_max = max(max_val, 0.1) + max_input = server.gui.add_slider( + f"Max {label}", + initial_value=safe_max, + step=0.1, + min=0.1, + max=10.0, + ) + slider = server.gui.add_slider( + label, + min=-safe_max, + max=safe_max, + step=0.05, + initial_value=0.0, + ) + + @max_input.on_update + def _(_ev, _slider=slider, _max_input=max_input) -> None: + _slider.min = -_max_input.value + _slider.max = _max_input.value + + sliders.append(slider) + + zero_btn = server.gui.add_button("Zero", icon=Icon.SQUARE_X) + + @zero_btn.on_click + def _(_) -> None: + for slider in sliders: + slider.value = 0.0 + + self._joystick_enabled = enabled + self._joystick_sliders = sliders + self._joystick_get_env_idx = get_env_idx + + def compute(self, dt: float) -> None: + super().compute(dt) + if self._joystick_enabled is None or not self._joystick_enabled.value: + return + + assert self._joystick_get_env_idx is not None + idx = self._joystick_get_env_idx() + self.is_standing_env[idx] = False + self.is_lateral_env[idx] = False + self.is_world_env[idx] = False + self.is_forward_env[idx] = False + self.vel_command_b[idx, 0] = self._joystick_sliders[0].value + self.vel_command_b[idx, 1] = self._joystick_sliders[1].value + self.vel_command_w[idx, :2] = self.vel_command_b[idx, :2] + + if self.cfg.heading_command: + heading_error = self._joystick_sliders[2].value + current_heading = self.robot.data.heading_w[idx] + self.is_heading_env[idx] = True + self.heading_error[idx] = heading_error + self.heading_target[idx] = wrap_to_pi(current_heading + heading_error) + self.vel_command_b[idx, 2] = torch.clamp( + self.cfg.heading_control_stiffness + * torch.tensor(heading_error, device=self.device), + min=self.cfg.ranges.ang_vel_z[0], + max=self.cfg.ranges.ang_vel_z[1], + ) + else: + self.is_heading_env[idx] = False + self.vel_command_b[idx, 2] = self._joystick_sliders[2].value + + # Visualization. + + def _debug_vis_impl(self, visualizer: "DebugVisualizer") -> None: + """Draw velocity command and actual velocity arrows.""" + env_indices = visualizer.get_env_indices(self.num_envs) + if not env_indices: + return + + cmds = self.command.cpu().numpy() + base_pos_ws = self.robot.data.root_link_pos_w.cpu().numpy() + base_quat_w = self.robot.data.root_link_quat_w + base_mat_ws = matrix_from_quat(base_quat_w).cpu().numpy() + lin_vel_bs = self.robot.data.root_link_lin_vel_b.cpu().numpy() + ang_vel_bs = self.robot.data.root_link_ang_vel_b.cpu().numpy() + + scale = self.cfg.viz.scale + z_offset = self.cfg.viz.z_offset + + for batch in env_indices: + base_pos_w = base_pos_ws[batch] + base_mat_w = base_mat_ws[batch] + cmd = cmds[batch] + lin_vel_b = lin_vel_bs[batch] + ang_vel_b = ang_vel_bs[batch] + + # Skip if robot appears uninitialized (at origin). + if np.linalg.norm(base_pos_w) < 1e-6: + continue + + # Helper to transform local to world coordinates. + def local_to_world( + vec: np.ndarray, pos: np.ndarray = base_pos_w, mat: np.ndarray = base_mat_w + ) -> np.ndarray: + return pos + mat @ vec + + # Command linear velocity arrow (blue). + cmd_lin_from = local_to_world(np.array([0, 0, z_offset]) * scale) + cmd_lin_to = local_to_world( + (np.array([0, 0, z_offset]) + np.array([cmd[0], cmd[1], 0])) * scale + ) + visualizer.add_arrow( + cmd_lin_from, cmd_lin_to, color=(0.2, 0.2, 0.6, 0.6), width=0.015 + ) + + # Command angular velocity arrow (green). + cmd_ang_from = cmd_lin_from + cmd_ang_to = local_to_world( + (np.array([0, 0, z_offset]) + np.array([0, 0, cmd[2]])) * scale + ) + visualizer.add_arrow( + cmd_ang_from, cmd_ang_to, color=(0.2, 0.6, 0.2, 0.6), width=0.015 + ) + + # Actual linear velocity arrow (cyan). + act_lin_from = local_to_world(np.array([0, 0, z_offset]) * scale) + act_lin_to = local_to_world( + (np.array([0, 0, z_offset]) + np.array([lin_vel_b[0], lin_vel_b[1], 0])) * scale + ) + visualizer.add_arrow( + act_lin_from, act_lin_to, color=(0.0, 0.6, 1.0, 0.7), width=0.015 + ) + + # Actual angular velocity arrow (light green). + act_ang_from = act_lin_from + act_ang_to = local_to_world( + (np.array([0, 0, z_offset]) + np.array([0, 0, ang_vel_b[2]])) * scale + ) + visualizer.add_arrow( + act_ang_from, act_ang_to, color=(0.0, 1.0, 0.4, 0.7), width=0.015 + ) + + +@dataclass(kw_only=True) +class UniformVelocityCommandCfg(CommandTermCfg): + entity_name: str + heading_command: bool = False + heading_control_stiffness: float = 1.0 + rel_standing_envs: float = 0.0 + rel_lateral_envs: float = 0.0 + rel_heading_envs: float = 1.0 + rel_world_envs: float = 0.0 + rel_forward_envs: float = 0.0 + init_velocity_prob: float = 0.0 + + @dataclass + class Ranges: + lin_vel_x: tuple[float, float] + lin_vel_y: tuple[float, float] + ang_vel_z: tuple[float, float] + heading: tuple[float, float] | None = None + + ranges: Ranges + + @dataclass + class VizCfg: + z_offset: float = 0.2 + scale: float = 0.5 + + viz: VizCfg = field(default_factory=VizCfg) + + def build(self, env: ManagerBasedRlEnv) -> UniformVelocityCommand: + return UniformVelocityCommand(self, env) + + def __post_init__(self): + if self.heading_command and self.ranges.heading is None: + raise ValueError( + "The velocity command has heading commands active (heading_command=True) but " + "the `ranges.heading` parameter is set to None." + ) + mode_total = self.rel_standing_envs + self.rel_lateral_envs + if mode_total > 1.0: + raise ValueError( + "The sum of rel_standing_envs and rel_lateral_envs must be <= 1.0." + ) diff --git a/src/mjlab_playground/tasks/velocity/velocity_env_cfg.py b/src/mjlab_playground/tasks/velocity/velocity_env_cfg.py new file mode 100644 index 0000000..62dbc13 --- /dev/null +++ b/src/mjlab_playground/tasks/velocity/velocity_env_cfg.py @@ -0,0 +1,477 @@ +# Adapted from KaydenKnapik/BoosterT1mjlab (Apache-2.0); modified for this fork. +"""Velocity task configuration. + +This module provides a factory function to create a base velocity task config. +Robot-specific configurations call the factory and customize as needed. +""" + +import math +from dataclasses import replace + +from mjlab.envs import ManagerBasedRlEnvCfg +from mjlab.envs import mdp as envs_mdp +from mjlab.envs.mdp import dr +from mjlab.envs.mdp.actions import JointPositionActionCfg +from mjlab.managers.action_manager import ActionTermCfg +from mjlab.managers.command_manager import CommandTermCfg +from mjlab.managers.curriculum_manager import CurriculumTermCfg +from mjlab.managers.event_manager import EventTermCfg +from mjlab.managers.metrics_manager import MetricsTermCfg +from mjlab.managers.observation_manager import ObservationGroupCfg, ObservationTermCfg +from mjlab.managers.reward_manager import RewardTermCfg +from mjlab.managers.scene_entity_config import SceneEntityCfg +from mjlab.managers.termination_manager import TerminationTermCfg +from mjlab.scene import SceneCfg +from mjlab.sensor import ( + GridPatternCfg, + ObjRef, + RayCastSensorCfg, + TerrainHeightSensorCfg, +) +from mjlab.sim import MujocoCfg, SimulationCfg +from mjlab.tasks.velocity import mdp +from mjlab.terrains import TerrainEntityCfg +from mjlab.terrains.config import ROUGH_TERRAINS_CFG +from mjlab.utils.noise import UniformNoiseCfg as Unoise +from mjlab.viewer import ViewerConfig + +from mjlab_playground.tasks.common import reset_joints_by_offset +from mjlab_playground.tasks.velocity.mdp import UniformVelocityCommandCfg +from mjlab_playground.tasks.velocity.mdp.curriculums import commands_vel +from mjlab_playground.tasks.velocity.mdp.observations import ( + generated_commands as heading_generated_commands, +) +from mjlab_playground.tasks.velocity.mdp.rewards import track_heading_command + + +def make_velocity_env_cfg() -> ManagerBasedRlEnvCfg: + """Create base velocity tracking task configuration.""" + + ## + # Sensors + ## + + terrain_scan = RayCastSensorCfg( + name="terrain_scan", + frame=ObjRef(type="body", name="", entity="robot"), # Set per-robot. + ray_alignment="yaw", + pattern=GridPatternCfg(size=(1.6, 1.0), resolution=0.1), + max_distance=5.0, + exclude_parent_body=True, + include_geom_groups=(0,), # Terrain only. + debug_vis=True, + ) + + foot_height_scan = TerrainHeightSensorCfg( + name="foot_height_scan", + frame=(), # Set per-robot: frame and pattern. + ray_alignment="yaw", + max_distance=1.0, + exclude_parent_body=True, + include_geom_groups=(0,), # Terrain only. + debug_vis=True, + viz=TerrainHeightSensorCfg.VizCfg( + show_rays=True, + hit_color=(1.0, 0.0, 1.0, 0.8), # Magenta rays. + hit_sphere_color=(1.0, 0.0, 1.0, 1.0), + ), + ) + + ## + # Observations + ## + + actor_terms = { + "base_lin_vel": ObservationTermCfg( + func=mdp.builtin_sensor, + params={"sensor_name": "robot/imu_lin_vel"}, + noise=Unoise(n_min=-0.5, n_max=0.5), + ), + "base_ang_vel": ObservationTermCfg( + func=mdp.builtin_sensor, + params={"sensor_name": "robot/imu_ang_vel"}, + noise=Unoise(n_min=-0.2, n_max=0.2), + ), + "projected_gravity": ObservationTermCfg( + func=mdp.projected_gravity, + noise=Unoise(n_min=-0.05, n_max=0.05), + ), + "joint_pos": ObservationTermCfg( + func=mdp.joint_pos_rel, + noise=Unoise(n_min=-0.01, n_max=0.01), + ), + "joint_vel": ObservationTermCfg( + func=mdp.joint_vel_rel, + noise=Unoise(n_min=-1.5, n_max=1.5), + ), + "actions": ObservationTermCfg(func=mdp.last_action), + "command": ObservationTermCfg( + func=heading_generated_commands, + params={"command_name": "twist"}, + ), + "height_scan": ObservationTermCfg( + func=envs_mdp.height_scan, + params={"sensor_name": "terrain_scan"}, + noise=Unoise(n_min=-0.1, n_max=0.1), + scale=1 / terrain_scan.max_distance, + ), + } + + critic_terms = { + **actor_terms, + "height_scan": ObservationTermCfg( + func=envs_mdp.height_scan, + params={"sensor_name": "terrain_scan"}, + scale=1 / terrain_scan.max_distance, + ), + "foot_height": ObservationTermCfg( + func=mdp.foot_height, + params={"sensor_name": "foot_height_scan"}, + ), + "foot_air_time": ObservationTermCfg( + func=mdp.foot_air_time, + params={"sensor_name": "feet_ground_contact"}, + ), + "foot_contact": ObservationTermCfg( + func=mdp.foot_contact, + params={"sensor_name": "feet_ground_contact"}, + ), + "foot_contact_forces": ObservationTermCfg( + func=mdp.foot_contact_forces, + params={"sensor_name": "feet_ground_contact"}, + ), + } + + observations = { + "actor": ObservationGroupCfg( + terms=actor_terms, + concatenate_terms=True, + enable_corruption=True, + ), + "critic": ObservationGroupCfg( + terms=critic_terms, + concatenate_terms=True, + enable_corruption=False, + ), + } + + ## + # Metrics + ## + + metrics = { + "mean_action_acc": MetricsTermCfg( + func=mdp.mean_action_acc, + ), + } + + ## + # Actions + ## + + actions: dict[str, ActionTermCfg] = { + "joint_pos": JointPositionActionCfg( + entity_name="robot", + actuator_names=(".*",), + scale=0.5, # Override per-robot. + use_default_offset=True, + ) + } + + ## + # Commands + ## + + commands: dict[str, CommandTermCfg] = { + "twist": UniformVelocityCommandCfg( + entity_name="robot", + resampling_time_range=(3.0, 8.0), + rel_standing_envs=0.1, + rel_lateral_envs=0.2, + rel_heading_envs=1.0, + rel_forward_envs=0.0, + heading_command=True, + heading_control_stiffness=0.5, + debug_vis=True, + ranges=UniformVelocityCommandCfg.Ranges( + lin_vel_x=(-1.0, 1.0), + lin_vel_y=(-0.3, 0.3), + ang_vel_z=(-0.5, 0.5), + heading=(-math.pi, math.pi), + ), + ) + } + + ## + # Events + ## + + events = { + "reset_base": EventTermCfg( + func=mdp.reset_root_state_uniform, + mode="reset", + params={ + "pose_range": { + "x": (-0.5, 0.5), + "y": (-0.5, 0.5), + "z": (0.01, 0.05), + "yaw": (-3.14, 3.14), + }, + "velocity_range": {}, + }, + ), + "reset_robot_joints": EventTermCfg( + func=reset_joints_by_offset, + mode="reset", + params={ + "position_range": (0.0, 0.0), + "velocity_range": (0.0, 0.0), + "asset_cfg": SceneEntityCfg("robot", joint_names=(".*",)), + }, + ), + "push_robot": EventTermCfg( + func=mdp.push_by_setting_velocity, + mode="interval", + interval_range_s=(1.0, 3.0), + params={ + "velocity_range": { + "x": (-0.5, 0.5), + "y": (-0.5, 0.5), + "z": (-0.4, 0.4), + "roll": (-0.52, 0.52), + "pitch": (-0.52, 0.52), + "yaw": (-0.78, 0.78), + }, + }, + ), + "foot_friction": EventTermCfg( + mode="startup", + func=dr.geom_friction, + params={ + "asset_cfg": SceneEntityCfg("robot", geom_names=()), # Set per-robot. + "operation": "abs", + "ranges": (0.3, 1.2), + "shared_random": True, # All foot geoms share the same friction. + }, + ), + "encoder_bias": EventTermCfg( + mode="startup", + func=dr.encoder_bias, + params={ + "asset_cfg": SceneEntityCfg("robot"), + "bias_range": (-0.015, 0.015), + }, + ), + "base_com": EventTermCfg( + mode="startup", + func=dr.body_com_offset, + params={ + "asset_cfg": SceneEntityCfg("robot", body_names=()), # Set per-robot. + "operation": "add", + "ranges": { + 0: (-0.025, 0.025), + 1: (-0.025, 0.025), + 2: (-0.03, 0.03), + }, + }, + ), + } + + ## + # Rewards + ## + + rewards = { + "track_linear_velocity": RewardTermCfg( + func=mdp.track_linear_velocity, + weight=2.0, + params={"command_name": "twist", "std": math.sqrt(0.25)}, + ), + "track_heading": RewardTermCfg( + func=track_heading_command, + weight=2.0, + params={"command_name": "twist", "std": math.sqrt(0.5)}, + ), + "upright": RewardTermCfg( + func=mdp.upright, + weight=1.0, + params={ + "std": math.sqrt(0.2), + "asset_cfg": SceneEntityCfg("robot", body_names=()), # Set per-robot. + }, + ), + "pose": RewardTermCfg( + func=mdp.variable_posture, + weight=1.0, + params={ + "asset_cfg": SceneEntityCfg("robot", joint_names=(".*",)), + "command_name": "twist", + "std_standing": {}, # Set per-robot. + "std_walking": {}, # Set per-robot. + "std_running": {}, # Set per-robot. + "walking_threshold": 0.05, + "running_threshold": 1.5, + }, + ), + "body_ang_vel": RewardTermCfg( + func=mdp.body_angular_velocity_penalty, + weight=0.0, # Override per-robot + params={"asset_cfg": SceneEntityCfg("robot", body_names=())}, # Set per-robot. + ), + "angular_momentum": RewardTermCfg( + func=mdp.angular_momentum_penalty, + weight=0.0, # Override per-robot + params={"sensor_name": "robot/root_angmom"}, + ), + "dof_pos_limits": RewardTermCfg(func=mdp.joint_pos_limits, weight=-1.0), + "action_rate_l2": RewardTermCfg(func=mdp.action_rate_l2, weight=-0.1), + "air_time": RewardTermCfg( + func=mdp.feet_air_time, + weight=0.0, # Override per-robot. + params={ + "sensor_name": "feet_ground_contact", + "threshold_min": 0.05, + "threshold_max": 0.5, + "command_name": "twist", + "command_threshold": 0.5, + }, + ), + "foot_clearance": RewardTermCfg( + func=mdp.feet_clearance, + weight=-2.0, + params={ + "target_height": 0.1, + "height_sensor_name": "foot_height_scan", + "command_name": "twist", + "command_threshold": 0.05, + "asset_cfg": SceneEntityCfg("robot", site_names=()), # Set per-robot. + }, + ), + "foot_swing_height": RewardTermCfg( + func=mdp.feet_swing_height, + weight=-0.25, + params={ + "sensor_name": "feet_ground_contact", + "height_sensor_name": "foot_height_scan", + "target_height": 0.1, + "command_name": "twist", + "command_threshold": 0.05, + }, + ), + "foot_slip": RewardTermCfg( + func=mdp.feet_slip, + weight=-0.1, + params={ + "sensor_name": "feet_ground_contact", + "command_name": "twist", + "command_threshold": 0.05, + "asset_cfg": SceneEntityCfg("robot", site_names=()), # Set per-robot. + }, + ), + "soft_landing": RewardTermCfg( + func=mdp.soft_landing, + weight=-1e-5, + params={ + "sensor_name": "feet_ground_contact", + "command_name": "twist", + "command_threshold": 0.05, + }, + ), + } + + ## + # Terminations + ## + + terminations = { + "time_out": TerminationTermCfg(func=mdp.time_out, time_out=True), + "fell_over": TerminationTermCfg( + func=mdp.bad_orientation, + params={"limit_angle": math.radians(70.0)}, + ), + "out_of_terrain_bounds": TerminationTermCfg( + func=mdp.out_of_terrain_bounds, + time_out=True, + ), + } + + ## + # Curriculum + ## + + curriculum = { + "terrain_levels": CurriculumTermCfg( + func=mdp.terrain_levels_vel, + params={"command_name": "twist"}, + ), + "command_vel": CurriculumTermCfg( + func=commands_vel, + params={ + "command_name": "twist", + "velocity_stages": [ + { + "step": 0, + "lin_vel_x": (-0.5, 1.5), + "lin_vel_y": (-0.3, 0.3), + "heading": (-0.5, 0.5), + }, + { + "step": 2500 * 24, + "lin_vel_x": (-1.0, 2.5), + "lin_vel_y": (-0.6, 0.6), + "heading": (-0.7, 0.7), + }, + { + "step": 5000 * 24, + "lin_vel_x": (0.0, 3.0), + "lin_vel_y": (-1.0, 1.0), + "heading": (-math.pi * 0.5, math.pi * 0.5), + }, + ], + }, + ), + } + + ## + # Assemble and return + ## + + return ManagerBasedRlEnvCfg( + scene=SceneCfg( + terrain=TerrainEntityCfg( + terrain_type="generator", + terrain_generator=replace(ROUGH_TERRAINS_CFG), + max_init_terrain_level=5, + ), + sensors=(terrain_scan, foot_height_scan), + num_envs=1, + extent=2.0, + ), + observations=observations, + actions=actions, + commands=commands, + events=events, + rewards=rewards, + terminations=terminations, + curriculum=curriculum, + metrics=metrics, + viewer=ViewerConfig( + origin_type=ViewerConfig.OriginType.ASSET_BODY, + entity_name="robot", + body_name="", # Set per-robot. + distance=3.0, + elevation=-5.0, + azimuth=90.0, + ), + sim=SimulationCfg( + nconmax=35, + njmax=1500, + mujoco=MujocoCfg( + timestep=0.005, + iterations=10, + ls_iterations=20, + ), + ), + decimation=4, + episode_length_s=20.0, + ) diff --git a/tests/test_getup.py b/tests/test_getup.py index faa3d40..334b2f2 100644 --- a/tests/test_getup.py +++ b/tests/test_getup.py @@ -8,8 +8,9 @@ from mjlab.envs import ManagerBasedRlEnv from mjlab.managers.scene_entity_config import SceneEntityCfg from mjlab.scene import Scene -from mjlab_playground.getup.config.go1.env_cfgs import unitree_go1_getup_env_cfg -from mjlab_playground.getup.config.t1.env_cfgs import booster_t1_getup_env_cfg +from mjlab_playground.tasks.getup.config.go1.env_cfgs import unitree_go1_getup_env_cfg +from mjlab_playground.tasks.getup.config.k1.env_cfgs import booster_k1_getup_env_cfg +from mjlab_playground.tasks.getup.config.t1.env_cfgs import booster_t1_getup_env_cfg _NUM_ENVS = 5 @@ -65,6 +66,14 @@ def go1_model() -> mujoco.MjModel: return scene.compile() +@pytest.fixture(scope="module") +def k1_model() -> mujoco.MjModel: + cfg = booster_k1_getup_env_cfg() + cfg.scene.num_envs = 1 + scene = Scene(cfg.scene, "cpu") + return scene.compile() + + @pytest.fixture(scope="module") def t1_env() -> ManagerBasedRlEnv: cfg = booster_t1_getup_env_cfg() @@ -83,6 +92,15 @@ def go1_env() -> ManagerBasedRlEnv: return env +@pytest.fixture(scope="module") +def k1_env() -> ManagerBasedRlEnv: + cfg = booster_k1_getup_env_cfg() + cfg.scene.num_envs = _NUM_ENVS + env = ManagerBasedRlEnv(cfg, device="cpu") + env.reset(seed=0) + return env + + # Collision priority. @@ -100,6 +118,10 @@ def test_go1_collision_priority(go1_model: mujoco.MjModel) -> None: assert priority == 1, f"Go1 geom {name!r} has priority={priority}, expected 1" +def test_k1_actuator_count(k1_model: mujoco.MjModel) -> None: + assert k1_model.nu == 22 + + # Friction domain randomization. @@ -127,3 +149,21 @@ def test_go1_friction_dr(go1_env: ManagerBasedRlEnv) -> None: _assert_varied_across_worlds( foot_friction[:, :, 2], "Go1 foot roll friction (axis 2)" ) + + +def test_k1_getup_smoke_and_obs_shapes(k1_env: ManagerBasedRlEnv) -> None: + obs, _ = k1_env.reset(seed=0) + + assert obs["actor"].shape[-1] == 72 + assert obs["critic"].shape[-1] == 75 + assert torch.isfinite(obs["actor"]).all() + assert torch.isfinite(obs["critic"]).all() + assert k1_env.action_manager.action.shape[-1] == 22 + + action = torch.zeros_like(k1_env.action_manager.action) + obs, reward, terminated, truncated, _ = k1_env.step(action) + assert torch.isfinite(obs["actor"]).all() + assert torch.isfinite(obs["critic"]).all() + assert torch.isfinite(reward).all() + assert terminated.shape[0] == _NUM_ENVS + assert truncated.shape[0] == _NUM_ENVS diff --git a/tests/test_target_location_amp_t1.py b/tests/test_target_location_amp_t1.py new file mode 100644 index 0000000..77c9e8c --- /dev/null +++ b/tests/test_target_location_amp_t1.py @@ -0,0 +1,359 @@ +"""Tests for T1 target-location AMP task variants.""" + +from __future__ import annotations + +import math +import os +from pathlib import Path +from types import SimpleNamespace + +import mjlab_playground # noqa: F401 +import mujoco +import pytest +import torch +from mjlab.tasks.registry import list_tasks, load_env_cfg, load_rl_cfg +from mjlab_playground.tasks.target_location_amp.mdp.commands import GoalPoseCommand +from mjlab_playground.tasks.target_location_amp.mdp.state import ( + update_goal_pose_success_hold_state, +) + +COMPETITION_FOOT_TASK_ID = "Mjlab-TargetLocationAmp-CompetitionFoot-Flat-Booster-T1" +COMPETITION_COLLISION_TASK_ID = ( + "Mjlab-TargetLocationAmp-CompetitionCollision-Flat-Booster-T1" +) +COMPETITION_T1_XML = Path(os.getenv("MJLAB_COMPETITION_T1_XML", "")) + + +def test_t1_target_location_amp_competition_foot_task_registered() -> None: + assert COMPETITION_FOOT_TASK_ID in list_tasks() + assert COMPETITION_COLLISION_TASK_ID in list_tasks() + + +def test_t1_target_location_amp_competition_foot_uses_box_contacts() -> None: + cfg = load_env_cfg(COMPETITION_FOOT_TASK_ID) + play_cfg = load_env_cfg(COMPETITION_FOOT_TASK_ID, play=True) + rl_cfg = load_rl_cfg(COMPETITION_FOOT_TASK_ID) + + robot_cfg = cfg.scene.entities["robot"] + spec = robot_cfg.spec_fn() + foot_geoms = {geom.name: geom for geom in spec.geoms if "foot" in geom.name} + + assert "left_foot" in foot_geoms + assert "right_foot" in foot_geoms + assert foot_geoms["left_foot"].type == mujoco.mjtGeom.mjGEOM_BOX + assert foot_geoms["right_foot"].type == mujoco.mjtGeom.mjGEOM_BOX + assert foot_geoms["left_foot"].condim == 3 + assert foot_geoms["right_foot"].condim == 3 + assert tuple(foot_geoms["left_foot"].friction) == (1.0, 0.01, 0.005) + assert tuple(foot_geoms["right_foot"].friction) == (1.0, 0.01, 0.005) + assert tuple(foot_geoms["left_foot"].solref) == (0.02, 1.0) + assert tuple(foot_geoms["right_foot"].solref) == (0.02, 1.0) + assert tuple(foot_geoms["left_foot"].solimp) == (0.015, 1.0, 0.015, 0.5, 2.0) + assert tuple(foot_geoms["right_foot"].solimp) == (0.015, 1.0, 0.015, 0.5, 2.0) + assert not any(name.endswith("_collision") for name in foot_geoms) + + foot_friction_cfg = cfg.events["foot_friction"].params["asset_cfg"] + assert foot_friction_cfg.geom_names == r"^(left|right)_foot$" + assert "foot_friction" not in play_cfg.events + assert tuple(cfg.observations["actor"].terms) == ( + "base_ang_vel", + "projected_gravity", + "joint_pos", + "joint_vel", + "actions", + "goal_local_pos", + "heading_to_target", + "final_yaw_error", + "goal_distance", + ) + assert cfg.actions["joint_pos"].clip is None + assert rl_cfg.clip_actions is None + assert rl_cfg.experiment_name == "t1_target_location_amp_competition_foot" + + +def test_t1_target_location_amp_competition_variants_use_server_physics() -> None: + for task_id in (COMPETITION_FOOT_TASK_ID, COMPETITION_COLLISION_TASK_ID): + for play in (False, True): + cfg = load_env_cfg(task_id, play=play) + mujoco_cfg = cfg.sim.mujoco + + assert mujoco_cfg.integrator == "euler" + assert mujoco_cfg.cone == "pyramidal" + assert mujoco_cfg.impratio == 1.0 + assert mujoco_cfg.iterations == 100 + assert mujoco_cfg.ls_iterations == 50 + assert mujoco_cfg.ccd_iterations == 35 + + +def _object_names( + model: mujoco.MjModel, objtype: mujoco.mjtObj, count: int +) -> list[str]: + return [mujoco.mj_id2name(model, objtype, idx) or "" for idx in range(count)] + + +def _exclude_body_pairs(model: mujoco.MjModel) -> set[frozenset[str]]: + pairs: set[frozenset[str]] = set() + for signature in model.exclude_signature: + body_1 = int(signature) >> 16 + body_2 = int(signature) & 0xFFFF + pairs.add( + frozenset( + ( + mujoco.mj_id2name(model, mujoco.mjtObj.mjOBJ_BODY, body_1) or "", + mujoco.mj_id2name(model, mujoco.mjtObj.mjOBJ_BODY, body_2) or "", + ) + ) + ) + return pairs + + +@pytest.mark.skipif( + not COMPETITION_T1_XML.is_file(), + reason="set MJLAB_COMPETITION_T1_XML to run simulator parity checks", +) +def test_t1_target_location_amp_competition_collision_matches_competition_contacts() -> ( + None +): + cfg = load_env_cfg(COMPETITION_COLLISION_TASK_ID) + play_cfg = load_env_cfg(COMPETITION_COLLISION_TASK_ID, play=True) + rl_cfg = load_rl_cfg(COMPETITION_COLLISION_TASK_ID) + + robot_cfg = cfg.scene.entities["robot"] + train_model = robot_cfg.spec_fn().compile() + competition_model = mujoco.MjModel.from_xml_path(str(COMPETITION_T1_XML)) + + train_geoms = _object_names(train_model, mujoco.mjtObj.mjOBJ_GEOM, train_model.ngeom) + competition_geoms = _object_names( + competition_model, mujoco.mjtObj.mjOBJ_GEOM, competition_model.ngeom + ) + train_collision_geoms = { + name + for idx, name in enumerate(train_geoms) + if train_model.geom_contype[idx] != 0 or train_model.geom_conaffinity[idx] != 0 + } + competition_collision_geoms = { + name + for idx, name in enumerate(competition_geoms) + if ( + competition_model.geom_contype[idx] != 0 + or competition_model.geom_conaffinity[idx] != 0 + ) + } + assert train_collision_geoms == competition_collision_geoms + assert not any(name.endswith("_collision") for name in train_collision_geoms) + assert _exclude_body_pairs(train_model) == _exclude_body_pairs(competition_model) + + for geom_name in sorted(competition_collision_geoms): + train_idx = train_geoms.index(geom_name) + competition_idx = competition_geoms.index(geom_name) + assert ( + train_model.geom_type[train_idx] == competition_model.geom_type[competition_idx] + ) + assert ( + train_model.geom_condim[train_idx] + == competition_model.geom_condim[competition_idx] + ) + assert ( + train_model.geom_contype[train_idx] + == competition_model.geom_contype[competition_idx] + ) + assert ( + train_model.geom_conaffinity[train_idx] + == competition_model.geom_conaffinity[competition_idx] + ) + assert tuple(train_model.geom_size[train_idx]) == tuple( + competition_model.geom_size[competition_idx] + ) + assert tuple(train_model.geom_pos[train_idx]) == tuple( + competition_model.geom_pos[competition_idx] + ) + assert tuple(train_model.geom_friction[train_idx]) == tuple( + competition_model.geom_friction[competition_idx] + ) + assert tuple(train_model.geom_solref[train_idx]) == tuple( + competition_model.geom_solref[competition_idx] + ) + assert tuple(train_model.geom_solimp[train_idx]) == tuple( + competition_model.geom_solimp[competition_idx] + ) + + train_bodies = _object_names(train_model, mujoco.mjtObj.mjOBJ_BODY, train_model.nbody) + competition_bodies = _object_names( + competition_model, mujoco.mjtObj.mjOBJ_BODY, competition_model.nbody + ) + for body_name in ("left_foot_link", "right_foot_link"): + train_idx = train_bodies.index(body_name) + competition_idx = competition_bodies.index(body_name) + assert tuple(train_model.body_inertia[train_idx]) == tuple( + competition_model.body_inertia[competition_idx] + ) + assert tuple(train_model.body_iquat[train_idx]) == tuple( + competition_model.body_iquat[competition_idx] + ) + + assert all( + actuator.viscous_damping == 0.01 for actuator in robot_cfg.articulation.actuators + ) + + foot_friction_cfg = cfg.events["foot_friction"].params["asset_cfg"] + assert foot_friction_cfg.geom_names == r"^(left|right)_foot$" + assert "foot_friction" not in play_cfg.events + assert tuple(cfg.observations["actor"].terms) == ( + "base_ang_vel", + "projected_gravity", + "joint_pos", + "joint_vel", + "actions", + "goal_local_pos", + "heading_to_target", + "final_yaw_error", + "goal_distance", + ) + assert cfg.actions["joint_pos"].clip is None + assert rl_cfg.clip_actions is None + assert rl_cfg.experiment_name == "t1_target_location_amp_competition_collision" + + +def test_t1_target_location_amp_competition_collision_play_uses_nearfield_stage() -> ( + None +): + play_cfg = load_env_cfg(COMPETITION_COLLISION_TASK_ID, play=True) + goal_pose_cmd = play_cfg.commands["goal_pose"] + + assert goal_pose_cmd.ranges.target_radius == (0.0, 6.0) + assert goal_pose_cmd.ranges.target_yaw == (-math.pi, math.pi) + assert goal_pose_cmd.ranges.target_angle == (-math.pi, math.pi) + assert goal_pose_cmd.ranges.target_radius_buckets is None + assert goal_pose_cmd.ranges.target_command_buckets is not None + assert tuple( + (bucket.radius_range, bucket.yaw_range, bucket.angle_range, bucket.probability) + for bucket in goal_pose_cmd.ranges.target_command_buckets + ) == ( + ((0.0, 0.0), (0.0, 0.0), (0.0, 0.0), 0.1), + ((0.0, 0.0), (-math.pi, math.pi), (0.0, 0.0), 0.1), + ((0.0, 2.0), None, None, 0.3), + ((5.0, 6.0), None, None, 0.4), + ((2.0, 4.0), None, None, 0.1), + ) + assert goal_pose_cmd.resampling_time_range == (1.0, 4.0) + assert goal_pose_cmd.resample_on_near_goal is False + assert goal_pose_cmd.resample_on_goal_success is True + assert math.isclose(goal_pose_cmd.resample_position_tolerance, 0.08) + assert math.isclose(goal_pose_cmd.resample_yaw_tolerance, math.radians(6.0)) + + assert play_cfg.rewards["joint_target_limits"].weight == -0.03 + assert play_cfg.rewards["near_goal_stability"].weight == 0.5 + assert math.isclose( + play_cfg.rewards["near_goal_stability"].params["position_tolerance"], + 0.08, + ) + assert math.isclose( + play_cfg.rewards["near_goal_stability"].params["yaw_tolerance"], + math.radians(6.0), + ) + assert play_cfg.rewards["goal_pose_bonus"].weight == 1.0 + assert play_cfg.rewards["goal_position_bonus"].weight == 0.5 + assert math.isclose( + play_cfg.rewards["goal_pose_bonus"].params["position_tolerance"], 0.08 + ) + assert math.isclose( + play_cfg.rewards["goal_pose_bonus"].params["yaw_tolerance"], + math.radians(6.0), + ) + assert play_cfg.rewards["goal_pose_bonus"].params["hold_steps"] == 25 + + +def test_goal_pose_command_can_resample_early_on_success() -> None: + reward_manager = SimpleNamespace( + get_term_cfg=lambda name: SimpleNamespace( + params={ + "position_tolerance": 0.08, + "yaw_tolerance": math.radians(6.0), + "hold_steps": 25, + "entity_name": "robot", + "max_lin_speed": 0.25, + "max_yaw_rate": 0.35, + "min_up_z": 0.80, + } + if name == "goal_pose_bonus" + else None + ) + ) + robot = SimpleNamespace( + data=SimpleNamespace( + root_link_pos_w=torch.tensor([[0.0, 0.0, 0.7]], dtype=torch.float32), + root_link_lin_vel_b=torch.zeros(1, 3, dtype=torch.float32), + root_link_ang_vel_b=torch.zeros(1, 3, dtype=torch.float32), + projected_gravity_b=torch.tensor([[0.0, 0.0, -1.0]], dtype=torch.float32), + ) + ) + env = SimpleNamespace( + num_envs=1, + device="cpu", + reward_manager=reward_manager, + scene={"robot": robot}, + episode_length_buf=torch.tensor([1], dtype=torch.long), + ) + command = GoalPoseCommand.__new__(GoalPoseCommand) + command._env = env + command.cfg = SimpleNamespace( + entity_name="robot", + resample_on_goal_success=True, + resample_on_near_goal=False, + resample_position_tolerance=0.08, + resample_yaw_tolerance=math.radians(6.0), + ) + command.distance_to_goal = torch.tensor([0.02], dtype=torch.float32) + command.final_heading_error = torch.tensor([math.radians(2.0)], dtype=torch.float32) + command._resample_on_next_step = torch.zeros(1, dtype=torch.bool) + + command._queue_goal_success_resample() + assert command._resample_on_next_step.tolist() == [False] + + env._goal_pose_prev_distance = torch.tensor([0.02], dtype=torch.float32) + env._goal_pose_prev_yaw_abs = torch.tensor([math.radians(2.0)], dtype=torch.float32) + env._goal_pose_distance_progress = torch.zeros(1, dtype=torch.float32) + env._goal_pose_yaw_progress = torch.zeros(1, dtype=torch.float32) + env._goal_pose_hold_steps = torch.tensor([24], dtype=torch.long) + env._goal_pose_success_hold_steps = torch.tensor([24], dtype=torch.long) + env._goal_pose_success_hold_last_step = torch.tensor([24], dtype=torch.long) + env._goal_pose_state_last_step = torch.tensor([24], dtype=torch.long) + env._goal_pose_just_resampled = torch.tensor([False], dtype=torch.bool) + env.episode_length_buf = torch.tensor([25], dtype=torch.long) + command._queue_goal_success_resample() + assert command._resample_on_next_step.tolist() == [True] + + +def test_goal_success_hold_requires_consecutive_full_success_steps() -> None: + env = SimpleNamespace( + num_envs=1, + device="cpu", + episode_length_buf=torch.tensor([1], dtype=torch.long), + ) + + hold = update_goal_pose_success_hold_state( + env, + success_now=torch.tensor([True]), + ) + assert hold.tolist() == [1] + + env.episode_length_buf[:] = 2 + hold = update_goal_pose_success_hold_state( + env, + success_now=torch.tensor([False]), + ) + assert hold.tolist() == [0] + + env.episode_length_buf[:] = 3 + hold = update_goal_pose_success_hold_state( + env, + success_now=torch.tensor([True]), + ) + assert hold.tolist() == [1] + + # Multiple reward/command consumers in one controller step must not double count. + hold = update_goal_pose_success_hold_state( + env, + success_now=torch.tensor([True]), + ) + assert hold.tolist() == [1] diff --git a/tests/test_tracking.py b/tests/test_tracking.py new file mode 100644 index 0000000..1c7ca15 --- /dev/null +++ b/tests/test_tracking.py @@ -0,0 +1,42 @@ +"""Tests for Booster T1 tracking task registration/configuration.""" + +from __future__ import annotations + +import mjlab_playground # noqa: F401 +from mjlab.tasks.registry import list_tasks, load_env_cfg, load_rl_cfg +from mjlab_playground.tasks.tracking.config.t1.env_cfgs import ( + booster_t1_flat_tracking_env_cfg, +) + + +def test_tracking_task_registered() -> None: + assert "Mjlab-Tracking-Flat-Booster-T1" in list_tasks() + + +def test_tracking_task_configs_load() -> None: + env_cfg = load_env_cfg("Mjlab-Tracking-Flat-Booster-T1") + rl_cfg = load_rl_cfg("Mjlab-Tracking-Flat-Booster-T1") + assert "motion" in env_cfg.commands + assert env_cfg.commands["motion"].anchor_body_name == "Trunk" + assert env_cfg.commands["motion"].body_names[0] == "Trunk" + assert "base_ang_vel_exceed" in env_cfg.terminations + assert rl_cfg.experiment_name == "t1_tracking" + + +def test_tracking_play_cfg_overrides() -> None: + cfg = booster_t1_flat_tracking_env_cfg(play=True) + motion = cfg.commands["motion"] + assert cfg.episode_length_s == int(1e9) + assert "push_robot" not in cfg.events + assert motion.pose_range == {} + assert motion.velocity_range == {} + assert motion.sampling_mode == "start" + + +def test_tracking_actor_terms_without_state_estimation() -> None: + cfg = booster_t1_flat_tracking_env_cfg(has_state_estimation=False) + actor_terms = cfg.observations["actor"].terms + assert "motion_anchor_pos_b" not in actor_terms + assert "base_lin_vel" not in actor_terms + assert "motion_anchor_ori_b" in actor_terms + assert "base_ang_vel" in actor_terms diff --git a/tests/test_velocity.py b/tests/test_velocity.py new file mode 100644 index 0000000..27983d6 --- /dev/null +++ b/tests/test_velocity.py @@ -0,0 +1,160 @@ +"""Tests for the velocity task configurations.""" + +from __future__ import annotations + +import torch +from mjlab.tasks.registry import list_tasks +from mjlab_playground.tasks.velocity import mdp as velocity_local_mdp +from mjlab_playground.tasks.velocity.config.t1.env_cfgs import booster_t1_flat_env_cfg +from mjlab_playground.tasks.velocity.mdp.curriculums import commands_vel +from mjlab_playground.tasks.velocity.mdp.observations import generated_commands +from mjlab_playground.tasks.velocity.mdp.rewards import _is_forward_command_active + + +def test_velocity_task_registration() -> None: + import mjlab_playground # noqa: F401 + + tasks = list_tasks() + assert "Mjlab-Velocity-Flat-Booster-T1" in tasks + assert "Mjlab-Velocity-Rough-Booster-T1" not in tasks + assert "Mjlab-VelocityYaw-Flat-Booster-T1" not in tasks + assert "Mjlab-VelocityRaw-Flat-Booster-T1" not in tasks + + +def test_t1_velocity_play_cfg_resamples_visible_commands() -> None: + cfg = booster_t1_flat_env_cfg(play=True) + twist = cfg.commands["twist"] + assert twist.resampling_time_range == (1.0, 1.5) + assert twist.rel_standing_envs == 0.0 + assert twist.rel_lateral_envs == 0.0 + assert twist.ranges.lin_vel_x == (2.0, 3.0) + assert twist.ranges.lin_vel_y == (0.0, 0.0) + assert twist.ranges.heading == (-0.5, 0.5) + + +def test_t1_velocity_uses_mixed_stand_x_heading_and_lateral_commands() -> None: + cfg = booster_t1_flat_env_cfg() + twist = cfg.commands["twist"] + assert twist.heading_command is True + assert twist.rel_lateral_envs == 0.2 + assert twist.rel_heading_envs == 1.0 + assert twist.rel_forward_envs == 0.0 + assert twist.ranges.lin_vel_y == (-0.3, 0.3) + assert twist.ranges.ang_vel_z == (-0.5, 0.5) + assert twist.ranges.heading == (-torch.pi, torch.pi) + + assert ( + cfg.observations["actor"].terms["command"].func + is velocity_local_mdp.generated_commands + ) + assert cfg.rewards["track_heading"].func is velocity_local_mdp.track_heading_command + + assert set(cfg.curriculum.keys()) == {"command_vel"} + stages = cfg.curriculum["command_vel"].params["velocity_stages"] + assert stages[0]["lin_vel_x"] == (-0.5, 1.5) + assert stages[0]["lin_vel_y"] == (-0.3, 0.3) + assert stages[0]["heading"] == (-0.5, 0.5) + assert "ang_vel_z" not in stages[0] + assert stages[1]["lin_vel_x"] == (-1.0, 2.5) + assert stages[1]["lin_vel_y"] == (-0.6, 0.6) + assert stages[1]["heading"] == (-0.7, 0.7) + assert "ang_vel_z" not in stages[1] + assert stages[2]["lin_vel_x"] == (0.0, 3.0) + assert stages[2]["lin_vel_y"] == (-1.0, 1.0) + assert stages[2]["heading"] == (-torch.pi * 0.5, torch.pi * 0.5) + assert "ang_vel_z" not in stages[2] + + +def test_generated_commands_returns_x_y_plus_heading_encoding() -> None: + class FakeCommandTerm: + def __init__(self) -> None: + self.heading_target = torch.tensor( + [0.0, torch.pi / 2, torch.pi], dtype=torch.float32 + ) + self.is_heading_env = torch.tensor([False, True, False]) + self.is_standing_env = torch.tensor([True, False, False]) + self.robot = type( + "Robot", + (), + { + "data": type( + "Data", + (), + {"heading_w": torch.tensor([1.2, 0.0, 0.4], dtype=torch.float32)}, + )() + }, + )() + + class FakeCommandManager: + def get_command(self, name: str) -> torch.Tensor: + assert name == "twist" + return torch.tensor( + [ + [0.0, 0.0, 0.0], + [1.1, 0.0, -0.1], + [0.0, -0.6, 0.0], + ], + dtype=torch.float32, + ) + + def get_term(self, name: str) -> FakeCommandTerm: + assert name == "twist" + return FakeCommandTerm() + + env = type("Env", (), {"command_manager": FakeCommandManager(), "scene": {}})() + obs = generated_commands(env, command_name="twist") + expected = torch.tensor( + [ + [0.0, 0.0, 0.0, 1.0], + [1.1, 0.0, 1.0, 0.0], + [0.0, -0.6, 0.0, 1.0], + ], + dtype=torch.float32, + ) + assert torch.allclose(obs, expected, atol=1e-5) + + +def test_commands_vel_logs_heading_ranges_only() -> None: + cfg = booster_t1_flat_env_cfg() + command_term = type("CommandTerm", (), {"cfg": cfg.commands["twist"]})() + env = type( + "Env", + (), + { + "common_step_counter": 120000, + "command_manager": type( + "CommandManager", (), {"get_term": lambda self, name: command_term} + )(), + }, + )() + metrics = commands_vel( + env, + env_ids=torch.tensor([], dtype=torch.long), + command_name="twist", + velocity_stages=cfg.curriculum["command_vel"].params["velocity_stages"], + ) + assert set(metrics.keys()) == { + "lin_vel_x_min", + "lin_vel_x_max", + "lin_vel_y_min", + "lin_vel_y_max", + "heading_min", + "heading_max", + } + + +def test_forward_command_gate_ignores_y_and_heading() -> None: + command = torch.tensor( + [ + [0.0, 0.8, 0.0], + [0.0, 0.0, 0.7], + [0.2, 0.8, 0.7], + [-0.2, 0.0, 0.0], + ], + dtype=torch.float32, + ) + + active = _is_forward_command_active(command, command_threshold=0.1) + + expected = torch.tensor([0.0, 0.0, 1.0, 1.0], dtype=torch.float32) + assert torch.equal(active, expected) diff --git a/third_party_licenses/RCSSServerMJ-MIT.txt b/third_party_licenses/RCSSServerMJ-MIT.txt new file mode 100644 index 0000000..d91f400 --- /dev/null +++ b/third_party_licenses/RCSSServerMJ-MIT.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 The RCSSServerMJ authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/uv.lock b/uv.lock index c807db1..89c2558 100644 --- a/uv.lock +++ b/uv.lock @@ -1,62 +1,36 @@ version = 1 revision = 3 -requires-python = ">=3.13" +requires-python = "==3.13.*" resolution-markers = [ - "platform_machine != 's390x' and sys_platform != 'darwin'", - "platform_machine == 's390x' and sys_platform != 'darwin'", - "platform_machine != 's390x' and sys_platform == 'darwin'", - "platform_machine == 's390x' and sys_platform == 'darwin'", + "sys_platform != 'darwin'", + "sys_platform == 'darwin'", ] -[manifest] -overrides = [{ name = "mujoco", specifier = ">=3.6.0.dev0", index = "https://py.mujoco.org/" }] - [[package]] name = "absl-py" -version = "2.4.0" +version = "2.5.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/64/c7/8de93764ad66968d19329a7e0c147a2bb3c7054c554d4a119111b8f9440f/absl_py-2.4.0.tar.gz", hash = "sha256:8c6af82722b35cf71e0f4d1d47dcaebfff286e27110a99fc359349b247dfb5d4", size = 116543, upload-time = "2026-01-28T10:17:05.322Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/4f/d79676ab82f2e42fc3611618139f13a9c4c31d0cff4b486982047679a802/absl_py-2.5.0.tar.gz", hash = "sha256:0c996f25c0490700fadabe6351630f6111534fa0ae252cc6d2014ea3b141135f", size = 118119, upload-time = "2026-07-03T10:57:48.157Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/18/a6/907a406bb7d359e6a63f99c313846d9eec4f7e6f7437809e03aa00fa3074/absl_py-2.4.0-py3-none-any.whl", hash = "sha256:88476fd881ca8aab94ffa78b7b6c632a782ab3ba1cd19c9bd423abc4fb4cd28d", size = 135750, upload-time = "2026-01-28T10:17:04.19Z" }, + { url = "https://files.pythonhosted.org/packages/58/0a/a10b45aab35b175aded078a462dc8d0c698f5b13946e7cb0869097b78bb6/absl_py-2.5.0-py3-none-any.whl", hash = "sha256:0f17b89f2a4eaaedc4f28c622998aa690564b3012a396a4ffad0821007fe03ba", size = 137410, upload-time = "2026-07-03T10:57:46.735Z" }, ] [[package]] name = "annotated-types" -version = "0.7.0" +version = "0.8.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" } +sdist = { url = "https://files.pythonhosted.org/packages/5f/56/a8120250d128bed162cd73c76d45f6ef9991f3e068f62a8ee060afa3104a/annotated_types-0.8.0.tar.gz", hash = "sha256:13b2beaad985e05e2d6407ee4c4f35590b11f8d693a258a561055cac8f64cab7", size = 15893, upload-time = "2026-07-23T20:16:13.995Z" } 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" }, -] -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" }, + { url = "https://files.pythonhosted.org/packages/99/91/8acff4f5e50511b911bbccb72b8628a49c68ce14148cd9f6431094859a90/annotated_types-0.8.0-py3-none-any.whl", hash = "sha256:f072f4d804ea359e4eaf198b1af7a8b0943881a87f31bb764f8bf219bb9419e0", size = 13427, upload-time = "2026-07-23T20:16:12.938Z" }, ] [[package]] name = "asttokens" -version = "3.0.1" +version = "3.0.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/be/a5/8e3f9b6771b0b408517c82d97aed8f2036509bc247d46114925e32fe33f0/asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7", size = 62308, upload-time = "2025-11-15T16:43:48.578Z" } +sdist = { url = "https://files.pythonhosted.org/packages/25/1e/faf0f247f6f881b98fc4d6d07e14085cb89d13665084e6d6ac1dc2c03d0b/asttokens-3.0.2.tar.gz", hash = "sha256:3ecdbd8f2cc195f53ccada3a613538bb5f9ef6f6869129f13e03c30a677b8fe2", size = 63136, upload-time = "2026-07-12T03:31:49.084Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047, upload-time = "2025-11-15T16:43:16.109Z" }, -] - -[[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" }, + { url = "https://files.pythonhosted.org/packages/d4/2b/04b8a15f3a1c77bc79ddf5c73875327f34b4fa75982df2b76e45e402d364/asttokens-3.0.2-py3-none-any.whl", hash = "sha256:9da13157f5b28becde0bd374fc677dcd3c290614264eff096f167c469cd9f933", size = 28702, upload-time = "2026-07-12T03:31:47.542Z" }, ] [[package]] @@ -80,19 +54,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/10/a6/ffb49d4254ed085e62e3e5dd05982b4393e32fe1e49bb1130186617c29cd/bcrypt-5.0.0-cp313-cp313t-win32.whl", hash = "sha256:9d52ed507c2488eddd6a95bccee4e808d3234fa78dd370e24bac65a21212b861", size = 148498, upload-time = "2025-09-25T19:49:24.134Z" }, { url = "https://files.pythonhosted.org/packages/48/a9/259559edc85258b6d5fc5471a62a3299a6aa37a6611a169756bf4689323c/bcrypt-5.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f6984a24db30548fd39a44360532898c33528b74aedf81c26cf29c51ee47057e", size = 145853, upload-time = "2025-09-25T19:49:25.702Z" }, { url = "https://files.pythonhosted.org/packages/2d/df/9714173403c7e8b245acf8e4be8876aac64a209d1b392af457c79e60492e/bcrypt-5.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:9fffdb387abe6aa775af36ef16f55e318dcda4194ddbf82007a6f21da29de8f5", size = 139626, upload-time = "2025-09-25T19:49:26.928Z" }, - { url = "https://files.pythonhosted.org/packages/f8/14/c18006f91816606a4abe294ccc5d1e6f0e42304df5a33710e9e8e95416e1/bcrypt-5.0.0-cp314-cp314t-macosx_10_12_universal2.whl", hash = "sha256:4870a52610537037adb382444fefd3706d96d663ac44cbb2f37e3919dca3d7ef", size = 481862, upload-time = "2025-09-25T19:49:28.365Z" }, - { url = "https://files.pythonhosted.org/packages/67/49/dd074d831f00e589537e07a0725cf0e220d1f0d5d8e85ad5bbff251c45aa/bcrypt-5.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:48f753100931605686f74e27a7b49238122aa761a9aefe9373265b8b7aa43ea4", size = 268544, upload-time = "2025-09-25T19:49:30.39Z" }, - { url = "https://files.pythonhosted.org/packages/f5/91/50ccba088b8c474545b034a1424d05195d9fcbaaf802ab8bfe2be5a4e0d7/bcrypt-5.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f70aadb7a809305226daedf75d90379c397b094755a710d7014b8b117df1ebbf", size = 271787, upload-time = "2025-09-25T19:49:32.144Z" }, - { url = "https://files.pythonhosted.org/packages/aa/e7/d7dba133e02abcda3b52087a7eea8c0d4f64d3e593b4fffc10c31b7061f3/bcrypt-5.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:744d3c6b164caa658adcb72cb8cc9ad9b4b75c7db507ab4bc2480474a51989da", size = 269753, upload-time = "2025-09-25T19:49:33.885Z" }, - { url = "https://files.pythonhosted.org/packages/33/fc/5b145673c4b8d01018307b5c2c1fc87a6f5a436f0ad56607aee389de8ee3/bcrypt-5.0.0-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a28bc05039bdf3289d757f49d616ab3efe8cf40d8e8001ccdd621cd4f98f4fc9", size = 289587, upload-time = "2025-09-25T19:49:35.144Z" }, - { url = "https://files.pythonhosted.org/packages/27/d7/1ff22703ec6d4f90e62f1a5654b8867ef96bafb8e8102c2288333e1a6ca6/bcrypt-5.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:7f277a4b3390ab4bebe597800a90da0edae882c6196d3038a73adf446c4f969f", size = 272178, upload-time = "2025-09-25T19:49:36.793Z" }, - { url = "https://files.pythonhosted.org/packages/c8/88/815b6d558a1e4d40ece04a2f84865b0fef233513bd85fd0e40c294272d62/bcrypt-5.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:79cfa161eda8d2ddf29acad370356b47f02387153b11d46042e93a0a95127493", size = 269295, upload-time = "2025-09-25T19:49:38.164Z" }, - { url = "https://files.pythonhosted.org/packages/51/8c/e0db387c79ab4931fc89827d37608c31cc57b6edc08ccd2386139028dc0d/bcrypt-5.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:a5393eae5722bcef046a990b84dff02b954904c36a194f6cfc817d7dca6c6f0b", size = 271700, upload-time = "2025-09-25T19:49:39.917Z" }, - { url = "https://files.pythonhosted.org/packages/06/83/1570edddd150f572dbe9fc00f6203a89fc7d4226821f67328a85c330f239/bcrypt-5.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7f4c94dec1b5ab5d522750cb059bb9409ea8872d4494fd152b53cca99f1ddd8c", size = 334034, upload-time = "2025-09-25T19:49:41.227Z" }, - { url = "https://files.pythonhosted.org/packages/c9/f2/ea64e51a65e56ae7a8a4ec236c2bfbdd4b23008abd50ac33fbb2d1d15424/bcrypt-5.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0cae4cb350934dfd74c020525eeae0a5f79257e8a201c0c176f4b84fdbf2a4b4", size = 352766, upload-time = "2025-09-25T19:49:43.08Z" }, - { url = "https://files.pythonhosted.org/packages/d7/d4/1a388d21ee66876f27d1a1f41287897d0c0f1712ef97d395d708ba93004c/bcrypt-5.0.0-cp314-cp314t-win32.whl", hash = "sha256:b17366316c654e1ad0306a6858e189fc835eca39f7eb2cafd6aaca8ce0c40a2e", size = 152449, upload-time = "2025-09-25T19:49:44.971Z" }, - { url = "https://files.pythonhosted.org/packages/3f/61/3291c2243ae0229e5bca5d19f4032cecad5dfb05a2557169d3a69dc0ba91/bcrypt-5.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:92864f54fb48b4c718fc92a32825d0e42265a627f956bc0361fe869f1adc3e7d", size = 149310, upload-time = "2025-09-25T19:49:46.162Z" }, - { url = "https://files.pythonhosted.org/packages/3e/89/4b01c52ae0c1a681d4021e5dd3e45b111a8fb47254a274fa9a378d8d834b/bcrypt-5.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dd19cf5184a90c873009244586396a6a884d591a5323f0e8a5922560718d4993", size = 143761, upload-time = "2025-09-25T19:49:47.345Z" }, { url = "https://files.pythonhosted.org/packages/84/29/6237f151fbfe295fe3e074ecc6d44228faa1e842a81f6d34a02937ee1736/bcrypt-5.0.0-cp38-abi3-macosx_10_12_universal2.whl", hash = "sha256:fc746432b951e92b58317af8e0ca746efe93e66555f1b40888865ef5bf56446b", size = 494553, upload-time = "2025-09-25T19:49:49.006Z" }, { url = "https://files.pythonhosted.org/packages/45/b6/4c1205dde5e464ea3bd88e8742e19f899c16fa8916fb8510a851fae985b5/bcrypt-5.0.0-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c2388ca94ffee269b6038d48747f4ce8df0ffbea43f31abfa18ac72f0218effb", size = 275009, upload-time = "2025-09-25T19:49:50.581Z" }, { url = "https://files.pythonhosted.org/packages/3b/71/427945e6ead72ccffe77894b2655b695ccf14ae1866cd977e185d606dd2f/bcrypt-5.0.0-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:560ddb6ec730386e7b3b26b8b4c88197aaed924430e7b74666a586ac997249ef", size = 278029, upload-time = "2025-09-25T19:49:52.533Z" }, @@ -127,125 +88,91 @@ wheels = [ [[package]] name = "certifi" -version = "2026.2.25" +version = "2026.7.22" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/c2/24167ea9858356b47a87a50d39908bfdb72ceeefe0041586e704e5376b3a/certifi-2026.7.22.tar.gz", hash = "sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55", size = 138112, upload-time = "2026-07-22T03:35:12.644Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, + { url = "https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl", hash = "sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775", size = 136983, upload-time = "2026-07-22T03:35:11.276Z" }, ] [[package]] name = "cffi" -version = "2.0.0" +version = "2.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pycparser", marker = "implementation_name != 'PyPy'" }, ] -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/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, - { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, - { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, - { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, - { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, - { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, - { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, - { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, - { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, - { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, - { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, - { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, - { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, - { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, - { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, - { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, - { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, - { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, - { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, - { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, - { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, - { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, - { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, - { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, - { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, - { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, - { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, - { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, - { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, - { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, - { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, - { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, - { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/9e/ef/008a1939e372c06329a3fce4279c02f328488f3526744906eeec3da7ad5f/cffi-2.1.1.tar.gz", hash = "sha256:dd31f52ea1086513bb9df30f8fcee9b8918323ae067a3d5b78bc826a000712be", size = 530807, upload-time = "2026-08-03T21:21:18.939Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/f4/035513d4117049066b4779dc3b7c0c0fdad175fa13731c9f4003f1cd1478/cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:b5bdfd1c873d4e093aabc0ca84c4ca6dbc4f752afb5c86f146d9742580c9da2e", size = 194248, upload-time = "2026-08-03T21:19:59.399Z" }, + { url = "https://files.pythonhosted.org/packages/76/af/2aeb4dbb5fc41a04161ae9ff1518de7cec08e164f44a8ce6a4cf7fd2cd1d/cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:31348097ff5bbe827ccc41795d4dd099d9f0625e7def00ee653c137a490c2a6c", size = 196908, upload-time = "2026-08-03T21:20:00.746Z" }, + { url = "https://files.pythonhosted.org/packages/a7/46/2e5fdde8555706dd98139a910ca11be02809f3f605ce956f655d0214e100/cffi-2.1.1-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:9d2055050ea716bd38b7f7f1579c275386646b4894c155a3e2f3cd62ed41b7c6", size = 184805, upload-time = "2026-08-03T21:20:02.02Z" }, + { url = "https://files.pythonhosted.org/packages/55/41/4c7042f317b9217502988f0873af87e16ad606dc20f84e546e3e6ce9764c/cffi-2.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19ee6127ee34de7d83ce3d371ebc5ed91addbdcc39f9ab15ce4eb35a4e534971", size = 184764, upload-time = "2026-08-03T21:20:03.141Z" }, + { url = "https://files.pythonhosted.org/packages/43/1f/1c3d90d91811c8f86ced9ed637956c54bfe5b79ca98fe976d7f8c8979f6b/cffi-2.1.1-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:6a8dddef476fab96d066d578fc88526767b836ab5ab21754e1d5bf3879c31c7c", size = 214722, upload-time = "2026-08-03T21:20:04.377Z" }, + { url = "https://files.pythonhosted.org/packages/37/6f/3b5ce4c3b2192d250f04908f2bfd91ef34552ec8f7716a5d4abdb8d67bb2/cffi-2.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f16c709686a78c727bbbf059f92b0bf41c6fc60deec706d2dc19f529175a6125", size = 222369, upload-time = "2026-08-03T21:20:05.544Z" }, + { url = "https://files.pythonhosted.org/packages/02/10/4b3c75dde3d9663c9e02ba05c2668b954f671d4bbe346413ca8c696b295a/cffi-2.1.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:fcd22650c908d7b7da162bbfaab594a1227a15d1643a98c68b122ac642fa2264", size = 210175, upload-time = "2026-08-03T21:20:06.75Z" }, + { url = "https://files.pythonhosted.org/packages/df/62/14f74b9543e605d17701dc797b815958b8bb70b7624ce1b832ddad48ed6c/cffi-2.1.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:aa9511c62d14da7aacc9b4bf51f3f697a621e83b2d6919008243c3aad168eea3", size = 208670, upload-time = "2026-08-03T21:20:08.04Z" }, + { url = "https://files.pythonhosted.org/packages/95/95/86342356ff5953b3fb06f7ef7c5bee212d45e770abc7218d451b9148313c/cffi-2.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a931079504ecc49efed7744c476a5c343a92fabf66dec2db95edb1b2fdc770e2", size = 221824, upload-time = "2026-08-03T21:20:09.274Z" }, + { url = "https://files.pythonhosted.org/packages/eb/ff/7b3429ff53aafe931ed8a5fc69f481bbef7ba6de87ddcbb63d08f483f613/cffi-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a2d7755bef5a12ed488f4ef1f1b69ee9191d7396083b755a5d2295f6edb4768b", size = 225148, upload-time = "2026-08-03T21:20:10.7Z" }, + { url = "https://files.pythonhosted.org/packages/34/34/a95870b9221e09cf4f2ce3178b1a210abdfe63a1bd357da940418d7b8d15/cffi-2.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e0bcb7e0f677f543555d2adff3bf19c05f66cdb4796e5ff602442ab2fe3c4ef7", size = 223564, upload-time = "2026-08-03T21:20:12.165Z" }, + { url = "https://files.pythonhosted.org/packages/70/ea/839b50531021a647fb5e929f72cf97bc1ff702b5472166164b5b6e76b851/cffi-2.1.1-cp313-cp313-win32.whl", hash = "sha256:334644fbac4eff73d985a17a91226df55d0f394160c4cfb880e084c8f7161cac", size = 175263, upload-time = "2026-08-03T21:20:13.559Z" }, + { url = "https://files.pythonhosted.org/packages/60/a6/8b149b2c3f2e11aaa1618ef64500b45f50f22c57a977a4dff1aff1f91042/cffi-2.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:1aa5645c30469b09530c4ebca77ebf8f17618293c58f8549cb1a543a50236e7d", size = 185688, upload-time = "2026-08-03T21:20:14.69Z" }, + { url = "https://files.pythonhosted.org/packages/01/9a/11f687cb39d6a3504060d5242f04f48c735afb4d3d533958a20594890cb2/cffi-2.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:63bbfd5ded17c4840ac07cd8f1c21ba9d9708141f840b324f422f41b207e3973", size = 180078, upload-time = "2026-08-03T21:20:15.917Z" }, ] [[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/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" }, - { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" }, - { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" }, - { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" }, - { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" }, - { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" }, - { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" }, - { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" }, - { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" }, - { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" }, - { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" }, - { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" }, - { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" }, - { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" }, - { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" }, - { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" }, - { url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" }, - { url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" }, - { url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" }, - { url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" }, - { url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" }, - { url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" }, - { url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" }, - { url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" }, - { url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" }, - { url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" }, - { url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" }, - { url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" }, - { url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" }, - { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" }, - { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" }, - { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" }, - { url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" }, - { url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" }, - { url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" }, - { url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" }, - { url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" }, - { url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" }, - { url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" }, - { url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" }, - { url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" }, - { url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" }, - { url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" }, - { url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" }, - { url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" }, - { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" }, - { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" }, - { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" }, - { 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" }, +version = "3.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/3f/143b048436775b0f76ac3eec145c019e8173ccc2885c8f20319b996d5e83/charset_normalizer-3.5.1.tar.gz", hash = "sha256:6117b84ea48435e5356dc737f5121485c30920ba43375fa7b434fd753df0eac3", size = 171764, upload-time = "2026-08-15T08:20:44.807Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/61/2cb6ad133dbbb449fa2d37ccae973232f4827e799af258d15e589a3d1e9e/charset_normalizer-3.5.1-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:4f298bdadb8f0b9e5672877f647d1be9373ef5320c9e2f049795e26cad28b6a9", size = 211584, upload-time = "2026-08-15T08:17:33.597Z" }, + { url = "https://files.pythonhosted.org/packages/18/57/a305c968be1ca13f3dd1b32f445877e97addf55d80b65c7cb35fac82b777/charset_normalizer-3.5.1-cp313-cp313-android_24_x86_64.whl", hash = "sha256:88ca277405c2d3b71c4e1c2ee0e7966e807bcba86a69d11e19ba199d18ae4491", size = 223359, upload-time = "2026-08-15T08:17:35.022Z" }, + { url = "https://files.pythonhosted.org/packages/09/0a/d3646670292ce8d8f8cc11ac067d44885e697a5591f57a9221128da5e7b3/charset_normalizer-3.5.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:9362dd90aa7dab48c0054a21187791ccf05473f7dba5d92b8033ae62164675e7", size = 194464, upload-time = "2026-08-15T08:17:36.452Z" }, + { url = "https://files.pythonhosted.org/packages/de/93/d51ec556e01042fed6f993ea859311bc7917b466684182fbbceb6ca24762/charset_normalizer-3.5.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:977cdbd483a9cff38179bea4fd754289a6f2195c7abd414aba85410b3e66cc5e", size = 197676, upload-time = "2026-08-15T08:17:37.819Z" }, + { url = "https://files.pythonhosted.org/packages/a4/a0/562247944386f7d4ef94467e84876600cc1e0f1b93239aaa9213d2bc3cbd/charset_normalizer-3.5.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e90251c0c7bdd54a100a0dce3c07b7e637278c93af29dbf78ebb89a58c4bac7d", size = 340473, upload-time = "2026-08-15T08:17:39.303Z" }, + { url = "https://files.pythonhosted.org/packages/31/e7/1d994be1b93d41e9502b8b0460eaa88a1dd8df335df415db87d6c3e91ab2/charset_normalizer-3.5.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:94d78ecec2605a8d0398b0f365d5f12a63248438516f5dac536a5eff7337df4a", size = 240156, upload-time = "2026-08-15T08:17:40.66Z" }, + { url = "https://files.pythonhosted.org/packages/09/53/27923ce5cc6cbccb832037b27dca98882d9c53e9b69e866bbbef4aae7fc8/charset_normalizer-3.5.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d59b75732e9b6f27388e10c14b0259cc5f2e48c78627d185e6a177b58ad3cffe", size = 228246, upload-time = "2026-08-15T08:17:42.003Z" }, + { url = "https://files.pythonhosted.org/packages/ce/48/5a97e84d63af1d55c07439cb80e56d99a8efb4295700eb4e18c0d1615d2c/charset_normalizer-3.5.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0d929fc574b4d6fd9e7c0f5c2ede8716a41911923aa7fa5fce38e0818aa4a1ac", size = 263660, upload-time = "2026-08-15T08:17:43.627Z" }, + { url = "https://files.pythonhosted.org/packages/7a/c2/071575791dcc88316c0a9a65ce38897a82e4cfe4a325f0f7fe1b1ac47bcf/charset_normalizer-3.5.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:394fea06235c8543390050ed5f529187074b029fb027213f6c46ac11ab5d950e", size = 260354, upload-time = "2026-08-15T08:17:45.094Z" }, + { url = "https://files.pythonhosted.org/packages/fb/af/63240b0c0248c075c2535a1f1bd992821d8251b9f173abc13329661d09e4/charset_normalizer-3.5.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62b55f6722735a6c472f88361cde6640608773d9443cebdbb51abf436a1fcdd3", size = 250638, upload-time = "2026-08-15T08:17:46.496Z" }, + { url = "https://files.pythonhosted.org/packages/4d/66/70dfad64f15be09c15ccfee81330a7e515895dbe296dd23114e9a231268a/charset_normalizer-3.5.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fa48b1b63d639f9483e0633e092f5851e2348c352f1f9bb6c8182f87884ef876", size = 244583, upload-time = "2026-08-15T08:17:47.963Z" }, + { url = "https://files.pythonhosted.org/packages/c0/24/ef36367d38b9ddd4bccbf72888c342e8de1f5ae506fa0b2dcf970e2732a1/charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c71fb0d56c920c269cd3e2e3fe7c610e3f1fdb21a6ce60efa6430ff63676cea6", size = 242038, upload-time = "2026-08-15T08:17:49.481Z" }, + { url = "https://files.pythonhosted.org/packages/db/ab/55e683ba0fff2e43adafc10daa3001eac90fdaa419a97227d5a7067eedde/charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:485a0d363cafefcd2538a73c7c838daa2035f09b2c9f9b5e3133f80c6aeb84c2", size = 233677, upload-time = "2026-08-15T08:17:50.845Z" }, + { url = "https://files.pythonhosted.org/packages/bd/67/0f40eaf8d1b6e7cf15e82382a2965efaca787fc1c2794b7021d37aaf5036/charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c0ea61a470e070686aa30892fed79e297d2c8d0ab46b8bcdf027d38c51da591", size = 264491, upload-time = "2026-08-15T08:17:52.61Z" }, + { url = "https://files.pythonhosted.org/packages/5c/64/12b4c2a11ee8df4fcc518c78b0d93e3a92bd3d5253d1617ce74ff0e8c7ef/charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:90b7481fb62fbe172c558bc6fd1c4c98d82004a54a7551f20e11ac9bf0b8708c", size = 245196, upload-time = "2026-08-15T08:17:54.023Z" }, + { url = "https://files.pythonhosted.org/packages/37/2e/651d910af6d0fba325eee1cda37ec5443462ed25360e666c144166eb6091/charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:35fe081843b35aad20ffeccec3eeffbe637b15d14f3fb22cc1b59cd8ec17e93c", size = 261660, upload-time = "2026-08-15T08:17:55.491Z" }, + { url = "https://files.pythonhosted.org/packages/90/c6/b09e05e6db7f64338e0dc067c79577b1138da86c1e38369096851d96be88/charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fd0350afdc3aabd5576f60ea109228bd5538139713c7b094c5cd27c73a98bc6f", size = 252618, upload-time = "2026-08-15T08:17:57.025Z" }, + { url = "https://files.pythonhosted.org/packages/76/4e/362d4f9fdcdf5556fb2aa3ce7d4a58ebce03ed1ff03aa1d9aca8d02f13f3/charset_normalizer-3.5.1-cp313-cp313-pyemscripten_2025_0_wasm32.whl", hash = "sha256:9d9a0dc7cbe9bec24c3f767c9122c41fe5a1bc43f47cd099d00d393e09769de4", size = 140362, upload-time = "2026-08-15T08:17:58.425Z" }, + { url = "https://files.pythonhosted.org/packages/b4/d4/703be739b26acce318bd29eb3b25b7209e1b1f527f9eae3d1f1f01fdde2b/charset_normalizer-3.5.1-cp313-cp313-win32.whl", hash = "sha256:d63600d620ad0064c3a748b950ac5ea38a80190e5498532efefa4b7b3f1da1f3", size = 177755, upload-time = "2026-08-15T08:18:00.037Z" }, + { url = "https://files.pythonhosted.org/packages/8a/33/56d97ade41c8db611e727168c52ae46c9224c362ec28d4b65d7e9869e8da/charset_normalizer-3.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:aea996a6aba25260827c9ea511d1addfde2da9eb686ac961838509086188b7e6", size = 199295, upload-time = "2026-08-15T08:18:01.506Z" }, + { url = "https://files.pythonhosted.org/packages/5b/75/5b20dd1e6573a01a08158fe104104fa2c8abf941745596954185726cd46c/charset_normalizer-3.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:fd0a274c0e5f9a21565cd9d3dd749b61f96b7aa1e20a93aa1ba4029518f2e5c0", size = 179856, upload-time = "2026-08-15T08:18:02.929Z" }, + { url = "https://files.pythonhosted.org/packages/5b/97/fb4e82231aba271ffd775a1b4993b0defc4e3059f286ae41d9433409fe85/charset_normalizer-3.5.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:41876ee62a3dddf48ff1121ad8f0798032aa03f2fd35f21f34a4cab14f18d8d2", size = 331467, upload-time = "2026-08-15T08:19:50.959Z" }, + { url = "https://files.pythonhosted.org/packages/9f/2f/fe3f187327aac18e2d54e9d2b08e15d27bf9b642d9e51c219f130fc34d1a/charset_normalizer-3.5.1-cp37-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a6dac12ff6b846103483683f60c5f8fee205121adc58ffd87e90a90a3af69e99", size = 253057, upload-time = "2026-08-15T08:19:52.654Z" }, + { url = "https://files.pythonhosted.org/packages/d7/c7/9e48cee5c161fe24da823b61bf381921d77cb994a0a4de148e95018c1984/charset_normalizer-3.5.1-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cee5dd7c6fb5dd52a0fe2a740f9bc6e3593f5f8b1788bde49de02086f30182b2", size = 240930, upload-time = "2026-08-15T08:19:54.163Z" }, + { url = "https://files.pythonhosted.org/packages/49/e0/716601f3cc69be7b198951150c75ead1ece33c3c8036ff6ffa46029659a0/charset_normalizer-3.5.1-cp37-abi3-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:343fb4f2821043bd87095f7b08a1a181febc8e36ac64212143bbfd0a0e1bc235", size = 230822, upload-time = "2026-08-15T08:19:55.807Z" }, + { url = "https://files.pythonhosted.org/packages/d3/05/71bfc5caa0abcc45aea1f6a4d50ac68e59605ddc7666fe8494f4cd229665/charset_normalizer-3.5.1-cp37-abi3-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ae4a097991662cd4fff0ddc74e0fe7874f82e00042fa0ea00855645ed0c79598", size = 260037, upload-time = "2026-08-15T08:19:57.312Z" }, + { url = "https://files.pythonhosted.org/packages/c3/92/de7e32ed05341e7a9c4c877c318418197b7f2d66a3b68d561bf2ac57ca3e/charset_normalizer-3.5.1-cp37-abi3-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4b599739b93b2cbeded49645ae3c8d1405c29ddfbceac1545c87a3f9580a9e96", size = 255097, upload-time = "2026-08-15T08:19:59.056Z" }, + { url = "https://files.pythonhosted.org/packages/f5/7b/ade0a122600319dfa0b1000ab0f9731c94a817904cf3c5de408c73a4ede7/charset_normalizer-3.5.1-cp37-abi3-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b39b69b347e5e47a3b5b8cfc005c68c1ba347474e3960236c4944a8ecd174962", size = 250166, upload-time = "2026-08-15T08:20:00.612Z" }, + { url = "https://files.pythonhosted.org/packages/75/9c/019fbb9f4834491a160951349b1a3714439376f66e5f7cf18b4f18f0c7aa/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:a2028475ba855475b8b4d3cfeb4994269c967aea8b9892dfba907f4263a863a3", size = 241821, upload-time = "2026-08-15T08:20:02.321Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b8/11d4840bfc99330cc7fbcc2681ee5a044553a6e77655508d8f9b2bff7b34/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:36047af20e17097c3bb9476c2b7655f2f7aa51322c0ba58c07695bedf755a950", size = 232529, upload-time = "2026-08-15T08:20:04.008Z" }, + { url = "https://files.pythonhosted.org/packages/18/96/2b3a21492d9f65171ac75d872f5018260013d00bfa0ff70ec9f179148cbd/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_ppc64le.whl", hash = "sha256:4c4fb141a727957c93edfe5c32a26ceb6b5f6461d67146e2d39f51e16170bea8", size = 260348, upload-time = "2026-08-15T08:20:05.877Z" }, + { url = "https://files.pythonhosted.org/packages/d6/aa/a69a2028e8bd052476c245460ab19d7de595de084dd968f2d75cd50c3e25/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_riscv64.whl", hash = "sha256:2f293479cce755c75f1697e87c409b7ae4c555c7dfecb6e988ad13abba943031", size = 247234, upload-time = "2026-08-15T08:20:07.487Z" }, + { url = "https://files.pythonhosted.org/packages/35/8a/3d130aeabcaf3d2466af76b7b141c08d9e89c9016ab4b7cdd0f7dc2d1c62/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_s390x.whl", hash = "sha256:3588e376b3ea2eea84976f67273d679f229e24c66dce7b82ae45aef04ff6e072", size = 256917, upload-time = "2026-08-15T08:20:09.142Z" }, + { url = "https://files.pythonhosted.org/packages/80/c2/a7379b840292d0c1ab9fbd17d1f3967aa81794dc95bc74be8999d7fedcf7/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e199fb99720074809a7720f1c0b4d919eea8b87e88713e0f8f602f7bef543d9d", size = 254846, upload-time = "2026-08-15T08:20:10.727Z" }, + { url = "https://files.pythonhosted.org/packages/01/65/d43b714731bb2f40d4053dfa00ecfc1c5a301f8e3316c5db3a09af59fe94/charset_normalizer-3.5.1-cp37-abi3-win32.whl", hash = "sha256:dd732602a7009217f658d5863d12d79d373a4de0eebc111094bcdd3bb8e0a6cc", size = 174216, upload-time = "2026-08-15T08:20:12.334Z" }, + { url = "https://files.pythonhosted.org/packages/35/4f/b911ed898b26a09789eba9c9200c999aff6c61b4bafaf4838e56d1a1e1a3/charset_normalizer-3.5.1-cp37-abi3-win_amd64.whl", hash = "sha256:70055ff39b97c99e7ae40ea3e393fb62aa2e44dbd9b29f8d14f42fb0025c3959", size = 199764, upload-time = "2026-08-15T08:20:13.908Z" }, + { url = "https://files.pythonhosted.org/packages/f0/a7/920baf467bfd9bf689f3b318340f37aee4572a71f162bd8db51da55ba4fa/charset_normalizer-3.5.1-cp37-abi3-win_arm64.whl", hash = "sha256:87e4f41d375c0b9be2fb5251aee4b8a689169e134535aed81bf085c3b647451e", size = 287318, upload-time = "2026-08-15T08:20:15.551Z" }, + { url = "https://files.pythonhosted.org/packages/cc/61/d01fc49b8dea277640b55a9e15960dbca9fdc8c9fde18e572d39c59f4019/charset_normalizer-3.5.1-py3-none-any.whl", hash = "sha256:6df0ec430f9a831772c23ca5a224cba36517a58a84bb32c32bb59a9fa67c47f6", size = 68658, upload-time = "2026-08-15T08:20:43.306Z" }, ] [[package]] name = "click" -version = "8.3.2" +version = "8.5.0" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/57/75/31212c6bf2503fdf920d87fee5d7a86a2e3bcf444984126f13d8e4016804/click-8.3.2.tar.gz", hash = "sha256:14162b8b3b3550a7d479eafa77dfd3c38d9dc8951f6f69c78913a8f9a7540fd5", size = 302856, upload-time = "2026-04-03T19:14:45.118Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/0e/7fa0ef50764b67090eca4114772a2abf8b6148198475e54c660b97caeee6/click-8.5.0.tar.gz", hash = "sha256:ba0d2089de75ea0310e2dde03160e6ca10009947fb95a182f9b54021bb272e34", size = 382235, upload-time = "2026-08-26T13:33:14.56Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e4/20/71885d8b97d4f3dde17b1fdb92dbd4908b00541c5a3379787137285f602e/click-8.3.2-py3-none-any.whl", hash = "sha256:1924d2c27c5653561cd2cae4548d1406039cb79b858b747cfea24924bbc1616d", size = 108379, upload-time = "2026-04-03T19:14:43.505Z" }, + { url = "https://files.pythonhosted.org/packages/58/50/6c0d534c5f134586a8e1ba4e330569e32f057e33372ae556463212fb4cd3/click-8.5.0-py3-none-any.whl", hash = "sha256:255bc9599cf7748b4b1a446ccc735421bd08a2ae529a8b88597d3de5664ee360", size = 125251, upload-time = "2026-08-26T13:33:12.928Z" }, ] [[package]] @@ -266,18 +193,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] -[[package]] -name = "colorlog" -version = "6.10.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a2/61/f083b5ac52e505dfc1c624eafbf8c7589a0d7f32daa398d2e7590efa5fda/colorlog-6.10.1.tar.gz", hash = "sha256:eb4ae5cb65fe7fec7773c2306061a8e63e02efc2c72eba9d27b0fa23c94f1321", size = 17162, upload-time = "2025-10-16T16:14:11.978Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/c1/e419ef3723a074172b68aaa89c9f3de486ed4c2399e2dbd8113a4fdcaf9e/colorlog-6.10.1-py3-none-any.whl", hash = "sha256:2d7e8348291948af66122cff006c9f8da6255d224e7cf8e37d8de2df3bad8c9c", size = 11743, upload-time = "2025-10-16T16:14:10.512Z" }, -] - [[package]] name = "contourpy" version = "1.3.3" @@ -309,81 +224,43 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b9/70/f308384a3ae9cd2209e0849f33c913f658d3326900d0ff5d378d6a1422d2/contourpy-1.3.3-cp313-cp313t-win32.whl", hash = "sha256:283edd842a01e3dcd435b1c5116798d661378d83d36d337b8dde1d16a5fc9ba3", size = 196157, upload-time = "2025-07-26T12:02:11.488Z" }, { url = "https://files.pythonhosted.org/packages/b2/dd/880f890a6663b84d9e34a6f88cded89d78f0091e0045a284427cb6b18521/contourpy-1.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:87acf5963fc2b34825e5b6b048f40e3635dd547f590b04d2ab317c2619ef7ae8", size = 240570, upload-time = "2025-07-26T12:02:12.754Z" }, { url = "https://files.pythonhosted.org/packages/80/99/2adc7d8ffead633234817ef8e9a87115c8a11927a94478f6bb3d3f4d4f7d/contourpy-1.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:3c30273eb2a55024ff31ba7d052dde990d7d8e5450f4bbb6e913558b3d6c2301", size = 199713, upload-time = "2025-07-26T12:02:14.4Z" }, - { url = "https://files.pythonhosted.org/packages/72/8b/4546f3ab60f78c514ffb7d01a0bd743f90de36f0019d1be84d0a708a580a/contourpy-1.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fde6c716d51c04b1c25d0b90364d0be954624a0ee9d60e23e850e8d48353d07a", size = 292189, upload-time = "2025-07-26T12:02:16.095Z" }, - { url = "https://files.pythonhosted.org/packages/fd/e1/3542a9cb596cadd76fcef413f19c79216e002623158befe6daa03dbfa88c/contourpy-1.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:cbedb772ed74ff5be440fa8eee9bd49f64f6e3fc09436d9c7d8f1c287b121d77", size = 273251, upload-time = "2025-07-26T12:02:17.524Z" }, - { url = "https://files.pythonhosted.org/packages/b1/71/f93e1e9471d189f79d0ce2497007731c1e6bf9ef6d1d61b911430c3db4e5/contourpy-1.3.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22e9b1bd7a9b1d652cd77388465dc358dafcd2e217d35552424aa4f996f524f5", size = 335810, upload-time = "2025-07-26T12:02:18.9Z" }, - { url = "https://files.pythonhosted.org/packages/91/f9/e35f4c1c93f9275d4e38681a80506b5510e9327350c51f8d4a5a724d178c/contourpy-1.3.3-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a22738912262aa3e254e4f3cb079a95a67132fc5a063890e224393596902f5a4", size = 382871, upload-time = "2025-07-26T12:02:20.418Z" }, - { url = "https://files.pythonhosted.org/packages/b5/71/47b512f936f66a0a900d81c396a7e60d73419868fba959c61efed7a8ab46/contourpy-1.3.3-cp314-cp314-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:afe5a512f31ee6bd7d0dda52ec9864c984ca3d66664444f2d72e0dc4eb832e36", size = 386264, upload-time = "2025-07-26T12:02:21.916Z" }, - { url = "https://files.pythonhosted.org/packages/04/5f/9ff93450ba96b09c7c2b3f81c94de31c89f92292f1380261bd7195bea4ea/contourpy-1.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f64836de09927cba6f79dcd00fdd7d5329f3fccc633468507079c829ca4db4e3", size = 363819, upload-time = "2025-07-26T12:02:23.759Z" }, - { url = "https://files.pythonhosted.org/packages/3e/a6/0b185d4cc480ee494945cde102cb0149ae830b5fa17bf855b95f2e70ad13/contourpy-1.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1fd43c3be4c8e5fd6e4f2baeae35ae18176cf2e5cced681cca908addf1cdd53b", size = 1333650, upload-time = "2025-07-26T12:02:26.181Z" }, - { url = "https://files.pythonhosted.org/packages/43/d7/afdc95580ca56f30fbcd3060250f66cedbde69b4547028863abd8aa3b47e/contourpy-1.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6afc576f7b33cf00996e5c1102dc2a8f7cc89e39c0b55df93a0b78c1bd992b36", size = 1404833, upload-time = "2025-07-26T12:02:28.782Z" }, - { url = "https://files.pythonhosted.org/packages/e2/e2/366af18a6d386f41132a48f033cbd2102e9b0cf6345d35ff0826cd984566/contourpy-1.3.3-cp314-cp314-win32.whl", hash = "sha256:66c8a43a4f7b8df8b71ee1840e4211a3c8d93b214b213f590e18a1beca458f7d", size = 189692, upload-time = "2025-07-26T12:02:30.128Z" }, - { url = "https://files.pythonhosted.org/packages/7d/c2/57f54b03d0f22d4044b8afb9ca0e184f8b1afd57b4f735c2fa70883dc601/contourpy-1.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:cf9022ef053f2694e31d630feaacb21ea24224be1c3ad0520b13d844274614fd", size = 232424, upload-time = "2025-07-26T12:02:31.395Z" }, - { url = "https://files.pythonhosted.org/packages/18/79/a9416650df9b525737ab521aa181ccc42d56016d2123ddcb7b58e926a42c/contourpy-1.3.3-cp314-cp314-win_arm64.whl", hash = "sha256:95b181891b4c71de4bb404c6621e7e2390745f887f2a026b2d99e92c17892339", size = 198300, upload-time = "2025-07-26T12:02:32.956Z" }, - { url = "https://files.pythonhosted.org/packages/1f/42/38c159a7d0f2b7b9c04c64ab317042bb6952b713ba875c1681529a2932fe/contourpy-1.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:33c82d0138c0a062380332c861387650c82e4cf1747aaa6938b9b6516762e772", size = 306769, upload-time = "2025-07-26T12:02:34.2Z" }, - { url = "https://files.pythonhosted.org/packages/c3/6c/26a8205f24bca10974e77460de68d3d7c63e282e23782f1239f226fcae6f/contourpy-1.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ea37e7b45949df430fe649e5de8351c423430046a2af20b1c1961cae3afcda77", size = 287892, upload-time = "2025-07-26T12:02:35.807Z" }, - { url = "https://files.pythonhosted.org/packages/66/06/8a475c8ab718ebfd7925661747dbb3c3ee9c82ac834ccb3570be49d129f4/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d304906ecc71672e9c89e87c4675dc5c2645e1f4269a5063b99b0bb29f232d13", size = 326748, upload-time = "2025-07-26T12:02:37.193Z" }, - { url = "https://files.pythonhosted.org/packages/b4/a3/c5ca9f010a44c223f098fccd8b158bb1cb287378a31ac141f04730dc49be/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca658cd1a680a5c9ea96dc61cdbae1e85c8f25849843aa799dfd3cb370ad4fbe", size = 375554, upload-time = "2025-07-26T12:02:38.894Z" }, - { url = "https://files.pythonhosted.org/packages/80/5b/68bd33ae63fac658a4145088c1e894405e07584a316738710b636c6d0333/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ab2fd90904c503739a75b7c8c5c01160130ba67944a7b77bbf36ef8054576e7f", size = 388118, upload-time = "2025-07-26T12:02:40.642Z" }, - { url = "https://files.pythonhosted.org/packages/40/52/4c285a6435940ae25d7410a6c36bda5145839bc3f0beb20c707cda18b9d2/contourpy-1.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7301b89040075c30e5768810bc96a8e8d78085b47d8be6e4c3f5a0b4ed478a0", size = 352555, upload-time = "2025-07-26T12:02:42.25Z" }, - { url = "https://files.pythonhosted.org/packages/24/ee/3e81e1dd174f5c7fefe50e85d0892de05ca4e26ef1c9a59c2a57e43b865a/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2a2a8b627d5cc6b7c41a4beff6c5ad5eb848c88255fda4a8745f7e901b32d8e4", size = 1322295, upload-time = "2025-07-26T12:02:44.668Z" }, - { url = "https://files.pythonhosted.org/packages/3c/b2/6d913d4d04e14379de429057cd169e5e00f6c2af3bb13e1710bcbdb5da12/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fd6ec6be509c787f1caf6b247f0b1ca598bef13f4ddeaa126b7658215529ba0f", size = 1391027, upload-time = "2025-07-26T12:02:47.09Z" }, - { url = "https://files.pythonhosted.org/packages/93/8a/68a4ec5c55a2971213d29a9374913f7e9f18581945a7a31d1a39b5d2dfe5/contourpy-1.3.3-cp314-cp314t-win32.whl", hash = "sha256:e74a9a0f5e3fff48fb5a7f2fd2b9b70a3fe014a67522f79b7cca4c0c7e43c9ae", size = 202428, upload-time = "2025-07-26T12:02:48.691Z" }, - { url = "https://files.pythonhosted.org/packages/fa/96/fd9f641ffedc4fa3ace923af73b9d07e869496c9cc7a459103e6e978992f/contourpy-1.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:13b68d6a62db8eafaebb8039218921399baf6e47bf85006fd8529f2a08ef33fc", size = 250331, upload-time = "2025-07-26T12:02:50.137Z" }, - { url = "https://files.pythonhosted.org/packages/ae/8c/469afb6465b853afff216f9528ffda78a915ff880ed58813ba4faf4ba0b6/contourpy-1.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b7448cb5a725bb1e35ce88771b86fba35ef418952474492cf7c764059933ff8b", size = 203831, upload-time = "2025-07-26T12:02:51.449Z" }, ] [[package]] name = "cryptography" -version = "46.0.6" +version = "50.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a4/ba/04b1bd4218cbc58dc90ce967106d51582371b898690f3ae0402876cc4f34/cryptography-46.0.6.tar.gz", hash = "sha256:27550628a518c5c6c903d84f637fbecf287f6cb9ced3804838a1295dc1fd0759", size = 750542, upload-time = "2026-03-25T23:34:53.396Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/47/23/9285e15e3bc57325b0a72e592921983a701efc1ee8f91c06c5f0235d86d9/cryptography-46.0.6-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:64235194bad039a10bb6d2d930ab3323baaec67e2ce36215fd0952fad0930ca8", size = 7176401, upload-time = "2026-03-25T23:33:22.096Z" }, - { url = "https://files.pythonhosted.org/packages/60/f8/e61f8f13950ab6195b31913b42d39f0f9afc7d93f76710f299b5ec286ae6/cryptography-46.0.6-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:26031f1e5ca62fcb9d1fcb34b2b60b390d1aacaa15dc8b895a9ed00968b97b30", size = 4275275, upload-time = "2026-03-25T23:33:23.844Z" }, - { url = "https://files.pythonhosted.org/packages/19/69/732a736d12c2631e140be2348b4ad3d226302df63ef64d30dfdb8db7ad1c/cryptography-46.0.6-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9a693028b9cbe51b5a1136232ee8f2bc242e4e19d456ded3fa7c86e43c713b4a", size = 4425320, upload-time = "2026-03-25T23:33:25.703Z" }, - { url = "https://files.pythonhosted.org/packages/d4/12/123be7292674abf76b21ac1fc0e1af50661f0e5b8f0ec8285faac18eb99e/cryptography-46.0.6-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:67177e8a9f421aa2d3a170c3e56eca4e0128883cf52a071a7cbf53297f18b175", size = 4278082, upload-time = "2026-03-25T23:33:27.423Z" }, - { url = "https://files.pythonhosted.org/packages/5b/ba/d5e27f8d68c24951b0a484924a84c7cdaed7502bac9f18601cd357f8b1d2/cryptography-46.0.6-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:d9528b535a6c4f8ff37847144b8986a9a143585f0540fbcb1a98115b543aa463", size = 4926514, upload-time = "2026-03-25T23:33:29.206Z" }, - { url = "https://files.pythonhosted.org/packages/34/71/1ea5a7352ae516d5512d17babe7e1b87d9db5150b21f794b1377eac1edc0/cryptography-46.0.6-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:22259338084d6ae497a19bae5d4c66b7ca1387d3264d1c2c0e72d9e9b6a77b97", size = 4457766, upload-time = "2026-03-25T23:33:30.834Z" }, - { url = "https://files.pythonhosted.org/packages/01/59/562be1e653accee4fdad92c7a2e88fced26b3fdfce144047519bbebc299e/cryptography-46.0.6-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:760997a4b950ff00d418398ad73fbc91aa2894b5c1db7ccb45b4f68b42a63b3c", size = 3986535, upload-time = "2026-03-25T23:33:33.02Z" }, - { url = "https://files.pythonhosted.org/packages/d6/8b/b1ebfeb788bf4624d36e45ed2662b8bd43a05ff62157093c1539c1288a18/cryptography-46.0.6-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:3dfa6567f2e9e4c5dceb8ccb5a708158a2a871052fa75c8b78cb0977063f1507", size = 4277618, upload-time = "2026-03-25T23:33:34.567Z" }, - { url = "https://files.pythonhosted.org/packages/dd/52/a005f8eabdb28df57c20f84c44d397a755782d6ff6d455f05baa2785bd91/cryptography-46.0.6-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:cdcd3edcbc5d55757e5f5f3d330dd00007ae463a7e7aa5bf132d1f22a4b62b19", size = 4890802, upload-time = "2026-03-25T23:33:37.034Z" }, - { url = "https://files.pythonhosted.org/packages/ec/4d/8e7d7245c79c617d08724e2efa397737715ca0ec830ecb3c91e547302555/cryptography-46.0.6-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:d4e4aadb7fc1f88687f47ca20bb7227981b03afaae69287029da08096853b738", size = 4457425, upload-time = "2026-03-25T23:33:38.904Z" }, - { url = "https://files.pythonhosted.org/packages/1d/5c/f6c3596a1430cec6f949085f0e1a970638d76f81c3ea56d93d564d04c340/cryptography-46.0.6-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2b417edbe8877cda9022dde3a008e2deb50be9c407eef034aeeb3a8b11d9db3c", size = 4405530, upload-time = "2026-03-25T23:33:40.842Z" }, - { url = "https://files.pythonhosted.org/packages/7e/c9/9f9cea13ee2dbde070424e0c4f621c091a91ffcc504ffea5e74f0e1daeff/cryptography-46.0.6-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:380343e0653b1c9d7e1f55b52aaa2dbb2fdf2730088d48c43ca1c7c0abb7cc2f", size = 4667896, upload-time = "2026-03-25T23:33:42.781Z" }, - { url = "https://files.pythonhosted.org/packages/ad/b5/1895bc0821226f129bc74d00eccfc6a5969e2028f8617c09790bf89c185e/cryptography-46.0.6-cp311-abi3-win32.whl", hash = "sha256:bcb87663e1f7b075e48c3be3ecb5f0b46c8fc50b50a97cf264e7f60242dca3f2", size = 3026348, upload-time = "2026-03-25T23:33:45.021Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f8/c9bcbf0d3e6ad288b9d9aa0b1dee04b063d19e8c4f871855a03ab3a297ab/cryptography-46.0.6-cp311-abi3-win_amd64.whl", hash = "sha256:6739d56300662c468fddb0e5e291f9b4d084bead381667b9e654c7dd81705124", size = 3483896, upload-time = "2026-03-25T23:33:46.649Z" }, - { url = "https://files.pythonhosted.org/packages/01/41/3a578f7fd5c70611c0aacba52cd13cb364a5dee895a5c1d467208a9380b0/cryptography-46.0.6-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:2ef9e69886cbb137c2aef9772c2e7138dc581fad4fcbcf13cc181eb5a3ab6275", size = 7117147, upload-time = "2026-03-25T23:33:48.249Z" }, - { url = "https://files.pythonhosted.org/packages/fa/87/887f35a6fca9dde90cad08e0de0c89263a8e59b2d2ff904fd9fcd8025b6f/cryptography-46.0.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7f417f034f91dcec1cb6c5c35b07cdbb2ef262557f701b4ecd803ee8cefed4f4", size = 4266221, upload-time = "2026-03-25T23:33:49.874Z" }, - { url = "https://files.pythonhosted.org/packages/aa/a8/0a90c4f0b0871e0e3d1ed126aed101328a8a57fd9fd17f00fb67e82a51ca/cryptography-46.0.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d24c13369e856b94892a89ddf70b332e0b70ad4a5c43cf3e9cb71d6d7ffa1f7b", size = 4408952, upload-time = "2026-03-25T23:33:52.128Z" }, - { url = "https://files.pythonhosted.org/packages/16/0b/b239701eb946523e4e9f329336e4ff32b1247e109cbab32d1a7b61da8ed7/cryptography-46.0.6-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:aad75154a7ac9039936d50cf431719a2f8d4ed3d3c277ac03f3339ded1a5e707", size = 4270141, upload-time = "2026-03-25T23:33:54.11Z" }, - { url = "https://files.pythonhosted.org/packages/0f/a8/976acdd4f0f30df7b25605f4b9d3d89295351665c2091d18224f7ad5cdbf/cryptography-46.0.6-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:3c21d92ed15e9cfc6eb64c1f5a0326db22ca9c2566ca46d845119b45b4400361", size = 4904178, upload-time = "2026-03-25T23:33:55.725Z" }, - { url = "https://files.pythonhosted.org/packages/b1/1b/bf0e01a88efd0e59679b69f42d4afd5bced8700bb5e80617b2d63a3741af/cryptography-46.0.6-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:4668298aef7cddeaf5c6ecc244c2302a2b8e40f384255505c22875eebb47888b", size = 4441812, upload-time = "2026-03-25T23:33:57.364Z" }, - { url = "https://files.pythonhosted.org/packages/bb/8b/11df86de2ea389c65aa1806f331cae145f2ed18011f30234cc10ca253de8/cryptography-46.0.6-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:8ce35b77aaf02f3b59c90b2c8a05c73bac12cea5b4e8f3fbece1f5fddea5f0ca", size = 3963923, upload-time = "2026-03-25T23:33:59.361Z" }, - { url = "https://files.pythonhosted.org/packages/91/e0/207fb177c3a9ef6a8108f234208c3e9e76a6aa8cf20d51932916bd43bda0/cryptography-46.0.6-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:c89eb37fae9216985d8734c1afd172ba4927f5a05cfd9bf0e4863c6d5465b013", size = 4269695, upload-time = "2026-03-25T23:34:00.909Z" }, - { url = "https://files.pythonhosted.org/packages/21/5e/19f3260ed1e95bced52ace7501fabcd266df67077eeb382b79c81729d2d3/cryptography-46.0.6-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:ed418c37d095aeddf5336898a132fba01091f0ac5844e3e8018506f014b6d2c4", size = 4869785, upload-time = "2026-03-25T23:34:02.796Z" }, - { url = "https://files.pythonhosted.org/packages/10/38/cd7864d79aa1d92ef6f1a584281433419b955ad5a5ba8d1eb6c872165bcb/cryptography-46.0.6-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:69cf0056d6947edc6e6760e5f17afe4bea06b56a9ac8a06de9d2bd6b532d4f3a", size = 4441404, upload-time = "2026-03-25T23:34:04.35Z" }, - { url = "https://files.pythonhosted.org/packages/09/0a/4fe7a8d25fed74419f91835cf5829ade6408fd1963c9eae9c4bce390ecbb/cryptography-46.0.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e7304c4f4e9490e11efe56af6713983460ee0780f16c63f219984dab3af9d2d", size = 4397549, upload-time = "2026-03-25T23:34:06.342Z" }, - { url = "https://files.pythonhosted.org/packages/5f/a0/7d738944eac6513cd60a8da98b65951f4a3b279b93479a7e8926d9cd730b/cryptography-46.0.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b928a3ca837c77a10e81a814a693f2295200adb3352395fad024559b7be7a736", size = 4651874, upload-time = "2026-03-25T23:34:07.916Z" }, - { url = "https://files.pythonhosted.org/packages/cb/f1/c2326781ca05208845efca38bf714f76939ae446cd492d7613808badedf1/cryptography-46.0.6-cp314-cp314t-win32.whl", hash = "sha256:97c8115b27e19e592a05c45d0dd89c57f81f841cc9880e353e0d3bf25b2139ed", size = 3001511, upload-time = "2026-03-25T23:34:09.892Z" }, - { url = "https://files.pythonhosted.org/packages/c9/57/fe4a23eb549ac9d903bd4698ffda13383808ef0876cc912bcb2838799ece/cryptography-46.0.6-cp314-cp314t-win_amd64.whl", hash = "sha256:c797e2517cb7880f8297e2c0f43bb910e91381339336f75d2c1c2cbf811b70b4", size = 3471692, upload-time = "2026-03-25T23:34:11.613Z" }, - { url = "https://files.pythonhosted.org/packages/c4/cc/f330e982852403da79008552de9906804568ae9230da8432f7496ce02b71/cryptography-46.0.6-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:12cae594e9473bca1a7aceb90536060643128bb274fcea0fc459ab90f7d1ae7a", size = 7162776, upload-time = "2026-03-25T23:34:13.308Z" }, - { url = "https://files.pythonhosted.org/packages/49/b3/dc27efd8dcc4bff583b3f01d4a3943cd8b5821777a58b3a6a5f054d61b79/cryptography-46.0.6-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:639301950939d844a9e1c4464d7e07f902fe9a7f6b215bb0d4f28584729935d8", size = 4270529, upload-time = "2026-03-25T23:34:15.019Z" }, - { url = "https://files.pythonhosted.org/packages/e6/05/e8d0e6eb4f0d83365b3cb0e00eb3c484f7348db0266652ccd84632a3d58d/cryptography-46.0.6-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77", size = 4414827, upload-time = "2026-03-25T23:34:16.604Z" }, - { url = "https://files.pythonhosted.org/packages/2f/97/daba0f5d2dc6d855e2dcb70733c812558a7977a55dd4a6722756628c44d1/cryptography-46.0.6-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8927ccfbe967c7df312ade694f987e7e9e22b2425976ddbf28271d7e58845290", size = 4271265, upload-time = "2026-03-25T23:34:18.586Z" }, - { url = "https://files.pythonhosted.org/packages/89/06/fe1fce39a37ac452e58d04b43b0855261dac320a2ebf8f5260dd55b201a9/cryptography-46.0.6-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:b12c6b1e1651e42ab5de8b1e00dc3b6354fdfd778e7fa60541ddacc27cd21410", size = 4916800, upload-time = "2026-03-25T23:34:20.561Z" }, - { url = "https://files.pythonhosted.org/packages/ff/8a/b14f3101fe9c3592603339eb5d94046c3ce5f7fc76d6512a2d40efd9724e/cryptography-46.0.6-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:063b67749f338ca9c5a0b7fe438a52c25f9526b851e24e6c9310e7195aad3b4d", size = 4448771, upload-time = "2026-03-25T23:34:22.406Z" }, - { url = "https://files.pythonhosted.org/packages/01/b3/0796998056a66d1973fd52ee89dc1bb3b6581960a91ad4ac705f182d398f/cryptography-46.0.6-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:02fad249cb0e090b574e30b276a3da6a149e04ee2f049725b1f69e7b8351ec70", size = 3978333, upload-time = "2026-03-25T23:34:24.281Z" }, - { url = "https://files.pythonhosted.org/packages/c5/3d/db200af5a4ffd08918cd55c08399dc6c9c50b0bc72c00a3246e099d3a849/cryptography-46.0.6-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e6142674f2a9291463e5e150090b95a8519b2fb6e6aaec8917dd8d094ce750d", size = 4271069, upload-time = "2026-03-25T23:34:25.895Z" }, - { url = "https://files.pythonhosted.org/packages/d7/18/61acfd5b414309d74ee838be321c636fe71815436f53c9f0334bf19064fa/cryptography-46.0.6-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:456b3215172aeefb9284550b162801d62f5f264a081049a3e94307fe20792cfa", size = 4878358, upload-time = "2026-03-25T23:34:27.67Z" }, - { url = "https://files.pythonhosted.org/packages/8b/65/5bf43286d566f8171917cae23ac6add941654ccf085d739195a4eacf1674/cryptography-46.0.6-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:341359d6c9e68834e204ceaf25936dffeafea3829ab80e9503860dcc4f4dac58", size = 4448061, upload-time = "2026-03-25T23:34:29.375Z" }, - { url = "https://files.pythonhosted.org/packages/e0/25/7e49c0fa7205cf3597e525d156a6bce5b5c9de1fd7e8cb01120e459f205a/cryptography-46.0.6-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9a9c42a2723999a710445bc0d974e345c32adfd8d2fac6d8a251fa829ad31cfb", size = 4399103, upload-time = "2026-03-25T23:34:32.036Z" }, - { url = "https://files.pythonhosted.org/packages/44/46/466269e833f1c4718d6cd496ffe20c56c9c8d013486ff66b4f69c302a68d/cryptography-46.0.6-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6617f67b1606dfd9fe4dbfa354a9508d4a6d37afe30306fe6c101b7ce3274b72", size = 4659255, upload-time = "2026-03-25T23:34:33.679Z" }, - { url = "https://files.pythonhosted.org/packages/0a/09/ddc5f630cc32287d2c953fc5d32705e63ec73e37308e5120955316f53827/cryptography-46.0.6-cp38-abi3-win32.whl", hash = "sha256:7f6690b6c55e9c5332c0b59b9c8a3fb232ebf059094c17f9019a51e9827df91c", size = 3010660, upload-time = "2026-03-25T23:34:35.418Z" }, - { url = "https://files.pythonhosted.org/packages/1b/82/ca4893968aeb2709aacfb57a30dec6fa2ab25b10fa9f064b8882ce33f599/cryptography-46.0.6-cp38-abi3-win_amd64.whl", hash = "sha256:79e865c642cfc5c0b3eb12af83c35c5aeff4fa5c672dc28c43721c2c9fdd2f0f", size = 3471160, upload-time = "2026-03-25T23:34:37.191Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/bb/ad/5d6702db60b1e40b41ef513b6967ff5848f307d50f8449baf1634f5908f1/cryptography-50.0.1.tar.gz", hash = "sha256:5dd9bda1c12b4162f6ff568eeb5e0ff956c28d14406e875cfe8a63a2d414ff20", size = 880381, upload-time = "2026-08-25T19:45:45.499Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/19/797e2aaac9df6a66f1550f49979dc1b1e39ecd2077501c30efa81e8d5d67/cryptography-50.0.1-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:b8f852c65863251b9e3a1b8c150ce21e59b522dbb6a7d4bc80e680d38388e986", size = 4010153, upload-time = "2026-08-25T19:44:03.155Z" }, + { url = "https://files.pythonhosted.org/packages/90/34/9ce9a62ed9dc82ca9fd6a34445b6904af56e5f38b3eae2ed32e49c36053d/cryptography-50.0.1-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:53e279950892dc102c6b4e52af03ae5ea92fac572a1ddab78ca73a997f62b69f", size = 4723133, upload-time = "2026-08-25T19:44:05.461Z" }, + { url = "https://files.pythonhosted.org/packages/57/26/e6d4fc8512a51a5f9ee7bfdbfb853bce1197087df40c9ad993ad370b846f/cryptography-50.0.1-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ff838d62ec1bfce4f9ba7fa16f4a7b554cd8d0c299e6be37502161a660c84eef", size = 4712478, upload-time = "2026-08-25T19:44:07.375Z" }, + { url = "https://files.pythonhosted.org/packages/e6/de/d3cdc2815697aae84126cbd6a030ca7b6b452e28a88b501b836bd3aa7a86/cryptography-50.0.1-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:e74591e283fe6eb956416c929eb58262a719fe0311fd9054c62c3350ed8760d8", size = 4730726, upload-time = "2026-08-25T19:44:09.294Z" }, + { url = "https://files.pythonhosted.org/packages/55/32/38c0d344b98c06d34b5df8946565a9c0d6dbf32c8e0730a7f05f0a3c6cab/cryptography-50.0.1-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5fe002589592ed749ce77fe0695fcbd3500dd61d7d6db5858a7544c612fa8e45", size = 5353524, upload-time = "2026-08-25T19:44:11.96Z" }, + { url = "https://files.pythonhosted.org/packages/e1/1b/82f0f0d8858d4432be1af790477edf62aef90324041aa07c57e57bef1af7/cryptography-50.0.1-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:51593d180cf6d179bde5c5d065bed81386b1f381656ae7d042b7ffc87a9895ad", size = 4746720, upload-time = "2026-08-25T19:44:14.051Z" }, + { url = "https://files.pythonhosted.org/packages/29/ba/042ca458b8c64348c768284b5d23e69b92ed53d057ab779fee628564676d/cryptography-50.0.1-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:359e62deae718bce96170e223fdcb6357e4fbd3bb7a3a75f4430763532560e49", size = 4361866, upload-time = "2026-08-25T19:44:16.167Z" }, + { url = "https://files.pythonhosted.org/packages/39/3b/e96c1ef71edef71057c7e3c3d982ce8fda554e0c52d0cc19c18845cde3eb/cryptography-50.0.1-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e2ca8fd1b6b4b82a1c4cb02841d0837e3c12336c2e24b520ab8ab3b969733d8f", size = 4730028, upload-time = "2026-08-25T19:44:18.085Z" }, + { url = "https://files.pythonhosted.org/packages/e3/38/45abd72ef63f2e7d0754a6cacf97bd8b69512ace7f6130d24c39ece65da2/cryptography-50.0.1-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:76de83fbd91ac49c0feaaa983d0748fd7a53176afac5fb3bf7478d244f0eb527", size = 5308405, upload-time = "2026-08-25T19:44:20.197Z" }, + { url = "https://files.pythonhosted.org/packages/85/66/6ccca4722987ddedaa7fc9c3f4708af7431f5535666c174350830888c6b7/cryptography-50.0.1-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:51afcfceb15597cf2635068e4ac9a56b2abde622edde17f37d85fd7b5306497a", size = 4746230, upload-time = "2026-08-25T19:44:22.376Z" }, + { url = "https://files.pythonhosted.org/packages/13/0e/b1f92e013228111413f2e6743948b80bc24dfd3c1b87ba98ceea16f5df89/cryptography-50.0.1-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:be224a65493ec5b74a158ff22a5522ce4a5ca1e543c647a3a4730d4a09e5f959", size = 4862596, upload-time = "2026-08-25T19:44:24.472Z" }, + { url = "https://files.pythonhosted.org/packages/7e/22/c3654cccc856e9d682817b04ac3ee79731cb09ca6f95996a95c904de2883/cryptography-50.0.1-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9ebcdd5519be9b652a46f507817a74591774fc3d6923ac364e4dfa64e36b291b", size = 5014082, upload-time = "2026-08-25T19:44:26.709Z" }, + { url = "https://files.pythonhosted.org/packages/42/8b/cb12b1b60c91b074ca6bf0fdd59aa8f10d8bc5f73af8faece86ef0421b37/cryptography-50.0.1-cp311-abi3-win_amd64.whl", hash = "sha256:aed8db4f6d71c51efb89530e12d9464e7bf2923d46c3205dc794a2a93f8c0648", size = 3842826, upload-time = "2026-08-25T19:44:28.784Z" }, + { url = "https://files.pythonhosted.org/packages/84/a9/ee16a903f13755e914d1eecc482fe64d1f10761c3960e5d8fa6837377aff/cryptography-50.0.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:ca83d00d9e69cd5eb63f2e69c3a5a59e0cecae5ae14c6ae0b35830fe3b37bad0", size = 4035307, upload-time = "2026-08-25T19:44:58.305Z" }, + { url = "https://files.pythonhosted.org/packages/5e/a5/9ec7e81e8526c0d7a387d73386b2daed3f39e10d81a85930bd1b6bfba65c/cryptography-50.0.1-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:05ba322c4da95b262a212c345af888ef2c37c88c0509756ea00a0e6d68850f23", size = 4751900, upload-time = "2026-08-25T19:45:00.401Z" }, + { url = "https://files.pythonhosted.org/packages/7e/3c/0e77bd5ffcf078e9dd27d3074aad6c030d9b10d0bf69329d573c927a188c/cryptography-50.0.1-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e22dfed744bd4002e909464cb23d2f0b05c6f3113a79ef2e9864a53db737c733", size = 4738357, upload-time = "2026-08-25T19:45:02.786Z" }, + { url = "https://files.pythonhosted.org/packages/27/3a/3c5f80daa4dcd47323c7af8a2fcb90de27a33564d4fcac69846c0972691a/cryptography-50.0.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:4c4188f7c0cf655be5c06342b817ed0f9595b69ffa2b12026e5353eed29dea88", size = 4758474, upload-time = "2026-08-25T19:45:04.889Z" }, + { url = "https://files.pythonhosted.org/packages/6e/2b/214cf0cf93db9628c3c20c896b229f327f6fb1b20e4b3743d8ad3f00af8b/cryptography-50.0.1-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:2ebbfb0f1fed745e91796e3e1080a1440423fdae8ece1b995a1d80883a409054", size = 5375862, upload-time = "2026-08-25T19:45:07.163Z" }, + { url = "https://files.pythonhosted.org/packages/d6/51/3f9701867a46b6c1740c9b52fc4d3bed6cbdcfedcc9b6e64305c07f39cff/cryptography-50.0.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:407fe2b6db00939c05c0e945e9914238f2f0a430974839429dafc82b1ee6bee5", size = 4772942, upload-time = "2026-08-25T19:45:09.396Z" }, + { url = "https://files.pythonhosted.org/packages/0d/5c/13ea642e08e2544d0f5396122055f4820cfacb3203562197b5967125ea97/cryptography-50.0.1-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:2b34d76a652ea2b6faf777c35df230c5637842cd904e04f16230c3f9f03e4361", size = 4383347, upload-time = "2026-08-25T19:45:11.659Z" }, + { url = "https://files.pythonhosted.org/packages/84/d5/7d1fe1cb93f91c428093ff234e128c89ba8ea61a6f26aab406081f9b996e/cryptography-50.0.1-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:01f41478cf33fc605a6a089cd56d28b45c6c0b45a1928b61797f2621a04bac71", size = 4758050, upload-time = "2026-08-25T19:45:13.745Z" }, + { url = "https://files.pythonhosted.org/packages/dd/04/557fc5ead96a829e0bc812a3b9dc4a52a2f27e4f7f5950da7ff27653a805/cryptography-50.0.1-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:fc3ed7ebd2a8c96f5b166de0ab9b624996bef3b07bbeb19364dfb78222c22c80", size = 5332955, upload-time = "2026-08-25T19:45:16.193Z" }, + { url = "https://files.pythonhosted.org/packages/8c/eb/5d7124083e8d8cda8f5b348f544b71ad6f707ad63193758ef4d8e569da02/cryptography-50.0.1-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9dde0a357190eb3b1da1bb9ab750e9c85cba82ca5977aa0836cbb94e92611239", size = 4772694, upload-time = "2026-08-25T19:45:18.315Z" }, + { url = "https://files.pythonhosted.org/packages/63/8e/f1f955e0921dd2b6d22eae7e8d24a4c4b638d10735ffbf6a71f99eb0fcb8/cryptography-50.0.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd3718b960d0b5dd213cdf03f3bcb7000e69dda0de8b956061947ff6bcff5558", size = 4888413, upload-time = "2026-08-25T19:45:20.4Z" }, + { url = "https://files.pythonhosted.org/packages/1f/ab/89e2b798d2c3925f82e2bb72d5979f3d2f6da2dd22ef4a8cd8b70d920039/cryptography-50.0.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2a93d05e34d5f67fba6f891fe85d929999baa7195e853923ea6d7576c9e68c5e", size = 5044355, upload-time = "2026-08-25T19:45:22.353Z" }, + { url = "https://files.pythonhosted.org/packages/99/89/87ef49ffe383ef4e147d27b7bf2088fb0b54ea409dd87b5a89442e5828a5/cryptography-50.0.1-cp39-abi3-win_amd64.whl", hash = "sha256:55d16b1ef3ee0958d893a977b19777887e546c9954ea81b200c3301a864013f2", size = 3875429, upload-time = "2026-08-25T19:45:24.418Z" }, ] [[package]] @@ -397,11 +274,11 @@ wheels = [ [[package]] name = "decorator" -version = "5.2.1" +version = "5.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711, upload-time = "2025-02-24T04:41:34.073Z" } +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/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" }, + { 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]] @@ -418,30 +295,11 @@ wheels = [ [[package]] name = "docstring-parser" -version = "0.17.0" +version = "0.18.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b2/9d/c3b43da9515bd270df0f80548d9944e389870713cc1fe2b8fb35fe2bcefd/docstring_parser-0.17.0.tar.gz", hash = "sha256:583de4a309722b3315439bb31d64ba3eebada841f2e2cee23b99df001434c912", size = 27442, upload-time = "2025-07-21T07:35:01.868Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/4d/f332313098c1de1b2d2ff91cf2674415cc7cddab2ca1b01ae29774bd5fdf/docstring_parser-0.18.0.tar.gz", hash = "sha256:292510982205c12b1248696f44959db3cdd1740237a968ea1e2e7a900eeb2015", size = 29341, upload-time = "2026-04-14T04:09:19.867Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/55/e2/2537ebcff11c1ee1ff17d8d0b6f4db75873e3b0fb32c2d4a2ee31ecb310a/docstring_parser-0.17.0-py3-none-any.whl", hash = "sha256:cf2569abd23dce8099b300f9b4fa8191e9582dda731fd533daf54c4551658708", size = 36896, upload-time = "2025-07-21T07:35:00.684Z" }, -] - -[[package]] -name = "embreex" -version = "2.17.7.post7" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy", marker = "platform_machine != 's390x'" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/99/1a/e6d80ab0104ba947e1e5a0c5251021a4bf374cfe3b0d88e2be62b150170d/embreex-2.17.7.post7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:377888d4f61b6d523e91b835aea3bd74193bd2b5fee9212adfe5fa0d5b80671b", size = 10734165, upload-time = "2025-10-22T20:07:36.707Z" }, - { url = "https://files.pythonhosted.org/packages/59/c7/410b62268fa98d9f0f2f5ab14b6e718e308a5f30dc4b9c2dcb0223d98ac8/embreex-2.17.7.post7-cp313-cp313-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c947025954a43a17a51d17be5ddbcfd1881ec5d0ea443bdf0ac4ac67e8e15e15", size = 17225042, upload-time = "2025-10-22T20:07:38.833Z" }, - { url = "https://files.pythonhosted.org/packages/c8/17/cf4bfc4f30cdaa9b64670e223d55949aae4f3d0005a8ba6559c7327477ac/embreex-2.17.7.post7-cp313-cp313-win_amd64.whl", hash = "sha256:c0f125c0517a4200a3f5d1979920704b924ec1ff6d6d0d826238216e40bf65fe", size = 13463316, upload-time = "2025-10-22T20:07:42.26Z" }, - { url = "https://files.pythonhosted.org/packages/47/30/dd0be2d0fec7ce0d032e8ebdfe6c1b3c7a70fb44821d32ff44ce5d0a389e/embreex-2.17.7.post7-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a8f8ba00c1270976f57bb608be623c64ec46d1f60a9b04dbdee2acc2b82b6224", size = 10734492, upload-time = "2025-10-22T20:07:45.211Z" }, - { url = "https://files.pythonhosted.org/packages/a5/be/ded6ef0197a112ec47a58550ad9471f879c2856169ebd3986d3e4ce1ec4f/embreex-2.17.7.post7-cp314-cp314-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a34feb5c30bebddf71a94c5b529646542e4209c0879c2644615744f1f407a60f", size = 17216580, upload-time = "2025-10-22T20:07:47.357Z" }, - { url = "https://files.pythonhosted.org/packages/bb/29/c814b7696fed98e08d524c856d92c36356e32ba80cb5edf419f2031025a4/embreex-2.17.7.post7-cp314-cp314-win_amd64.whl", hash = "sha256:13b2ecbaeb016a64d278835960223e78fd9266f63f74f1cbc4d427335994c4b5", size = 13755202, upload-time = "2025-10-22T20:07:49.909Z" }, - { url = "https://files.pythonhosted.org/packages/32/2c/34bdcd616daeb1a7066d28af419ef5728bd28b74c1b4c5658c4945809bdf/embreex-2.17.7.post7-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2341a479cb996e2e4e36cabc499c61d81d8e67acc8f2b3c1eb1628ccacde88a1", size = 10742295, upload-time = "2025-10-22T20:07:51.997Z" }, - { url = "https://files.pythonhosted.org/packages/8c/5c/940683357ad9aa52ecb0fc040e8f4422bcd2fd7596654f5ccfe7f2c43b44/embreex-2.17.7.post7-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f943bb39ea6494e20632ff02613c7cff730a8349ceb572ae4742b2201f2de2f", size = 17364382, upload-time = "2025-10-22T20:07:54.373Z" }, - { url = "https://files.pythonhosted.org/packages/f4/c3/d6d020ecb8701aba43fe210d038459dfc8e1e3d3baf9365163362f50c0ec/embreex-2.17.7.post7-cp314-cp314t-win_amd64.whl", hash = "sha256:29c95e5f6487db6a4f71fcce80d8b1f318e454b936e6318babcd478ae68e0acf", size = 13771100, upload-time = "2025-10-22T20:07:58.099Z" }, + { url = "https://files.pythonhosted.org/packages/a7/5f/ed01f9a3cdffbd5a008556fc7b2a08ddb1cc6ace7effa7340604b1d16699/docstring_parser-0.18.0-py3-none-any.whl", hash = "sha256:b3fcbed555c47d8479be0796ef7e19c2670d428d72e96da63f3a40122860374b", size = 22484, upload-time = "2026-04-14T04:09:18.638Z" }, ] [[package]] @@ -471,7 +329,7 @@ wheels = [ [[package]] name = "fabric" -version = "3.2.2" +version = "3.2.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "decorator" }, @@ -479,60 +337,44 @@ dependencies = [ { name = "invoke" }, { name = "paramiko" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0d/3f/337f278b70ba339c618a490f6b8033b7006c583bd197a897f12fbc468c51/fabric-3.2.2.tar.gz", hash = "sha256:8783ca42e3b0076f08b26901aac6b9d9b1f19c410074e7accfab902c184ff4a3", size = 183215, upload-time = "2023-08-31T01:42:05.55Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/7e/29cd6237c3b7ce79c3ca945eb99ab5affd101db54b2f7a78dde0cfa19fd4/fabric-3.2.3.tar.gz", hash = "sha256:dcbd2c47ad87688facaef5cc11aab6d1ec9ed05645fed97a5de7204d5d17cc44", size = 183497, upload-time = "2026-04-06T00:00:11.481Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d6/1f/e99e23ee01847147fa194e8d41cfcf2535a2dbfcb51414c541cadb15c5d7/fabric-3.2.2-py3-none-any.whl", hash = "sha256:91c47c0be68b14936c88b34da8a1f55e5710fd28397dac5d4ff2e21558113a6f", size = 59417, upload-time = "2023-08-31T01:42:03.917Z" }, + { url = "https://files.pythonhosted.org/packages/37/f9/f8497ef8b873a8bb2a750ee2a6c5f0fc22258e1acb6245fd237042a6c279/fabric-3.2.3-py3-none-any.whl", hash = "sha256:ce61917f4f398018337ce279b357650a3a74baecf3fdd53a5839013944af965e", size = 59502, upload-time = "2026-04-06T00:00:10.176Z" }, ] [[package]] name = "filelock" -version = "3.25.2" +version = "3.32.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/b8/00651a0f559862f3bb7d6f7477b192afe3f583cc5e26403b44e59a55ab34/filelock-3.25.2.tar.gz", hash = "sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694", size = 40480, upload-time = "2026-03-11T20:45:38.487Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/30/03b03951873a1a0ffc7e8ca0e10c15597b59e8d0e39260704cd2ea087bc4/filelock-3.32.4.tar.gz", hash = "sha256:2bde2e4cf732e0153406d8a7bc80620ecf5e621fe0d25e41143c4e3b4733ff30", size = 222126, upload-time = "2026-08-23T17:37:55.363Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl", hash = "sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70", size = 26759, upload-time = "2026-03-11T20:45:37.437Z" }, + { url = "https://files.pythonhosted.org/packages/01/a4/9b63d595d748e3aff8812b65eacc1a2c4bd90b7c2012e08e72373b4835eb/filelock-3.32.4-py3-none-any.whl", hash = "sha256:22e58ca3b1ae3b98993b762d7338367ae64fe50252bf78d59da3bfebcdf1cedd", size = 99864, upload-time = "2026-08-23T17:37:53.913Z" }, ] [[package]] name = "fonttools" -version = "4.62.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9a/08/7012b00a9a5874311b639c3920270c36ee0c445b69d9989a85e5c92ebcb0/fonttools-4.62.1.tar.gz", hash = "sha256:e54c75fd6041f1122476776880f7c3c3295ffa31962dc6ebe2543c00dca58b5d", size = 3580737, upload-time = "2026-03-13T13:54:25.52Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/56/6f389de21c49555553d6a5aeed5ac9767631497ac836c4f076273d15bd72/fonttools-4.62.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c22b1014017111c401469e3acc5433e6acf6ebcc6aa9efb538a533c800971c79", size = 2865155, upload-time = "2026-03-13T13:53:16.132Z" }, - { url = "https://files.pythonhosted.org/packages/03/c5/0e3966edd5ec668d41dfe418787726752bc07e2f5fd8c8f208615e61fa89/fonttools-4.62.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:68959f5fc58ed4599b44aad161c2837477d7f35f5f79402d97439974faebfebe", size = 2412802, upload-time = "2026-03-13T13:53:18.878Z" }, - { url = "https://files.pythonhosted.org/packages/52/94/e6ac4b44026de7786fe46e3bfa0c87e51d5d70a841054065d49cd62bb909/fonttools-4.62.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef46db46c9447103b8f3ff91e8ba009d5fe181b1920a83757a5762551e32bb68", size = 5013926, upload-time = "2026-03-13T13:53:21.379Z" }, - { url = "https://files.pythonhosted.org/packages/e2/98/8b1e801939839d405f1f122e7d175cebe9aeb4e114f95bfc45e3152af9a7/fonttools-4.62.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6706d1cb1d5e6251a97ad3c1b9347505c5615c112e66047abbef0f8545fa30d1", size = 4964575, upload-time = "2026-03-13T13:53:23.857Z" }, - { url = "https://files.pythonhosted.org/packages/46/76/7d051671e938b1881670528fec69cc4044315edd71a229c7fd712eaa5119/fonttools-4.62.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2e7abd2b1e11736f58c1de27819e1955a53267c21732e78243fa2fa2e5c1e069", size = 4953693, upload-time = "2026-03-13T13:53:26.569Z" }, - { url = "https://files.pythonhosted.org/packages/1f/ae/b41f8628ec0be3c1b934fc12b84f4576a5c646119db4d3bdd76a217c90b5/fonttools-4.62.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:403d28ce06ebfc547fbcb0cb8b7f7cc2f7a2d3e1a67ba9a34b14632df9e080f9", size = 5094920, upload-time = "2026-03-13T13:53:29.329Z" }, - { url = "https://files.pythonhosted.org/packages/f2/f6/53a1e9469331a23dcc400970a27a4caa3d9f6edbf5baab0260285238b884/fonttools-4.62.1-cp313-cp313-win32.whl", hash = "sha256:93c316e0f5301b2adbe6a5f658634307c096fd5aae60a5b3412e4f3e1728ab24", size = 2279928, upload-time = "2026-03-13T13:53:32.352Z" }, - { url = "https://files.pythonhosted.org/packages/38/60/35186529de1db3c01f5ad625bde07c1f576305eab6d86bbda4c58445f721/fonttools-4.62.1-cp313-cp313-win_amd64.whl", hash = "sha256:7aa21ff53e28a9c2157acbc44e5b401149d3c9178107130e82d74ceb500e5056", size = 2330514, upload-time = "2026-03-13T13:53:34.991Z" }, - { url = "https://files.pythonhosted.org/packages/36/f0/2888cdac391807d68d90dcb16ef858ddc1b5309bfc6966195a459dd326e2/fonttools-4.62.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fa1d16210b6b10a826d71bed68dd9ec24a9e218d5a5e2797f37c573e7ec215ca", size = 2864442, upload-time = "2026-03-13T13:53:37.509Z" }, - { url = "https://files.pythonhosted.org/packages/4b/b2/e521803081f8dc35990816b82da6360fa668a21b44da4b53fc9e77efcd62/fonttools-4.62.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:aa69d10ed420d8121118e628ad47d86e4caa79ba37f968597b958f6cceab7eca", size = 2410901, upload-time = "2026-03-13T13:53:40.55Z" }, - { url = "https://files.pythonhosted.org/packages/00/a4/8c3511ff06e53110039358dbbdc1a65d72157a054638387aa2ada300a8b8/fonttools-4.62.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd13b7999d59c5eb1c2b442eb2d0c427cb517a0b7a1f5798fc5c9e003f5ff782", size = 4999608, upload-time = "2026-03-13T13:53:42.798Z" }, - { url = "https://files.pythonhosted.org/packages/28/63/cd0c3b26afe60995a5295f37c246a93d454023726c3261cfbb3559969bb9/fonttools-4.62.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8d337fdd49a79b0d51c4da87bc38169d21c3abbf0c1aa9367eff5c6656fb6dae", size = 4912726, upload-time = "2026-03-13T13:53:45.405Z" }, - { url = "https://files.pythonhosted.org/packages/70/b9/ac677cb07c24c685cf34f64e140617d58789d67a3dd524164b63648c6114/fonttools-4.62.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d241cdc4a67b5431c6d7f115fdf63335222414995e3a1df1a41e1182acd4bcc7", size = 4951422, upload-time = "2026-03-13T13:53:48.326Z" }, - { url = "https://files.pythonhosted.org/packages/e6/10/11c08419a14b85b7ca9a9faca321accccc8842dd9e0b1c8a72908de05945/fonttools-4.62.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c05557a78f8fa514da0f869556eeda40887a8abc77c76ee3f74cf241778afd5a", size = 5060979, upload-time = "2026-03-13T13:53:51.366Z" }, - { url = "https://files.pythonhosted.org/packages/4e/3c/12eea4a4cf054e7ab058ed5ceada43b46809fce2bf319017c4d63ae55bb4/fonttools-4.62.1-cp314-cp314-win32.whl", hash = "sha256:49a445d2f544ce4a69338694cad575ba97b9a75fff02720da0882d1a73f12800", size = 2283733, upload-time = "2026-03-13T13:53:53.606Z" }, - { url = "https://files.pythonhosted.org/packages/6b/67/74b070029043186b5dd13462c958cb7c7f811be0d2e634309d9a1ffb1505/fonttools-4.62.1-cp314-cp314-win_amd64.whl", hash = "sha256:1eecc128c86c552fb963fe846ca4e011b1be053728f798185a1687502f6d398e", size = 2335663, upload-time = "2026-03-13T13:53:56.23Z" }, - { url = "https://files.pythonhosted.org/packages/42/c5/4d2ed3ca6e33617fc5624467da353337f06e7f637707478903c785bd8e20/fonttools-4.62.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:1596aeaddf7f78e21e68293c011316a25267b3effdaccaf4d59bc9159d681b82", size = 2947288, upload-time = "2026-03-13T13:53:59.397Z" }, - { url = "https://files.pythonhosted.org/packages/1f/e9/7ab11ddfda48ed0f89b13380e5595ba572619c27077be0b2c447a63ff351/fonttools-4.62.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:8f8fca95d3bb3208f59626a4b0ea6e526ee51f5a8ad5d91821c165903e8d9260", size = 2449023, upload-time = "2026-03-13T13:54:01.642Z" }, - { url = "https://files.pythonhosted.org/packages/b2/10/a800fa090b5e8819942e54e19b55fc7c21fe14a08757c3aa3ca8db358939/fonttools-4.62.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee91628c08e76f77b533d65feb3fbe6d9dad699f95be51cf0d022db94089cdc4", size = 5137599, upload-time = "2026-03-13T13:54:04.495Z" }, - { url = "https://files.pythonhosted.org/packages/37/dc/8ccd45033fffd74deb6912fa1ca524643f584b94c87a16036855b498a1ed/fonttools-4.62.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5f37df1cac61d906e7b836abe356bc2f34c99d4477467755c216b72aa3dc748b", size = 4920933, upload-time = "2026-03-13T13:54:07.557Z" }, - { url = "https://files.pythonhosted.org/packages/99/eb/e618adefb839598d25ac8136cd577925d6c513dc0d931d93b8af956210f0/fonttools-4.62.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:92bb00a947e666169c99b43753c4305fc95a890a60ef3aeb2a6963e07902cc87", size = 5016232, upload-time = "2026-03-13T13:54:10.611Z" }, - { url = "https://files.pythonhosted.org/packages/d9/5f/9b5c9bfaa8ec82def8d8168c4f13615990d6ce5996fe52bd49bfb5e05134/fonttools-4.62.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:bdfe592802ef939a0e33106ea4a318eeb17822c7ee168c290273cbd5fabd746c", size = 5042987, upload-time = "2026-03-13T13:54:13.569Z" }, - { url = "https://files.pythonhosted.org/packages/90/aa/dfbbe24c6a6afc5c203d90cc0343e24bcbb09e76d67c4d6eef8c2558d7ba/fonttools-4.62.1-cp314-cp314t-win32.whl", hash = "sha256:b820fcb92d4655513d8402d5b219f94481c4443d825b4372c75a2072aa4b357a", size = 2348021, upload-time = "2026-03-13T13:54:16.98Z" }, - { url = "https://files.pythonhosted.org/packages/13/6f/ae9c4e4dd417948407b680855c2c7790efb52add6009aaecff1e3bc50e8e/fonttools-4.62.1-cp314-cp314t-win_amd64.whl", hash = "sha256:59b372b4f0e113d3746b88985f1c796e7bf830dd54b28374cd85c2b8acd7583e", size = 2414147, upload-time = "2026-03-13T13:54:19.416Z" }, - { url = "https://files.pythonhosted.org/packages/fd/ba/56147c165442cc5ba7e82ecf301c9a68353cede498185869e6e02b4c264f/fonttools-4.62.1-py3-none-any.whl", hash = "sha256:7487782e2113861f4ddcc07c3436450659e3caa5e470b27dc2177cade2d8e7fd", size = 1152647, upload-time = "2026-03-13T13:54:22.735Z" }, +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/0f/8d/d8fec3dcde2963f8c908fb315e5ff2cd0ac34f82394bbbf73a2aa5145ce3/fonttools-4.63.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:cd7e9857e5e63738b9d9fd707bc1f59c8b09e5177726d23664db393c59bb08bd", size = 2876062, upload-time = "2026-05-14T12:03:32.554Z" }, + { url = "https://files.pythonhosted.org/packages/ef/71/d935dc54e4ff121bfdd11e08702db63a7e6f25af21d8a3d7b7212df53641/fonttools-4.63.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c2a2a42198b696a6f48fad91709afb55176e66a5e566131219dba372fb7f8c59", size = 2424594, upload-time = "2026-05-14T12:03:34.86Z" }, + { url = "https://files.pythonhosted.org/packages/8e/40/e76320afa1df918e146155ef239b1719ee266092e96f5423bfd075affba1/fonttools-4.63.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e874792a8212b44583ea02189d9e693906b2f78b261f372f95d6c563210ac1d", size = 5024840, upload-time = "2026-05-14T12:03:36.745Z" }, + { url = "https://files.pythonhosted.org/packages/ce/36/0b805d8c485f872f65a509cbe3b58a5d0d17bee855333b54a150c79d3061/fonttools-4.63.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:22135da48a348785c5e2d5d2d9d6bec5ed44adacbaeb9db12d9493bf6c6bfa68", size = 4975801, upload-time = "2026-05-14T12:03:38.833Z" }, + { url = "https://files.pythonhosted.org/packages/c8/26/2cee03d0aa083ab022da5c07aff9ed3f689da1defb81ad6917c9627896da/fonttools-4.63.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ccf41f2efdf56994d22d73bef4ced1052161958169428d06ba9724ea9e9a64be", size = 4965009, upload-time = "2026-05-14T12:03:41.494Z" }, + { url = "https://files.pythonhosted.org/packages/7e/48/cc4b66d9058c0d0982c833fad10127c4b0e9324606aafa41382295ca4102/fonttools-4.63.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9ced0bd02ac751dd6319b0da88aaef24414e3b0dbc32bb4f24944821a3741a27", size = 5105892, upload-time = "2026-05-14T12:03:43.525Z" }, + { url = "https://files.pythonhosted.org/packages/d8/1f/a98a30a814b9ddef3a2e706025f90b9e0bc94890e6cb15254bc86547d11a/fonttools-4.63.0-cp313-cp313-win32.whl", hash = "sha256:85be818f5506e8a7753153def2c9550178f0ecae6a47b5e0e8dbb23f7cc90380", size = 2291313, upload-time = "2026-05-14T12:03:45.594Z" }, + { url = "https://files.pythonhosted.org/packages/92/46/5177b01f3b4abfdd4409f31cca4ab279c9343a26efbe9ec78c97fc612e02/fonttools-4.63.0-cp313-cp313-win_amd64.whl", hash = "sha256:ba04cb5891d4c0c21b6da95eda8d7b090021508a294fff33464fc7d241e0856b", size = 2342299, upload-time = "2026-05-14T12:03:47.414Z" }, + { 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 = "fsspec" -version = "2026.3.0" +version = "2026.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e1/cf/b50ddf667c15276a9ab15a70ef5f257564de271957933ffea49d2cdbcdfb/fsspec-2026.3.0.tar.gz", hash = "sha256:1ee6a0e28677557f8c2f994e3eea77db6392b4de9cd1f5d7a9e87a0ae9d01b41", size = 313547, upload-time = "2026-03-27T19:11:14.892Z" } +sdist = { url = "https://files.pythonhosted.org/packages/00/78/f34251dadb8f3921264a1d9b8946f5e542014ee2614b285261b4e40e6775/fsspec-2026.7.0.tar.gz", hash = "sha256:c803c40f4cf860b49dea58ee3e1c33cb9c790520e233537e1340049f89b82a88", size = 317040, upload-time = "2026-07-28T16:34:51.052Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d5/1f/5f4a3cd9e4440e9d9bc78ad0a91a1c8d46b4d429d5239ebe6793c9fe5c41/fsspec-2026.3.0-py3-none-any.whl", hash = "sha256:d2ceafaad1b3457968ed14efa28798162f1638dbb5d2a6868a2db002a5ee39a4", size = 202595, upload-time = "2026-03-27T19:11:13.595Z" }, + { url = "https://files.pythonhosted.org/packages/fd/3c/6a2bf344106328fd04963664a60b9bb6496fc25df8e962fcdc1367285fb9/fsspec-2026.7.0-py3-none-any.whl", hash = "sha256:b57ddbafedfaef7018c1ecab32aa200a9d7ca26b77965f64e48b70061249d279", size = 206583, upload-time = "2026-07-28T16:34:49.538Z" }, ] [[package]] @@ -549,136 +391,73 @@ wheels = [ [[package]] name = "gitpython" -version = "3.1.46" +version = "3.1.60" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "gitdb" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/b5/59d16470a1f0dfe8c793f9ef56fd3826093fc52b3bd96d6b9d6c26c7e27b/gitpython-3.1.46.tar.gz", hash = "sha256:400124c7d0ef4ea03f7310ac2fbf7151e09ff97f2a3288d64a440c584a29c37f", size = 215371, upload-time = "2026-01-01T15:37:32.073Z" } +sdist = { url = "https://files.pythonhosted.org/packages/84/14/e6b1a48d831755a53c2029351fcef82e70db4a08f338daefe29d8d0cf31c/gitpython-3.1.60.tar.gz", hash = "sha256:e936431879fa85581b4311fa63492ea52251909e2d655b6529c704c904ddcc24", size = 230793, upload-time = "2026-08-25T18:33:46.102Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/09/e21df6aef1e1ffc0c816f0522ddc3f6dcded766c3261813131c78a704470/gitpython-3.1.46-py3-none-any.whl", hash = "sha256:79812ed143d9d25b6d176a10bb511de0f9c67b1fa641d82097b0ab90398a2058", size = 208620, upload-time = "2026-01-01T15:37:30.574Z" }, + { url = "https://files.pythonhosted.org/packages/71/63/ba28697918b7c190af9f3f21940d03e8814e25dd4ddd39d6929f3a553995/gitpython-3.1.60-py3-none-any.whl", hash = "sha256:39548bffb8fa0f3a548133348868bb4838e79d73283052207dc97781a569b6b4", size = 221893, upload-time = "2026-08-25T18:33:44.75Z" }, ] [[package]] name = "glfw" -version = "2.10.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/72/642d4f12f61816ac96777f7360d413e3977a7dd08237d196f02da681b186/glfw-2.10.0.tar.gz", hash = "sha256:801e55d8581b34df9aa2cfea43feb06ff617576e2a8cc5dac23ee75b26d10abe", size = 31475, upload-time = "2025-09-12T08:54:38.871Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/1f/a9ce08b1173b0ab625ee92f0c47a5278b3e76fd367699880d8ee7d56c338/glfw-2.10.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.p39.p310.p311.p312.p313-none-macosx_10_6_intel.whl", hash = "sha256:5f365a8c94bcea71ec91327e7c16e7cf739128479a18b8c1241b004b40acc412", size = 105329, upload-time = "2025-09-12T08:54:27.938Z" }, - { url = "https://files.pythonhosted.org/packages/7c/96/5a2220abcbd027eebcf8bedd28207a2de168899e51be13ba01ebdd4147a1/glfw-2.10.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.p39.p310.p311.p312.p313-none-macosx_11_0_arm64.whl", hash = "sha256:5328db1a92d07abd988730517ec02aa8390d3e6ef7ce98c8b57ecba2f43a39ba", size = 102179, upload-time = "2025-09-12T08:54:29.163Z" }, - { url = "https://files.pythonhosted.org/packages/9d/41/a5bd1d9e1808f400102bd7d328c4ac17b65fb2fc8014014ec6f23d02f662/glfw-2.10.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.p39.p310.p311.p312.p313-none-manylinux2014_aarch64.whl", hash = "sha256:312c4c1dd5509613ed6bc1e95a8dbb75a36b6dcc4120f50dc3892b40172e9053", size = 230039, upload-time = "2025-09-12T08:54:30.201Z" }, - { url = "https://files.pythonhosted.org/packages/80/aa/3b503c448609dee6cb4e7138b4109338f0e65b97be107ab85562269d378d/glfw-2.10.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.p39.p310.p311.p312.p313-none-manylinux2014_x86_64.whl", hash = "sha256:59c53387dc08c62e8bed86bbe3a8d53ab1b27161281ffa0e7f27b64284e2627c", size = 241984, upload-time = "2025-09-12T08:54:31.347Z" }, - { url = "https://files.pythonhosted.org/packages/ac/2d/bfe39a42cad8e80b02bf5f7cae19ba67832c1810bbd3624a8e83153d74a4/glfw-2.10.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.p39.p310.p311.p312.p313-none-manylinux_2_28_aarch64.whl", hash = "sha256:c6f292fdaf3f9a99e598ede6582d21c523a6f51f8f5e66213849101a6bcdc699", size = 231052, upload-time = "2025-09-12T08:54:32.859Z" }, - { url = "https://files.pythonhosted.org/packages/f7/02/6e639e90f181dc9127046e00d0528f9f7ad12d428972e3a5378b9aefdb0b/glfw-2.10.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.p39.p310.p311.p312.p313-none-manylinux_2_28_x86_64.whl", hash = "sha256:7916034efa867927892635733a3b6af8cd95ceb10566fd7f1e0d2763c2ee8b12", size = 243525, upload-time = "2025-09-12T08:54:34.006Z" }, - { url = "https://files.pythonhosted.org/packages/84/06/cb588ca65561defe0fc48d1df4c2ac12569b81231ae4f2b52ab37007d0bd/glfw-2.10.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.p39.p310.p311.p312.p313-none-win32.whl", hash = "sha256:6c9549da71b93e367b4d71438798daae1da2592039fd14204a80a1a2348ae127", size = 552685, upload-time = "2025-09-12T08:54:35.723Z" }, - { url = "https://files.pythonhosted.org/packages/86/27/00c9c96af18ac0a5eac2ff61cbe306551a2d770d7173f396d0792ee1a59e/glfw-2.10.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.p39.p310.p311.p312.p313-none-win_amd64.whl", hash = "sha256:6292d5d6634d668cd23d337e6089491d3945a9aa4ac6e1667b0003520d7caa51", size = 559466, upload-time = "2025-09-12T08:54:37.661Z" }, - { url = "https://files.pythonhosted.org/packages/b3/87/de0b33f6f00687499ca1371f22aa73396341b85bf88f1a284f9da8842493/glfw-2.10.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.py39.py310.py311.py312.py313.py314-none-macosx_10_6_intel.whl", hash = "sha256:2aab89d2d9535635ba011fc7303390685169a1aa6731ad580d08d043524b8899", size = 105326, upload-time = "2026-01-28T05:57:56.083Z" }, - { url = "https://files.pythonhosted.org/packages/b6/a6/6ea2f73ad4474896d9e38b3ffbe6ffd5a802c738392269e99e8c6621a461/glfw-2.10.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.py39.py310.py311.py312.py313.py314-none-macosx_11_0_arm64.whl", hash = "sha256:23936202a107039b5372f0b88ae1d11080746aa1c78910a45d4a0c4cf408cfaa", size = 102180, upload-time = "2026-01-28T05:57:57.787Z" }, - { url = "https://files.pythonhosted.org/packages/58/19/d81b19e8261b9cb51b81d1402167791fef81088dfe91f0c4e9d136fdc5ca/glfw-2.10.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.py39.py310.py311.py312.py313.py314-none-manylinux2014_aarch64.whl", hash = "sha256:7be06d0838f61df67bd54cb6266a6193d54083acb3624ff3c3812a6358406fa4", size = 230038, upload-time = "2026-01-28T05:57:59.105Z" }, - { url = "https://files.pythonhosted.org/packages/e2/fa/b035636cd82198b97b51a93efe9cfc4343d6b15cefbd336a3f2be871d848/glfw-2.10.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.py39.py310.py311.py312.py313.py314-none-manylinux2014_x86_64.whl", hash = "sha256:91d36b3582a766512eff8e3b5dcc2d3ffcbf10b7cf448551085a08a10f1b8244", size = 241983, upload-time = "2026-01-28T05:58:00.352Z" }, - { url = "https://files.pythonhosted.org/packages/ff/b4/f7b6cc022dd7c68b6c702d19da5d591f978f89c958b9bd3090615db0c739/glfw-2.10.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.py39.py310.py311.py312.py313.py314-none-manylinux_2_28_aarch64.whl", hash = "sha256:27c9e9a2d5e1dc3c9e3996171d844d9df9a5a101e797cb94cce217b7afcf8fd9", size = 231053, upload-time = "2026-01-28T05:58:01.683Z" }, - { url = "https://files.pythonhosted.org/packages/5a/3f/efeb7c6801c46e11bd666a5180f0d615f74f72264212f74f39586c6fda9d/glfw-2.10.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.py39.py310.py311.py312.py313.py314-none-manylinux_2_28_x86_64.whl", hash = "sha256:ce6724bb7cb3d0543dcba17206dce909f94176e68220b8eafee72e9f92bcf542", size = 243522, upload-time = "2026-01-28T05:58:03.517Z" }, - { url = "https://files.pythonhosted.org/packages/cf/b9/b04c3aa0aad2870cfe799f32f8b59789c98e1816bbce9e83f4823c5b840b/glfw-2.10.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.py39.py310.py311.py312.py313.py314-none-win32.whl", hash = "sha256:fca724a21a372731edb290841edd28a9fb1ee490f833392752844ac807c0086a", size = 552682, upload-time = "2026-01-28T05:58:05.649Z" }, - { url = "https://files.pythonhosted.org/packages/bd/e1/6d6816b296a529ac9b897ad228b1e084eb1f92319e96371880eebdc874a6/glfw-2.10.0-py2.py27.py3.py30.py31.py32.py33.py34.py35.py36.py37.py38.py39.py310.py311.py312.py313.py314-none-win_amd64.whl", hash = "sha256:823c0bd7770977d4b10e0ed0aef2f3682276b7c88b8b65cfc540afce5951392f", size = 559464, upload-time = "2026-01-28T05:58:07.261Z" }, - { url = "https://files.pythonhosted.org/packages/8c/a8/d4dab8a58fc2e6981fc7a58c4e56ba9d777fb24931cec6a22152edbb3540/glfw-2.10.0-py2.py3-none-macosx_10_6_intel.whl", hash = "sha256:a0d1f29f206219cc291edfb6cace663a86da2470632551c998e3db82d48ea177", size = 105288, upload-time = "2026-03-10T17:21:19.929Z" }, - { url = "https://files.pythonhosted.org/packages/14/61/68d35e001872a7705112418da236fa2418d4f2e5419f8b2837f9b81bb3da/glfw-2.10.0-py2.py3-none-macosx_11_0_arm64.whl", hash = "sha256:d28d6f3ef217e64e35dc6fd0a7acb4cec9bfe7cd14dd9b35a7228a87002de154", size = 102139, upload-time = "2026-03-10T17:21:21.645Z" }, - { url = "https://files.pythonhosted.org/packages/4e/e1/ca5984081aaae07c9d371cb11dc4e4ff603510678ed9b73e58b6c351fe63/glfw-2.10.0-py2.py3-none-manylinux2014_aarch64.whl", hash = "sha256:f968b522bb6a0e04aaf4dcac30a476d7229308bb2bac406a60587debb5a61e29", size = 229998, upload-time = "2026-03-10T17:21:23.549Z" }, - { url = "https://files.pythonhosted.org/packages/fa/c4/82ac75fdcfba2896da7a573c0fc7f8ceb8f77ead6866d500d06c32f1c464/glfw-2.10.0-py2.py3-none-manylinux2014_x86_64.whl", hash = "sha256:68cf3752bdadb6f4bc0a876247c28c88c7251ac39f8af076ed938fdfd71e72dd", size = 241944, upload-time = "2026-03-10T17:21:26.102Z" }, - { url = "https://files.pythonhosted.org/packages/e3/96/9f691823cca5eb6a08f346bd0ff03b78032db9370b509a1e9c8976fb20a5/glfw-2.10.0-py2.py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:44d98de5dbf8f727e0cb29f9b29d29528ea7570f2e6f42f8430a69df05f12b48", size = 231009, upload-time = "2026-03-10T17:21:28.481Z" }, - { url = "https://files.pythonhosted.org/packages/3f/93/977b9e679e356871d428ae7a1139ec767dd5177bed58a6344b4d2199e00f/glfw-2.10.0-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:cca5158d62189e08792b1ae54f92307a282921a0e7783315b467e21b0a381c88", size = 243480, upload-time = "2026-03-10T17:21:30.538Z" }, - { url = "https://files.pythonhosted.org/packages/f9/bd/cea9569c8f2188b0a104472951420434a3e1f5cf26f5836ef9d7227a1a30/glfw-2.10.0-py2.py3-none-win32.whl", hash = "sha256:5e024509989740e8e7b86cc4aab508195495f79879072b0e1f68bd036a2916ad", size = 552641, upload-time = "2026-03-10T17:21:32.653Z" }, - { url = "https://files.pythonhosted.org/packages/cc/9b/4366ad3e1c0688146c70aa6143584d6a8d88583b9390f106250e25a3d5cd/glfw-2.10.0-py2.py3-none-win_amd64.whl", hash = "sha256:7f787ee8645781f10e8800438ce4357ab38c573ffb191aba380c1e72eba6311c", size = 559423, upload-time = "2026-03-10T17:21:34.766Z" }, -] - -[[package]] -name = "grpcio" -version = "1.80.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b7/48/af6173dbca4454f4637a4678b67f52ca7e0c1ed7d5894d89d434fecede05/grpcio-1.80.0.tar.gz", hash = "sha256:29aca15edd0688c22ba01d7cc01cb000d72b2033f4a3c72a81a19b56fd143257", size = 12978905, upload-time = "2026-03-30T08:49:10.502Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2f/3a/7c3c25789e3f069e581dc342e03613c5b1cb012c4e8c7d9d5cf960a75856/grpcio-1.80.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:e9e408fc016dffd20661f0126c53d8a31c2821b5c13c5d67a0f5ed5de93319ad", size = 6017243, upload-time = "2026-03-30T08:47:40.075Z" }, - { url = "https://files.pythonhosted.org/packages/04/19/21a9806eb8240e174fd1ab0cd5b9aa948bb0e05c2f2f55f9d5d7405e6d08/grpcio-1.80.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:92d787312e613754d4d8b9ca6d3297e69994a7912a32fa38c4c4e01c272974b0", size = 12010840, upload-time = "2026-03-30T08:47:43.11Z" }, - { url = "https://files.pythonhosted.org/packages/18/3a/23347d35f76f639e807fb7a36fad3068aed100996849a33809591f26eca6/grpcio-1.80.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8ac393b58aa16991a2f1144ec578084d544038c12242da3a215966b512904d0f", size = 6567644, upload-time = "2026-03-30T08:47:46.806Z" }, - { url = "https://files.pythonhosted.org/packages/ff/40/96e07ecb604a6a67ae6ab151e3e35b132875d98bc68ec65f3e5ab3e781d7/grpcio-1.80.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:68e5851ac4b9afe07e7f84483803ad167852570d65326b34d54ca560bfa53fb6", size = 7277830, upload-time = "2026-03-30T08:47:49.643Z" }, - { url = "https://files.pythonhosted.org/packages/9b/e2/da1506ecea1f34a5e365964644b35edef53803052b763ca214ba3870c856/grpcio-1.80.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:873ff5d17d68992ef6605330127425d2fc4e77e612fa3c3e0ed4e668685e3140", size = 6783216, upload-time = "2026-03-30T08:47:52.817Z" }, - { url = "https://files.pythonhosted.org/packages/44/83/3b20ff58d0c3b7f6caaa3af9a4174d4023701df40a3f39f7f1c8e7c48f9d/grpcio-1.80.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2bea16af2750fd0a899bf1abd9022244418b55d1f37da2202249ba4ba673838d", size = 7385866, upload-time = "2026-03-30T08:47:55.687Z" }, - { url = "https://files.pythonhosted.org/packages/47/45/55c507599c5520416de5eefecc927d6a0d7af55e91cfffb2e410607e5744/grpcio-1.80.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba0db34f7e1d803a878284cd70e4c63cb6ae2510ba51937bf8f45ba997cefcf7", size = 8391602, upload-time = "2026-03-30T08:47:58.303Z" }, - { url = "https://files.pythonhosted.org/packages/10/bb/dd06f4c24c01db9cf11341b547d0a016b2c90ed7dbbb086a5710df7dd1d7/grpcio-1.80.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8eb613f02d34721f1acf3626dfdb3545bd3c8505b0e52bf8b5710a28d02e8aa7", size = 7826752, upload-time = "2026-03-30T08:48:01.311Z" }, - { url = "https://files.pythonhosted.org/packages/f9/1e/9d67992ba23371fd63d4527096eb8c6b76d74d52b500df992a3343fd7251/grpcio-1.80.0-cp313-cp313-win32.whl", hash = "sha256:93b6f823810720912fd131f561f91f5fed0fda372b6b7028a2681b8194d5d294", size = 4142310, upload-time = "2026-03-30T08:48:04.594Z" }, - { url = "https://files.pythonhosted.org/packages/cf/e6/283326a27da9e2c3038bc93eeea36fb118ce0b2d03922a9cda6688f53c5b/grpcio-1.80.0-cp313-cp313-win_amd64.whl", hash = "sha256:e172cf795a3ba5246d3529e4d34c53db70e888fa582a8ffebd2e6e48bc0cba50", size = 4882833, upload-time = "2026-03-30T08:48:07.363Z" }, - { url = "https://files.pythonhosted.org/packages/c5/6d/e65307ce20f5a09244ba9e9d8476e99fb039de7154f37fb85f26978b59c3/grpcio-1.80.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:3d4147a97c8344d065d01bbf8b6acec2cf86fb0400d40696c8bdad34a64ffc0e", size = 6017376, upload-time = "2026-03-30T08:48:10.005Z" }, - { url = "https://files.pythonhosted.org/packages/69/10/9cef5d9650c72625a699c549940f0abb3c4bfdb5ed45a5ce431f92f31806/grpcio-1.80.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:d8e11f167935b3eb089ac9038e1a063e6d7dbe995c0bb4a661e614583352e76f", size = 12018133, upload-time = "2026-03-30T08:48:12.927Z" }, - { url = "https://files.pythonhosted.org/packages/04/82/983aabaad82ba26113caceeb9091706a0696b25da004fe3defb5b346e15b/grpcio-1.80.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f14b618fc30de822681ee986cfdcc2d9327229dc4c98aed16896761cacd468b9", size = 6574748, upload-time = "2026-03-30T08:48:16.386Z" }, - { url = "https://files.pythonhosted.org/packages/07/d7/031666ef155aa0bf399ed7e19439656c38bbd143779ae0861b038ce82abd/grpcio-1.80.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4ed39fbdcf9b87370f6e8df4e39ca7b38b3e5e9d1b0013c7b6be9639d6578d14", size = 7277711, upload-time = "2026-03-30T08:48:19.627Z" }, - { url = "https://files.pythonhosted.org/packages/e8/43/f437a78f7f4f1d311804189e8f11fb311a01049b2e08557c1068d470cb2e/grpcio-1.80.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2dcc70e9f0ba987526e8e8603a610fb4f460e42899e74e7a518bf3c68fe1bf05", size = 6785372, upload-time = "2026-03-30T08:48:22.373Z" }, - { url = "https://files.pythonhosted.org/packages/93/3d/f6558e9c6296cb4227faa5c43c54a34c68d32654b829f53288313d16a86e/grpcio-1.80.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:448c884b668b868562b1bda833c5fce6272d26e1926ec46747cda05741d302c1", size = 7395268, upload-time = "2026-03-30T08:48:25.638Z" }, - { url = "https://files.pythonhosted.org/packages/06/21/0fdd77e84720b08843c371a2efa6f2e19dbebf56adc72df73d891f5506f0/grpcio-1.80.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a1dc80fe55685b4a543555e6eef975303b36c8db1023b1599b094b92aa77965f", size = 8392000, upload-time = "2026-03-30T08:48:28.974Z" }, - { url = "https://files.pythonhosted.org/packages/f5/68/67f4947ed55d2e69f2cc199ab9fd85e0a0034d813bbeef84df6d2ba4d4b7/grpcio-1.80.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:31b9ac4ad1aa28ffee5503821fafd09e4da0a261ce1c1281c6c8da0423c83b6e", size = 7828477, upload-time = "2026-03-30T08:48:32.054Z" }, - { url = "https://files.pythonhosted.org/packages/44/b6/8d4096691b2e385e8271911a0de4f35f0a6c7d05aff7098e296c3de86939/grpcio-1.80.0-cp314-cp314-win32.whl", hash = "sha256:367ce30ba67d05e0592470428f0ec1c31714cab9ef19b8f2e37be1f4c7d32fae", size = 4218563, upload-time = "2026-03-30T08:48:34.538Z" }, - { url = "https://files.pythonhosted.org/packages/e5/8c/bbe6baf2557262834f2070cf668515fa308b2d38a4bbf771f8f7872a7036/grpcio-1.80.0-cp314-cp314-win_amd64.whl", hash = "sha256:3b01e1f5464c583d2f567b2e46ff0d516ef979978f72091fd81f5ab7fa6e2e7f", size = 5019457, upload-time = "2026-03-30T08:48:37.308Z" }, -] - -[[package]] -name = "h11" -version = "0.16.0" +version = "2.10.2" 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" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/62/096058bcb4b4fb28f7ecd28fb048f07969d90b243c417af5f6d09d45a0c2/glfw-2.10.2.tar.gz", hash = "sha256:5d2cf97c66bc42a6b583be0e307eae5a3945438322e2ed0c5e4f14dc251d693d", size = 36307, upload-time = "2026-07-21T14:42:36.732Z" } 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" }, + { url = "https://files.pythonhosted.org/packages/c4/80/5e575ec14f54dc0a644e7215985b9ee7c5044fcc3594d6b83dfdeb04ccd4/glfw-2.10.2-py2.py3-none-macosx_10_6_intel.whl", hash = "sha256:a04d25bb535a3a29e173ea1abd658e161681b5d3816e00bc51c122f74cd7fa3a", size = 110295, upload-time = "2026-07-21T14:42:25.625Z" }, + { url = "https://files.pythonhosted.org/packages/e5/2e/22216db429690aa51fa87d2e2b5e6b610c5a37b9df7768da2927f52f8704/glfw-2.10.2-py2.py3-none-macosx_11_0_arm64.whl", hash = "sha256:c000b8a5e5fb4374b2c18d12347255ccb92001b9bcf80ef74c56072acbb98fe3", size = 107146, upload-time = "2026-07-21T14:42:26.854Z" }, + { url = "https://files.pythonhosted.org/packages/75/a4/9ccee9d5c3b9c5d6bafce3e8d53ab8318eff30124e3c19b95580bebaa6d4/glfw-2.10.2-py2.py3-none-manylinux2014_aarch64.whl", hash = "sha256:0ffe2b48b51f37b05e8f027f57c0b8cd213affaf22fc83401bd9621692d4c6c8", size = 235005, upload-time = "2026-07-21T14:42:27.955Z" }, + { url = "https://files.pythonhosted.org/packages/77/44/37aeed50c581c76e1c5bb83b696a06f98f8ecc979ed8564dde3eef908e8b/glfw-2.10.2-py2.py3-none-manylinux2014_x86_64.whl", hash = "sha256:82daf1f87b2a48815637dc6f6720d4a55a57ba1d3683322dc20dc28065754f97", size = 246951, upload-time = "2026-07-21T14:42:29.107Z" }, + { url = "https://files.pythonhosted.org/packages/52/fc/29b4f1a8c8e33d8934781330a0845458b01da7f389d9fe6a3350e752b7f3/glfw-2.10.2-py2.py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:7604425ec95665cc6a25c3ed7d07e9660d394a34472ceffaec0843c844e7b649", size = 236018, upload-time = "2026-07-21T14:42:30.623Z" }, + { url = "https://files.pythonhosted.org/packages/69/84/1b98671314ea5f40397586b6cd14db913de3196333be558fdc177e61708f/glfw-2.10.2-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:b860de3ca0686182483f98f3ddd12e660acf25b3e0d521450ec9a3f999f72a65", size = 248490, upload-time = "2026-07-21T14:42:32.377Z" }, + { url = "https://files.pythonhosted.org/packages/12/d9/bd2e6c8dbadcf69fcd2289705e75881258b46221c196903a52e9f6e97322/glfw-2.10.2-py2.py3-none-win32.whl", hash = "sha256:a94aefe8c48886fd83cd6e11e936846166a4b5b94abf1f4bf599d984774b0b1b", size = 557654, upload-time = "2026-07-21T14:42:33.903Z" }, + { url = "https://files.pythonhosted.org/packages/e0/6c/4ca5f3ab85a8d7f612ce857208726e9b834e54e559751e3d4f98cc2f7509/glfw-2.10.2-py2.py3-none-win_amd64.whl", hash = "sha256:15fd0666cd8f1b0ecb535c3abb712b8ddda053bf8d16de2353b1c6ced0e65402", size = 564433, upload-time = "2026-07-21T14:42:35.465Z" }, ] [[package]] -name = "httpcore" -version = "1.0.9" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "h11" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, -] - -[[package]] -name = "httpx" -version = "0.28.1" +name = "grpcio" +version = "1.83.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "anyio" }, - { name = "certifi" }, - { name = "httpcore" }, - { name = "idna" }, + { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0c/98/304898ac4e04e2d5e4e4c2eadc178b1f2a16d5f4bc2f91306c87d64680b9/grpcio-1.83.0.tar.gz", hash = "sha256:7674587248fbbb2ac6e4eecf83a8a0f3d91a928f941de571acfd3a2f007fbc24", size = 13428824, upload-time = "2026-07-23T15:20:37.759Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, + { url = "https://files.pythonhosted.org/packages/56/eb/135daaa713f32d33b8f99b4153b3f8dc3b2a124996ac15581bf9ebdad3c3/grpcio-1.83.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:6662f3b1e07cc7493d437351860dc867bddc6a93c83ecf33bbfdaf0c217ab2d0", size = 6304480, upload-time = "2026-07-23T15:19:53.962Z" }, + { url = "https://files.pythonhosted.org/packages/8f/a1/121806ce69f23138dabe06aa595b0e5f1ae051a37e4c1954eed7d692c800/grpcio-1.83.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:74fe6f9e8a35c7dbf32255ee154d15e3e5338a81ed39173d079d594d2e544cd1", size = 12154419, upload-time = "2026-07-23T15:19:56.3Z" }, + { url = "https://files.pythonhosted.org/packages/b0/e8/d0389e09cd6b4c4d3089b92967ae4e3ffd64795bd349bf2f85cd6656d3da/grpcio-1.83.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:10b3fa0475eb572c9a81a6fe37fa16a9c500c0c91cfc148cac15692b7e3c2867", size = 6873200, upload-time = "2026-07-23T15:19:58.701Z" }, + { url = "https://files.pythonhosted.org/packages/f8/51/f464c1d211fa50d5adbabe1b2e519948d99c13757052bfc9ea7afa28e284/grpcio-1.83.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:5f20a988480b0f28207f057f7f7ae1313393c3cef0adcfeae8248f9947eaf881", size = 7618811, upload-time = "2026-07-23T15:20:00.733Z" }, + { url = "https://files.pythonhosted.org/packages/e8/c0/539fe0832f2dd6500a28f5263071623fb34e8d4867aec632ccf81bd21156/grpcio-1.83.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7bd82671b39065ba18cd536e9cd45b27ff649053f81ddd2c6a966d595067080f", size = 7042310, upload-time = "2026-07-23T15:20:02.675Z" }, + { url = "https://files.pythonhosted.org/packages/8c/ca/ccf617d37ffa72567fa8e005ec7090c99da922799be2fb9847c8b21ca18c/grpcio-1.83.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bc60215b5cb9fc8ca72942c498b551ac2305bd08f6ef8d4e3f0d21b64fbecd61", size = 7575412, upload-time = "2026-07-23T15:20:04.712Z" }, + { url = "https://files.pythonhosted.org/packages/eb/b9/fd8d5245f823a8e0fd35d90e20ea3aa4acd47f8d5318fa8df307df52dec6/grpcio-1.83.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f1c3e5689d4b90987b1d72022bcfe866a9a3dc66197484cf856d96b6150e7f45", size = 8604248, upload-time = "2026-07-23T15:20:06.77Z" }, + { url = "https://files.pythonhosted.org/packages/14/1e/f37632fc11db72dfa4bba86c3a43e54358e53030df111ecae5e91a733ad6/grpcio-1.83.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a21cb4eeeba124443f399be2e8b624943cde864dcbe588cb42e5c483a52a906c", size = 7977458, upload-time = "2026-07-23T15:20:09.109Z" }, + { url = "https://files.pythonhosted.org/packages/93/b6/d70b69ae5c0cfc341b9ba474980e4ed99cbf05c0e4a14e9eee8cb73db0a5/grpcio-1.83.0-cp313-cp313-win32.whl", hash = "sha256:8fe04f1050a59f875601eb55d42b4f66946fe89817f967e34db1462ccd07dadf", size = 4393993, upload-time = "2026-07-23T15:20:11.017Z" }, + { url = "https://files.pythonhosted.org/packages/0f/13/45d4cccb555cf4c476226979bf3d2fd0b0254216f7564c3a053e35117efc/grpcio-1.83.0-cp313-cp313-win_amd64.whl", hash = "sha256:6e01ecd9d8ef280abe1365138a4dc318f9a5287f4cb1b41d07816f796653f735", size = 5159650, upload-time = "2026-07-23T15:20:12.979Z" }, ] [[package]] name = "idna" -version = "3.11" +version = "3.19" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5f/f7/abb373e5757eaec4b922b92f97ec8d6d7e057cf06778247604fbc4e7c3f3/idna-3.19.tar.gz", hash = "sha256:5e0811a4383b21dc5838069f801c4fb62113b7447663d2530d2bd6e77b49bf15", size = 215237, upload-time = "2026-08-18T05:14:24.27Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, + { url = "https://files.pythonhosted.org/packages/57/b0/0e52c878c53f245edd3a11020f20979b3f490f245af532c7cae3027754b5/idna-3.19-py3-none-any.whl", hash = "sha256:815e7be7a7806d54abb586dc943addc79e8b2ee16915059658cbeff4b1b43bf4", size = 68550, upload-time = "2026-08-18T05:14:22.343Z" }, ] [[package]] name = "imageio" -version = "2.37.3" +version = "2.37.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, { name = "pillow" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b1/84/93bcd1300216ea50811cee96873b84a1bebf8d0489ffaf7f2a3756bab866/imageio-2.37.3.tar.gz", hash = "sha256:bbb37efbfc4c400fcd534b367b91fcd66d5da639aaa138034431a1c5e0a41451", size = 389673, upload-time = "2026-03-09T11:31:12.573Z" } +sdist = { url = "https://files.pythonhosted.org/packages/48/62/aa770a9307508d2a2a2c62d536a49347bffe9e55322db27838d3c93d0b07/imageio-2.37.4.tar.gz", hash = "sha256:e45cbc5e83502047fb138f7f585f7f105a136a57eea5f4b3cfc6ce1b52720bd3", size = 390173, upload-time = "2026-07-20T05:26:11.369Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/49/fa/391e437a34e55095173dca5f24070d89cbc233ff85bf1c29c93248c6588d/imageio-2.37.3-py3-none-any.whl", hash = "sha256:46f5bb8522cd421c0f5ae104d8268f569d856b29eb1a13b92829d1970f32c9f0", size = 317646, upload-time = "2026-03-09T11:31:10.771Z" }, + { url = "https://files.pythonhosted.org/packages/3e/2d/ca050652104bab2cf55e569db2a178b1b61cb041fef28307f2db383f6d9f/imageio-2.37.4-py3-none-any.whl", hash = "sha256:1ab2e22c8debf700f24c3ac43e8f95f3b3a8110c83b93411e97b4b0b2cd1c7e6", size = 318000, upload-time = "2026-07-20T05:26:09.874Z" }, ] [[package]] @@ -727,23 +506,23 @@ wheels = [ [[package]] name = "ipython" -version = "9.12.0" +version = "9.16.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "decorator" }, { name = "ipython-pygments-lexers" }, { name = "jedi" }, { name = "matplotlib-inline" }, { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, { name = "prompt-toolkit" }, + { name = "psutil", marker = "sys_platform != 'cygwin' and sys_platform != 'emscripten'" }, { name = "pygments" }, { name = "stack-data" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3a/73/7114f80a8f9cabdb13c27732dce24af945b2923dcab80723602f7c8bc2d8/ipython-9.12.0.tar.gz", hash = "sha256:01daa83f504b693ba523b5a407246cabde4eb4513285a3c6acaff11a66735ee4", size = 4428879, upload-time = "2026-03-27T09:42:45.312Z" } +sdist = { url = "https://files.pythonhosted.org/packages/06/96/b150fe7e25a5a29ae9ac1374e71488639605d39a1ea4abb74c9ce33af235/ipython-9.16.1.tar.gz", hash = "sha256:5a3d1f9a47ff216d6cf9cf863124f6a2c1a198d1354c546a4d24a370a283b64c", size = 4515302, upload-time = "2026-08-03T08:36:15.571Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/59/22/906c8108974c673ebef6356c506cebb6870d48cedea3c41e949e2dd556bb/ipython-9.12.0-py3-none-any.whl", hash = "sha256:0f2701e8ee86e117e37f50563205d36feaa259d2e08d4a6bc6b6d74b18ce128d", size = 625661, upload-time = "2026-03-27T09:42:42.831Z" }, + { url = "https://files.pythonhosted.org/packages/bc/8e/1239df488393d61076653bfb29f759d0f60cab8e030abdf7c17c31539b51/ipython-9.16.1-py3-none-any.whl", hash = "sha256:4acae635506f6d352d94c4899a19d5f85f8bc4d230932342dca556fdab1c69b4", size = 625974, upload-time = "2026-08-03T08:36:13.654Z" }, ] [[package]] @@ -760,14 +539,14 @@ wheels = [ [[package]] name = "jedi" -version = "0.19.2" +version = "0.20.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "parso" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287, upload-time = "2024-11-11T01:41:42.873Z" } +sdist = { url = "https://files.pythonhosted.org/packages/46/b7/a3635f6a2d7cf5b5dd98064fc1d5fbbafcb25477bcea204a3a92145d158b/jedi-0.20.0.tar.gz", hash = "sha256:c3f4ccbd276696f4b19c54618d4fb18f9fc24b0aef02acf704b23f487daa1011", size = 3119416, upload-time = "2026-05-01T23:38:47.814Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278, upload-time = "2024-11-11T01:41:40.175Z" }, + { url = "https://files.pythonhosted.org/packages/9a/93/242e2eab5fe682ffcb8b0084bde703a41d51e17ee0f3a31ff0d9d813620a/jedi-0.20.0-py2.py3-none-any.whl", hash = "sha256:7bdd9c2634f56713299976f4cbd59cb3fa92165cc5e05ea811fb253480728b67", size = 4884812, upload-time = "2026-05-01T23:38:43.919Z" }, ] [[package]] @@ -782,33 +561,6 @@ 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 = "jsonschema" -version = "4.26.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "jsonschema-specifications" }, - { name = "referencing" }, - { name = "rpds-py" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, -] - -[[package]] -name = "jsonschema-specifications" -version = "2025.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "referencing" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, -] - [[package]] name = "kiwisolver" version = "1.5.0" @@ -844,185 +596,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1b/bd/877056304626943ff0f1f44c08f584300c199b887cb3176cd7e34f1515f1/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:fc4d3f1fb9ca0ae9f97b095963bc6326f1dbfd3779d6679a1e016b9baaa153d3", size = 2597482, upload-time = "2026-03-09T13:14:34.971Z" }, { url = "https://files.pythonhosted.org/packages/75/19/c60626c47bf0f8ac5dcf72c6c98e266d714f2fbbfd50cf6dab5ede3aaa50/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f443b4825c50a51ee68585522ab4a1d1257fac65896f282b4c6763337ac9f5d2", size = 2394328, upload-time = "2026-03-09T13:14:36.816Z" }, { url = "https://files.pythonhosted.org/packages/47/84/6a6d5e5bb8273756c27b7d810d47f7ef2f1f9b9fd23c9ee9a3f8c75c9cef/kiwisolver-1.5.0-cp313-cp313t-win_arm64.whl", hash = "sha256:893ff3a711d1b515ba9da14ee090519bad4610ed1962fbe298a434e8c5f8db53", size = 68410, upload-time = "2026-03-09T13:14:38.695Z" }, - { url = "https://files.pythonhosted.org/packages/e4/d7/060f45052f2a01ad5762c8fdecd6d7a752b43400dc29ff75cd47225a40fd/kiwisolver-1.5.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8df31fe574b8b3993cc61764f40941111b25c2d9fea13d3ce24a49907cd2d615", size = 123231, upload-time = "2026-03-09T13:14:41.323Z" }, - { url = "https://files.pythonhosted.org/packages/c2/a7/78da680eadd06ff35edef6ef68a1ad273bad3e2a0936c9a885103230aece/kiwisolver-1.5.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1d49a49ac4cbfb7c1375301cd1ec90169dfeae55ff84710d782260ce77a75a02", size = 66489, upload-time = "2026-03-09T13:14:42.534Z" }, - { url = "https://files.pythonhosted.org/packages/49/b2/97980f3ad4fae37dd7fe31626e2bf75fbf8bdf5d303950ec1fab39a12da8/kiwisolver-1.5.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0cbe94b69b819209a62cb27bdfa5dc2a8977d8de2f89dfd97ba4f53ed3af754e", size = 64063, upload-time = "2026-03-09T13:14:44.759Z" }, - { url = "https://files.pythonhosted.org/packages/e7/f9/b06c934a6aa8bc91f566bd2a214fd04c30506c2d9e2b6b171953216a65b6/kiwisolver-1.5.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:80aa065ffd378ff784822a6d7c3212f2d5f5e9c3589614b5c228b311fd3063ac", size = 1475913, upload-time = "2026-03-09T13:14:46.247Z" }, - { url = "https://files.pythonhosted.org/packages/6b/f0/f768ae564a710135630672981231320bc403cf9152b5596ec5289de0f106/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e7f886f47ab881692f278ae901039a234e4025a68e6dfab514263a0b1c4ae05", size = 1282782, upload-time = "2026-03-09T13:14:48.458Z" }, - { url = "https://files.pythonhosted.org/packages/e2/9f/1de7aad00697325f05238a5f2eafbd487fb637cc27a558b5367a5f37fb7f/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5060731cc3ed12ca3a8b57acd4aeca5bbc2f49216dd0bec1650a1acd89486bcd", size = 1300815, upload-time = "2026-03-09T13:14:50.721Z" }, - { url = "https://files.pythonhosted.org/packages/5a/c2/297f25141d2e468e0ce7f7a7b92e0cf8918143a0cbd3422c1ad627e85a06/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7a4aa69609f40fce3cbc3f87b2061f042eee32f94b8f11db707b66a26461591a", size = 1347925, upload-time = "2026-03-09T13:14:52.304Z" }, - { url = "https://files.pythonhosted.org/packages/b9/d3/f4c73a02eb41520c47610207b21afa8cdd18fdbf64ffd94674ae21c4812d/kiwisolver-1.5.0-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:d168fda2dbff7b9b5f38e693182d792a938c31db4dac3a80a4888de603c99554", size = 991322, upload-time = "2026-03-09T13:14:54.637Z" }, - { url = "https://files.pythonhosted.org/packages/7b/46/d3f2efef7732fcda98d22bf4ad5d3d71d545167a852ca710a494f4c15343/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:413b820229730d358efd838ecbab79902fe97094565fdc80ddb6b0a18c18a581", size = 2232857, upload-time = "2026-03-09T13:14:56.471Z" }, - { url = "https://files.pythonhosted.org/packages/3f/ec/2d9756bf2b6d26ae4349b8d3662fb3993f16d80c1f971c179ce862b9dbae/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5124d1ea754509b09e53738ec185584cc609aae4a3b510aaf4ed6aa047ef9303", size = 2329376, upload-time = "2026-03-09T13:14:58.072Z" }, - { url = "https://files.pythonhosted.org/packages/8f/9f/876a0a0f2260f1bde92e002b3019a5fabc35e0939c7d945e0fa66185eb20/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e4415a8db000bf49a6dd1c478bf70062eaacff0f462b92b0ba68791a905861f9", size = 1982549, upload-time = "2026-03-09T13:14:59.668Z" }, - { url = "https://files.pythonhosted.org/packages/6c/4f/ba3624dfac23a64d54ac4179832860cb537c1b0af06024936e82ca4154a0/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d618fd27420381a4f6044faa71f46d8bfd911bd077c555f7138ed88729bfbe79", size = 2494680, upload-time = "2026-03-09T13:15:01.364Z" }, - { url = "https://files.pythonhosted.org/packages/39/b7/97716b190ab98911b20d10bf92eca469121ec483b8ce0edd314f51bc85af/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5092eb5b1172947f57d6ea7d89b2f29650414e4293c47707eb499ec07a0ac796", size = 2297905, upload-time = "2026-03-09T13:15:03.925Z" }, - { url = "https://files.pythonhosted.org/packages/a3/36/4e551e8aa55c9188bca9abb5096805edbf7431072b76e2298e34fd3a3008/kiwisolver-1.5.0-cp314-cp314-win_amd64.whl", hash = "sha256:d76e2d8c75051d58177e762164d2e9ab92886534e3a12e795f103524f221dd8e", size = 75086, upload-time = "2026-03-09T13:15:07.775Z" }, - { url = "https://files.pythonhosted.org/packages/70/15/9b90f7df0e31a003c71649cf66ef61c3c1b862f48c81007fa2383c8bd8d7/kiwisolver-1.5.0-cp314-cp314-win_arm64.whl", hash = "sha256:fa6248cd194edff41d7ea9425ced8ca3a6f838bfb295f6f1d6e6bb694a8518df", size = 66577, upload-time = "2026-03-09T13:15:09.139Z" }, - { url = "https://files.pythonhosted.org/packages/17/01/7dc8c5443ff42b38e72731643ed7cf1ed9bf01691ae5cdca98501999ed83/kiwisolver-1.5.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:d1ffeb80b5676463d7a7d56acbe8e37a20ce725570e09549fe738e02ca6b7e1e", size = 125794, upload-time = "2026-03-09T13:15:10.525Z" }, - { url = "https://files.pythonhosted.org/packages/46/8a/b4ebe46ebaac6a303417fab10c2e165c557ddaff558f9699d302b256bc53/kiwisolver-1.5.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:bc4d8e252f532ab46a1de9349e2d27b91fce46736a9eedaa37beaca66f574ed4", size = 67646, upload-time = "2026-03-09T13:15:12.016Z" }, - { url = "https://files.pythonhosted.org/packages/60/35/10a844afc5f19d6f567359bf4789e26661755a2f36200d5d1ed8ad0126e5/kiwisolver-1.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6783e069732715ad0c3ce96dbf21dbc2235ab0593f2baf6338101f70371f4028", size = 65511, upload-time = "2026-03-09T13:15:13.311Z" }, - { url = "https://files.pythonhosted.org/packages/f8/8a/685b297052dd041dcebce8e8787b58923b6e78acc6115a0dc9189011c44b/kiwisolver-1.5.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e7c4c09a490dc4d4a7f8cbee56c606a320f9dc28cf92a7157a39d1ce7676a657", size = 1584858, upload-time = "2026-03-09T13:15:15.103Z" }, - { url = "https://files.pythonhosted.org/packages/9e/80/04865e3d4638ac5bddec28908916df4a3075b8c6cc101786a96803188b96/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2a075bd7bd19c70cf67c8badfa36cf7c5d8de3c9ddb8420c51e10d9c50e94920", size = 1392539, upload-time = "2026-03-09T13:15:16.661Z" }, - { url = "https://files.pythonhosted.org/packages/ba/01/77a19cacc0893fa13fafa46d1bba06fb4dc2360b3292baf4b56d8e067b24/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bdd3e53429ff02aa319ba59dfe4ceeec345bf46cf180ec2cf6fd5b942e7975e9", size = 1405310, upload-time = "2026-03-09T13:15:18.229Z" }, - { url = "https://files.pythonhosted.org/packages/53/39/bcaf5d0cca50e604cfa9b4e3ae1d64b50ca1ae5b754122396084599ef903/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cdcb35dc9d807259c981a85531048ede628eabcffb3239adf3d17463518992d", size = 1456244, upload-time = "2026-03-09T13:15:20.444Z" }, - { url = "https://files.pythonhosted.org/packages/d0/7a/72c187abc6975f6978c3e39b7cf67aeb8b3c0a8f9790aa7fd412855e9e1f/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:70d593af6a6ca332d1df73d519fddb5148edb15cd90d5f0155e3746a6d4fcc65", size = 1073154, upload-time = "2026-03-09T13:15:22.039Z" }, - { url = "https://files.pythonhosted.org/packages/c7/ca/cf5b25783ebbd59143b4371ed0c8428a278abe68d6d0104b01865b1bbd0f/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:377815a8616074cabbf3f53354e1d040c35815a134e01d7614b7692e4bf8acfa", size = 2334377, upload-time = "2026-03-09T13:15:23.741Z" }, - { url = "https://files.pythonhosted.org/packages/4a/e5/b1f492adc516796e88751282276745340e2a72dcd0d36cf7173e0daf3210/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0255a027391d52944eae1dbb5d4cc5903f57092f3674e8e544cdd2622826b3f0", size = 2425288, upload-time = "2026-03-09T13:15:25.789Z" }, - { url = "https://files.pythonhosted.org/packages/e6/e5/9b21fbe91a61b8f409d74a26498706e97a48008bfcd1864373d32a6ba31c/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:012b1eb16e28718fa782b5e61dc6f2da1f0792ca73bd05d54de6cb9561665fc9", size = 2063158, upload-time = "2026-03-09T13:15:27.63Z" }, - { url = "https://files.pythonhosted.org/packages/b1/02/83f47986138310f95ea95531f851b2a62227c11cbc3e690ae1374fe49f0f/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0e3aafb33aed7479377e5e9a82e9d4bf87063741fc99fc7ae48b0f16e32bdd6f", size = 2597260, upload-time = "2026-03-09T13:15:29.421Z" }, - { url = "https://files.pythonhosted.org/packages/07/18/43a5f24608d8c313dd189cf838c8e68d75b115567c6279de7796197cfb6a/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e7a116ae737f0000343218c4edf5bd45893bfeaff0993c0b215d7124c9f77646", size = 2394403, upload-time = "2026-03-09T13:15:31.517Z" }, - { url = "https://files.pythonhosted.org/packages/3b/b5/98222136d839b8afabcaa943b09bd05888c2d36355b7e448550211d1fca4/kiwisolver-1.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1dd9b0b119a350976a6d781e7278ec7aca0b201e1a9e2d23d9804afecb6ca681", size = 79687, upload-time = "2026-03-09T13:15:33.204Z" }, - { url = "https://files.pythonhosted.org/packages/99/a2/ca7dc962848040befed12732dff6acae7fb3c4f6fc4272b3f6c9a30b8713/kiwisolver-1.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:58f812017cd2985c21fbffb4864d59174d4903dd66fa23815e74bbc7a0e2dd57", size = 70032, upload-time = "2026-03-09T13:15:34.411Z" }, -] - -[[package]] -name = "lxml" -version = "6.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/88/262177de60548e5a2bfc46ad28232c9e9cbde697bd94132aeb80364675cb/lxml-6.0.2.tar.gz", hash = "sha256:cd79f3367bd74b317dda655dc8fcfa304d9eb6e4fb06b7168c5cf27f96e0cd62", size = 4073426, upload-time = "2025-09-22T04:04:59.287Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/53/fd/4e8f0540608977aea078bf6d79f128e0e2c2bba8af1acf775c30baa70460/lxml-6.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9b33d21594afab46f37ae58dfadd06636f154923c4e8a4d754b0127554eb2e77", size = 8648494, upload-time = "2025-09-22T04:01:54.242Z" }, - { url = "https://files.pythonhosted.org/packages/5d/f4/2a94a3d3dfd6c6b433501b8d470a1960a20ecce93245cf2db1706adf6c19/lxml-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6c8963287d7a4c5c9a432ff487c52e9c5618667179c18a204bdedb27310f022f", size = 4661146, upload-time = "2025-09-22T04:01:56.282Z" }, - { url = "https://files.pythonhosted.org/packages/25/2e/4efa677fa6b322013035d38016f6ae859d06cac67437ca7dc708a6af7028/lxml-6.0.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1941354d92699fb5ffe6ed7b32f9649e43c2feb4b97205f75866f7d21aa91452", size = 4946932, upload-time = "2025-09-22T04:01:58.989Z" }, - { url = "https://files.pythonhosted.org/packages/ce/0f/526e78a6d38d109fdbaa5049c62e1d32fdd70c75fb61c4eadf3045d3d124/lxml-6.0.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bb2f6ca0ae2d983ded09357b84af659c954722bbf04dea98030064996d156048", size = 5100060, upload-time = "2025-09-22T04:02:00.812Z" }, - { url = "https://files.pythonhosted.org/packages/81/76/99de58d81fa702cc0ea7edae4f4640416c2062813a00ff24bd70ac1d9c9b/lxml-6.0.2-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb2a12d704f180a902d7fa778c6d71f36ceb7b0d317f34cdc76a5d05aa1dd1df", size = 5019000, upload-time = "2025-09-22T04:02:02.671Z" }, - { url = "https://files.pythonhosted.org/packages/b5/35/9e57d25482bc9a9882cb0037fdb9cc18f4b79d85df94fa9d2a89562f1d25/lxml-6.0.2-cp313-cp313-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:6ec0e3f745021bfed19c456647f0298d60a24c9ff86d9d051f52b509663feeb1", size = 5348496, upload-time = "2025-09-22T04:02:04.904Z" }, - { url = "https://files.pythonhosted.org/packages/a6/8e/cb99bd0b83ccc3e8f0f528e9aa1f7a9965dfec08c617070c5db8d63a87ce/lxml-6.0.2-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:846ae9a12d54e368933b9759052d6206a9e8b250291109c48e350c1f1f49d916", size = 5643779, upload-time = "2025-09-22T04:02:06.689Z" }, - { url = "https://files.pythonhosted.org/packages/d0/34/9e591954939276bb679b73773836c6684c22e56d05980e31d52a9a8deb18/lxml-6.0.2-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ef9266d2aa545d7374938fb5c484531ef5a2ec7f2d573e62f8ce722c735685fd", size = 5244072, upload-time = "2025-09-22T04:02:08.587Z" }, - { url = "https://files.pythonhosted.org/packages/8d/27/b29ff065f9aaca443ee377aff699714fcbffb371b4fce5ac4ca759e436d5/lxml-6.0.2-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:4077b7c79f31755df33b795dc12119cb557a0106bfdab0d2c2d97bd3cf3dffa6", size = 4718675, upload-time = "2025-09-22T04:02:10.783Z" }, - { url = "https://files.pythonhosted.org/packages/2b/9f/f756f9c2cd27caa1a6ef8c32ae47aadea697f5c2c6d07b0dae133c244fbe/lxml-6.0.2-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a7c5d5e5f1081955358533be077166ee97ed2571d6a66bdba6ec2f609a715d1a", size = 5255171, upload-time = "2025-09-22T04:02:12.631Z" }, - { url = "https://files.pythonhosted.org/packages/61/46/bb85ea42d2cb1bd8395484fd72f38e3389611aa496ac7772da9205bbda0e/lxml-6.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:8f8d0cbd0674ee89863a523e6994ac25fd5be9c8486acfc3e5ccea679bad2679", size = 5057175, upload-time = "2025-09-22T04:02:14.718Z" }, - { url = "https://files.pythonhosted.org/packages/95/0c/443fc476dcc8e41577f0af70458c50fe299a97bb6b7505bb1ae09aa7f9ac/lxml-6.0.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:2cbcbf6d6e924c28f04a43f3b6f6e272312a090f269eff68a2982e13e5d57659", size = 4785688, upload-time = "2025-09-22T04:02:16.957Z" }, - { url = "https://files.pythonhosted.org/packages/48/78/6ef0b359d45bb9697bc5a626e1992fa5d27aa3f8004b137b2314793b50a0/lxml-6.0.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:dfb874cfa53340009af6bdd7e54ebc0d21012a60a4e65d927c2e477112e63484", size = 5660655, upload-time = "2025-09-22T04:02:18.815Z" }, - { url = "https://files.pythonhosted.org/packages/ff/ea/e1d33808f386bc1339d08c0dcada6e4712d4ed8e93fcad5f057070b7988a/lxml-6.0.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:fb8dae0b6b8b7f9e96c26fdd8121522ce5de9bb5538010870bd538683d30e9a2", size = 5247695, upload-time = "2025-09-22T04:02:20.593Z" }, - { url = "https://files.pythonhosted.org/packages/4f/47/eba75dfd8183673725255247a603b4ad606f4ae657b60c6c145b381697da/lxml-6.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:358d9adae670b63e95bc59747c72f4dc97c9ec58881d4627fe0120da0f90d314", size = 5269841, upload-time = "2025-09-22T04:02:22.489Z" }, - { url = "https://files.pythonhosted.org/packages/76/04/5c5e2b8577bc936e219becb2e98cdb1aca14a4921a12995b9d0c523502ae/lxml-6.0.2-cp313-cp313-win32.whl", hash = "sha256:e8cd2415f372e7e5a789d743d133ae474290a90b9023197fd78f32e2dc6873e2", size = 3610700, upload-time = "2025-09-22T04:02:24.465Z" }, - { url = "https://files.pythonhosted.org/packages/fe/0a/4643ccc6bb8b143e9f9640aa54e38255f9d3b45feb2cbe7ae2ca47e8782e/lxml-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:b30d46379644fbfc3ab81f8f82ae4de55179414651f110a1514f0b1f8f6cb2d7", size = 4010347, upload-time = "2025-09-22T04:02:26.286Z" }, - { url = "https://files.pythonhosted.org/packages/31/ef/dcf1d29c3f530577f61e5fe2f1bd72929acf779953668a8a47a479ae6f26/lxml-6.0.2-cp313-cp313-win_arm64.whl", hash = "sha256:13dcecc9946dca97b11b7c40d29fba63b55ab4170d3c0cf8c0c164343b9bfdcf", size = 3671248, upload-time = "2025-09-22T04:02:27.918Z" }, - { url = "https://files.pythonhosted.org/packages/03/15/d4a377b385ab693ce97b472fe0c77c2b16ec79590e688b3ccc71fba19884/lxml-6.0.2-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:b0c732aa23de8f8aec23f4b580d1e52905ef468afb4abeafd3fec77042abb6fe", size = 8659801, upload-time = "2025-09-22T04:02:30.113Z" }, - { url = "https://files.pythonhosted.org/packages/c8/e8/c128e37589463668794d503afaeb003987373c5f94d667124ffd8078bbd9/lxml-6.0.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4468e3b83e10e0317a89a33d28f7aeba1caa4d1a6fd457d115dd4ffe90c5931d", size = 4659403, upload-time = "2025-09-22T04:02:32.119Z" }, - { url = "https://files.pythonhosted.org/packages/00/ce/74903904339decdf7da7847bb5741fc98a5451b42fc419a86c0c13d26fe2/lxml-6.0.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:abd44571493973bad4598a3be7e1d807ed45aa2adaf7ab92ab7c62609569b17d", size = 4966974, upload-time = "2025-09-22T04:02:34.155Z" }, - { url = "https://files.pythonhosted.org/packages/1f/d3/131dec79ce61c5567fecf82515bd9bc36395df42501b50f7f7f3bd065df0/lxml-6.0.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:370cd78d5855cfbffd57c422851f7d3864e6ae72d0da615fca4dad8c45d375a5", size = 5102953, upload-time = "2025-09-22T04:02:36.054Z" }, - { url = "https://files.pythonhosted.org/packages/3a/ea/a43ba9bb750d4ffdd885f2cd333572f5bb900cd2408b67fdda07e85978a0/lxml-6.0.2-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:901e3b4219fa04ef766885fb40fa516a71662a4c61b80c94d25336b4934b71c0", size = 5055054, upload-time = "2025-09-22T04:02:38.154Z" }, - { url = "https://files.pythonhosted.org/packages/60/23/6885b451636ae286c34628f70a7ed1fcc759f8d9ad382d132e1c8d3d9bfd/lxml-6.0.2-cp314-cp314-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:a4bf42d2e4cf52c28cc1812d62426b9503cdb0c87a6de81442626aa7d69707ba", size = 5352421, upload-time = "2025-09-22T04:02:40.413Z" }, - { url = "https://files.pythonhosted.org/packages/48/5b/fc2ddfc94ddbe3eebb8e9af6e3fd65e2feba4967f6a4e9683875c394c2d8/lxml-6.0.2-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2c7fdaa4d7c3d886a42534adec7cfac73860b89b4e5298752f60aa5984641a0", size = 5673684, upload-time = "2025-09-22T04:02:42.288Z" }, - { url = "https://files.pythonhosted.org/packages/29/9c/47293c58cc91769130fbf85531280e8cc7868f7fbb6d92f4670071b9cb3e/lxml-6.0.2-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:98a5e1660dc7de2200b00d53fa00bcd3c35a3608c305d45a7bbcaf29fa16e83d", size = 5252463, upload-time = "2025-09-22T04:02:44.165Z" }, - { url = "https://files.pythonhosted.org/packages/9b/da/ba6eceb830c762b48e711ded880d7e3e89fc6c7323e587c36540b6b23c6b/lxml-6.0.2-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:dc051506c30b609238d79eda75ee9cab3e520570ec8219844a72a46020901e37", size = 4698437, upload-time = "2025-09-22T04:02:46.524Z" }, - { url = "https://files.pythonhosted.org/packages/a5/24/7be3f82cb7990b89118d944b619e53c656c97dc89c28cfb143fdb7cd6f4d/lxml-6.0.2-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8799481bbdd212470d17513a54d568f44416db01250f49449647b5ab5b5dccb9", size = 5269890, upload-time = "2025-09-22T04:02:48.812Z" }, - { url = "https://files.pythonhosted.org/packages/1b/bd/dcfb9ea1e16c665efd7538fc5d5c34071276ce9220e234217682e7d2c4a5/lxml-6.0.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9261bb77c2dab42f3ecd9103951aeca2c40277701eb7e912c545c1b16e0e4917", size = 5097185, upload-time = "2025-09-22T04:02:50.746Z" }, - { url = "https://files.pythonhosted.org/packages/21/04/a60b0ff9314736316f28316b694bccbbabe100f8483ad83852d77fc7468e/lxml-6.0.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:65ac4a01aba353cfa6d5725b95d7aed6356ddc0a3cd734de00124d285b04b64f", size = 4745895, upload-time = "2025-09-22T04:02:52.968Z" }, - { url = "https://files.pythonhosted.org/packages/d6/bd/7d54bd1846e5a310d9c715921c5faa71cf5c0853372adf78aee70c8d7aa2/lxml-6.0.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:b22a07cbb82fea98f8a2fd814f3d1811ff9ed76d0fc6abc84eb21527596e7cc8", size = 5695246, upload-time = "2025-09-22T04:02:54.798Z" }, - { url = "https://files.pythonhosted.org/packages/fd/32/5643d6ab947bc371da21323acb2a6e603cedbe71cb4c99c8254289ab6f4e/lxml-6.0.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:d759cdd7f3e055d6bc8d9bec3ad905227b2e4c785dc16c372eb5b5e83123f48a", size = 5260797, upload-time = "2025-09-22T04:02:57.058Z" }, - { url = "https://files.pythonhosted.org/packages/33/da/34c1ec4cff1eea7d0b4cd44af8411806ed943141804ac9c5d565302afb78/lxml-6.0.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:945da35a48d193d27c188037a05fec5492937f66fb1958c24fc761fb9d40d43c", size = 5277404, upload-time = "2025-09-22T04:02:58.966Z" }, - { url = "https://files.pythonhosted.org/packages/82/57/4eca3e31e54dc89e2c3507e1cd411074a17565fa5ffc437c4ae0a00d439e/lxml-6.0.2-cp314-cp314-win32.whl", hash = "sha256:be3aaa60da67e6153eb15715cc2e19091af5dc75faef8b8a585aea372507384b", size = 3670072, upload-time = "2025-09-22T04:03:38.05Z" }, - { url = "https://files.pythonhosted.org/packages/e3/e0/c96cf13eccd20c9421ba910304dae0f619724dcf1702864fd59dd386404d/lxml-6.0.2-cp314-cp314-win_amd64.whl", hash = "sha256:fa25afbadead523f7001caf0c2382afd272c315a033a7b06336da2637d92d6ed", size = 4080617, upload-time = "2025-09-22T04:03:39.835Z" }, - { url = "https://files.pythonhosted.org/packages/d5/5d/b3f03e22b3d38d6f188ef044900a9b29b2fe0aebb94625ce9fe244011d34/lxml-6.0.2-cp314-cp314-win_arm64.whl", hash = "sha256:063eccf89df5b24e361b123e257e437f9e9878f425ee9aae3144c77faf6da6d8", size = 3754930, upload-time = "2025-09-22T04:03:41.565Z" }, - { url = "https://files.pythonhosted.org/packages/5e/5c/42c2c4c03554580708fc738d13414801f340c04c3eff90d8d2d227145275/lxml-6.0.2-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:6162a86d86893d63084faaf4ff937b3daea233e3682fb4474db07395794fa80d", size = 8910380, upload-time = "2025-09-22T04:03:01.645Z" }, - { url = "https://files.pythonhosted.org/packages/bf/4f/12df843e3e10d18d468a7557058f8d3733e8b6e12401f30b1ef29360740f/lxml-6.0.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:414aaa94e974e23a3e92e7ca5b97d10c0cf37b6481f50911032c69eeb3991bba", size = 4775632, upload-time = "2025-09-22T04:03:03.814Z" }, - { url = "https://files.pythonhosted.org/packages/e4/0c/9dc31e6c2d0d418483cbcb469d1f5a582a1cd00a1f4081953d44051f3c50/lxml-6.0.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:48461bd21625458dd01e14e2c38dd0aea69addc3c4f960c30d9f59d7f93be601", size = 4975171, upload-time = "2025-09-22T04:03:05.651Z" }, - { url = "https://files.pythonhosted.org/packages/e7/2b/9b870c6ca24c841bdd887504808f0417aa9d8d564114689266f19ddf29c8/lxml-6.0.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:25fcc59afc57d527cfc78a58f40ab4c9b8fd096a9a3f964d2781ffb6eb33f4ed", size = 5110109, upload-time = "2025-09-22T04:03:07.452Z" }, - { url = "https://files.pythonhosted.org/packages/bf/0c/4f5f2a4dd319a178912751564471355d9019e220c20d7db3fb8307ed8582/lxml-6.0.2-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5179c60288204e6ddde3f774a93350177e08876eaf3ab78aa3a3649d43eb7d37", size = 5041061, upload-time = "2025-09-22T04:03:09.297Z" }, - { url = "https://files.pythonhosted.org/packages/12/64/554eed290365267671fe001a20d72d14f468ae4e6acef1e179b039436967/lxml-6.0.2-cp314-cp314t-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:967aab75434de148ec80597b75062d8123cadf2943fb4281f385141e18b21338", size = 5306233, upload-time = "2025-09-22T04:03:11.651Z" }, - { url = "https://files.pythonhosted.org/packages/7a/31/1d748aa275e71802ad9722df32a7a35034246b42c0ecdd8235412c3396ef/lxml-6.0.2-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d100fcc8930d697c6561156c6810ab4a508fb264c8b6779e6e61e2ed5e7558f9", size = 5604739, upload-time = "2025-09-22T04:03:13.592Z" }, - { url = "https://files.pythonhosted.org/packages/8f/41/2c11916bcac09ed561adccacceaedd2bf0e0b25b297ea92aab99fd03d0fa/lxml-6.0.2-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ca59e7e13e5981175b8b3e4ab84d7da57993eeff53c07764dcebda0d0e64ecd", size = 5225119, upload-time = "2025-09-22T04:03:15.408Z" }, - { url = "https://files.pythonhosted.org/packages/99/05/4e5c2873d8f17aa018e6afde417c80cc5d0c33be4854cce3ef5670c49367/lxml-6.0.2-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:957448ac63a42e2e49531b9d6c0fa449a1970dbc32467aaad46f11545be9af1d", size = 4633665, upload-time = "2025-09-22T04:03:17.262Z" }, - { url = "https://files.pythonhosted.org/packages/0f/c9/dcc2da1bebd6275cdc723b515f93edf548b82f36a5458cca3578bc899332/lxml-6.0.2-cp314-cp314t-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b7fc49c37f1786284b12af63152fe1d0990722497e2d5817acfe7a877522f9a9", size = 5234997, upload-time = "2025-09-22T04:03:19.14Z" }, - { url = "https://files.pythonhosted.org/packages/9c/e2/5172e4e7468afca64a37b81dba152fc5d90e30f9c83c7c3213d6a02a5ce4/lxml-6.0.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e19e0643cc936a22e837f79d01a550678da8377d7d801a14487c10c34ee49c7e", size = 5090957, upload-time = "2025-09-22T04:03:21.436Z" }, - { url = "https://files.pythonhosted.org/packages/a5/b3/15461fd3e5cd4ddcb7938b87fc20b14ab113b92312fc97afe65cd7c85de1/lxml-6.0.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:1db01e5cf14345628e0cbe71067204db658e2fb8e51e7f33631f5f4735fefd8d", size = 4764372, upload-time = "2025-09-22T04:03:23.27Z" }, - { url = "https://files.pythonhosted.org/packages/05/33/f310b987c8bf9e61c4dd8e8035c416bd3230098f5e3cfa69fc4232de7059/lxml-6.0.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:875c6b5ab39ad5291588aed6925fac99d0097af0dd62f33c7b43736043d4a2ec", size = 5634653, upload-time = "2025-09-22T04:03:25.767Z" }, - { url = "https://files.pythonhosted.org/packages/70/ff/51c80e75e0bc9382158133bdcf4e339b5886c6ee2418b5199b3f1a61ed6d/lxml-6.0.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:cdcbed9ad19da81c480dfd6dd161886db6096083c9938ead313d94b30aadf272", size = 5233795, upload-time = "2025-09-22T04:03:27.62Z" }, - { url = "https://files.pythonhosted.org/packages/56/4d/4856e897df0d588789dd844dbed9d91782c4ef0b327f96ce53c807e13128/lxml-6.0.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:80dadc234ebc532e09be1975ff538d154a7fa61ea5031c03d25178855544728f", size = 5257023, upload-time = "2025-09-22T04:03:30.056Z" }, - { url = "https://files.pythonhosted.org/packages/0f/85/86766dfebfa87bea0ab78e9ff7a4b4b45225df4b4d3b8cc3c03c5cd68464/lxml-6.0.2-cp314-cp314t-win32.whl", hash = "sha256:da08e7bb297b04e893d91087df19638dc7a6bb858a954b0cc2b9f5053c922312", size = 3911420, upload-time = "2025-09-22T04:03:32.198Z" }, - { url = "https://files.pythonhosted.org/packages/fe/1a/b248b355834c8e32614650b8008c69ffeb0ceb149c793961dd8c0b991bb3/lxml-6.0.2-cp314-cp314t-win_amd64.whl", hash = "sha256:252a22982dca42f6155125ac76d3432e548a7625d56f5a273ee78a5057216eca", size = 4406837, upload-time = "2025-09-22T04:03:34.027Z" }, - { url = "https://files.pythonhosted.org/packages/92/aa/df863bcc39c5e0946263454aba394de8a9084dbaff8ad143846b0d844739/lxml-6.0.2-cp314-cp314t-win_arm64.whl", hash = "sha256:bb4c1847b303835d89d785a18801a883436cdfd5dc3d62947f9c49e24f0f5a2c", size = 3822205, upload-time = "2025-09-22T04:03:36.249Z" }, -] - -[[package]] -name = "manifold3d" -version = "3.4.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b0/fd/4dfc246e076e3912c45a821764f4de8b6c8117fa36fc67f8e44bf9dfe59b/manifold3d-3.4.1.tar.gz", hash = "sha256:b517927e2c15dc52169fff0cd12e1949eceb4ca49f3a5b8c0568b1116a561ab1", size = 269275, upload-time = "2026-03-24T06:22:40.062Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e3/d0/b066b476242dddfad98db51425a28ac41ab008a4e7697f6d6bca21a24881/manifold3d-3.4.1-cp313-cp313-macosx_10_14_universal2.whl", hash = "sha256:210ec6918870611d9e3f888c00657aad842cfa89a7967e94546a568bf8dfc2f1", size = 1752343, upload-time = "2026-03-24T06:22:04.731Z" }, - { url = "https://files.pythonhosted.org/packages/11/2c/10b5cb142b00bb7b14bb5698c584ce7722c68c3ed58ae4173693a35d2108/manifold3d-3.4.1-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:d4e1dd76a3c5fe935bc14eb62f98ce2361e0e505fcfca08abb2f9d8a1d01e0db", size = 956241, upload-time = "2026-03-24T06:22:06.404Z" }, - { url = "https://files.pythonhosted.org/packages/05/a7/af84e5f6e6af2d07d800355345ffb303c4e8de96dbf3194633322f3d8335/manifold3d-3.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7e1bf4857f64311fb5113ff286898c47efc0f199e4d860cfc663b5f69ce90ede", size = 840900, upload-time = "2026-03-24T06:22:07.974Z" }, - { url = "https://files.pythonhosted.org/packages/80/1c/f274d6e35652c3fb72b54c5fcafc5fc474e1a93d3fd17fb8df3c9c765873/manifold3d-3.4.1-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9b23435456d5ed64e48a34a281869c3b626da8ccd8872e54637b77f420716f9a", size = 1252521, upload-time = "2026-03-24T06:22:09.259Z" }, - { url = "https://files.pythonhosted.org/packages/97/90/82081bcbffc68e36f9f34c36f041d6e0176cbb462e9041683d82ff17b626/manifold3d-3.4.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6871adaf9e5081303d2c9446de5a76a9af84bcf938949fa198cc5f0ae9cad19f", size = 1355366, upload-time = "2026-03-24T06:22:10.892Z" }, - { url = "https://files.pythonhosted.org/packages/0d/db/26df1d96a2c61a4d79aeb0ca2f8bfbfd4af94fdb944469dda38ced240f2f/manifold3d-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:0a93e8202cccea16c76a6c3a7d02300755cccea6536874ccfc160f8c4d8948c4", size = 983317, upload-time = "2026-03-24T06:22:12.257Z" }, - { url = "https://files.pythonhosted.org/packages/26/50/d62f9fed01bdeaea50d3dd821498573c0bf1c286e54e5a632f47cef8fffa/manifold3d-3.4.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:632525867f23a5d34ab4a7b9129dc440a6ed2b4a0444d9feddb6069361107555", size = 1752191, upload-time = "2026-03-24T06:22:13.521Z" }, - { url = "https://files.pythonhosted.org/packages/1c/2e/464f3898f8f1b727a248d4b2bedc310d60efed1a0f43f9977f95f122fcf4/manifold3d-3.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c4e755c0a1d808603185fb5a562c045c71918b4e64a498c489eba658df49692b", size = 956239, upload-time = "2026-03-24T06:22:14.864Z" }, - { url = "https://files.pythonhosted.org/packages/0e/ec/da4bdba9ae1fe9049be9e2f42336ebf700dfd839795b561fb0cee9cb03f6/manifold3d-3.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2c9c20b479e15ec5f0710951d3ae4f95635d0bbf6502f03e43ed87e310e69230", size = 840646, upload-time = "2026-03-24T06:22:16.471Z" }, - { url = "https://files.pythonhosted.org/packages/eb/e0/c476d79d5940da31cf6d0aa9353e56d70fe709fefbadd3c99804594aeb4d/manifold3d-3.4.1-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:145b94b7bb73b425fa42bb795d5c8eaea5de5e45cba9731f2542c7311f3a73c3", size = 1253522, upload-time = "2026-03-24T06:22:17.734Z" }, - { url = "https://files.pythonhosted.org/packages/89/fa/2d5838248950b8cb41ba22496dfc7e95222582761ec83e473a210a0be059/manifold3d-3.4.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1093c002a5f5c012219208bfb0b49f4821974f887cbd357316b9dbab74b7fa5b", size = 1356044, upload-time = "2026-03-24T06:22:19.375Z" }, - { url = "https://files.pythonhosted.org/packages/95/a7/2b8e4b88a613b0057b871ca71342d7237289c5eccf2f75ce10afa04e080e/manifold3d-3.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:04c99b94fbb92572051288d3c6163baf9c81a647dab33d1fdce418457b0a1a44", size = 1014216, upload-time = "2026-03-24T06:22:20.977Z" }, - { url = "https://files.pythonhosted.org/packages/4f/06/c8c5e38b5f93ce2268aa0fc2e4e995a04c8722045868dc75021a7c2a5bc9/manifold3d-3.4.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:22bb8c202c88072fd5cd3fb24243715e7b200151a8aa3da78661b3611e07924f", size = 1760559, upload-time = "2026-03-24T06:22:22.701Z" }, - { url = "https://files.pythonhosted.org/packages/7e/93/5defaadef6a57bb864a51f68c824435929a7c7de1205f95e166325cba55e/manifold3d-3.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:9ade2f14cf906271604ccfa5c49bb5704f0c14b08367e6d3aa0c4ed6ed56f919", size = 961249, upload-time = "2026-03-24T06:22:24.364Z" }, - { url = "https://files.pythonhosted.org/packages/fd/fa/5705986493097e268f26d87b3cfce8e966f6c62a1b8f38fa4086b704cf4d/manifold3d-3.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b4b24047cf423b024f477c09e2c44fed5991cbca4906abf34bf6ced62f37ef93", size = 846070, upload-time = "2026-03-24T06:22:25.946Z" }, - { url = "https://files.pythonhosted.org/packages/2a/84/925d96698fbd90e9990a7a4fdd9a7a8b038e166de696673cfd141bf54e85/manifold3d-3.4.1-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2e02108ee43c6dcf1f40b208569ad49dcf1a6ceed21fd6d5fd6e8f09c02a8b60", size = 1260343, upload-time = "2026-03-24T06:22:27.266Z" }, - { url = "https://files.pythonhosted.org/packages/07/0a/f2d1c6390ebd565fa44357cddc9e6aa783fbccb0cc952996217bcbc69699/manifold3d-3.4.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b1ac2b29c6ca1412f63a1ac2c240ae067fe03f666bb12a21729012275fbbde85", size = 1361860, upload-time = "2026-03-24T06:22:28.945Z" }, - { url = "https://files.pythonhosted.org/packages/8b/9b/5d0cf29530e31c2ef66cc9b2780031a82955002ad924b0eb23ac5e3dd90f/manifold3d-3.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:4d3f795cfcaa857f4bd9bf62bd3f15061bae502fb4cea87e820f4bba67045ff8", size = 1022928, upload-time = "2026-03-24T06:22:30.285Z" }, -] - -[[package]] -name = "mapbox-earcut" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bc/7b/bbf6b00488662be5d2eb7a188222c264b6f713bac10dc4a77bf37a4cb4b6/mapbox_earcut-2.0.0.tar.gz", hash = "sha256:81eab6b86cf99551deb698b98e3f7502c57900e5c479df15e1bdaf1a57f0f9d6", size = 39934, upload-time = "2025-11-16T18:41:27.251Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/7c/c5dd5b255b9828ba5df729e62fdd470a322c938f07ef392ca03c0592bb3a/mapbox_earcut-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:582329a81bd36cf0f82e443c395bcb8cfdb10caddafec76acaebac7c20bf1c31", size = 55619, upload-time = "2025-11-16T18:40:44.44Z" }, - { url = "https://files.pythonhosted.org/packages/1a/3f/03f23eac9831e7d0d8da3d6993695a9a3724659c94e9997f6b7aaccc199d/mapbox_earcut-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d2ac5f610b3e44a3a0c4df06b5552d503b4f1c2c409eeca20dbe05112bd60955", size = 52023, upload-time = "2025-11-16T18:40:45.857Z" }, - { url = "https://files.pythonhosted.org/packages/39/f3/a92ccee494b3e437e4bd81ecd358e39d231dc90af010d6c43930506c10ad/mapbox_earcut-2.0.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:58cc88513b87734b243d86f0d3fb87e96e0a78d9abd8fd615c55f766dd63f949", size = 56357, upload-time = "2025-11-16T18:40:47.27Z" }, - { url = "https://files.pythonhosted.org/packages/03/30/e54ececd0403a5495c340b693075abec92a6d17dc44283b6cb059534f7ed/mapbox_earcut-2.0.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:40218d887798451932f3c335992834aa807c35cd497c6e0733470fdbd77f9521", size = 59215, upload-time = "2025-11-16T18:40:48.682Z" }, - { url = "https://files.pythonhosted.org/packages/6e/e1/8fbff13a074c1fbf702b30ce7ec4d878bc664d659c1c2b1697831f4ea3a8/mapbox_earcut-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:39fa5cfa0e855b028ec9b0200c88ebfa252448f343ce2f67b6fc07fe1f22a3ae", size = 152304, upload-time = "2025-11-16T18:40:49.85Z" }, - { url = "https://files.pythonhosted.org/packages/b9/d5/c757030b3cb3a9f2278ded6f7312d2b9d3761db6f3da8d395f7f7303dd66/mapbox_earcut-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:476b558473b8a43f238d46e819bc0f830c427842ec5feb19e23b4dcac8ad2455", size = 157270, upload-time = "2025-11-16T18:40:51.093Z" }, - { url = "https://files.pythonhosted.org/packages/96/63/589c6decb1f032d8811f1066da552f0a718830f592e6d6539fa4c3c766b8/mapbox_earcut-2.0.0-cp313-cp313-win32.whl", hash = "sha256:8c2d125c182acbc490b39503c0dec4f937bae180d0849a26bcea0ee4a76024bd", size = 51207, upload-time = "2025-11-16T18:40:52.285Z" }, - { url = "https://files.pythonhosted.org/packages/76/75/a79a6020c46d4f07731e88ec5cc9324f6b43343aba835def1dc0bf59fecf/mapbox_earcut-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:e049e6a37c228d7a9cb2f54ae405aa21d35c5175d849530fb32064ddb38ad5ab", size = 56416, upload-time = "2025-11-16T18:40:53.474Z" }, - { url = "https://files.pythonhosted.org/packages/ce/5f/83e878c2b3e9e6db1f60b598a2cc5ed4c2b5bc8d281575c964869414a159/mapbox_earcut-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:8a833d73d63d4b6291bbd8b4d2f551e87f663282cdc547ecbbd9b423849ee996", size = 50103, upload-time = "2025-11-16T18:40:54.954Z" }, - { url = "https://files.pythonhosted.org/packages/96/fc/f1b74324c83f510213ff91eb8b1d2697ad5a12418c5fba966e80f1104a5f/mapbox_earcut-2.0.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ad1dc141797037b7d4c9d8d2e52b9665b36294913a8ec31008b282d1a95b9bdc", size = 55728, upload-time = "2025-11-16T18:40:56.098Z" }, - { url = "https://files.pythonhosted.org/packages/7b/59/053c04e29c4bd22157d3b6255f1e5c19c46cb7a594c4314298bdcbca723f/mapbox_earcut-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0f0f5c6f5ed8ffdce8efe6a003ba598089d0ee07eabd41868db183be50484f9f", size = 52063, upload-time = "2025-11-16T18:40:57.227Z" }, - { url = "https://files.pythonhosted.org/packages/a6/77/acc2d553c3bb8c769535a280545bb7d9608141e90511a2e6215a54611776/mapbox_earcut-2.0.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:82cd92775f37fd1e4b8464c5e74a00e87130eecc55ee3df2492b8ca2bdf6ef3e", size = 56522, upload-time = "2025-11-16T18:40:58.349Z" }, - { url = "https://files.pythonhosted.org/packages/1a/f5/627dd6defd3c1a2b3069e9e27482aa04d268c841735e576c1e22848a34f6/mapbox_earcut-2.0.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:626ffc1310e0cc8910283e4ac3139e5fb0458f18f2c4874162f66159951933ff", size = 59204, upload-time = "2025-11-16T18:41:00.095Z" }, - { url = "https://files.pythonhosted.org/packages/4a/3e/819185542ab095ba1244ad65ececb3edcde6fd0111248a0f9318d695bfcf/mapbox_earcut-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ea951d764a356cad95b23fef950d8aa3b44b933795ad09d977fea7d4dbe377c3", size = 152550, upload-time = "2025-11-16T18:41:01.233Z" }, - { url = "https://files.pythonhosted.org/packages/a9/ad/85e0f815e4774b90ad6761bce55c80d13ee21b2a24014b0be0d5010b0049/mapbox_earcut-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:df1f217624abb5e02ecabcbd84369de970b8d8bc1e4e7c164c1cfcaddad76ca3", size = 157322, upload-time = "2025-11-16T18:41:02.866Z" }, - { url = "https://files.pythonhosted.org/packages/27/4c/0f56369e7a000d2f3177d17baf34263559b206ae524fcd0c4c5d1d960dab/mapbox_earcut-2.0.0-cp314-cp314-win32.whl", hash = "sha256:6fa61307d38b50fc9bd5449c00dbae46d270a32b372c6fc3b8af4b85c85746e4", size = 52916, upload-time = "2025-11-16T18:41:04.122Z" }, - { url = "https://files.pythonhosted.org/packages/c2/9d/8c557dd9b3d9fe2344f5bd5ff3bb0b2a42ed6addb7e43ca4358051743b04/mapbox_earcut-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:0da20ed3c81b240450118773bcedfac34e70a56998f66147222c46f4356fff67", size = 57713, upload-time = "2025-11-16T18:41:05.204Z" }, - { url = "https://files.pythonhosted.org/packages/3b/ec/678c5553938d3a29d02dd41dd898672267f054afc4e2821958dee6ec86ce/mapbox_earcut-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:847e74bd5878e4c64793dc100f9288f5443f87c55c3fe391fd90509029136ff6", size = 51872, upload-time = "2025-11-16T18:41:06.323Z" }, - { url = "https://files.pythonhosted.org/packages/18/37/94f2d973669cbfef811e536713fe56ec012ba74e5f8795a832337b1866a3/mapbox_earcut-2.0.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:ddc9e7175fc903185c64afbbf91febee56b50787dd0962fce2bfb4f20cf80d27", size = 56447, upload-time = "2025-11-16T18:41:07.443Z" }, - { url = "https://files.pythonhosted.org/packages/c9/1c/e0afcc82659cc1727a7e59c4f9e9880bbc3f048a4a5325772b44d4a91dfd/mapbox_earcut-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6dc8a7568066af9a858018d6d92b7e77e164578f9fcd79093f1cbe4ec203461b", size = 53154, upload-time = "2025-11-16T18:41:08.618Z" }, - { url = "https://files.pythonhosted.org/packages/6c/2d/9845281c8c35da2bea733b8c2df5b9fe694e73e7b05fe8a1d4c3c439a1bc/mapbox_earcut-2.0.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6abc5340edd9b433ab2dab2ee033082a199d5c51cce445124626c0040ec0d81b", size = 56285, upload-time = "2025-11-16T18:41:09.728Z" }, - { url = "https://files.pythonhosted.org/packages/97/8e/eeea762a519490662b8f480e2b35bf03701b0bcc5a446b62a4c5a1500b06/mapbox_earcut-2.0.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:df7afdd8078a9aa28f469d9242531d304e09a4b14e514f048e021a949f3777b4", size = 58601, upload-time = "2025-11-16T18:41:10.872Z" }, - { url = "https://files.pythonhosted.org/packages/b9/67/932f80aa6af9bc1a317b6119052c74f327d81e00b457003a049e324b810c/mapbox_earcut-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1a286f73e612a46cafd6d6c843365265090517af16823e2f37277c13cd8b6f09", size = 154924, upload-time = "2025-11-16T18:41:12.104Z" }, - { url = "https://files.pythonhosted.org/packages/87/38/5db4a91f9f90cbb447be61da5468a2955fad3a840ae4c7dbde789b09d45a/mapbox_earcut-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8d081fe1d00dc553e3e68c02fc395324aad0d8ed955f3ff59289264c9b21ace4", size = 159194, upload-time = "2025-11-16T18:41:13.364Z" }, - { url = "https://files.pythonhosted.org/packages/6b/03/de3843b13fe854a010fb2f8b25551d4d5fe1c879ff2e7c8d7d8d7d735a8e/mapbox_earcut-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:13049ca96431bbc7ef7fd7780dd1872209ca11a5c1977f7aa91a1b574a8af863", size = 54143, upload-time = "2025-11-16T18:41:14.564Z" }, - { url = "https://files.pythonhosted.org/packages/9a/89/fbdee5a56ba51df9be6098b5428636ad75aa994e98d8bec6113d5cba401e/mapbox_earcut-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6ace78e4fdba3b8cbb7768d44d77a981698305862a07f94bbb6f5cc16659adb4", size = 60833, upload-time = "2025-11-16T18:41:15.694Z" }, ] [[package]] name = "markdown" -version = "3.10.2" +version = "3.10.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2b/f4/69fa6ed85ae003c2378ffa8f6d2e3234662abd02c10d216c0ba96081a238/markdown-3.10.2.tar.gz", hash = "sha256:994d51325d25ad8aa7ce4ebaec003febcce822c3f8c911e3b17c52f7f589f950", size = 368805, upload-time = "2026-02-09T14:57:26.942Z" } +sdist = { url = "https://files.pythonhosted.org/packages/29/6f/da4c6aea59b3001f2e8c0ec7497475aadaf3b021c10cab5b2858f0f32b26/markdown-3.10.3.tar.gz", hash = "sha256:3589362618f743188b4d955b874402bc814f4f83f544dc207719f4baa7d9c45f", size = 372596, upload-time = "2026-07-30T19:05:29.005Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl", hash = "sha256:e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36", size = 108180, upload-time = "2026-02-09T14:57:25.787Z" }, + { url = "https://files.pythonhosted.org/packages/64/69/4a5af2bc115a9a33fefe51709749de8262be3f9ba063d1753a837cdbc49c/markdown-3.10.3-py3-none-any.whl", hash = "sha256:fa6c92a00a4a3c98b22728c64a935ae1928250ae65058a6ded814d2cc29a4cea", size = 110757, upload-time = "2026-07-30T19:05:27.883Z" }, ] [[package]] name = "markdown-it-py" -version = "4.0.0" +version = "4.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mdurl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } +sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, + { url = "https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a", size = 91687, upload-time = "2026-05-07T12:08:27.182Z" }, ] [[package]] @@ -1053,33 +647,11 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, - { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, - { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, - { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, - { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, - { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, - { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, - { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, - { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, - { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, - { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, - { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, - { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, - { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, - { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, - { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, - { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, - { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, - { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, - { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, - { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, - { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, - { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, ] [[package]] name = "matplotlib" -version = "3.10.8" +version = "3.11.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "contourpy" }, @@ -1092,48 +664,34 @@ dependencies = [ { name = "pyparsing" }, { name = "python-dateutil" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8a/76/d3c6e3a13fe484ebe7718d14e269c9569c4eb0020a968a327acb3b9a8fe6/matplotlib-3.10.8.tar.gz", hash = "sha256:2299372c19d56bcd35cf05a2738308758d32b9eaed2371898d8f5bd33f084aa3", size = 34806269, upload-time = "2025-12-10T22:56:51.155Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/b9/15fd5541ef4f5b9a17eefd379356cf12175fe577424e7b1d80676516031a/matplotlib-3.10.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3f2e409836d7f5ac2f1c013110a4d50b9f7edc26328c108915f9075d7d7a91b6", size = 8261076, upload-time = "2025-12-10T22:55:44.648Z" }, - { url = "https://files.pythonhosted.org/packages/8d/a0/2ba3473c1b66b9c74dc7107c67e9008cb1782edbe896d4c899d39ae9cf78/matplotlib-3.10.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56271f3dac49a88d7fca5060f004d9d22b865f743a12a23b1e937a0be4818ee1", size = 8148794, upload-time = "2025-12-10T22:55:46.252Z" }, - { url = "https://files.pythonhosted.org/packages/75/97/a471f1c3eb1fd6f6c24a31a5858f443891d5127e63a7788678d14e249aea/matplotlib-3.10.8-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a0a7f52498f72f13d4a25ea70f35f4cb60642b466cbb0a9be951b5bc3f45a486", size = 8718474, upload-time = "2025-12-10T22:55:47.864Z" }, - { url = "https://files.pythonhosted.org/packages/01/be/cd478f4b66f48256f42927d0acbcd63a26a893136456cd079c0cc24fbabf/matplotlib-3.10.8-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:646d95230efb9ca614a7a594d4fcacde0ac61d25e37dd51710b36477594963ce", size = 9549637, upload-time = "2025-12-10T22:55:50.048Z" }, - { url = "https://files.pythonhosted.org/packages/5d/7c/8dc289776eae5109e268c4fb92baf870678dc048a25d4ac903683b86d5bf/matplotlib-3.10.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f89c151aab2e2e23cb3fe0acad1e8b82841fd265379c4cecd0f3fcb34c15e0f6", size = 9613678, upload-time = "2025-12-10T22:55:52.21Z" }, - { url = "https://files.pythonhosted.org/packages/64/40/37612487cc8a437d4dd261b32ca21fe2d79510fe74af74e1f42becb1bdb8/matplotlib-3.10.8-cp313-cp313-win_amd64.whl", hash = "sha256:e8ea3e2d4066083e264e75c829078f9e149fa119d27e19acd503de65e0b13149", size = 8142686, upload-time = "2025-12-10T22:55:54.253Z" }, - { url = "https://files.pythonhosted.org/packages/66/52/8d8a8730e968185514680c2a6625943f70269509c3dcfc0dcf7d75928cb8/matplotlib-3.10.8-cp313-cp313-win_arm64.whl", hash = "sha256:c108a1d6fa78a50646029cb6d49808ff0fc1330fda87fa6f6250c6b5369b6645", size = 8012917, upload-time = "2025-12-10T22:55:56.268Z" }, - { url = "https://files.pythonhosted.org/packages/b5/27/51fe26e1062f298af5ef66343d8ef460e090a27fea73036c76c35821df04/matplotlib-3.10.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:ad3d9833a64cf48cc4300f2b406c3d0f4f4724a91c0bd5640678a6ba7c102077", size = 8305679, upload-time = "2025-12-10T22:55:57.856Z" }, - { url = "https://files.pythonhosted.org/packages/2c/1e/4de865bc591ac8e3062e835f42dd7fe7a93168d519557837f0e37513f629/matplotlib-3.10.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:eb3823f11823deade26ce3b9f40dcb4a213da7a670013929f31d5f5ed1055b22", size = 8198336, upload-time = "2025-12-10T22:55:59.371Z" }, - { url = "https://files.pythonhosted.org/packages/c6/cb/2f7b6e75fb4dce87ef91f60cac4f6e34f4c145ab036a22318ec837971300/matplotlib-3.10.8-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d9050fee89a89ed57b4fb2c1bfac9a3d0c57a0d55aed95949eedbc42070fea39", size = 8731653, upload-time = "2025-12-10T22:56:01.032Z" }, - { url = "https://files.pythonhosted.org/packages/46/b3/bd9c57d6ba670a37ab31fb87ec3e8691b947134b201f881665b28cc039ff/matplotlib-3.10.8-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b44d07310e404ba95f8c25aa5536f154c0a8ec473303535949e52eb71d0a1565", size = 9561356, upload-time = "2025-12-10T22:56:02.95Z" }, - { url = "https://files.pythonhosted.org/packages/c0/3d/8b94a481456dfc9dfe6e39e93b5ab376e50998cddfd23f4ae3b431708f16/matplotlib-3.10.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0a33deb84c15ede243aead39f77e990469fff93ad1521163305095b77b72ce4a", size = 9614000, upload-time = "2025-12-10T22:56:05.411Z" }, - { url = "https://files.pythonhosted.org/packages/bd/cd/bc06149fe5585ba800b189a6a654a75f1f127e8aab02fd2be10df7fa500c/matplotlib-3.10.8-cp313-cp313t-win_amd64.whl", hash = "sha256:3a48a78d2786784cc2413e57397981fb45c79e968d99656706018d6e62e57958", size = 8220043, upload-time = "2025-12-10T22:56:07.551Z" }, - { url = "https://files.pythonhosted.org/packages/e3/de/b22cf255abec916562cc04eef457c13e58a1990048de0c0c3604d082355e/matplotlib-3.10.8-cp313-cp313t-win_arm64.whl", hash = "sha256:15d30132718972c2c074cd14638c7f4592bd98719e2308bccea40e0538bc0cb5", size = 8062075, upload-time = "2025-12-10T22:56:09.178Z" }, - { url = "https://files.pythonhosted.org/packages/3c/43/9c0ff7a2f11615e516c3b058e1e6e8f9614ddeca53faca06da267c48345d/matplotlib-3.10.8-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b53285e65d4fa4c86399979e956235deb900be5baa7fc1218ea67fbfaeaadd6f", size = 8262481, upload-time = "2025-12-10T22:56:10.885Z" }, - { url = "https://files.pythonhosted.org/packages/6f/ca/e8ae28649fcdf039fda5ef554b40a95f50592a3c47e6f7270c9561c12b07/matplotlib-3.10.8-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:32f8dce744be5569bebe789e46727946041199030db8aeb2954d26013a0eb26b", size = 8151473, upload-time = "2025-12-10T22:56:12.377Z" }, - { url = "https://files.pythonhosted.org/packages/f1/6f/009d129ae70b75e88cbe7e503a12a4c0670e08ed748a902c2568909e9eb5/matplotlib-3.10.8-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4cf267add95b1c88300d96ca837833d4112756045364f5c734a2276038dae27d", size = 9553896, upload-time = "2025-12-10T22:56:14.432Z" }, - { url = "https://files.pythonhosted.org/packages/f5/26/4221a741eb97967bc1fd5e4c52b9aa5a91b2f4ec05b59f6def4d820f9df9/matplotlib-3.10.8-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2cf5bd12cecf46908f286d7838b2abc6c91cda506c0445b8223a7c19a00df008", size = 9824193, upload-time = "2025-12-10T22:56:16.29Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f3/3abf75f38605772cf48a9daf5821cd4f563472f38b4b828c6fba6fa6d06e/matplotlib-3.10.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:41703cc95688f2516b480f7f339d8851a6035f18e100ee6a32bc0b8536a12a9c", size = 9615444, upload-time = "2025-12-10T22:56:18.155Z" }, - { url = "https://files.pythonhosted.org/packages/93/a5/de89ac80f10b8dc615807ee1133cd99ac74082581196d4d9590bea10690d/matplotlib-3.10.8-cp314-cp314-win_amd64.whl", hash = "sha256:83d282364ea9f3e52363da262ce32a09dfe241e4080dcedda3c0db059d3c1f11", size = 8272719, upload-time = "2025-12-10T22:56:20.366Z" }, - { url = "https://files.pythonhosted.org/packages/69/ce/b006495c19ccc0a137b48083168a37bd056392dee02f87dba0472f2797fe/matplotlib-3.10.8-cp314-cp314-win_arm64.whl", hash = "sha256:2c1998e92cd5999e295a731bcb2911c75f597d937341f3030cc24ef2733d78a8", size = 8144205, upload-time = "2025-12-10T22:56:22.239Z" }, - { url = "https://files.pythonhosted.org/packages/68/d9/b31116a3a855bd313c6fcdb7226926d59b041f26061c6c5b1be66a08c826/matplotlib-3.10.8-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b5a2b97dbdc7d4f353ebf343744f1d1f1cca8aa8bfddb4262fcf4306c3761d50", size = 8305785, upload-time = "2025-12-10T22:56:24.218Z" }, - { url = "https://files.pythonhosted.org/packages/1e/90/6effe8103f0272685767ba5f094f453784057072f49b393e3ea178fe70a5/matplotlib-3.10.8-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3f5c3e4da343bba819f0234186b9004faba952cc420fbc522dc4e103c1985908", size = 8198361, upload-time = "2025-12-10T22:56:26.787Z" }, - { url = "https://files.pythonhosted.org/packages/d7/65/a73188711bea603615fc0baecca1061429ac16940e2385433cc778a9d8e7/matplotlib-3.10.8-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f62550b9a30afde8c1c3ae450e5eb547d579dd69b25c2fc7a1c67f934c1717a", size = 9561357, upload-time = "2025-12-10T22:56:28.953Z" }, - { url = "https://files.pythonhosted.org/packages/f4/3d/b5c5d5d5be8ce63292567f0e2c43dde9953d3ed86ac2de0a72e93c8f07a1/matplotlib-3.10.8-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:495672de149445ec1b772ff2c9ede9b769e3cb4f0d0aa7fa730d7f59e2d4e1c1", size = 9823610, upload-time = "2025-12-10T22:56:31.455Z" }, - { url = "https://files.pythonhosted.org/packages/4d/4b/e7beb6bbd49f6bae727a12b270a2654d13c397576d25bd6786e47033300f/matplotlib-3.10.8-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:595ba4d8fe983b88f0eec8c26a241e16d6376fe1979086232f481f8f3f67494c", size = 9614011, upload-time = "2025-12-10T22:56:33.85Z" }, - { url = "https://files.pythonhosted.org/packages/7c/e6/76f2813d31f032e65f6f797e3f2f6e4aab95b65015924b1c51370395c28a/matplotlib-3.10.8-cp314-cp314t-win_amd64.whl", hash = "sha256:25d380fe8b1dc32cf8f0b1b448470a77afb195438bafdf1d858bfb876f3edf7b", size = 8362801, upload-time = "2025-12-10T22:56:36.107Z" }, - { url = "https://files.pythonhosted.org/packages/5d/49/d651878698a0b67f23aa28e17f45a6d6dd3d3f933fa29087fa4ce5947b5a/matplotlib-3.10.8-cp314-cp314t-win_arm64.whl", hash = "sha256:113bb52413ea508ce954a02c10ffd0d565f9c3bc7f2eddc27dfe1731e71c7b5f", size = 8192560, upload-time = "2025-12-10T22:56:38.008Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/49/64/f9a391af28f518b11ad45a8a712353c94a0aefce09d3703200e5c54b610a/matplotlib-3.11.1.tar.gz", hash = "sha256:69647db5746941c793d6e445a4cd349323ffb87d9cc958c2ad84a659b4832d30", size = 32612045, upload-time = "2026-07-18T03:39:46.63Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/6c/4798363b7fb5644e309fe1fac30216e9146c9f70859d80d588c18caf5317/matplotlib-3.11.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6771b0cd7838c6a857a7209814158c0ad09bfef878db3033dd82d70ad101f191", size = 9454341, upload-time = "2026-07-18T03:38:35.001Z" }, + { url = "https://files.pythonhosted.org/packages/59/98/6acadbe7f98df19d274bc107ac58bb439fa75df82c33dc110d71a4a8501f/matplotlib-3.11.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2abdee5ffa2fe11b2d19f7a5c63b785fb7c28cc46c7bc1814156341d9d1a33e1", size = 9283627, upload-time = "2026-07-18T03:38:37.061Z" }, + { url = "https://files.pythonhosted.org/packages/24/ea/65cec46fe241390ccea1b1754207ee28eb71c5ab866bd5f22fe47e538fa4/matplotlib-3.11.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b0a19dcf73406d3746d25a5ed42d713604c9a3e024d129b102852b0d941cb9f3", size = 10035860, upload-time = "2026-07-18T03:38:39.663Z" }, + { url = "https://files.pythonhosted.org/packages/c7/10/63fdccccbabe002fb0960876baabc5e3f24d9c1bb4cfb25651457f74b3a0/matplotlib-3.11.1-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7389b77ed2ab0552f46d9a90b81b7b8e6dfcdc42adc36c37a0865799843e0e3e", size = 10843594, upload-time = "2026-07-18T03:38:42.144Z" }, + { url = "https://files.pythonhosted.org/packages/98/51/a1155945bff7b91381875022ac1522c5dfdac0d006be8e7df389b3134eae/matplotlib-3.11.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c90be0b73568da4f662afac580956a76e308437e641b4a45aa08925eeb67d95f", size = 10927962, upload-time = "2026-07-18T03:38:44.302Z" }, + { url = "https://files.pythonhosted.org/packages/0d/3a/3d5e1f42dc761bf53401a62a83ff93389b37de9d2c093b2a3aa49ac34f1b/matplotlib-3.11.1-cp313-cp313-win_amd64.whl", hash = "sha256:68408341f2312836fbbdf6b3c78047f65b2d8752f5fd221c3e72d348f5b34f8b", size = 9334074, upload-time = "2026-07-18T03:38:46.616Z" }, + { url = "https://files.pythonhosted.org/packages/e2/db/3f5ea5a5b64060ef5e1ff60a19170423e41ce21b8497a6fe15a36e0b43e3/matplotlib-3.11.1-cp313-cp313-win_arm64.whl", hash = "sha256:0c1f44890d435c1b4ef52f701ad5828cb450ea97bcc83918fda6be74965d6cd2", size = 9007662, upload-time = "2026-07-18T03:38:49.112Z" }, + { url = "https://files.pythonhosted.org/packages/98/6e/c7ae5e0531425b69c0826b00ebbc264c85cab853f1cd6e096c9983c2cdc1/matplotlib-3.11.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:5e510088c27a89d53580a752f959146893563e63c330e161d159b0fee652af6f", size = 9503790, upload-time = "2026-07-18T03:38:51.527Z" }, + { url = "https://files.pythonhosted.org/packages/92/79/15be162e0a2ed546939674e2e97d0e33ec2447d86d4d4e611fa295bb178c/matplotlib-3.11.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:1524e2bdd48a93557aa47ddcfe9c225dfdd57d5a01a5c49128c20f0632980ee1", size = 9336148, upload-time = "2026-07-18T03:38:53.564Z" }, + { url = "https://files.pythonhosted.org/packages/6a/7f/36ffe144fc4aacfe0e3ed2318f72b6755d1e73b041d619b4d393e60f5a66/matplotlib-3.11.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:11664c551345553db92e61cae6cf1376f138f8c47cafdf13b64b18f3e3e9e464", size = 10049244, upload-time = "2026-07-18T03:38:55.911Z" }, + { url = "https://files.pythonhosted.org/packages/ab/5f/55812d68c0a840d3a463638f48c00ab1fe338518ec49a640cb6473b444af/matplotlib-3.11.1-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e1f8922ba31959cf6a9dfb51be64b7f7bc582801a3957dc0c2f3afcd3537adf", size = 10860798, upload-time = "2026-07-18T03:38:58.282Z" }, + { url = "https://files.pythonhosted.org/packages/7a/64/cca444b4eb5e6c768c44fc5e1f0b5211f20ca2b282778051996e996a2bdf/matplotlib-3.11.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:83235693abde86e5e0129998f80ee39fc7f58e6d56a88fafb28a9278833e9d5f", size = 10943282, upload-time = "2026-07-18T03:39:00.465Z" }, + { url = "https://files.pythonhosted.org/packages/e5/0f/a49c329d394f2e9ef38506982107e8b04ecf94dd41a9d8423ff82cc737c7/matplotlib-3.11.1-cp313-cp313t-win_amd64.whl", hash = "sha256:9a076f4fc5cdc43fdf510f5981418d25c2db4973418d9f22d8bb3dc8045ada78", size = 9383532, upload-time = "2026-07-18T03:39:02.468Z" }, + { url = "https://files.pythonhosted.org/packages/e4/50/103e86afb806d8f64d04ede14e4cfc09dbfc25f512421ff85fdd6ebd59cf/matplotlib-3.11.1-cp313-cp313t-win_arm64.whl", hash = "sha256:216fbb93a74add02ddb4cb38ef5348f59ac00b3e84567eaf16598772d40e150a", size = 9059665, upload-time = "2026-07-18T03:39:04.607Z" }, ] [[package]] name = "matplotlib-inline" -version = "0.2.1" +version = "0.2.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c7/74/97e72a36efd4ae2bccb3463284300f8953f199b5ffbc04cbbb0ec78f74b1/matplotlib_inline-0.2.1.tar.gz", hash = "sha256:e1ee949c340d771fc39e241ea75683deb94762c8fa5f2927ec57c83c4dffa9fe", size = 8110, upload-time = "2025-10-23T09:00:22.126Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/c0/9f7c9a46090390368a4d7bcb76bb87a4a36c421e4c0792cdb53486ffac7a/matplotlib_inline-0.2.2.tar.gz", hash = "sha256:72f3fe8fce36b70d4a5b612f899090cd0401deddc4ea90e1572b9f4bfb058c79", size = 8150, upload-time = "2026-05-08T17:33:33.49Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/af/33/ee4519fa02ed11a94aef9559552f3b17bb863f2ecfe1a35dc7f548cde231/matplotlib_inline-0.2.1-py3-none-any.whl", hash = "sha256:d56ce5156ba6085e00a9d54fead6ed29a9c47e215cd1bba2e976ef39f5710a76", size = 9516, upload-time = "2025-10-23T09:00:20.675Z" }, + { url = "https://files.pythonhosted.org/packages/41/09/5b161152e2d90f7b87f781c2e1267494aef9c32498df793f73ad0a0a494a/matplotlib_inline-0.2.2-py3-none-any.whl", hash = "sha256:3c821cf1c209f59fb2d2d64abbf5b23b67bcb2210d663f9918dd851c6da1fcf6", size = 9534, upload-time = "2026-05-08T17:33:32.055Z" }, ] [[package]] @@ -1147,7 +705,7 @@ wheels = [ [[package]] name = "mediapy" -version = "1.2.6" +version = "1.2.7" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ipython" }, @@ -1155,15 +713,15 @@ dependencies = [ { name = "numpy" }, { name = "pillow" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b3/eb/8a0499fb1a2f373f97e2b4df91797507c3971c42c59f1610bed090c57ddc/mediapy-1.2.6.tar.gz", hash = "sha256:2c866cfa0a170213f771b1dd5584a2e82d8d0dc0fa94982f83e29aae27e49c83", size = 28143, upload-time = "2026-02-03T10:29:31.104Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7c/99/8b59dbcebf7e84b35e86c893f52fdc2953299d66ef1fcbacca26162818b0/mediapy-1.2.7.tar.gz", hash = "sha256:fd3826ca156f9165cd369f44d4d345d5609df1da3caf867df7ce304e19dd7fde", size = 28660, upload-time = "2026-07-01T13:14:43.448Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/37/8c/52f0299f1675cdfa1ab39a6028a2e5adf9032ae1118c9895c84b08af162b/mediapy-1.2.6-py3-none-any.whl", hash = "sha256:0a0ea00eb0da83c3c54d588b49c49a41ba456174aa33e530ffe13e17269c9072", size = 27494, upload-time = "2026-02-03T10:29:30.245Z" }, + { url = "https://files.pythonhosted.org/packages/10/70/eeb41d81fea2b3165cfe7ae1c662f9dc6310b14b230a96260376c2891cc7/mediapy-1.2.7-py3-none-any.whl", hash = "sha256:fc81a9190d9724b02286efac4e7a67f384024e078fda0a3cac9fdf25e2f8878f", size = 28015, upload-time = "2026-07-01T13:14:42.524Z" }, ] [[package]] name = "mjlab" -version = "1.2.0" -source = { git = "https://github.com/mujocolab/mjlab?rev=f7fdb163b590e980865e59a5633ff5e1143553bd#f7fdb163b590e980865e59a5633ff5e1143553bd" } +version = "1.5.0" +source = { git = "https://github.com/mujocolab/mjlab?rev=9298ba5691858dec9f0fa7c53c0ffb453192da67#9298ba5691858dec9f0fa7c53c0ffb453192da67" } dependencies = [ { name = "imageio-ffmpeg" }, { name = "mediapy" }, @@ -1173,6 +731,7 @@ dependencies = [ { name = "onnxscript" }, { name = "prettytable" }, { name = "rsl-rl-lib" }, + { name = "scipy" }, { name = "tensorboard" }, { name = "tensordict" }, { name = "torch", version = "2.9.1", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform == 'darwin'" }, @@ -1183,7 +742,8 @@ dependencies = [ { name = "tyro" }, { name = "viser" }, { name = "wandb" }, - { name = "warp-lang" }, + { name = "warp-lang", version = "1.16.0", source = { registry = "https://pypi.nvidia.com/" }, marker = "sys_platform != 'darwin'" }, + { name = "warp-lang", version = "1.16.0", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform == 'darwin'" }, ] [package.optional-dependencies] @@ -1212,7 +772,7 @@ dev = [ [package.metadata] requires-dist = [ - { name = "mjlab", extras = ["cu128"], git = "https://github.com/mujocolab/mjlab?rev=f7fdb163b590e980865e59a5633ff5e1143553bd" }, + { name = "mjlab", extras = ["cu128"], git = "https://github.com/mujocolab/mjlab?rev=9298ba5691858dec9f0fa7c53c0ffb453192da67" }, { name = "torch", marker = "sys_platform != 'darwin'", specifier = ">=2.7.0,<2.10.0", index = "https://download.pytorch.org/whl/cu128" }, { name = "torch", marker = "sys_platform == 'darwin'", specifier = ">=2.7.0,<2.10.0" }, ] @@ -1227,7 +787,7 @@ dev = [ [[package]] name = "mjviser" -version = "0.0.11" +version = "0.0.14" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mujoco" }, @@ -1236,40 +796,25 @@ dependencies = [ { name = "trimesh" }, { name = "viser" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d6/c4/7de1e89d8abc19785e307eebc0990cee9d76e9e045a654f425b3cef2f595/mjviser-0.0.11.tar.gz", hash = "sha256:aebc12785995d4fe1aa3e46cb13f0e464dfd8abe9f63d30ebe77f78767e8cedf", size = 29193, upload-time = "2026-04-02T03:32:01.777Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/e4/eef89b279fb1811b5f120a99ac9284c32ff2ca4fad5e6f5c93035f72ba9a/mjviser-0.0.14.tar.gz", hash = "sha256:ebde2203dab89959a13ae549b4d3e5e5cf9eb69de11a1a2fd759cbe8f8c641f3", size = 29576, upload-time = "2026-05-07T03:35:13.128Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/71/da/116ec59e1de628fbd6330e21d05452d6755f5ab0b55c55a2b37c81ec0868/mjviser-0.0.11-py3-none-any.whl", hash = "sha256:8c4ed7570e0e74171a983cb1603329315fcc46ace0a24d47ab1fde2044a56b59", size = 31937, upload-time = "2026-04-02T03:32:02.717Z" }, + { url = "https://files.pythonhosted.org/packages/9c/c2/4534d678ad1b3f7dee6fd83110112800287be21ba007d988abb9ddc8e0ac/mjviser-0.0.14-py3-none-any.whl", hash = "sha256:4b09f8e90506fc4a71d76fc628872147157947e9292428213ab78cfe137c9a26", size = 32296, upload-time = "2026-05-07T03:35:14.252Z" }, ] [[package]] name = "ml-dtypes" -version = "0.5.4" +version = "0.6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0e/4a/c27b42ed9b1c7d13d9ba8b6905dece787d6259152f2309338aed29b2447b/ml_dtypes-0.5.4.tar.gz", hash = "sha256:8ab06a50fb9bf9666dd0fe5dfb4676fa2b0ac0f31ecff72a6c3af8e22c063453", size = 692314, upload-time = "2025-11-17T22:32:31.031Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/a1/4008f14bbc616cfb1ac5b39ea485f9c63031c4634ab3f4cf72e7541f816a/ml_dtypes-0.5.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c760d85a2f82e2bed75867079188c9d18dae2ee77c25a54d60e9cc79be1bc48", size = 676888, upload-time = "2025-11-17T22:31:56.907Z" }, - { url = "https://files.pythonhosted.org/packages/d3/b7/dff378afc2b0d5a7d6cd9d3209b60474d9819d1189d347521e1688a60a53/ml_dtypes-0.5.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce756d3a10d0c4067172804c9cc276ba9cc0ff47af9078ad439b075d1abdc29b", size = 5036993, upload-time = "2025-11-17T22:31:58.497Z" }, - { url = "https://files.pythonhosted.org/packages/eb/33/40cd74219417e78b97c47802037cf2d87b91973e18bb968a7da48a96ea44/ml_dtypes-0.5.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:533ce891ba774eabf607172254f2e7260ba5f57bdd64030c9a4fcfbd99815d0d", size = 5010956, upload-time = "2025-11-17T22:31:59.931Z" }, - { url = "https://files.pythonhosted.org/packages/e1/8b/200088c6859d8221454825959df35b5244fa9bdf263fd0249ac5fb75e281/ml_dtypes-0.5.4-cp313-cp313-win_amd64.whl", hash = "sha256:f21c9219ef48ca5ee78402d5cc831bd58ea27ce89beda894428bc67a52da5328", size = 212224, upload-time = "2025-11-17T22:32:01.349Z" }, - { url = "https://files.pythonhosted.org/packages/8f/75/dfc3775cb36367816e678f69a7843f6f03bd4e2bcd79941e01ea960a068e/ml_dtypes-0.5.4-cp313-cp313-win_arm64.whl", hash = "sha256:35f29491a3e478407f7047b8a4834e4640a77d2737e0b294d049746507af5175", size = 160798, upload-time = "2025-11-17T22:32:02.864Z" }, - { url = "https://files.pythonhosted.org/packages/4f/74/e9ddb35fd1dd43b1106c20ced3f53c2e8e7fc7598c15638e9f80677f81d4/ml_dtypes-0.5.4-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:304ad47faa395415b9ccbcc06a0350800bc50eda70f0e45326796e27c62f18b6", size = 702083, upload-time = "2025-11-17T22:32:04.08Z" }, - { url = "https://files.pythonhosted.org/packages/74/f5/667060b0aed1aa63166b22897fdf16dca9eb704e6b4bbf86848d5a181aa7/ml_dtypes-0.5.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6a0df4223b514d799b8a1629c65ddc351b3efa833ccf7f8ea0cf654a61d1e35d", size = 5354111, upload-time = "2025-11-17T22:32:05.546Z" }, - { url = "https://files.pythonhosted.org/packages/40/49/0f8c498a28c0efa5f5c95a9e374c83ec1385ca41d0e85e7cf40e5d519a21/ml_dtypes-0.5.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:531eff30e4d368cb6255bc2328d070e35836aa4f282a0fb5f3a0cd7260257298", size = 5366453, upload-time = "2025-11-17T22:32:07.115Z" }, - { url = "https://files.pythonhosted.org/packages/8c/27/12607423d0a9c6bbbcc780ad19f1f6baa2b68b18ce4bddcdc122c4c68dc9/ml_dtypes-0.5.4-cp313-cp313t-win_amd64.whl", hash = "sha256:cb73dccfc991691c444acc8c0012bee8f2470da826a92e3a20bb333b1a7894e6", size = 225612, upload-time = "2025-11-17T22:32:08.615Z" }, - { url = "https://files.pythonhosted.org/packages/e5/80/5a5929e92c72936d5b19872c5fb8fc09327c1da67b3b68c6a13139e77e20/ml_dtypes-0.5.4-cp313-cp313t-win_arm64.whl", hash = "sha256:3bbbe120b915090d9dd1375e4684dd17a20a2491ef25d640a908281da85e73f1", size = 164145, upload-time = "2025-11-17T22:32:09.782Z" }, - { url = "https://files.pythonhosted.org/packages/72/4e/1339dc6e2557a344f5ba5590872e80346f76f6cb2ac3dd16e4666e88818c/ml_dtypes-0.5.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:2b857d3af6ac0d39db1de7c706e69c7f9791627209c3d6dedbfca8c7e5faec22", size = 673781, upload-time = "2025-11-17T22:32:11.364Z" }, - { url = "https://files.pythonhosted.org/packages/04/f9/067b84365c7e83bda15bba2b06c6ca250ce27b20630b1128c435fb7a09aa/ml_dtypes-0.5.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:805cef3a38f4eafae3a5bf9ebdcdb741d0bcfd9e1bd90eb54abd24f928cd2465", size = 5036145, upload-time = "2025-11-17T22:32:12.783Z" }, - { url = "https://files.pythonhosted.org/packages/c6/bb/82c7dcf38070b46172a517e2334e665c5bf374a262f99a283ea454bece7c/ml_dtypes-0.5.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14a4fd3228af936461db66faccef6e4f41c1d82fcc30e9f8d58a08916b1d811f", size = 5010230, upload-time = "2025-11-17T22:32:14.38Z" }, - { url = "https://files.pythonhosted.org/packages/e9/93/2bfed22d2498c468f6bcd0d9f56b033eaa19f33320389314c19ef6766413/ml_dtypes-0.5.4-cp314-cp314-win_amd64.whl", hash = "sha256:8c6a2dcebd6f3903e05d51960a8058d6e131fe69f952a5397e5dbabc841b6d56", size = 221032, upload-time = "2025-11-17T22:32:15.763Z" }, - { url = "https://files.pythonhosted.org/packages/76/a3/9c912fe6ea747bb10fe2f8f54d027eb265db05dfb0c6335e3e063e74e6e8/ml_dtypes-0.5.4-cp314-cp314-win_arm64.whl", hash = "sha256:5a0f68ca8fd8d16583dfa7793973feb86f2fbb56ce3966daf9c9f748f52a2049", size = 163353, upload-time = "2025-11-17T22:32:16.932Z" }, - { url = "https://files.pythonhosted.org/packages/cd/02/48aa7d84cc30ab4ee37624a2fd98c56c02326785750cd212bc0826c2f15b/ml_dtypes-0.5.4-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:bfc534409c5d4b0bf945af29e5d0ab075eae9eecbb549ff8a29280db822f34f9", size = 702085, upload-time = "2025-11-17T22:32:18.175Z" }, - { url = "https://files.pythonhosted.org/packages/5a/e7/85cb99fe80a7a5513253ec7faa88a65306be071163485e9a626fce1b6e84/ml_dtypes-0.5.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2314892cdc3fcf05e373d76d72aaa15fda9fb98625effa73c1d646f331fcecb7", size = 5355358, upload-time = "2025-11-17T22:32:19.7Z" }, - { url = "https://files.pythonhosted.org/packages/79/2b/a826ba18d2179a56e144aef69e57fb2ab7c464ef0b2111940ee8a3a223a2/ml_dtypes-0.5.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0d2ffd05a2575b1519dc928c0b93c06339eb67173ff53acb00724502cda231cf", size = 5366332, upload-time = "2025-11-17T22:32:21.193Z" }, - { url = "https://files.pythonhosted.org/packages/84/44/f4d18446eacb20ea11e82f133ea8f86e2bf2891785b67d9da8d0ab0ef525/ml_dtypes-0.5.4-cp314-cp314t-win_amd64.whl", hash = "sha256:4381fe2f2452a2d7589689693d3162e876b3ddb0a832cde7a414f8e1adf7eab1", size = 236612, upload-time = "2025-11-17T22:32:22.579Z" }, - { url = "https://files.pythonhosted.org/packages/ad/3f/3d42e9a78fe5edf792a83c074b13b9b770092a4fbf3462872f4303135f09/ml_dtypes-0.5.4-cp314-cp314t-win_arm64.whl", hash = "sha256:11942cbf2cf92157db91e5022633c0d9474d4dfd813a909383bd23ce828a4b7d", size = 168825, upload-time = "2025-11-17T22:32:23.766Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/12/72/307d7c4bd0600601c7133fba5cb78af7db968152951c1cd473abb1cda782/ml_dtypes-0.6.0.tar.gz", hash = "sha256:5e60251d32ced5598972e4d5e06a2f044341f9291402551a3f6f0ec44f9299b0", size = 3032327, upload-time = "2026-08-13T14:14:40.215Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/51/fd1582b8f5ed8a9e7be0e161a6ea0dff70cb280479a12178df0b3a72700e/ml_dtypes-0.6.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:084dfe51a7ad58b171f05115f8226ed4233a454a1611371947e806e76f0c638d", size = 565468, upload-time = "2026-08-13T14:14:08.5Z" }, + { url = "https://files.pythonhosted.org/packages/d2/22/20fd70ca6ed12446cb92d5b2a7745bd185f9d8b8cdeeadad976574398e6b/ml_dtypes-0.6.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:28d676428b104bb9717b0928bc5c5129f2d6b51b6727587cc4289e7bf8713cb5", size = 360232, upload-time = "2026-08-13T14:14:09.873Z" }, + { url = "https://files.pythonhosted.org/packages/89/a5/da8ae6c6f1babe4b68e3e55d43d39b529e29774f10e0910671a6b8c86eb8/ml_dtypes-0.6.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:26b1f1fa4f0435a2946859823f6e2bf06796f1e9f10f5a05b08a5e3c8f46ff69", size = 410169, upload-time = "2026-08-13T14:14:11.036Z" }, + { url = "https://files.pythonhosted.org/packages/e2/55/4561acefa00fa4bcbfb82ca6a48578b41f372cd7dd7cdd6eb4720abc2e5f/ml_dtypes-0.6.0-cp313-cp313-win_amd64.whl", hash = "sha256:fb87f46b4f7ad7b5d3ad8f4b452b024bd4229d44c8ff934798c1fe656210387a", size = 439357, upload-time = "2026-08-13T14:14:12.172Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5d/6a01538e507ef0ed5e879985b13a92467bf8960696fb1131f8b8cadc60ff/ml_dtypes-0.6.0-cp313-cp313-win_arm64.whl", hash = "sha256:57ed0d6b4ac5e7868361303a9c57fbcf63b768236ee14456f585dfcf260d0292", size = 552278, upload-time = "2026-08-13T14:14:13.539Z" }, ] [[package]] @@ -1283,40 +828,24 @@ wheels = [ [[package]] name = "msgspec" -version = "0.20.0" +version = "0.21.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ea/9c/bfbd12955a49180cbd234c5d29ec6f74fe641698f0cd9df154a854fc8a15/msgspec-0.20.0.tar.gz", hash = "sha256:692349e588fde322875f8d3025ac01689fead5901e7fb18d6870a44519d62a29", size = 317862, upload-time = "2025-11-24T03:56:28.934Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/d1/b902d38b6e5ba3bdddbec469bba388d647f960aeed7b5b3623a8debe8a76/msgspec-0.20.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9c1ff8db03be7598b50dd4b4a478d6fe93faae3bd54f4f17aa004d0e46c14c46", size = 196463, upload-time = "2025-11-24T03:55:43.405Z" }, - { url = "https://files.pythonhosted.org/packages/57/b6/eff0305961a1d9447ec2b02f8c73c8946f22564d302a504185b730c9a761/msgspec-0.20.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f6532369ece217fd37c5ebcfd7e981f2615628c21121b7b2df9d3adcf2fd69b8", size = 188650, upload-time = "2025-11-24T03:55:44.761Z" }, - { url = "https://files.pythonhosted.org/packages/99/93/f2ec1ae1de51d3fdee998a1ede6b2c089453a2ee82b5c1b361ed9095064a/msgspec-0.20.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f9a1697da2f85a751ac3cc6a97fceb8e937fc670947183fb2268edaf4016d1ee", size = 218834, upload-time = "2025-11-24T03:55:46.441Z" }, - { url = "https://files.pythonhosted.org/packages/28/83/36557b04cfdc317ed8a525c4993b23e43a8fbcddaddd78619112ca07138c/msgspec-0.20.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7fac7e9c92eddcd24c19d9e5f6249760941485dff97802461ae7c995a2450111", size = 224917, upload-time = "2025-11-24T03:55:48.06Z" }, - { url = "https://files.pythonhosted.org/packages/8f/56/362037a1ed5be0b88aced59272442c4b40065c659700f4b195a7f4d0ac88/msgspec-0.20.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f953a66f2a3eb8d5ea64768445e2bb301d97609db052628c3e1bcb7d87192a9f", size = 222821, upload-time = "2025-11-24T03:55:49.388Z" }, - { url = "https://files.pythonhosted.org/packages/92/75/fa2370ec341cedf663731ab7042e177b3742645c5dd4f64dc96bd9f18a6b/msgspec-0.20.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:247af0313ae64a066d3aea7ba98840f6681ccbf5c90ba9c7d17f3e39dbba679c", size = 227227, upload-time = "2025-11-24T03:55:51.125Z" }, - { url = "https://files.pythonhosted.org/packages/f1/25/5e8080fe0117f799b1b68008dc29a65862077296b92550632de015128579/msgspec-0.20.0-cp313-cp313-win_amd64.whl", hash = "sha256:67d5e4dfad52832017018d30a462604c80561aa62a9d548fc2bd4e430b66a352", size = 189966, upload-time = "2025-11-24T03:55:52.458Z" }, - { url = "https://files.pythonhosted.org/packages/79/b6/63363422153937d40e1cb349c5081338401f8529a5a4e216865decd981bf/msgspec-0.20.0-cp313-cp313-win_arm64.whl", hash = "sha256:91a52578226708b63a9a13de287b1ec3ed1123e4a088b198143860c087770458", size = 175378, upload-time = "2025-11-24T03:55:53.721Z" }, - { url = "https://files.pythonhosted.org/packages/bb/18/62dc13ab0260c7d741dda8dc7f481495b93ac9168cd887dda5929880eef8/msgspec-0.20.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:eead16538db1b3f7ec6e3ed1f6f7c5dec67e90f76e76b610e1ffb5671815633a", size = 196407, upload-time = "2025-11-24T03:55:55.001Z" }, - { url = "https://files.pythonhosted.org/packages/dd/1d/b9949e4ad6953e9f9a142c7997b2f7390c81e03e93570c7c33caf65d27e1/msgspec-0.20.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:703c3bb47bf47801627fb1438f106adbfa2998fe586696d1324586a375fca238", size = 188889, upload-time = "2025-11-24T03:55:56.311Z" }, - { url = "https://files.pythonhosted.org/packages/1e/19/f8bb2dc0f1bfe46cc7d2b6b61c5e9b5a46c62298e8f4d03bbe499c926180/msgspec-0.20.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6cdb227dc585fb109305cee0fd304c2896f02af93ecf50a9c84ee54ee67dbb42", size = 219691, upload-time = "2025-11-24T03:55:57.908Z" }, - { url = "https://files.pythonhosted.org/packages/b8/8e/6b17e43f6eb9369d9858ee32c97959fcd515628a1df376af96c11606cf70/msgspec-0.20.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:27d35044dd8818ac1bd0fedb2feb4fbdff4e3508dd7c5d14316a12a2d96a0de0", size = 224918, upload-time = "2025-11-24T03:55:59.322Z" }, - { url = "https://files.pythonhosted.org/packages/1c/db/0e833a177db1a4484797adba7f429d4242585980b90882cc38709e1b62df/msgspec-0.20.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b4296393a29ee42dd25947981c65506fd4ad39beaf816f614146fa0c5a6c91ae", size = 223436, upload-time = "2025-11-24T03:56:00.716Z" }, - { url = "https://files.pythonhosted.org/packages/c3/30/d2ee787f4c918fd2b123441d49a7707ae9015e0e8e1ab51aa7967a97b90e/msgspec-0.20.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:205fbdadd0d8d861d71c8f3399fe1a82a2caf4467bc8ff9a626df34c12176980", size = 227190, upload-time = "2025-11-24T03:56:02.371Z" }, - { url = "https://files.pythonhosted.org/packages/ff/37/9c4b58ff11d890d788e700b827db2366f4d11b3313bf136780da7017278b/msgspec-0.20.0-cp314-cp314-win_amd64.whl", hash = "sha256:7dfebc94fe7d3feec6bc6c9df4f7e9eccc1160bb5b811fbf3e3a56899e398a6b", size = 193950, upload-time = "2025-11-24T03:56:03.668Z" }, - { url = "https://files.pythonhosted.org/packages/e9/4e/cab707bf2fa57408e2934e5197fc3560079db34a1e3cd2675ff2e47e07de/msgspec-0.20.0-cp314-cp314-win_arm64.whl", hash = "sha256:2ad6ae36e4a602b24b4bf4eaf8ab5a441fec03e1f1b5931beca8ebda68f53fc0", size = 179018, upload-time = "2025-11-24T03:56:05.038Z" }, - { url = "https://files.pythonhosted.org/packages/4c/06/3da3fc9aaa55618a8f43eb9052453cfe01f82930bca3af8cea63a89f3a11/msgspec-0.20.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:f84703e0e6ef025663dd1de828ca028774797b8155e070e795c548f76dde65d5", size = 200389, upload-time = "2025-11-24T03:56:06.375Z" }, - { url = "https://files.pythonhosted.org/packages/83/3b/cc4270a5ceab40dfe1d1745856951b0a24fd16ac8539a66ed3004a60c91e/msgspec-0.20.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7c83fc24dd09cf1275934ff300e3951b3adc5573f0657a643515cc16c7dee131", size = 193198, upload-time = "2025-11-24T03:56:07.742Z" }, - { url = "https://files.pythonhosted.org/packages/cd/ae/4c7905ac53830c8e3c06fdd60e3cdcfedc0bbc993872d1549b84ea21a1bd/msgspec-0.20.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f13ccb1c335a124e80c4562573b9b90f01ea9521a1a87f7576c2e281d547f56", size = 225973, upload-time = "2025-11-24T03:56:09.18Z" }, - { url = "https://files.pythonhosted.org/packages/d9/da/032abac1de4d0678d99eaeadb1323bd9d247f4711c012404ba77ed6f15ca/msgspec-0.20.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:17c2b5ca19f19306fc83c96d85e606d2cc107e0caeea85066b5389f664e04846", size = 229509, upload-time = "2025-11-24T03:56:10.898Z" }, - { url = "https://files.pythonhosted.org/packages/69/52/fdc7bdb7057a166f309e0b44929e584319e625aaba4771b60912a9321ccd/msgspec-0.20.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d931709355edabf66c2dd1a756b2d658593e79882bc81aae5964969d5a291b63", size = 230434, upload-time = "2025-11-24T03:56:12.48Z" }, - { url = "https://files.pythonhosted.org/packages/cb/fe/1dfd5f512b26b53043884e4f34710c73e294e7cc54278c3fe28380e42c37/msgspec-0.20.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:565f915d2e540e8a0c93a01ff67f50aebe1f7e22798c6a25873f9fda8d1325f8", size = 231758, upload-time = "2025-11-24T03:56:13.765Z" }, - { url = "https://files.pythonhosted.org/packages/97/f6/9ba7121b8e0c4e0beee49575d1dbc804e2e72467692f0428cf39ceba1ea5/msgspec-0.20.0-cp314-cp314t-win_amd64.whl", hash = "sha256:726f3e6c3c323f283f6021ebb6c8ccf58d7cd7baa67b93d73bfbe9a15c34ab8d", size = 206540, upload-time = "2025-11-24T03:56:15.029Z" }, - { url = "https://files.pythonhosted.org/packages/c8/3e/c5187de84bb2c2ca334ab163fcacf19a23ebb1d876c837f81a1b324a15bf/msgspec-0.20.0-cp314-cp314t-win_arm64.whl", hash = "sha256:93f23528edc51d9f686808a361728e903d6f2be55c901d6f5c92e44c6d546bfc", size = 183011, upload-time = "2025-11-24T03:56:16.442Z" }, +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/7e/74/f11ede02839b19ff459f88e3145df5d711626ca84da4e23520cebf819367/msgspec-0.21.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:764173717a01743f007e9f74520ed281f24672c604514f7d76c1c3a10e8edb66", size = 196176, upload-time = "2026-04-12T21:44:17.613Z" }, + { url = "https://files.pythonhosted.org/packages/bb/40/4476c1bd341418a046c4955aff632ec769315d1e3cb94e6acf86d461f9ed/msgspec-0.21.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:344c7cd0eaed1fb81d7959f99100ef71ec9b536881a376f11b9a6c4803365697", size = 188524, upload-time = "2026-04-12T21:44:18.815Z" }, + { url = "https://files.pythonhosted.org/packages/ca/d9/9e9d7d7e5061b47540d03d640fab9b3965ba7ae49c1b2154861c8f007518/msgspec-0.21.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48943e278b3854c2f89f955ddc6f9f430d3f0784b16e47d10604ee0463cd21f5", size = 218880, upload-time = "2026-04-12T21:44:20.028Z" }, + { url = "https://files.pythonhosted.org/packages/74/66/2bb344f34abb4b57e60c7c9c761994e0417b9718ec1460bf00c296f2a7ea/msgspec-0.21.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a9aa659ebb0101b1cbc31461212b87e341d961f0ab0772aaf068a99e001ec4aa", size = 225050, upload-time = "2026-04-12T21:44:21.577Z" }, + { url = "https://files.pythonhosted.org/packages/1a/84/7c1e412f76092277bf760cef12b7979d03314d259ab5b5cafde5d0c1722d/msgspec-0.21.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7b27d1a8ead2b6f5b0c4f2d07b8be1ccfcc041c8a0e704781edebe3ae13c484", size = 222713, upload-time = "2026-04-12T21:44:22.83Z" }, + { url = "https://files.pythonhosted.org/packages/4e/27/0bba04b2b4ef05f3d068429410bc71d2cea925f1596a8f41152cccd5edb8/msgspec-0.21.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:38fe93e86b61328fe544cb7fd871fad5a27c8734bfda90f65e5dbe288ae50f61", size = 227259, upload-time = "2026-04-12T21:44:24.11Z" }, + { url = "https://files.pythonhosted.org/packages/b0/2d/09574b0eea02fed2c2c1383dbaae2c7f79dc16dcd6487a886000afb5d7c4/msgspec-0.21.1-cp313-cp313-win_amd64.whl", hash = "sha256:8bc666331c35fcce05a7cd2d6221adbe0f6058f8e750711413d22793c080ac6a", size = 189857, upload-time = "2026-04-12T21:44:25.359Z" }, + { url = "https://files.pythonhosted.org/packages/46/34/105b1576ad182879914f0c821f17ee1d13abb165cb060448f96fe2aff078/msgspec-0.21.1-cp313-cp313-win_arm64.whl", hash = "sha256:42bb1241e0750c1a4346f2aa84db26c5ffd99a4eb3a954927d9f149ff2f42898", size = 175403, upload-time = "2026-04-12T21:44:26.608Z" }, ] [[package]] name = "mujoco" -version = "3.7.0.dev892927987" -source = { registry = "https://py.mujoco.org/" } +version = "3.10.0" +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "absl-py" }, { name = "etils", extra = ["epath"] }, @@ -1324,23 +853,30 @@ dependencies = [ { name = "numpy" }, { name = "pyopengl" }, ] +sdist = { url = "https://files.pythonhosted.org/packages/a2/3b/c76837b7fdb007f7605ff783689a0bd23a5a49b065928bec2f1fa7ea3d67/mujoco-3.10.0.tar.gz", hash = "sha256:c9e8d5d87d82204ed5bccc87d843c0a53e75aaf381de2938ec46d04f1ac6e24e", size = 1094987, upload-time = "2026-06-22T17:40:59.904Z" } wheels = [ - { url = "https://py.mujoco.org/mujoco/mujoco-3.7.0.dev892927987-cp313-cp313-macosx_11_0_universal2.whl" }, - { url = "https://py.mujoco.org/mujoco/mujoco-3.7.0.dev892927987-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl" }, - { url = "https://py.mujoco.org/mujoco/mujoco-3.7.0.dev892927987-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl" }, - { url = "https://py.mujoco.org/mujoco/mujoco-3.7.0.dev892927987-cp313-cp313-win_amd64.whl" }, + { url = "https://files.pythonhosted.org/packages/c0/ea/a9a19f025d3b323aca9d00e34fdc177e7290c5f23adf6341109a7e961920/mujoco-3.10.0-cp313-cp313-macosx_10_16_x86_64.whl", hash = "sha256:396a49c1baeb4cb1efd1fc213e54baf6d2a5ca42bcd99da82278501a32cb5d42", size = 7772943, upload-time = "2026-06-22T17:40:31.951Z" }, + { url = "https://files.pythonhosted.org/packages/68/ea/15dfc7cc10f44f46286a0634499f3a63e03269adb4788903f3751e964be0/mujoco-3.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:db794d8b8a64e3bb6a0f226a9200293a4890f490c93411fcc531a145f373521b", size = 19381290, upload-time = "2026-06-22T17:40:34.251Z" }, + { url = "https://files.pythonhosted.org/packages/76/82/ab50abb150805e711e02b7b7424b86bd12297bc5476fb1455ce739fa0d1b/mujoco-3.10.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:720174e7132ccf5f7e9eb86a0855c1b1e93330755f734c21fa58e7bde65bd140", size = 19705727, upload-time = "2026-06-22T17:40:36.968Z" }, + { url = "https://files.pythonhosted.org/packages/52/49/f6f0c7c54c646adda647aa7495bdc69572419480c37d5cd0bee132ebacd6/mujoco-3.10.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e0a5725cc9c9a4da1d5f2b8314ad6d39007e09430ac00aa8d023995ce1f2a3e9", size = 20904596, upload-time = "2026-06-22T17:40:40.423Z" }, + { url = "https://files.pythonhosted.org/packages/05/ea/53c7bcef3ef4de63563d9536e4fe6728996f0fc13e82040b1614ad0e32d9/mujoco-3.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:928dca15b06161b88153df2dc2f7c141d41f39e08e20d1804df8d53f42d9f62c", size = 17865523, upload-time = "2026-06-22T17:40:43.233Z" }, ] [[package]] name = "mujoco-warp" -version = "3.6.0" -source = { git = "https://github.com/google-deepmind/mujoco_warp?rev=875c4caf06d71b12b2036c327e7a07ce08a78b9a#875c4caf06d71b12b2036c327e7a07ce08a78b9a" } +version = "3.10.0.3" +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "absl-py" }, { name = "etils", extra = ["epath"] }, { name = "mujoco" }, { name = "numpy" }, - { name = "warp-lang" }, + { name = "warp-lang", version = "1.16.0", source = { registry = "https://pypi.nvidia.com/" }, marker = "sys_platform != 'darwin'" }, + { name = "warp-lang", version = "1.16.0", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform == 'darwin'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4f/02/1687ee928ea468345546af79dcfd65da9cd5840e16d1e71a244223494e54/mujoco_warp-3.10.0.3.tar.gz", hash = "sha256:f22196465cb1350677f66d8b65aa23bf37d95e150ce3ba3c68ea934ba35e3070", size = 2074757, upload-time = "2026-07-22T15:28:01.367Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9c/a1/a8e616daafb219fccc2d23367894d305e442e2e18bc5e0dd855d9986d979/mujoco_warp-3.10.0.3-py3-none-any.whl", hash = "sha256:9435e5d6a7061af32aecc8da38124bfccceea27fc85176b33b66b7c4b76b4c1a", size = 2152650, upload-time = "2026-07-22T15:27:59.463Z" }, ] [[package]] @@ -1363,52 +899,21 @@ wheels = [ [[package]] name = "numpy" -version = "2.4.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/9f/b8cef5bffa569759033adda9481211426f12f53299629b410340795c2514/numpy-2.4.4.tar.gz", hash = "sha256:2d390634c5182175533585cc89f3608a4682ccb173cc9bb940b2881c8d6f8fa0", size = 20731587, upload-time = "2026-03-29T13:22:01.298Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/14/1d/d0a583ce4fefcc3308806a749a536c201ed6b5ad6e1322e227ee4848979d/numpy-2.4.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:08f2e31ed5e6f04b118e49821397f12767934cfdd12a1ce86a058f91e004ee50", size = 16684933, upload-time = "2026-03-29T13:19:22.47Z" }, - { url = "https://files.pythonhosted.org/packages/c1/62/2b7a48fbb745d344742c0277f01286dead15f3f68e4f359fbfcf7b48f70f/numpy-2.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e823b8b6edc81e747526f70f71a9c0a07ac4e7ad13020aa736bb7c9d67196115", size = 14694532, upload-time = "2026-03-29T13:19:25.581Z" }, - { url = "https://files.pythonhosted.org/packages/e5/87/499737bfba066b4a3bebff24a8f1c5b2dee410b209bc6668c9be692580f0/numpy-2.4.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4a19d9dba1a76618dd86b164d608566f393f8ec6ac7c44f0cc879011c45e65af", size = 5199661, upload-time = "2026-03-29T13:19:28.31Z" }, - { url = "https://files.pythonhosted.org/packages/cd/da/464d551604320d1491bc345efed99b4b7034143a85787aab78d5691d5a0e/numpy-2.4.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:d2a8490669bfe99a233298348acc2d824d496dee0e66e31b66a6022c2ad74a5c", size = 6547539, upload-time = "2026-03-29T13:19:30.97Z" }, - { url = "https://files.pythonhosted.org/packages/7d/90/8d23e3b0dafd024bf31bdec225b3bb5c2dbfa6912f8a53b8659f21216cbf/numpy-2.4.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:45dbed2ab436a9e826e302fcdcbe9133f9b0006e5af7168afb8963a6520da103", size = 15668806, upload-time = "2026-03-29T13:19:33.887Z" }, - { url = "https://files.pythonhosted.org/packages/d1/73/a9d864e42a01896bb5974475438f16086be9ba1f0d19d0bb7a07427c4a8b/numpy-2.4.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c901b15172510173f5cb310eae652908340f8dede90fff9e3bf6c0d8dfd92f83", size = 16632682, upload-time = "2026-03-29T13:19:37.336Z" }, - { url = "https://files.pythonhosted.org/packages/34/fb/14570d65c3bde4e202a031210475ae9cde9b7686a2e7dc97ee67d2833b35/numpy-2.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:99d838547ace2c4aace6c4f76e879ddfe02bb58a80c1549928477862b7a6d6ed", size = 17019810, upload-time = "2026-03-29T13:19:40.963Z" }, - { url = "https://files.pythonhosted.org/packages/8a/77/2ba9d87081fd41f6d640c83f26fb7351e536b7ce6dd9061b6af5904e8e46/numpy-2.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0aec54fd785890ecca25a6003fd9a5aed47ad607bbac5cd64f836ad8666f4959", size = 18357394, upload-time = "2026-03-29T13:19:44.859Z" }, - { url = "https://files.pythonhosted.org/packages/a2/23/52666c9a41708b0853fa3b1a12c90da38c507a3074883823126d4e9d5b30/numpy-2.4.4-cp313-cp313-win32.whl", hash = "sha256:07077278157d02f65c43b1b26a3886bce886f95d20aabd11f87932750dfb14ed", size = 5959556, upload-time = "2026-03-29T13:19:47.661Z" }, - { url = "https://files.pythonhosted.org/packages/57/fb/48649b4971cde70d817cf97a2a2fdc0b4d8308569f1dd2f2611959d2e0cf/numpy-2.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:5c70f1cc1c4efbe316a572e2d8b9b9cc44e89b95f79ca3331553fbb63716e2bf", size = 12317311, upload-time = "2026-03-29T13:19:50.67Z" }, - { url = "https://files.pythonhosted.org/packages/ba/d8/11490cddd564eb4de97b4579ef6bfe6a736cc07e94c1598590ae25415e01/numpy-2.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:ef4059d6e5152fa1a39f888e344c73fdc926e1b2dd58c771d67b0acfbf2aa67d", size = 10222060, upload-time = "2026-03-29T13:19:54.229Z" }, - { url = "https://files.pythonhosted.org/packages/99/5d/dab4339177a905aad3e2221c915b35202f1ec30d750dd2e5e9d9a72b804b/numpy-2.4.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4bbc7f303d125971f60ec0aaad5e12c62d0d2c925f0ab1273debd0e4ba37aba5", size = 14822302, upload-time = "2026-03-29T13:19:57.585Z" }, - { url = "https://files.pythonhosted.org/packages/eb/e4/0564a65e7d3d97562ed6f9b0fd0fb0a6f559ee444092f105938b50043876/numpy-2.4.4-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:4d6d57903571f86180eb98f8f0c839fa9ebbfb031356d87f1361be91e433f5b7", size = 5327407, upload-time = "2026-03-29T13:20:00.601Z" }, - { url = "https://files.pythonhosted.org/packages/29/8d/35a3a6ce5ad371afa58b4700f1c820f8f279948cca32524e0a695b0ded83/numpy-2.4.4-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:4636de7fd195197b7535f231b5de9e4b36d2c440b6e566d2e4e4746e6af0ca93", size = 6647631, upload-time = "2026-03-29T13:20:02.855Z" }, - { url = "https://files.pythonhosted.org/packages/f4/da/477731acbd5a58a946c736edfdabb2ac5b34c3d08d1ba1a7b437fa0884df/numpy-2.4.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ad2e2ef14e0b04e544ea2fa0a36463f847f113d314aa02e5b402fdf910ef309e", size = 15727691, upload-time = "2026-03-29T13:20:06.004Z" }, - { url = "https://files.pythonhosted.org/packages/e6/db/338535d9b152beabeb511579598418ba0212ce77cf9718edd70262cc4370/numpy-2.4.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a285b3b96f951841799528cd1f4f01cd70e7e0204b4abebac9463eecfcf2a40", size = 16681241, upload-time = "2026-03-29T13:20:09.417Z" }, - { url = "https://files.pythonhosted.org/packages/e2/a9/ad248e8f58beb7a0219b413c9c7d8151c5d285f7f946c3e26695bdbbe2df/numpy-2.4.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f8474c4241bc18b750be2abea9d7a9ec84f46ef861dbacf86a4f6e043401f79e", size = 17085767, upload-time = "2026-03-29T13:20:13.126Z" }, - { url = "https://files.pythonhosted.org/packages/b5/1a/3b88ccd3694681356f70da841630e4725a7264d6a885c8d442a697e1146b/numpy-2.4.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4e874c976154687c1f71715b034739b45c7711bec81db01914770373d125e392", size = 18403169, upload-time = "2026-03-29T13:20:17.096Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c9/fcfd5d0639222c6eac7f304829b04892ef51c96a75d479214d77e3ce6e33/numpy-2.4.4-cp313-cp313t-win32.whl", hash = "sha256:9c585a1790d5436a5374bac930dad6ed244c046ed91b2b2a3634eb2971d21008", size = 6083477, upload-time = "2026-03-29T13:20:20.195Z" }, - { url = "https://files.pythonhosted.org/packages/d5/e3/3938a61d1c538aaec8ed6fd6323f57b0c2d2d2219512434c5c878db76553/numpy-2.4.4-cp313-cp313t-win_amd64.whl", hash = "sha256:93e15038125dc1e5345d9b5b68aa7f996ec33b98118d18c6ca0d0b7d6198b7e8", size = 12457487, upload-time = "2026-03-29T13:20:22.946Z" }, - { url = "https://files.pythonhosted.org/packages/97/6a/7e345032cc60501721ef94e0e30b60f6b0bd601f9174ebd36389a2b86d40/numpy-2.4.4-cp313-cp313t-win_arm64.whl", hash = "sha256:0dfd3f9d3adbe2920b68b5cd3d51444e13a10792ec7154cd0a2f6e74d4ab3233", size = 10292002, upload-time = "2026-03-29T13:20:25.909Z" }, - { url = "https://files.pythonhosted.org/packages/6e/06/c54062f85f673dd5c04cbe2f14c3acb8c8b95e3384869bb8cc9bff8cb9df/numpy-2.4.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:f169b9a863d34f5d11b8698ead99febeaa17a13ca044961aa8e2662a6c7766a0", size = 16684353, upload-time = "2026-03-29T13:20:29.504Z" }, - { url = "https://files.pythonhosted.org/packages/4c/39/8a320264a84404c74cc7e79715de85d6130fa07a0898f67fb5cd5bd79908/numpy-2.4.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2483e4584a1cb3092da4470b38866634bafb223cbcd551ee047633fd2584599a", size = 14704914, upload-time = "2026-03-29T13:20:33.547Z" }, - { url = "https://files.pythonhosted.org/packages/91/fb/287076b2614e1d1044235f50f03748f31fa287e3dbe6abeb35cdfa351eca/numpy-2.4.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:2d19e6e2095506d1736b7d80595e0f252d76b89f5e715c35e06e937679ea7d7a", size = 5210005, upload-time = "2026-03-29T13:20:36.45Z" }, - { url = "https://files.pythonhosted.org/packages/63/eb/fcc338595309910de6ecabfcef2419a9ce24399680bfb149421fa2df1280/numpy-2.4.4-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:6a246d5914aa1c820c9443ddcee9c02bec3e203b0c080349533fae17727dfd1b", size = 6544974, upload-time = "2026-03-29T13:20:39.014Z" }, - { url = "https://files.pythonhosted.org/packages/44/5d/e7e9044032a716cdfaa3fba27a8e874bf1c5f1912a1ddd4ed071bf8a14a6/numpy-2.4.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:989824e9faf85f96ec9c7761cd8d29c531ad857bfa1daa930cba85baaecf1a9a", size = 15684591, upload-time = "2026-03-29T13:20:42.146Z" }, - { url = "https://files.pythonhosted.org/packages/98/7c/21252050676612625449b4807d6b695b9ce8a7c9e1c197ee6216c8a65c7c/numpy-2.4.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:27a8d92cd10f1382a67d7cf4db7ce18341b66438bdd9f691d7b0e48d104c2a9d", size = 16637700, upload-time = "2026-03-29T13:20:46.204Z" }, - { url = "https://files.pythonhosted.org/packages/b1/29/56d2bbef9465db24ef25393383d761a1af4f446a1df9b8cded4fe3a5a5d7/numpy-2.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e44319a2953c738205bf3354537979eaa3998ed673395b964c1176083dd46252", size = 17035781, upload-time = "2026-03-29T13:20:50.242Z" }, - { url = "https://files.pythonhosted.org/packages/e3/2b/a35a6d7589d21f44cea7d0a98de5ddcbb3d421b2622a5c96b1edf18707c3/numpy-2.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e892aff75639bbef0d2a2cfd55535510df26ff92f63c92cd84ef8d4ba5a5557f", size = 18362959, upload-time = "2026-03-29T13:20:54.019Z" }, - { url = "https://files.pythonhosted.org/packages/64/c9/d52ec581f2390e0f5f85cbfd80fb83d965fc15e9f0e1aec2195faa142cde/numpy-2.4.4-cp314-cp314-win32.whl", hash = "sha256:1378871da56ca8943c2ba674530924bb8ca40cd228358a3b5f302ad60cf875fc", size = 6008768, upload-time = "2026-03-29T13:20:56.912Z" }, - { url = "https://files.pythonhosted.org/packages/fa/22/4cc31a62a6c7b74a8730e31a4274c5dc80e005751e277a2ce38e675e4923/numpy-2.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:715d1c092715954784bc79e1174fc2a90093dc4dc84ea15eb14dad8abdcdeb74", size = 12449181, upload-time = "2026-03-29T13:20:59.548Z" }, - { url = "https://files.pythonhosted.org/packages/70/2e/14cda6f4d8e396c612d1bf97f22958e92148801d7e4f110cabebdc0eef4b/numpy-2.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:2c194dd721e54ecad9ad387c1d35e63dce5c4450c6dc7dd5611283dda239aabb", size = 10496035, upload-time = "2026-03-29T13:21:02.524Z" }, - { url = "https://files.pythonhosted.org/packages/b1/e8/8fed8c8d848d7ecea092dc3469643f9d10bc3a134a815a3b033da1d2039b/numpy-2.4.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2aa0613a5177c264ff5921051a5719d20095ea586ca88cc802c5c218d1c67d3e", size = 14824958, upload-time = "2026-03-29T13:21:05.671Z" }, - { url = "https://files.pythonhosted.org/packages/05/1a/d8007a5138c179c2bf33ef44503e83d70434d2642877ee8fbb230e7c0548/numpy-2.4.4-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:42c16925aa5a02362f986765f9ebabf20de75cdefdca827d14315c568dcab113", size = 5330020, upload-time = "2026-03-29T13:21:08.635Z" }, - { url = "https://files.pythonhosted.org/packages/99/64/ffb99ac6ae93faf117bcbd5c7ba48a7f45364a33e8e458545d3633615dda/numpy-2.4.4-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:874f200b2a981c647340f841730fc3a2b54c9d940566a3c4149099591e2c4c3d", size = 6650758, upload-time = "2026-03-29T13:21:10.949Z" }, - { url = "https://files.pythonhosted.org/packages/6e/6e/795cc078b78a384052e73b2f6281ff7a700e9bf53bcce2ee579d4f6dd879/numpy-2.4.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9b39d38a9bd2ae1becd7eac1303d031c5c110ad31f2b319c6e7d98b135c934d", size = 15729948, upload-time = "2026-03-29T13:21:14.047Z" }, - { url = "https://files.pythonhosted.org/packages/5f/86/2acbda8cc2af5f3d7bfc791192863b9e3e19674da7b5e533fded124d1299/numpy-2.4.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b268594bccac7d7cf5844c7732e3f20c50921d94e36d7ec9b79e9857694b1b2f", size = 16679325, upload-time = "2026-03-29T13:21:17.561Z" }, - { url = "https://files.pythonhosted.org/packages/bc/59/cafd83018f4aa55e0ac6fa92aa066c0a1877b77a615ceff1711c260ffae8/numpy-2.4.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ac6b31e35612a26483e20750126d30d0941f949426974cace8e6b5c58a3657b0", size = 17084883, upload-time = "2026-03-29T13:21:21.106Z" }, - { url = "https://files.pythonhosted.org/packages/f0/85/a42548db84e65ece46ab2caea3d3f78b416a47af387fcbb47ec28e660dc2/numpy-2.4.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8e3ed142f2728df44263aaf5fb1f5b0b99f4070c553a0d7f033be65338329150", size = 18403474, upload-time = "2026-03-29T13:21:24.828Z" }, - { url = "https://files.pythonhosted.org/packages/ed/ad/483d9e262f4b831000062e5d8a45e342166ec8aaa1195264982bca267e62/numpy-2.4.4-cp314-cp314t-win32.whl", hash = "sha256:dddbbd259598d7240b18c9d87c56a9d2fb3b02fe266f49a7c101532e78c1d871", size = 6155500, upload-time = "2026-03-29T13:21:28.205Z" }, - { url = "https://files.pythonhosted.org/packages/c7/03/2fc4e14c7bd4ff2964b74ba90ecb8552540b6315f201df70f137faa5c589/numpy-2.4.4-cp314-cp314t-win_amd64.whl", hash = "sha256:a7164afb23be6e37ad90b2f10426149fd75aee07ca55653d2aa41e66c4ef697e", size = 12637755, upload-time = "2026-03-29T13:21:31.107Z" }, - { url = "https://files.pythonhosted.org/packages/58/78/548fb8e07b1a341746bfbecb32f2c268470f45fa028aacdbd10d9bc73aab/numpy-2.4.4-cp314-cp314t-win_arm64.whl", hash = "sha256:ba203255017337d39f89bdd58417f03c4426f12beed0440cfd933cb15f8669c7", size = 10566643, upload-time = "2026-03-29T13:21:34.339Z" }, +version = "2.5.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/80/db0b4559e57ec36362bedbb05530a87fafbcb6067708c946967a41d449e7/numpy-2.5.2.tar.gz", hash = "sha256:d482d171c406ae88c5b19cad3b6a1c4c5209f886ab74bc44c2c865c23f52d860", size = 20773161, upload-time = "2026-08-09T13:48:27.962Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/d2/6b24738a0ef4557d189b150046cd07823c50e4273e8aebd651222e24306f/numpy-2.5.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8e4cb9a754c8a0c62eaa88273a5fba3391f4a610d1dee893c0755da31c083f15", size = 16886595, upload-time = "2026-08-09T13:45:27.323Z" }, + { url = "https://files.pythonhosted.org/packages/65/60/f2d208d366f263f39c6e69ed309290717aab41078b6d04c9be2a84fa2a07/numpy-2.5.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:52c808f96484f5571a5cc863775ce50247c17dfb3b0361f8ed6b4b0456f80080", size = 11896845, upload-time = "2026-08-09T13:45:31.638Z" }, + { url = "https://files.pythonhosted.org/packages/3c/79/81e0bf24f4d020a2b1d5cd297a9f60c3f24eeb116f9bba5870443f7b6a4a/numpy-2.5.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:29d81e97f668489cba8ebfd796b9bdd453525d35dd9e162e2daec94bf3fc7740", size = 5343880, upload-time = "2026-08-09T13:45:34.373Z" }, + { url = "https://files.pythonhosted.org/packages/ba/cc/e3141cf06d1a8a2c7e107543fe1269c1d1af760d4d683c0794a4ee1127c2/numpy-2.5.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:afb3f0632d6b2e3ba04dbce8d1e48d321b369138b73830b5ca371a0e8d479d56", size = 6682264, upload-time = "2026-08-09T13:45:36.7Z" }, + { url = "https://files.pythonhosted.org/packages/29/f1/2a64a307d92c5d98f5255a4014eb43bb6103ee477087b61ecae44a3aa9b9/numpy-2.5.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0aadf13b60048d501e05fa699efaf7734e2494f3498a4c2a5521d822640324f3", size = 15609566, upload-time = "2026-08-09T13:45:39.518Z" }, + { url = "https://files.pythonhosted.org/packages/7b/44/59a1eb68e773c4098d107ef34a0dbdeca501d72ffcfbff9a7707343921ce/numpy-2.5.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29b86ff8a6cc556b47ec6b64b194815cc80e6bf5eedcc6cddfd65318cb0b4eee", size = 16709995, upload-time = "2026-08-09T13:45:43.661Z" }, + { url = "https://files.pythonhosted.org/packages/8a/4c/3e54d4ddbc359a1295f8b633e8106bcd4d7d4a206e82df051bdfb3058755/numpy-2.5.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6950c4b7dd562453090548ba7f5da7e59f57f85663f15d5dcc60e249192f7e59", size = 16972511, upload-time = "2026-08-09T13:45:47.094Z" }, + { url = "https://files.pythonhosted.org/packages/f2/9f/02e371638ebf19b66d46231e4be52999e87f32d1961b113bc45656608b22/numpy-2.5.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b9727f472d2f3888053b8a75ab0cb94745a9de224bb5846dbadc0092101bc71d", size = 18465609, upload-time = "2026-08-09T13:45:50.808Z" }, + { url = "https://files.pythonhosted.org/packages/eb/ae/ad6645abc7a3510fe48e8ea1ab4598166f500057ef4ebf38bfad4f1577de/numpy-2.5.2-cp313-cp313-win32.whl", hash = "sha256:4f9744f9fbdcea0bc552e8f19e1f141f811a3f9bc2be2cc6e86d982cab23e3f4", size = 6070204, upload-time = "2026-08-09T13:45:54.111Z" }, + { url = "https://files.pythonhosted.org/packages/15/20/f3489f86d81ea460b2bcdceaed094142ca6579f6be0ec527b781d39afe68/numpy-2.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:85aaccb24182c25df891ad0ec333585967e115269d5f1b17f2c9ae005bc96657", size = 12460532, upload-time = "2026-08-09T13:45:57.167Z" }, + { url = "https://files.pythonhosted.org/packages/d5/21/35b31dde1b283b79de828b80f876afd8c94e28fe1e9c375f89e261cc4c0d/numpy-2.5.2-cp313-cp313-win_arm64.whl", hash = "sha256:bd68ece1553d2023c09a4226d9e41c586ad2d20594d1a456186c33513d2cb3f2", size = 10396725, upload-time = "2026-08-09T13:46:00.478Z" }, ] [[package]] @@ -1562,7 +1067,7 @@ wheels = [ [[package]] name = "onnx" -version = "1.21.0" +version = "1.22.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ml-dtypes" }, @@ -1570,29 +1075,20 @@ dependencies = [ { name = "protobuf" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c5/93/942d2a0f6a70538eea042ce0445c8aefd46559ad153469986f29a743c01c/onnx-1.21.0.tar.gz", hash = "sha256:4d8b67d0aaec5864c87633188b91cc520877477ec0254eda122bef8be43cd764", size = 12074608, upload-time = "2026-03-27T21:33:36.118Z" } +sdist = { url = "https://files.pythonhosted.org/packages/04/19/8ea73a64b368b75fe339771a20a02bc61ea1f551484c9e3d9d0bfbd0450f/onnx-1.22.0.tar.gz", hash = "sha256:ef40c0aaf0b643857ea9306fc7eddce17eaf9fb0407e4801f1fc5758443a38e0", size = 12024721, upload-time = "2026-06-15T12:50:05.354Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7d/ae/cb644ec84c25e63575d9d8790fdcc5d1a11d67d3f62f872edb35fa38d158/onnx-1.21.0-cp312-abi3-macosx_12_0_universal2.whl", hash = "sha256:fc2635400fe39ff37ebc4e75342cc54450eadadf39c540ff132c319bf4960095", size = 17965930, upload-time = "2026-03-27T21:32:48.089Z" }, - { url = "https://files.pythonhosted.org/packages/6f/b6/eeb5903586645ef8a49b4b7892580438741acc3df91d7a5bd0f3a59ea9cb/onnx-1.21.0-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9003d5206c01fa2ff4b46311566865d8e493e1a6998d4009ec6de39843f1b59b", size = 17531344, upload-time = "2026-03-27T21:32:50.837Z" }, - { url = "https://files.pythonhosted.org/packages/a7/00/4823f06357892d1e60d6f34e7299d2ba4ed2108c487cc394f7ce85a3ff14/onnx-1.21.0-cp312-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a9261bd580fb8548c9c37b3c6750387eb8f21ea43c63880d37b2c622e1684285", size = 17613697, upload-time = "2026-03-27T21:32:54.222Z" }, - { url = "https://files.pythonhosted.org/packages/23/1d/391f3c567ae068c8ac4f1d1316bae97c9eb45e702f05975fe0e17ad441f0/onnx-1.21.0-cp312-abi3-win32.whl", hash = "sha256:9ea4e824964082811938a9250451d89c4ec474fe42dd36c038bfa5df31993d1e", size = 16287200, upload-time = "2026-03-27T21:32:57.277Z" }, - { url = "https://files.pythonhosted.org/packages/9c/a6/5eefbe5b40ea96de95a766bd2e0e751f35bdea2d4b951991ec9afaa69531/onnx-1.21.0-cp312-abi3-win_amd64.whl", hash = "sha256:458d91948ad9a7729a347550553b49ab6939f9af2cddf334e2116e45467dc61f", size = 16441045, upload-time = "2026-03-27T21:33:00.081Z" }, - { url = "https://files.pythonhosted.org/packages/63/c4/0ed8dc037a39113d2a4d66e0005e07751c299c46b993f1ad5c2c35664c20/onnx-1.21.0-cp312-abi3-win_arm64.whl", hash = "sha256:ca14bc4842fccc3187eb538f07eabeb25a779b39388b006db4356c07403a7bbb", size = 16403134, upload-time = "2026-03-27T21:33:03.987Z" }, - { url = "https://files.pythonhosted.org/packages/f8/89/0e1a9beb536401e2f45ac88735e123f2735e12fc7b56ff6c11727e097526/onnx-1.21.0-cp313-cp313t-macosx_12_0_universal2.whl", hash = "sha256:257d1d1deb6a652913698f1e3f33ef1ca0aa69174892fe38946d4572d89dd94f", size = 17975430, upload-time = "2026-03-27T21:33:07.005Z" }, - { url = "https://files.pythonhosted.org/packages/ec/46/e6dc71a7b3b317265591b20a5f71d0ff5c0d26c24e52283139dc90c66038/onnx-1.21.0-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7cd7cb8f6459311bdb557cbf6c0ccc6d8ace11c304d1bba0a30b4a4688e245f8", size = 17537435, upload-time = "2026-03-27T21:33:09.765Z" }, - { url = "https://files.pythonhosted.org/packages/49/2e/27affcac63eaf2ef183a44fd1a1354b11da64a6c72fe6f3fdcf5571bcee5/onnx-1.21.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b58a4cfec8d9311b73dc083e4c1fa362069267881144c05139b3eba5dc3a840", size = 17617687, upload-time = "2026-03-27T21:33:12.619Z" }, - { url = "https://files.pythonhosted.org/packages/1c/5c/ac8ed15e941593a3672ce424280b764979026317811f2e8508432bfc3429/onnx-1.21.0-cp313-cp313t-win_amd64.whl", hash = "sha256:1a9baf882562c4cebf79589bebb7cd71a20e30b51158cac3e3bbaf27da6163bd", size = 16449402, upload-time = "2026-03-27T21:33:15.555Z" }, - { url = "https://files.pythonhosted.org/packages/0e/aa/d2231e0dcaad838217afc64c306c8152a080134d2034e247cc973d577674/onnx-1.21.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bba12181566acf49b35875838eba49536a327b2944664b17125577d230c637ad", size = 16408273, upload-time = "2026-03-27T21:33:18.599Z" }, - { url = "https://files.pythonhosted.org/packages/bf/0a/8905b14694def6ad23edf1011fdd581500384062f8c4c567e114be7aa272/onnx-1.21.0-cp314-cp314t-macosx_12_0_universal2.whl", hash = "sha256:7ee9d8fd6a4874a5fa8b44bbcabea104ce752b20469b88bc50c7dcf9030779ad", size = 17975331, upload-time = "2026-03-27T21:33:21.69Z" }, - { url = "https://files.pythonhosted.org/packages/61/28/f4e401e5199d1b9c8b76c7e7ae1169e050515258e877b58fa8bb49d3bdcc/onnx-1.21.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5489f25fe461e7f32128218251a466cabbeeaf1eaa791c79daebf1a80d5a2cc9", size = 17537430, upload-time = "2026-03-27T21:33:24.547Z" }, - { url = "https://files.pythonhosted.org/packages/cf/cf/5d13320eb3660d5af360ea3b43aa9c63a70c92a9b4d1ea0d34501a32fcb8/onnx-1.21.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:db17fc0fec46180b6acbd1d5d8650a04e5527c02b09381da0b5b888d02a204c8", size = 17617662, upload-time = "2026-03-27T21:33:27.418Z" }, - { url = "https://files.pythonhosted.org/packages/4d/50/3eaa1878338247be021e6423696813d61e77e534dccbd15a703a144e703d/onnx-1.21.0-cp314-cp314t-win_amd64.whl", hash = "sha256:19d9971a3e52a12968ae6c70fd0f86c349536de0b0c33922ecdbe52d1972fe60", size = 16463688, upload-time = "2026-03-27T21:33:30.229Z" }, - { url = "https://files.pythonhosted.org/packages/a7/48/38d46b43bbb525e0b6a4c2c4204cc6795d67e45687a2f7403e06d8e7053d/onnx-1.21.0-cp314-cp314t-win_arm64.whl", hash = "sha256:efba467efb316baf2a9452d892c2f982b9b758c778d23e38c7f44fa211b30bb9", size = 16423387, upload-time = "2026-03-27T21:33:33.446Z" }, + { url = "https://files.pythonhosted.org/packages/ee/6a/481561f1093834376ed493e4ca42a73e5be0d50031f2969c86593bdc7c96/onnx-1.22.0-cp312-abi3-macosx_12_0_universal2.whl", hash = "sha256:596fbf0490947533c1c1045ba860851dc9fb77471023dac9a71ba5b42ceab103", size = 20167081, upload-time = "2026-06-15T12:49:32.078Z" }, + { url = "https://files.pythonhosted.org/packages/84/55/b34fc2aa30aa54b4a775402d24c4082242c720283a274fe976ac8eb94480/onnx-1.22.0-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae5a563f281cd9d2845622cecf6c092a57e4ee1b138f66fdbbdd4200567a5e16", size = 18889249, upload-time = "2026-06-15T12:49:34.7Z" }, + { url = "https://files.pythonhosted.org/packages/09/a6/bd32357e6cc1ecb473afd78193d7231724f284435d2db25696ecfaaa1503/onnx-1.22.0-cp312-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:955e02e1f6d385b53d52f9cd7b9cdf5caf417c300bcfe3c64c6d542be763845b", size = 19106514, upload-time = "2026-06-15T12:49:37.424Z" }, + { url = "https://files.pythonhosted.org/packages/5a/9d/3af461ac6c714b8b369cb71499659932f4f12cfb066250b62f7567c3d530/onnx-1.22.0-cp312-abi3-pyemscripten_2025_0_wasm32.whl", hash = "sha256:82e9f27fc1223cb06d68a56bed6f9d3caf3d0dad1b61bce45006d529b15bd94c", size = 16966387, upload-time = "2026-06-15T12:49:40.918Z" }, + { url = "https://files.pythonhosted.org/packages/d0/f0/68195b5e5a53e333faf2660f5352ee43738d0e42fc5216cc6b1871a9fbfb/onnx-1.22.0-cp312-abi3-win32.whl", hash = "sha256:cc8b66b312f8f03a53e268afb67180a2d97dd12cc79e2b61361c6c0073448016", size = 17081568, upload-time = "2026-06-15T12:49:43.398Z" }, + { url = "https://files.pythonhosted.org/packages/13/a8/734725bb703c5fabb687f79c79e51249475212b3eb37771ac4a4ac9b487f/onnx-1.22.0-cp312-abi3-win_amd64.whl", hash = "sha256:72ccebab3bac07215c204ce8848d42e78eaaa666badbf72d25cd359b9f269e3a", size = 17213290, upload-time = "2026-06-15T12:49:45.933Z" }, + { url = "https://files.pythonhosted.org/packages/bd/2a/8ce48d8ae26a8761ad4e5dc771961b155c5c3c7c8540ec7f2f2d71b69af0/onnx-1.22.0-cp312-abi3-win_arm64.whl", hash = "sha256:f3c120dcdb70ad738f3c061b32798f408ea299eb69f84dd69ab4a6bf3c2ec01f", size = 17207030, upload-time = "2026-06-15T12:49:48.635Z" }, ] [[package]] name = "onnx-ir" -version = "0.2.0" +version = "1.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ml-dtypes" }, @@ -1601,14 +1097,14 @@ dependencies = [ { name = "sympy" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b2/a5/acc43c8fa6edbc584d127fb6bbd13ae9ebfc01b9675c74e0da2de15fa4a6/onnx_ir-0.2.0.tar.gz", hash = "sha256:8bad3906691987290789b26d05e0dbff467029a0b1e411e12e4cae02e43503e4", size = 141693, upload-time = "2026-02-24T02:31:10.998Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/c2/61194cec0dbc5622273c0ebd592d37cc1dca0d7f1a744f02edd45ac905a3/onnx_ir-1.0.0.tar.gz", hash = "sha256:9e261f25fde8da9612ae5cb43b3b374d5ff469c04af0363cad588b2bb000b812", size = 163121, upload-time = "2026-08-11T14:49:46.895Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/df/a99736bcca6b16e36c687ce4996abcf4ce73c514fddd9e730cfcb6a334f2/onnx_ir-0.2.0-py3-none-any.whl", hash = "sha256:eb14d1399c2442bd1ff702719e70074e9cedfa3af5729416a32752c9e0f82591", size = 164100, upload-time = "2026-02-24T02:31:09.454Z" }, + { url = "https://files.pythonhosted.org/packages/91/cd/6d1637172eb59c7b18ac90ed089d1f599a11fe0e63b4db2d017f3bb38a32/onnx_ir-1.0.0-py3-none-any.whl", hash = "sha256:e578f0d608d3062866b48223616eb2d10a6d6d01f8b8faac596129034f483cc7", size = 185849, upload-time = "2026-08-11T14:49:45.524Z" }, ] [[package]] name = "onnxscript" -version = "0.6.2" +version = "0.7.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ml-dtypes" }, @@ -1618,23 +1114,35 @@ dependencies = [ { name = "packaging" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e7/2b/538fdeb0e25bed5d7e0f954af5710543e2629499fb74381afc3333f8a8ae/onnxscript-0.6.2.tar.gz", hash = "sha256:abb2e6f464db40c9b8c7fbb3e64cca04cf3f4495e67c4eda5eac17b784191ce3", size = 590865, upload-time = "2026-02-10T22:53:39.638Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c4/3a/4d79bce3f460e0df7fed54a92ce80827f25da66511da368bb00783ad8d20/onnxscript-0.7.1.tar.gz", hash = "sha256:309fb86484b11fa4ded90dba580e0d63f1a0827588e521cecaf2eeddb46d6e86", size = 618160, upload-time = "2026-06-29T23:33:21.526Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dd/bd/a0c8e737b6afda10e42a597787d53d5b66e00268df6f59184701eeae37d9/onnxscript-0.7.1-py3-none-any.whl", hash = "sha256:544763b7fdef49940cdd9412ff5135cbae96d59ac6bc1921457f21280f40f4b7", size = 721970, upload-time = "2026-06-29T23:33:23.298Z" }, +] + +[[package]] +name = "opentelemetry-api" +version = "1.44.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ee/8b/aa9e2d8b8dfa7c946f7dec5d1f8f6ba8eca062f43509a06bdb5ce93d26c0/opentelemetry_api-1.44.0.tar.gz", hash = "sha256:67647e5e9566edcf421166fdf022b3537f818635daa852b289e34604dc6fb33a", size = 72406, upload-time = "2026-07-16T15:25:32.678Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/66/56/e6b179397497ab93266b6eb00743403a6a699a29063a423c4a14595d3db9/onnxscript-0.6.2-py3-none-any.whl", hash = "sha256:20e3c3fd1da19b3655549d5455a2df719db47374fe430e01e865ae69127c37b9", size = 689064, upload-time = "2026-02-10T22:53:41.663Z" }, + { url = "https://files.pythonhosted.org/packages/ca/6f/a04e900f465ff3221ccc395522503e2d10e79fa21f2723c8e177aae1e0d1/opentelemetry_api-1.44.0-py3-none-any.whl", hash = "sha256:94b98c893a91b88657eaac1e3ba89618cdb85be6918196705354f34728b2cdef", size = 60018, upload-time = "2026-07-16T15:25:11.657Z" }, ] [[package]] name = "packaging" -version = "26.0" +version = "26.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/fa/3944b40b07da9ce895c0e6303a5ab7d53da063554f534556b134a54d6093/packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79", size = 313412, upload-time = "2026-08-04T18:15:28.737Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, + { url = "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z" }, ] [[package]] name = "paramiko" -version = "4.0.0" +version = "5.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "bcrypt" }, @@ -1642,18 +1150,18 @@ dependencies = [ { name = "invoke" }, { name = "pynacl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1f/e7/81fdcbc7f190cdb058cffc9431587eb289833bdd633e2002455ca9bb13d4/paramiko-4.0.0.tar.gz", hash = "sha256:6a25f07b380cc9c9a88d2b920ad37167ac4667f8d9886ccebd8f90f654b5d69f", size = 1630743, upload-time = "2025-08-04T01:02:03.711Z" } +sdist = { url = "https://files.pythonhosted.org/packages/62/93/dcc25d52f49022ae6175d15e6bd751f1acc99b98bc61fc55e5155a7be2e7/paramiko-5.0.0.tar.gz", hash = "sha256:36763b5b95c2a0dcfdf1abc48e48156ee425b21efe2f0e787c2dd5a95c0e5e79", size = 1548586, upload-time = "2026-05-09T18:28:52.256Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/90/a744336f5af32c433bd09af7854599682a383b37cfd78f7de263de6ad6cb/paramiko-4.0.0-py3-none-any.whl", hash = "sha256:0e20e00ac666503bf0b4eda3b6d833465a2b7aff2e2b3d79a8bba5ef144ee3b9", size = 223932, upload-time = "2025-08-04T01:02:02.029Z" }, + { url = "https://files.pythonhosted.org/packages/82/5b/eadf6d45de38d30ab603f49393b6cd2cbe7e233af8cf90197e32782b68a9/paramiko-5.0.0-py3-none-any.whl", hash = "sha256:b7044611c30140d9a75261653210e2002977b71a0497ff3ba0d98d7edbf62f7c", size = 208919, upload-time = "2026-05-09T18:28:50.295Z" }, ] [[package]] name = "parso" -version = "0.8.6" +version = "0.8.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/81/76/a1e769043c0c0c9fe391b702539d594731a4362334cdf4dc25d0c09761e7/parso-0.8.6.tar.gz", hash = "sha256:2b9a0332696df97d454fa67b81618fd69c35a7b90327cbe6ba5c92d2c68a7bfd", size = 401621, upload-time = "2026-02-09T15:45:24.425Z" } +sdist = { url = "https://files.pythonhosted.org/packages/30/4b/90c937815137d43ce71ba043cd3566221e9df6b9c805f24b5d138c9d40a7/parso-0.8.7.tar.gz", hash = "sha256:eaaac4c9fdd5e9e8852dc778d2d7405897ec510f2a298071453e5e3a07914bb1", size = 401824, upload-time = "2026-05-01T23:13:02.138Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b6/61/fae042894f4296ec49e3f193aff5d7c18440da9e48102c3315e1bc4519a7/parso-0.8.6-py2.py3-none-any.whl", hash = "sha256:2c549f800b70a5c4952197248825584cb00f033b29c692671d3bf08bf380baff", size = 106894, upload-time = "2026-02-09T15:45:21.391Z" }, + { url = "https://files.pythonhosted.org/packages/99/5d/8268b644392ee874ee82a635cd0df1773de230bde356c38de28e298392cc/parso-0.8.7-py2.py3-none-any.whl", hash = "sha256:a8926eb2a1b915486941fdbd31e86a4baf88fe8c210f25f2f35ecec5b574ca1c", size = 107025, upload-time = "2026-05-01T23:12:58.867Z" }, ] [[package]] @@ -1670,69 +1178,31 @@ wheels = [ [[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/4a/01/53d10cf0dbad820a8db274d259a37ba50b88b24768ddccec07355382d5ad/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8297651f5b5679c19968abefd6bb84d95fe30ef712eb1b2d9b2d31ca61267f4c", size = 4100837, upload-time = "2026-04-01T14:43:41.506Z" }, - { url = "https://files.pythonhosted.org/packages/0f/98/f3a6657ecb698c937f6c76ee564882945f29b79bad496abcba0e84659ec5/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:50d8520da2a6ce0af445fa6d648c4273c3eeefbc32d7ce049f22e8b5c3daecc2", size = 4176528, upload-time = "2026-04-01T14:43:43.773Z" }, - { url = "https://files.pythonhosted.org/packages/69/bc/8986948f05e3ea490b8442ea1c1d4d990b24a7e43d8a51b2c7d8b1dced36/pillow-12.2.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:766cef22385fa1091258ad7e6216792b156dc16d8d3fa607e7545b2b72061f1c", size = 3640401, upload-time = "2026-04-01T14:43:45.87Z" }, - { url = "https://files.pythonhosted.org/packages/34/46/6c717baadcd62bc8ed51d238d521ab651eaa74838291bda1f86fe1f864c9/pillow-12.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5d2fd0fa6b5d9d1de415060363433f28da8b1526c1c129020435e186794b3795", size = 5308094, upload-time = "2026-04-01T14:43:48.438Z" }, - { url = "https://files.pythonhosted.org/packages/71/43/905a14a8b17fdb1ccb58d282454490662d2cb89a6bfec26af6d3520da5ec/pillow-12.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56b25336f502b6ed02e889f4ece894a72612fe885889a6e8c4c80239ff6e5f5f", size = 4695402, upload-time = "2026-04-01T14:43:51.292Z" }, - { url = "https://files.pythonhosted.org/packages/73/dd/42107efcb777b16fa0393317eac58f5b5cf30e8392e266e76e51cff28c3d/pillow-12.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f1c943e96e85df3d3478f7b691f229887e143f81fedab9b20205349ab04d73ed", size = 6280005, upload-time = "2026-04-01T14:43:54.242Z" }, - { url = "https://files.pythonhosted.org/packages/a8/68/b93e09e5e8549019e61acf49f65b1a8530765a7f812c77a7461bca7e4494/pillow-12.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:03f6fab9219220f041c74aeaa2939ff0062bd5c364ba9ce037197f4c6d498cd9", size = 8090669, upload-time = "2026-04-01T14:43:57.335Z" }, - { url = "https://files.pythonhosted.org/packages/4b/6e/3ccb54ce8ec4ddd1accd2d89004308b7b0b21c4ac3d20fa70af4760a4330/pillow-12.2.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdfebd752ec52bf5bb4e35d9c64b40826bc5b40a13df7c3cda20a2c03a0f5ed", size = 6395194, upload-time = "2026-04-01T14:43:59.864Z" }, - { url = "https://files.pythonhosted.org/packages/67/ee/21d4e8536afd1a328f01b359b4d3997b291ffd35a237c877b331c1c3b71c/pillow-12.2.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eedf4b74eda2b5a4b2b2fb4c006d6295df3bf29e459e198c90ea48e130dc75c3", size = 7082423, upload-time = "2026-04-01T14:44:02.74Z" }, - { url = "https://files.pythonhosted.org/packages/78/5f/e9f86ab0146464e8c133fe85df987ed9e77e08b29d8d35f9f9f4d6f917ba/pillow-12.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:00a2865911330191c0b818c59103b58a5e697cae67042366970a6b6f1b20b7f9", size = 6505667, upload-time = "2026-04-01T14:44:05.381Z" }, - { url = "https://files.pythonhosted.org/packages/ed/1e/409007f56a2fdce61584fd3acbc2bbc259857d555196cedcadc68c015c82/pillow-12.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e1757442ed87f4912397c6d35a0db6a7b52592156014706f17658ff58bbf795", size = 7208580, upload-time = "2026-04-01T14:44:08.39Z" }, - { url = "https://files.pythonhosted.org/packages/23/c4/7349421080b12fb35414607b8871e9534546c128a11965fd4a7002ccfbee/pillow-12.2.0-cp313-cp313-win32.whl", hash = "sha256:144748b3af2d1b358d41286056d0003f47cb339b8c43a9ea42f5fea4d8c66b6e", size = 6375896, upload-time = "2026-04-01T14:44:11.197Z" }, - { url = "https://files.pythonhosted.org/packages/3f/82/8a3739a5e470b3c6cbb1d21d315800d8e16bff503d1f16b03a4ec3212786/pillow-12.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:390ede346628ccc626e5730107cde16c42d3836b89662a115a921f28440e6a3b", size = 7081266, upload-time = "2026-04-01T14:44:13.947Z" }, - { url = "https://files.pythonhosted.org/packages/c3/25/f968f618a062574294592f668218f8af564830ccebdd1fa6200f598e65c5/pillow-12.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:8023abc91fba39036dbce14a7d6535632f99c0b857807cbbbf21ecc9f4717f06", size = 2463508, upload-time = "2026-04-01T14:44:16.312Z" }, - { url = "https://files.pythonhosted.org/packages/4d/a4/b342930964e3cb4dce5038ae34b0eab4653334995336cd486c5a8c25a00c/pillow-12.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:042db20a421b9bafecc4b84a8b6e444686bd9d836c7fd24542db3e7df7baad9b", size = 5309927, upload-time = "2026-04-01T14:44:18.89Z" }, - { url = "https://files.pythonhosted.org/packages/9f/de/23198e0a65a9cf06123f5435a5d95cea62a635697f8f03d134d3f3a96151/pillow-12.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd025009355c926a84a612fecf58bb315a3f6814b17ead51a8e48d3823d9087f", size = 4698624, upload-time = "2026-04-01T14:44:21.115Z" }, - { url = "https://files.pythonhosted.org/packages/01/a6/1265e977f17d93ea37aa28aa81bad4fa597933879fac2520d24e021c8da3/pillow-12.2.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88ddbc66737e277852913bd1e07c150cc7bb124539f94c4e2df5344494e0a612", size = 6321252, upload-time = "2026-04-01T14:44:23.663Z" }, - { url = "https://files.pythonhosted.org/packages/3c/83/5982eb4a285967baa70340320be9f88e57665a387e3a53a7f0db8231a0cd/pillow-12.2.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d362d1878f00c142b7e1a16e6e5e780f02be8195123f164edf7eddd911eefe7c", size = 8126550, upload-time = "2026-04-01T14:44:26.772Z" }, - { url = "https://files.pythonhosted.org/packages/4e/48/6ffc514adce69f6050d0753b1a18fd920fce8cac87620d5a31231b04bfc5/pillow-12.2.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c727a6d53cb0018aadd8018c2b938376af27914a68a492f59dfcaca650d5eea", size = 6433114, upload-time = "2026-04-01T14:44:29.615Z" }, - { url = "https://files.pythonhosted.org/packages/36/a3/f9a77144231fb8d40ee27107b4463e205fa4677e2ca2548e14da5cf18dce/pillow-12.2.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:efd8c21c98c5cc60653bcb311bef2ce0401642b7ce9d09e03a7da87c878289d4", size = 7115667, upload-time = "2026-04-01T14:44:32.773Z" }, - { url = "https://files.pythonhosted.org/packages/c1/fc/ac4ee3041e7d5a565e1c4fd72a113f03b6394cc72ab7089d27608f8aaccb/pillow-12.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f08483a632889536b8139663db60f6724bfcb443c96f1b18855860d7d5c0fd4", size = 6538966, upload-time = "2026-04-01T14:44:35.252Z" }, - { url = "https://files.pythonhosted.org/packages/c0/a8/27fb307055087f3668f6d0a8ccb636e7431d56ed0750e07a60547b1e083e/pillow-12.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dac8d77255a37e81a2efcbd1fc05f1c15ee82200e6c240d7e127e25e365c39ea", size = 7238241, upload-time = "2026-04-01T14:44:37.875Z" }, - { url = "https://files.pythonhosted.org/packages/ad/4b/926ab182c07fccae9fcb120043464e1ff1564775ec8864f21a0ebce6ac25/pillow-12.2.0-cp313-cp313t-win32.whl", hash = "sha256:ee3120ae9dff32f121610bb08e4313be87e03efeadfc6c0d18f89127e24d0c24", size = 6379592, upload-time = "2026-04-01T14:44:40.336Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c4/f9e476451a098181b30050cc4c9a3556b64c02cf6497ea421ac047e89e4b/pillow-12.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:325ca0528c6788d2a6c3d40e3568639398137346c3d6e66bb61db96b96511c98", size = 7085542, upload-time = "2026-04-01T14:44:43.251Z" }, - { url = "https://files.pythonhosted.org/packages/00/a4/285f12aeacbe2d6dc36c407dfbbe9e96d4a80b0fb710a337f6d2ad978c75/pillow-12.2.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2e5a76d03a6c6dcef67edabda7a52494afa4035021a79c8558e14af25313d453", size = 2465765, upload-time = "2026-04-01T14:44:45.996Z" }, - { url = "https://files.pythonhosted.org/packages/bf/98/4595daa2365416a86cb0d495248a393dfc84e96d62ad080c8546256cb9c0/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:3adc9215e8be0448ed6e814966ecf3d9952f0ea40eb14e89a102b87f450660d8", size = 4100848, upload-time = "2026-04-01T14:44:48.48Z" }, - { url = "https://files.pythonhosted.org/packages/0b/79/40184d464cf89f6663e18dfcf7ca21aae2491fff1a16127681bf1fa9b8cf/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:6a9adfc6d24b10f89588096364cc726174118c62130c817c2837c60cf08a392b", size = 4176515, upload-time = "2026-04-01T14:44:51.353Z" }, - { url = "https://files.pythonhosted.org/packages/b0/63/703f86fd4c422a9cf722833670f4f71418fb116b2853ff7da722ea43f184/pillow-12.2.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:6a6e67ea2e6feda684ed370f9a1c52e7a243631c025ba42149a2cc5934dec295", size = 3640159, upload-time = "2026-04-01T14:44:53.588Z" }, - { url = "https://files.pythonhosted.org/packages/71/e0/fb22f797187d0be2270f83500aab851536101b254bfa1eae10795709d283/pillow-12.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2bb4a8d594eacdfc59d9e5ad972aa8afdd48d584ffd5f13a937a664c3e7db0ed", size = 5312185, upload-time = "2026-04-01T14:44:56.039Z" }, - { url = "https://files.pythonhosted.org/packages/ba/8c/1a9e46228571de18f8e28f16fabdfc20212a5d019f3e3303452b3f0a580d/pillow-12.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:80b2da48193b2f33ed0c32c38140f9d3186583ce7d516526d462645fd98660ae", size = 4695386, upload-time = "2026-04-01T14:44:58.663Z" }, - { url = "https://files.pythonhosted.org/packages/70/62/98f6b7f0c88b9addd0e87c217ded307b36be024d4ff8869a812b241d1345/pillow-12.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22db17c68434de69d8ecfc2fe821569195c0c373b25cccb9cbdacf2c6e53c601", size = 6280384, upload-time = "2026-04-01T14:45:01.5Z" }, - { url = "https://files.pythonhosted.org/packages/5e/03/688747d2e91cfbe0e64f316cd2e8005698f76ada3130d0194664174fa5de/pillow-12.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7b14cc0106cd9aecda615dd6903840a058b4700fcb817687d0ee4fc8b6e389be", size = 8091599, upload-time = "2026-04-01T14:45:04.5Z" }, - { url = "https://files.pythonhosted.org/packages/f6/35/577e22b936fcdd66537329b33af0b4ccfefaeabd8aec04b266528cddb33c/pillow-12.2.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cbeb542b2ebc6fcdacabf8aca8c1a97c9b3ad3927d46b8723f9d4f033288a0f", size = 6396021, upload-time = "2026-04-01T14:45:07.117Z" }, - { url = "https://files.pythonhosted.org/packages/11/8d/d2532ad2a603ca2b93ad9f5135732124e57811d0168155852f37fbce2458/pillow-12.2.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4bfd07bc812fbd20395212969e41931001fd59eb55a60658b0e5710872e95286", size = 7083360, upload-time = "2026-04-01T14:45:09.763Z" }, - { url = "https://files.pythonhosted.org/packages/5e/26/d325f9f56c7e039034897e7380e9cc202b1e368bfd04d4cbe6a441f02885/pillow-12.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9aba9a17b623ef750a4d11b742cbafffeb48a869821252b30ee21b5e91392c50", size = 6507628, upload-time = "2026-04-01T14:45:12.378Z" }, - { url = "https://files.pythonhosted.org/packages/5f/f7/769d5632ffb0988f1c5e7660b3e731e30f7f8ec4318e94d0a5d674eb65a4/pillow-12.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:deede7c263feb25dba4e82ea23058a235dcc2fe1f6021025dc71f2b618e26104", size = 7209321, upload-time = "2026-04-01T14:45:15.122Z" }, - { url = "https://files.pythonhosted.org/packages/6a/7a/c253e3c645cd47f1aceea6a8bacdba9991bf45bb7dfe927f7c893e89c93c/pillow-12.2.0-cp314-cp314-win32.whl", hash = "sha256:632ff19b2778e43162304d50da0181ce24ac5bb8180122cbe1bf4673428328c7", size = 6479723, upload-time = "2026-04-01T14:45:17.797Z" }, - { url = "https://files.pythonhosted.org/packages/cd/8b/601e6566b957ca50e28725cb6c355c59c2c8609751efbecd980db44e0349/pillow-12.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:4e6c62e9d237e9b65fac06857d511e90d8461a32adcc1b9065ea0c0fa3a28150", size = 7217400, upload-time = "2026-04-01T14:45:20.529Z" }, - { url = "https://files.pythonhosted.org/packages/d6/94/220e46c73065c3e2951bb91c11a1fb636c8c9ad427ac3ce7d7f3359b9b2f/pillow-12.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:b1c1fbd8a5a1af3412a0810d060a78b5136ec0836c8a4ef9aa11807f2a22f4e1", size = 2554835, upload-time = "2026-04-01T14:45:23.162Z" }, - { url = "https://files.pythonhosted.org/packages/b6/ab/1b426a3974cb0e7da5c29ccff4807871d48110933a57207b5a676cccc155/pillow-12.2.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:57850958fe9c751670e49b2cecf6294acc99e562531f4bd317fa5ddee2068463", size = 5314225, upload-time = "2026-04-01T14:45:25.637Z" }, - { url = "https://files.pythonhosted.org/packages/19/1e/dce46f371be2438eecfee2a1960ee2a243bbe5e961890146d2dee1ff0f12/pillow-12.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d5d38f1411c0ed9f97bcb49b7bd59b6b7c314e0e27420e34d99d844b9ce3b6f3", size = 4698541, upload-time = "2026-04-01T14:45:28.355Z" }, - { url = "https://files.pythonhosted.org/packages/55/c3/7fbecf70adb3a0c33b77a300dc52e424dc22ad8cdc06557a2e49523b703d/pillow-12.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c0a9f29ca8e79f09de89293f82fc9b0270bb4af1d58bc98f540cc4aedf03166", size = 6322251, upload-time = "2026-04-01T14:45:30.924Z" }, - { url = "https://files.pythonhosted.org/packages/1c/3c/7fbc17cfb7e4fe0ef1642e0abc17fc6c94c9f7a16be41498e12e2ba60408/pillow-12.2.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1610dd6c61621ae1cf811bef44d77e149ce3f7b95afe66a4512f8c59f25d9ebe", size = 8127807, upload-time = "2026-04-01T14:45:33.908Z" }, - { url = "https://files.pythonhosted.org/packages/ff/c3/a8ae14d6defd2e448493ff512fae903b1e9bd40b72efb6ec55ce0048c8ce/pillow-12.2.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a34329707af4f73cf1782a36cd2289c0368880654a2c11f027bcee9052d35dd", size = 6433935, upload-time = "2026-04-01T14:45:36.623Z" }, - { url = "https://files.pythonhosted.org/packages/6e/32/2880fb3a074847ac159d8f902cb43278a61e85f681661e7419e6596803ed/pillow-12.2.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e9c4f5b3c546fa3458a29ab22646c1c6c787ea8f5ef51300e5a60300736905e", size = 7116720, upload-time = "2026-04-01T14:45:39.258Z" }, - { url = "https://files.pythonhosted.org/packages/46/87/495cc9c30e0129501643f24d320076f4cc54f718341df18cc70ec94c44e1/pillow-12.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fb043ee2f06b41473269765c2feae53fc2e2fbf96e5e22ca94fb5ad677856f06", size = 6540498, upload-time = "2026-04-01T14:45:41.879Z" }, - { url = "https://files.pythonhosted.org/packages/18/53/773f5edca692009d883a72211b60fdaf8871cbef075eaa9d577f0a2f989e/pillow-12.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f278f034eb75b4e8a13a54a876cc4a5ab39173d2cdd93a638e1b467fc545ac43", size = 7239413, upload-time = "2026-04-01T14:45:44.705Z" }, - { url = "https://files.pythonhosted.org/packages/c9/e4/4b64a97d71b2a83158134abbb2f5bd3f8a2ea691361282f010998f339ec7/pillow-12.2.0-cp314-cp314t-win32.whl", hash = "sha256:6bb77b2dcb06b20f9f4b4a8454caa581cd4dd0643a08bacf821216a16d9c8354", size = 6482084, upload-time = "2026-04-01T14:45:47.568Z" }, - { url = "https://files.pythonhosted.org/packages/ba/13/306d275efd3a3453f72114b7431c877d10b1154014c1ebbedd067770d629/pillow-12.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6562ace0d3fb5f20ed7290f1f929cae41b25ae29528f2af1722966a0a02e2aa1", size = 7225152, upload-time = "2026-04-01T14:45:50.032Z" }, - { url = "https://files.pythonhosted.org/packages/ff/6e/cf826fae916b8658848d7b9f38d88da6396895c676e8086fc0988073aaf8/pillow-12.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:aa88ccfe4e32d362816319ed727a004423aab09c5cea43c01a4b435643fa34eb", size = 2556579, upload-time = "2026-04-01T14:45:52.529Z" }, +version = "12.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/3d/bb7fca845737cf9d7dbde16ed1843984665ff2e0a518f5db43e77ec540b9/pillow-12.3.0.tar.gz", hash = "sha256:3b8182a766685eaa002637e28b4ec8d6b18819a0c71f579bf0dbaa5830297cce", size = 47025035, upload-time = "2026-07-01T11:56:38.965Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/ac/31fb64e1e7efb5a4b50cd3d92049ba89ac6e4d8d3bb6a74e15048ca3353e/pillow-12.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:21900ce7ba264168cd50defae43cd75d25c833ad4ad6e73ffc5596d12e25ac89", size = 4161684, upload-time = "2026-07-01T11:54:25.934Z" }, + { url = "https://files.pythonhosted.org/packages/87/b4/9805e23d2b4d77842b468513841fda254ee42f0289d25088340e4ff46e2d/pillow-12.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:4e8c2a84d977f50b9daed6eeaf3baef67d00d5d74d932288f02cb94518ee3ace", size = 4255487, upload-time = "2026-07-01T11:54:27.935Z" }, + { url = "https://files.pythonhosted.org/packages/df/39/ecf519435a200c693fe053a6ee4d835b41cf963a4dfc2551c4e637cb2a71/pillow-12.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:ae26d61dfa7a47befdc7572b521024e8745f3d809bd95ca9505a7bba9ef849ec", size = 3696433, upload-time = "2026-07-01T11:54:29.813Z" }, + { url = "https://files.pythonhosted.org/packages/42/92/2fc3ffad878ae8dd5469ec1bc8eb83b71f48e13efdf68f02709003982a32/pillow-12.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7a743ff716f746fc19a9557f60dab1600d4613255f8a7aeb3cdde4db7eb15a66", size = 5345889, upload-time = "2026-07-01T11:54:31.97Z" }, + { url = "https://files.pythonhosted.org/packages/10/76/8803c13605b763d33d156c4678fc77f8443389c0c51c8aef707bb02015f4/pillow-12.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d69141514cc30b774ceea5e3ed3a6635c8d8a96edf664689b890f4089111fb35", size = 4780109, upload-time = "2026-07-01T11:54:34.026Z" }, + { url = "https://files.pythonhosted.org/packages/1f/01/e18aff37cb0b4aac47ac90f016d347a49aca667ef97f190b06ac2aabc928/pillow-12.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f7401aebd7f581d7f83a439d87d474999317ee099218e5ad25d125290990ba65", size = 6263736, upload-time = "2026-07-01T11:54:36.131Z" }, + { url = "https://files.pythonhosted.org/packages/f7/62/de5bdd77d935331f4f802edc11e4d82950f642caad6cb2f949837b8560e2/pillow-12.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0847a763afefb695bc912d7c131e7e0632d4edc1d8698f58ddabec8e46b8b6d3", size = 6937129, upload-time = "2026-07-01T11:54:38.216Z" }, + { url = "https://files.pythonhosted.org/packages/70/4d/105627a13300c5e0df1d174230b32fd1273062c96f7745fd552b945d1e1d/pillow-12.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:571b9fcb07b97ef3a492028fb3d2dc0993ca23a06138b0315286566d29ef718a", size = 6339562, upload-time = "2026-07-01T11:54:40.354Z" }, + { url = "https://files.pythonhosted.org/packages/6b/1d/f13de01a553988ab895ba1c722e06cf3144d4f57656fd5b81b6d881f1179/pillow-12.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:756c768d0c9c2955feb7a56c37ea24aea2e369f8d36a88da270b6a9f19e62b5e", size = 7049439, upload-time = "2026-07-01T11:54:42.489Z" }, + { url = "https://files.pythonhosted.org/packages/c9/f9/066794cca041b969964f779ee5fa66a9498bbf34248ac39c5d7954e4198f/pillow-12.3.0-cp313-cp313-win32.whl", hash = "sha256:a876864214e136f0eb367788dbd7df045f4806801518e2cfe9e13229cfe06d8f", size = 6473287, upload-time = "2026-07-01T11:54:44.9Z" }, + { url = "https://files.pythonhosted.org/packages/a6/9b/7a58e61d62be561da3a356fe2384d4059a6345fc130e23ef1c36a5b81d24/pillow-12.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:1cca606cd25738df4ed873d5ad46bbdb3d83b5cbca291f6b4ff13a4df6b0bbe8", size = 7239691, upload-time = "2026-07-01T11:54:47.141Z" }, + { url = "https://files.pythonhosted.org/packages/aa/b0/c4ed4f0ef8f8fa5ee8351537db6650bb8189f7e118842978dd6589065692/pillow-12.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:b629de27fda84b42cde7edef0d85f13b958b47f6e9bbcbba9b673c562a89bd8b", size = 2568185, upload-time = "2026-07-01T11:54:49.137Z" }, ] [[package]] name = "platformdirs" -version = "4.9.4" +version = "4.11.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/19/56/8d4c30c8a1d07013911a8fdbd8f89440ef9f08d07a1b50ab8ca8be5a20f9/platformdirs-4.9.4.tar.gz", hash = "sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934", size = 28737, upload-time = "2026-03-05T18:34:13.271Z" } +sdist = { url = "https://files.pythonhosted.org/packages/50/bb/ebc6636e1ae41314f796ebb7215fd28febb45f9aac72f2b04cb74b5071dc/platformdirs-4.11.4.tar.gz", hash = "sha256:f3373be828247211d0febabea97e238c3dfde8a60b3c90c32756fb52cb21556d", size = 34079, upload-time = "2026-08-24T14:53:49.676Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/63/d7/97f7e3a6abb67d8080dd406fd4df842c2be0efaf712d1c899c32a075027c/platformdirs-4.9.4-py3-none-any.whl", hash = "sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868", size = 21216, upload-time = "2026-03-05T18:34:12.172Z" }, + { url = "https://files.pythonhosted.org/packages/28/be/0ff05fcd2938fb58ad9219bd54135968342d214737e012d62d43f06a2dd6/platformdirs-4.11.4-py3-none-any.whl", hash = "sha256:e34ff91a24bcddc6d939b878bdf3f5c437c9c46fe9e212b1bf455fdf1ee57586", size = 23741, upload-time = "2026-08-24T14:53:48.406Z" }, ] [[package]] @@ -1746,41 +1216,63 @@ wheels = [ [[package]] name = "prettytable" -version = "3.17.0" +version = "3.18.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "wcwidth" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/79/45/b0847d88d6cfeb4413566738c8bbf1e1995fad3d42515327ff32cc1eb578/prettytable-3.17.0.tar.gz", hash = "sha256:59f2590776527f3c9e8cf9fe7b66dd215837cca96a9c39567414cbc632e8ddb0", size = 67892, upload-time = "2025-11-14T17:33:20.212Z" } +sdist = { url = "https://files.pythonhosted.org/packages/81/74/ba08d81e668ccfe8658d7520a307e63c19862c08eb4ccb26f356c5239a7a/prettytable-3.18.0.tar.gz", hash = "sha256:439217116152244369caf3d9f1caf2f9fe29b03bd79e88d2928c8e718c95d680", size = 76373, upload-time = "2026-06-22T16:07:50.174Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/8c/83087ebc47ab0396ce092363001fa37c17153119ee282700c0713a195853/prettytable-3.17.0-py3-none-any.whl", hash = "sha256:aad69b294ddbe3e1f95ef8886a060ed1666a0b83018bbf56295f6f226c43d287", size = 34433, upload-time = "2025-11-14T17:33:19.093Z" }, + { url = "https://files.pythonhosted.org/packages/fe/be/2e6798ace5cc036f5d05d36b7b2fd85346f1a708c87060890b070d0ec607/prettytable-3.18.0-py3-none-any.whl", hash = "sha256:b3346e0e6f79180833aebaac088ae926340586cf6d7d991b9eb125b65f72313a", size = 37357, upload-time = "2026-06-22T16:07:48.595Z" }, ] [[package]] name = "prompt-toolkit" -version = "3.0.52" +version = "3.0.53" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "wcwidth" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198, upload-time = "2025-08-27T15:24:02.057Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/ea/39b988c938f75cb75d7045b5c69f8bfed47ee2152c8837fb403de29d6fb8/prompt_toolkit-3.0.53.tar.gz", hash = "sha256:9ec8a0ad96d5c56148b3f914aa79c1564c3fde5d2e6b876e7bc327e353cf8fa6", size = 435492, upload-time = "2026-07-26T20:56:14.758Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431, upload-time = "2025-08-27T15:23:59.498Z" }, + { url = "https://files.pythonhosted.org/packages/54/6f/84908cad2d6aa5144abcf7b42709fe4fdb459bc640ec7ac5786e7693dabc/prompt_toolkit-3.0.53-py3-none-any.whl", hash = "sha256:01c0891d7f9237d5e339f7d3e42cdae80b7534abb1c7c0e3352efba6231492f2", size = 392288, upload-time = "2026-07-26T20:56:12.512Z" }, ] [[package]] name = "protobuf" -version = "6.33.6" +version = "7.36.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/e7/0553e21d25ca4d9f573135775348a372c3ec34a93a71d5f297c3bac38341/protobuf-7.36.0.tar.gz", hash = "sha256:e8e09cb0d794c6687926fa558a8a6e72aa10edb997d5ca61da0765f12a3e00ea", size = 510034, upload-time = "2026-08-20T16:34:01.071Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/ae/58e3ca96cb2e118cc546b677359b3c6659f79a140935c08dec94c7998585/protobuf-7.36.0-cp310-abi3-macosx_10_9_universal2.whl", hash = "sha256:9103532dffd80c6fab7e50c65a31007680a06eb57537d437bb1b35812c138a37", size = 453256, upload-time = "2026-08-20T16:33:53.945Z" }, + { url = "https://files.pythonhosted.org/packages/f0/15/5162230af4912697f0fe406f6800f80760945babcff0e2c2fe6c84ef2d5d/protobuf-7.36.0-cp310-abi3-manylinux2014_aarch64.whl", hash = "sha256:bf94a5917c71058262de683669bc0a797a7669d3de71f0b36d058e3194f47b44", size = 341436, upload-time = "2026-08-20T16:33:55.134Z" }, + { url = "https://files.pythonhosted.org/packages/d7/09/1670b2bfc9a45e807e520c3e9be36524db9ccc7dc05ea17af7681cabdc61/protobuf-7.36.0-cp310-abi3-manylinux2014_s390x.whl", hash = "sha256:3297e60abdff301e5f74393d87f6cc59dacab5f024a89548a6e8de1d26576b16", size = 354440, upload-time = "2026-08-20T16:33:56.077Z" }, + { url = "https://files.pythonhosted.org/packages/c7/f8/bd5804695ba400e423c33fd4d9f58c28d86633d5ba1945c36ff3967d98cb/protobuf-7.36.0-cp310-abi3-manylinux2014_x86_64.whl", hash = "sha256:70f5ec8eb0da81a44360c0dc0beac99a0d78071d21956a7076bae8bd2051841b", size = 340439, upload-time = "2026-08-20T16:33:56.992Z" }, + { url = "https://files.pythonhosted.org/packages/ef/9f/acd02338235a3e7d03168c4303478347b7624fc8189ff4e7f0d2654bbe86/protobuf-7.36.0-cp310-abi3-win32.whl", hash = "sha256:7326fd717bdc419162a735938d89d4032332bcc3408804012b24ff3a37086071", size = 440216, upload-time = "2026-08-20T16:33:57.99Z" }, + { url = "https://files.pythonhosted.org/packages/0e/4e/12cb93270967a2affff5b3f720694700d4d87712a67afd05c8cb3f6fa52c/protobuf-7.36.0-cp310-abi3-win_amd64.whl", hash = "sha256:1781cc1de61249b750848029bca452c0a8b7e990080316b9bbc2518b2117b488", size = 453731, upload-time = "2026-08-20T16:33:58.951Z" }, + { url = "https://files.pythonhosted.org/packages/01/c3/629999e78d46c1115c11886d51c6bd68c17ce4a944f1ea3e153a91316a33/protobuf-7.36.0-py3-none-any.whl", hash = "sha256:53374d53fc29a67f7dbbf0ade47d7526a0f0137bf0f9c90e48d8a60790ef748c", size = 177024, upload-time = "2026-08-20T16:34:00.053Z" }, +] + +[[package]] +name = "psutil" +version = "7.2.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/66/70/e908e9c5e52ef7c3a6c7902c9dfbb34c7e29c25d2f81ade3856445fd5c94/protobuf-6.33.6.tar.gz", hash = "sha256:a6768d25248312c297558af96a9f9c929e8c4cee0659cb07e780731095f38135", size = 444531, upload-time = "2026-03-18T19:05:00.988Z" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", size = 493740, upload-time = "2026-01-28T18:14:54.428Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/9f/2f509339e89cfa6f6a4c4ff50438db9ca488dec341f7e454adad60150b00/protobuf-6.33.6-cp310-abi3-win32.whl", hash = "sha256:7d29d9b65f8afef196f8334e80d6bc1d5d4adedb449971fefd3723824e6e77d3", size = 425739, upload-time = "2026-03-18T19:04:48.373Z" }, - { url = "https://files.pythonhosted.org/packages/76/5d/683efcd4798e0030c1bab27374fd13a89f7c2515fb1f3123efdfaa5eab57/protobuf-6.33.6-cp310-abi3-win_amd64.whl", hash = "sha256:0cd27b587afca21b7cfa59a74dcbd48a50f0a6400cfb59391340ad729d91d326", size = 437089, upload-time = "2026-03-18T19:04:50.381Z" }, - { url = "https://files.pythonhosted.org/packages/5c/01/a3c3ed5cd186f39e7880f8303cc51385a198a81469d53d0fdecf1f64d929/protobuf-6.33.6-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:9720e6961b251bde64edfdab7d500725a2af5280f3f4c87e57c0208376aa8c3a", size = 427737, upload-time = "2026-03-18T19:04:51.866Z" }, - { url = "https://files.pythonhosted.org/packages/ee/90/b3c01fdec7d2f627b3a6884243ba328c1217ed2d978def5c12dc50d328a3/protobuf-6.33.6-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:e2afbae9b8e1825e3529f88d514754e094278bb95eadc0e199751cdd9a2e82a2", size = 324610, upload-time = "2026-03-18T19:04:53.096Z" }, - { url = "https://files.pythonhosted.org/packages/9b/ca/25afc144934014700c52e05103c2421997482d561f3101ff352e1292fb81/protobuf-6.33.6-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:c96c37eec15086b79762ed265d59ab204dabc53056e3443e702d2681f4b39ce3", size = 339381, upload-time = "2026-03-18T19:04:54.616Z" }, - { url = "https://files.pythonhosted.org/packages/16/92/d1e32e3e0d894fe00b15ce28ad4944ab692713f2e7f0a99787405e43533a/protobuf-6.33.6-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:e9db7e292e0ab79dd108d7f1a94fe31601ce1ee3f7b79e0692043423020b0593", size = 323436, upload-time = "2026-03-18T19:04:55.768Z" }, - { url = "https://files.pythonhosted.org/packages/c4/72/02445137af02769918a93807b2b7890047c32bfb9f90371cbc12688819eb/protobuf-6.33.6-py3-none-any.whl", hash = "sha256:77179e006c476e69bf8e8ce866640091ec42e1beb80b213c3900006ecfba6901", size = 170656, upload-time = "2026-03-18T19:04:59.826Z" }, + { url = "https://files.pythonhosted.org/packages/51/08/510cbdb69c25a96f4ae523f733cdc963ae654904e8db864c07585ef99875/psutil-7.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b", size = 130595, upload-time = "2026-01-28T18:14:57.293Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f5/97baea3fe7a5a9af7436301f85490905379b1c6f2dd51fe3ecf24b4c5fbf/psutil-7.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea", size = 131082, upload-time = "2026-01-28T18:14:59.732Z" }, + { url = "https://files.pythonhosted.org/packages/37/d6/246513fbf9fa174af531f28412297dd05241d97a75911ac8febefa1a53c6/psutil-7.2.2-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63", size = 181476, upload-time = "2026-01-28T18:15:01.884Z" }, + { url = "https://files.pythonhosted.org/packages/b8/b5/9182c9af3836cca61696dabe4fd1304e17bc56cb62f17439e1154f225dd3/psutil-7.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312", size = 184062, upload-time = "2026-01-28T18:15:04.436Z" }, + { url = "https://files.pythonhosted.org/packages/16/ba/0756dca669f5a9300d0cbcbfae9a4c30e446dfc7440ffe43ded5724bfd93/psutil-7.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b", size = 139893, upload-time = "2026-01-28T18:15:06.378Z" }, + { url = "https://files.pythonhosted.org/packages/1c/61/8fa0e26f33623b49949346de05ec1ddaad02ed8ba64af45f40a147dbfa97/psutil-7.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9", size = 135589, upload-time = "2026-01-28T18:15:08.03Z" }, + { url = "https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486", size = 129090, upload-time = "2026-01-28T18:15:22.168Z" }, + { url = "https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979", size = 129859, upload-time = "2026-01-28T18:15:23.795Z" }, + { url = "https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", size = 155560, upload-time = "2026-01-28T18:15:25.976Z" }, + { url = "https://files.pythonhosted.org/packages/63/65/37648c0c158dc222aba51c089eb3bdfa238e621674dc42d48706e639204f/psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e", size = 156997, upload-time = "2026-01-28T18:15:27.794Z" }, + { url = "https://files.pythonhosted.org/packages/8e/13/125093eadae863ce03c6ffdbae9929430d116a246ef69866dad94da3bfbc/psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8", size = 148972, upload-time = "2026-01-28T18:15:29.342Z" }, + { url = "https://files.pythonhosted.org/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", size = 148266, upload-time = "2026-01-28T18:15:31.597Z" }, + { url = "https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl", hash = "sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988", size = 137737, upload-time = "2026-01-28T18:15:33.849Z" }, + { url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617, upload-time = "2026-01-28T18:15:36.514Z" }, ] [[package]] @@ -1801,19 +1293,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" }, ] -[[package]] -name = "pycollada" -version = "0.9.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, - { name = "python-dateutil" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5a/8d/52a5364a17eb96129962cae8d3ee7658775e085ad0ba38388684ad5944e9/pycollada-0.9.3.tar.gz", hash = "sha256:c34d6dcf0fe2eba5896f71c96d37a1c0fe1a61f08440fa0cfcec3dc2895d3302", size = 110826, upload-time = "2026-01-24T15:45:23.625Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/07/86/f1f61b7a0701f9d1299e5293d083318019f91021a4d449f94d59dbe024e4/pycollada-0.9.3-py3-none-any.whl", hash = "sha256:636e6496f60987586db82455ea7bbd9ade775e8181c6590c83b698b6cd53a9f5", size = 129206, upload-time = "2026-01-24T15:45:22.182Z" }, -] - [[package]] name = "pycparser" version = "3.0" @@ -1825,7 +1304,7 @@ wheels = [ [[package]] name = "pydantic" -version = "2.12.5" +version = "2.13.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, @@ -1833,71 +1312,44 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } +sdist = { url = "https://files.pythonhosted.org/packages/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775, upload-time = "2026-05-06T13:43:05.343Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, + { url = "https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba", size = 472262, upload-time = "2026-05-06T13:43:02.641Z" }, ] [[package]] name = "pydantic-core" -version = "2.41.5" +version = "2.46.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, - { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, - { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, - { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" }, - { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" }, - { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" }, - { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" }, - { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" }, - { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" }, - { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" }, - { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" }, - { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" }, - { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" }, - { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" }, - { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, - { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, - { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, - { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" }, - { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" }, - { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" }, - { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, - { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" }, - { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, - { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" }, - { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, - { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" }, - { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" }, - { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" }, - { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, - { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, - { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, - { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" }, - { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" }, - { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" }, - { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, - { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" }, - { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, - { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" }, - { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, - { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, - { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, - { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/a2/5d30b469c5267a17b39dec53208222f76a8d351dfac4af661888c5aee77d/pydantic_core-2.46.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008", size = 2106306, upload-time = "2026-05-06T13:37:48.029Z" }, + { url = "https://files.pythonhosted.org/packages/c1/81/4fa520eaffa8bd7d1525e644cd6d39e7d60b1592bc5b516693c7340b50f1/pydantic_core-2.46.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4", size = 1951906, upload-time = "2026-05-06T13:37:17.012Z" }, + { url = "https://files.pythonhosted.org/packages/03/d5/fd02da45b659668b05923b17ba3a0100a0a3d5541e3bd8fcc4ecb711309e/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76", size = 1976802, upload-time = "2026-05-06T13:37:35.113Z" }, + { url = "https://files.pythonhosted.org/packages/21/f2/95727e1368be3d3ed485eaab7adbd7dda408f33f7a36e8b48e0144002b91/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3", size = 2052446, upload-time = "2026-05-06T13:37:12.313Z" }, + { url = "https://files.pythonhosted.org/packages/9c/86/5d99feea3f77c7234b8718075b23db11532773c1a0dbd9b9490215dc2eeb/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76", size = 2232757, upload-time = "2026-05-06T13:39:01.149Z" }, + { url = "https://files.pythonhosted.org/packages/d2/3a/508ac615935ef7588cf6d9e9b91309fdc2da751af865e02a9098de88258c/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4", size = 2309275, upload-time = "2026-05-06T13:37:41.406Z" }, + { url = "https://files.pythonhosted.org/packages/07/f8/41db9de19d7987d6b04715a02b3b40aea467000275d9d758ffaa31af7d50/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a", size = 2094467, upload-time = "2026-05-06T13:39:18.847Z" }, + { url = "https://files.pythonhosted.org/packages/2c/e2/f35033184cb11d0052daf4416e8e10a502ea2ac006fc4f459aee872727d1/pydantic_core-2.46.4-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262", size = 2134417, upload-time = "2026-05-06T13:40:17.944Z" }, + { url = "https://files.pythonhosted.org/packages/7e/7b/6ceeb1cc90e193862f444ebe373d8fdf613f0a82572dde03fb10734c6c71/pydantic_core-2.46.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e", size = 2179782, upload-time = "2026-05-06T13:40:32.618Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f2/c8d7773ede6af08036423a00ae0ceffce266c3c52a096c435d68c896083f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd", size = 2188782, upload-time = "2026-05-06T13:36:51.018Z" }, + { url = "https://files.pythonhosted.org/packages/59/31/0c864784e31f09f05cdd87606f08923b9c9e7f6e51dd27f20f62f975ce9f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be", size = 2328334, upload-time = "2026-05-06T13:40:37.764Z" }, + { url = "https://files.pythonhosted.org/packages/c2/eb/4f6c8a41efa30baa755590f4141abf3a8c370fab610915733e74134a7270/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d", size = 2372986, upload-time = "2026-05-06T13:39:34.152Z" }, + { url = "https://files.pythonhosted.org/packages/5b/24/b375a480d53113860c299764bfe9f349a3dc9108b3adc0d7f0d786492ebf/pydantic_core-2.46.4-cp313-cp313-win32.whl", hash = "sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb", size = 1973693, upload-time = "2026-05-06T13:37:55.072Z" }, + { url = "https://files.pythonhosted.org/packages/7e/e8/cff247591966f2d22ec8c003cd7587e27b7ba7b81ab2fb888e3ab75dc285/pydantic_core-2.46.4-cp313-cp313-win_amd64.whl", hash = "sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292", size = 2071819, upload-time = "2026-05-06T13:38:49.139Z" }, + { url = "https://files.pythonhosted.org/packages/c6/1a/f4aee670d5670e9e148e0c82c7db98d780be566c6e6a97ee8035528ca0b3/pydantic_core-2.46.4-cp313-cp313-win_arm64.whl", hash = "sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d", size = 2027411, upload-time = "2026-05-06T13:40:45.796Z" }, ] [[package]] name = "pygments" -version = "2.20.0" +version = "2.21.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" } +sdist = { url = "https://files.pythonhosted.org/packages/49/2e/ced460408999b33da6b31b0021b0f37d329e202d4169aeb164493778f25b/pygments-2.21.0.tar.gz", hash = "sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c", size = 5005329, upload-time = "2026-08-17T08:02:48.824Z" } 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" }, + { url = "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl", hash = "sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", size = 1250147, upload-time = "2026-08-17T08:02:44.912Z" }, ] [[package]] @@ -1909,18 +1361,6 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/d9/9a/4019b524b03a13438637b11538c82781a5eda427394380381af8f04f467a/pynacl-1.6.2.tar.gz", hash = "sha256:018494d6d696ae03c7e656e5e74cdfd8ea1326962cc401bcf018f1ed8436811c", size = 3511692, upload-time = "2026-01-01T17:48:10.851Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4b/79/0e3c34dc3c4671f67d251c07aa8eb100916f250ee470df230b0ab89551b4/pynacl-1.6.2-cp314-cp314t-macosx_10_10_universal2.whl", hash = "sha256:622d7b07cc5c02c666795792931b50c91f3ce3c2649762efb1ef0d5684c81594", size = 390064, upload-time = "2026-01-01T17:31:57.264Z" }, - { url = "https://files.pythonhosted.org/packages/eb/1c/23a26e931736e13b16483795c8a6b2f641bf6a3d5238c22b070a5112722c/pynacl-1.6.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d071c6a9a4c94d79eb665db4ce5cedc537faf74f2355e4d502591d850d3913c0", size = 809370, upload-time = "2026-01-01T17:31:59.198Z" }, - { url = "https://files.pythonhosted.org/packages/87/74/8d4b718f8a22aea9e8dcc8b95deb76d4aae380e2f5b570cc70b5fd0a852d/pynacl-1.6.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fe9847ca47d287af41e82be1dd5e23023d3c31a951da134121ab02e42ac218c9", size = 1408304, upload-time = "2026-01-01T17:32:01.162Z" }, - { url = "https://files.pythonhosted.org/packages/fd/73/be4fdd3a6a87fe8a4553380c2b47fbd1f7f58292eb820902f5c8ac7de7b0/pynacl-1.6.2-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:04316d1fc625d860b6c162fff704eb8426b1a8bcd3abacea11142cbd99a6b574", size = 844871, upload-time = "2026-01-01T17:32:02.824Z" }, - { url = "https://files.pythonhosted.org/packages/55/ad/6efc57ab75ee4422e96b5f2697d51bbcf6cdcc091e66310df91fbdc144a8/pynacl-1.6.2-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44081faff368d6c5553ccf55322ef2819abb40e25afaec7e740f159f74813634", size = 1446356, upload-time = "2026-01-01T17:32:04.452Z" }, - { url = "https://files.pythonhosted.org/packages/78/b7/928ee9c4779caa0a915844311ab9fb5f99585621c5d6e4574538a17dca07/pynacl-1.6.2-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:a9f9932d8d2811ce1a8ffa79dcbdf3970e7355b5c8eb0c1a881a57e7f7d96e88", size = 826814, upload-time = "2026-01-01T17:32:06.078Z" }, - { url = "https://files.pythonhosted.org/packages/f7/a9/1bdba746a2be20f8809fee75c10e3159d75864ef69c6b0dd168fc60e485d/pynacl-1.6.2-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:bc4a36b28dd72fb4845e5d8f9760610588a96d5a51f01d84d8c6ff9849968c14", size = 1411742, upload-time = "2026-01-01T17:32:07.651Z" }, - { url = "https://files.pythonhosted.org/packages/f3/2f/5e7ea8d85f9f3ea5b6b87db1d8388daa3587eed181bdeb0306816fdbbe79/pynacl-1.6.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3bffb6d0f6becacb6526f8f42adfb5efb26337056ee0831fb9a7044d1a964444", size = 801714, upload-time = "2026-01-01T17:32:09.558Z" }, - { url = "https://files.pythonhosted.org/packages/06/ea/43fe2f7eab5f200e40fb10d305bf6f87ea31b3bbc83443eac37cd34a9e1e/pynacl-1.6.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2fef529ef3ee487ad8113d287a593fa26f48ee3620d92ecc6f1d09ea38e0709b", size = 1372257, upload-time = "2026-01-01T17:32:11.026Z" }, - { url = "https://files.pythonhosted.org/packages/4d/54/c9ea116412788629b1347e415f72195c25eb2f3809b2d3e7b25f5c79f13a/pynacl-1.6.2-cp314-cp314t-win32.whl", hash = "sha256:a84bf1c20339d06dc0c85d9aea9637a24f718f375d861b2668b2f9f96fa51145", size = 231319, upload-time = "2026-01-01T17:32:12.46Z" }, - { url = "https://files.pythonhosted.org/packages/ce/04/64e9d76646abac2dccf904fccba352a86e7d172647557f35b9fe2a5ee4a1/pynacl-1.6.2-cp314-cp314t-win_amd64.whl", hash = "sha256:320ef68a41c87547c91a8b58903c9caa641ab01e8512ce291085b5fe2fcb7590", size = 244044, upload-time = "2026-01-01T17:32:13.781Z" }, - { url = "https://files.pythonhosted.org/packages/33/33/7873dc161c6a06f43cda13dec67b6fe152cb2f982581151956fa5e5cdb47/pynacl-1.6.2-cp314-cp314t-win_arm64.whl", hash = "sha256:d29bfe37e20e015a7d8b23cfc8bd6aa7909c92a1b8f41ee416bbb3e79ef182b2", size = 188740, upload-time = "2026-01-01T17:32:15.083Z" }, { url = "https://files.pythonhosted.org/packages/be/7b/4845bbf88e94586ec47a432da4e9107e3fc3ce37eb412b1398630a37f7dd/pynacl-1.6.2-cp38-abi3-macosx_10_10_universal2.whl", hash = "sha256:c949ea47e4206af7c8f604b8278093b674f7c79ed0d4719cc836902bf4517465", size = 388458, upload-time = "2026-01-01T17:32:16.829Z" }, { url = "https://files.pythonhosted.org/packages/1e/b4/e927e0653ba63b02a4ca5b4d852a8d1d678afbf69b3dbf9c4d0785ac905c/pynacl-1.6.2-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8845c0631c0be43abdd865511c41eab235e0be69c81dc66a50911594198679b0", size = 800020, upload-time = "2026-01-01T17:32:18.34Z" }, { url = "https://files.pythonhosted.org/packages/7f/81/d60984052df5c97b1d24365bc1e30024379b42c4edcd79d2436b1b9806f2/pynacl-1.6.2-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:22de65bb9010a725b0dac248f353bb072969c94fa8d6b1f34b87d7953cf7bbe4", size = 1399174, upload-time = "2026-01-01T17:32:20.239Z" }, @@ -1955,20 +1395,20 @@ wheels = [ [[package]] name = "pyright" -version = "1.1.408" +version = "1.1.411" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nodeenv" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/74/b2/5db700e52554b8f025faa9c3c624c59f1f6c8841ba81ab97641b54322f16/pyright-1.1.408.tar.gz", hash = "sha256:f28f2321f96852fa50b5829ea492f6adb0e6954568d1caa3f3af3a5f555eb684", size = 4400578, upload-time = "2026-01-08T08:07:38.795Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/ab/265f7dc69d28113ebba19092e57b075f41543b2ed048429c5f56e2b88eac/pyright-1.1.411.tar.gz", hash = "sha256:d885a0551f2e763b089a02702174e7f4ba77548cddabc972ab86d1f7f1b0f998", size = 4112861, upload-time = "2026-06-25T02:14:06.37Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/82/a2c93e32800940d9573fb28c346772a14778b84ba7524e691b324620ab89/pyright-1.1.408-py3-none-any.whl", hash = "sha256:090b32865f4fdb1e0e6cd82bf5618480d48eecd2eb2e70f960982a3d9a4c17c1", size = 6399144, upload-time = "2026-01-08T08:07:37.082Z" }, + { url = "https://files.pythonhosted.org/packages/0a/49/385be530a6a5b78d1cbcd5c2e38debc8959a2fc6bdb716f4e581002979fc/pyright-1.1.411-py3-none-any.whl", hash = "sha256:dc7c72a8e2700c55baa127554040e067041ea53ccfd50bf96308cc4291c7d5d9", size = 6181526, upload-time = "2026-06-25T02:14:04.691Z" }, ] [[package]] name = "pytest" -version = "9.0.2" +version = "9.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -1977,9 +1417,9 @@ dependencies = [ { name = "pluggy" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, + { url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" }, ] [[package]] @@ -1996,11 +1436,11 @@ wheels = [ [[package]] name = "pyvers" -version = "0.2.2" +version = "0.2.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/32/99/23c73a1298b1c642d8ebdd78e1db4daf1e474152e6839df4f5c93357a3db/pyvers-0.2.2.tar.gz", hash = "sha256:205026bcd0b4c09198cb3a32f243fd179ef012882ce16d93dcb755320acd56f7", size = 12104, upload-time = "2026-01-23T14:12:07.619Z" } +sdist = { url = "https://files.pythonhosted.org/packages/62/21/9daf8da2793d112a04b46ac33ab9046c91b0af8292cd93b3cfdd07ff7169/pyvers-0.2.3.tar.gz", hash = "sha256:c4b81c3a033963245e124cdecb052783c9c4cea3bb08c051833af1c44faa6283", size = 12347, upload-time = "2026-07-09T13:58:07.046Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/36/bf/ea840f706b7824dd57220484465995309c8c217995ddb7ce4b262240e912/pyvers-0.2.2-py3-none-any.whl", hash = "sha256:c4696408a0b15fbaa90df33d3bc579cf23a74a73541858f5470216f12f51f3b1", size = 11569, upload-time = "2026-01-23T14:12:06.246Z" }, + { url = "https://files.pythonhosted.org/packages/2d/8d/63787e8feda59d981ebc953b6581ccab3c97cf7bfe0a6a407c07b50fc86c/pyvers-0.2.3-py3-none-any.whl", hash = "sha256:6f5b5612f2f4bd08caa49baa70fc5f875fc7da701a5385c13e244eea6b8114dd", size = 11757, upload-time = "2026-07-09T13:58:06.078Z" }, ] [[package]] @@ -2019,42 +1459,11 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, - { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, - { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, - { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, - { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, - { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, - { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, - { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, - { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, - { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, - { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, - { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, - { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, - { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, -] - -[[package]] -name = "referencing" -version = "0.37.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "rpds-py" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, ] [[package]] name = "requests" -version = "2.33.1" +version = "2.34.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, @@ -2062,264 +1471,110 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5f/a4/98b9c7c6428a668bf7e42ebb7c79d576a1c3c1e3ae2d47e674b468388871/requests-2.33.1.tar.gz", hash = "sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517", size = 134120, upload-time = "2026-03-30T16:09:15.531Z" } +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/d7/8e/7540e8a2036f79a125c1d2ebadf69ed7901608859186c856fa0388ef4197/requests-2.33.1-py3-none-any.whl", hash = "sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a", size = 64947, upload-time = "2026-03-30T16:09:13.83Z" }, + { 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 = "rich" -version = "14.3.3" +version = "14.3.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown-it-py" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b3/c6/f3b320c27991c46f43ee9d856302c70dc2d0fb2dba4842ff739d5f46b393/rich-14.3.3.tar.gz", hash = "sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b", size = 230582, upload-time = "2026-02-19T17:23:12.474Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl", hash = "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d", size = 310458, upload-time = "2026-02-19T17:23:13.732Z" }, -] - -[[package]] -name = "rpds-py" -version = "0.30.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2", size = 374887, upload-time = "2025-11-30T20:22:41.812Z" }, - { url = "https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8", size = 358904, upload-time = "2025-11-30T20:22:43.479Z" }, - { url = "https://files.pythonhosted.org/packages/58/70/faed8186300e3b9bdd138d0273109784eea2396c68458ed580f885dfe7ad/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4", size = 389945, upload-time = "2025-11-30T20:22:44.819Z" }, - { url = "https://files.pythonhosted.org/packages/bd/a8/073cac3ed2c6387df38f71296d002ab43496a96b92c823e76f46b8af0543/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136", size = 407783, upload-time = "2025-11-30T20:22:46.103Z" }, - { url = "https://files.pythonhosted.org/packages/77/57/5999eb8c58671f1c11eba084115e77a8899d6e694d2a18f69f0ba471ec8b/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7", size = 515021, upload-time = "2025-11-30T20:22:47.458Z" }, - { url = "https://files.pythonhosted.org/packages/e0/af/5ab4833eadc36c0a8ed2bc5c0de0493c04f6c06de223170bd0798ff98ced/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2", size = 414589, upload-time = "2025-11-30T20:22:48.872Z" }, - { url = "https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6", size = 394025, upload-time = "2025-11-30T20:22:50.196Z" }, - { url = "https://files.pythonhosted.org/packages/91/c4/fc70cd0249496493500e7cc2de87504f5aa6509de1e88623431fec76d4b6/rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e", size = 408895, upload-time = "2025-11-30T20:22:51.87Z" }, - { url = "https://files.pythonhosted.org/packages/58/95/d9275b05ab96556fefff73a385813eb66032e4c99f411d0795372d9abcea/rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d", size = 422799, upload-time = "2025-11-30T20:22:53.341Z" }, - { url = "https://files.pythonhosted.org/packages/06/c1/3088fc04b6624eb12a57eb814f0d4997a44b0d208d6cace713033ff1a6ba/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7", size = 572731, upload-time = "2025-11-30T20:22:54.778Z" }, - { url = "https://files.pythonhosted.org/packages/d8/42/c612a833183b39774e8ac8fecae81263a68b9583ee343db33ab571a7ce55/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31", size = 599027, upload-time = "2025-11-30T20:22:56.212Z" }, - { url = "https://files.pythonhosted.org/packages/5f/60/525a50f45b01d70005403ae0e25f43c0384369ad24ffe46e8d9068b50086/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95", size = 563020, upload-time = "2025-11-30T20:22:58.2Z" }, - { url = "https://files.pythonhosted.org/packages/0b/5d/47c4655e9bcd5ca907148535c10e7d489044243cc9941c16ed7cd53be91d/rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d", size = 223139, upload-time = "2025-11-30T20:23:00.209Z" }, - { url = "https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15", size = 240224, upload-time = "2025-11-30T20:23:02.008Z" }, - { url = "https://files.pythonhosted.org/packages/24/95/ffd128ed1146a153d928617b0ef673960130be0009c77d8fbf0abe306713/rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1", size = 230645, upload-time = "2025-11-30T20:23:03.43Z" }, - { url = "https://files.pythonhosted.org/packages/ff/1b/b10de890a0def2a319a2626334a7f0ae388215eb60914dbac8a3bae54435/rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a", size = 364443, upload-time = "2025-11-30T20:23:04.878Z" }, - { url = "https://files.pythonhosted.org/packages/0d/bf/27e39f5971dc4f305a4fb9c672ca06f290f7c4e261c568f3dea16a410d47/rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e", size = 353375, upload-time = "2025-11-30T20:23:06.342Z" }, - { url = "https://files.pythonhosted.org/packages/40/58/442ada3bba6e8e6615fc00483135c14a7538d2ffac30e2d933ccf6852232/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000", size = 383850, upload-time = "2025-11-30T20:23:07.825Z" }, - { url = "https://files.pythonhosted.org/packages/14/14/f59b0127409a33c6ef6f5c1ebd5ad8e32d7861c9c7adfa9a624fc3889f6c/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db", size = 392812, upload-time = "2025-11-30T20:23:09.228Z" }, - { url = "https://files.pythonhosted.org/packages/b3/66/e0be3e162ac299b3a22527e8913767d869e6cc75c46bd844aa43fb81ab62/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2", size = 517841, upload-time = "2025-11-30T20:23:11.186Z" }, - { url = "https://files.pythonhosted.org/packages/3d/55/fa3b9cf31d0c963ecf1ba777f7cf4b2a2c976795ac430d24a1f43d25a6ba/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa", size = 408149, upload-time = "2025-11-30T20:23:12.864Z" }, - { url = "https://files.pythonhosted.org/packages/60/ca/780cf3b1a32b18c0f05c441958d3758f02544f1d613abf9488cd78876378/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083", size = 383843, upload-time = "2025-11-30T20:23:14.638Z" }, - { url = "https://files.pythonhosted.org/packages/82/86/d5f2e04f2aa6247c613da0c1dd87fcd08fa17107e858193566048a1e2f0a/rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9", size = 396507, upload-time = "2025-11-30T20:23:16.105Z" }, - { url = "https://files.pythonhosted.org/packages/4b/9a/453255d2f769fe44e07ea9785c8347edaf867f7026872e76c1ad9f7bed92/rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0", size = 414949, upload-time = "2025-11-30T20:23:17.539Z" }, - { url = "https://files.pythonhosted.org/packages/a3/31/622a86cdc0c45d6df0e9ccb6becdba5074735e7033c20e401a6d9d0e2ca0/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94", size = 565790, upload-time = "2025-11-30T20:23:19.029Z" }, - { url = "https://files.pythonhosted.org/packages/1c/5d/15bbf0fb4a3f58a3b1c67855ec1efcc4ceaef4e86644665fff03e1b66d8d/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08", size = 590217, upload-time = "2025-11-30T20:23:20.885Z" }, - { url = "https://files.pythonhosted.org/packages/6d/61/21b8c41f68e60c8cc3b2e25644f0e3681926020f11d06ab0b78e3c6bbff1/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27", size = 555806, upload-time = "2025-11-30T20:23:22.488Z" }, - { url = "https://files.pythonhosted.org/packages/f9/39/7e067bb06c31de48de3eb200f9fc7c58982a4d3db44b07e73963e10d3be9/rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6", size = 211341, upload-time = "2025-11-30T20:23:24.449Z" }, - { url = "https://files.pythonhosted.org/packages/0a/4d/222ef0b46443cf4cf46764d9c630f3fe4abaa7245be9417e56e9f52b8f65/rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d", size = 225768, upload-time = "2025-11-30T20:23:25.908Z" }, - { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099, upload-time = "2025-11-30T20:23:27.316Z" }, - { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192, upload-time = "2025-11-30T20:23:29.151Z" }, - { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080, upload-time = "2025-11-30T20:23:30.785Z" }, - { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841, upload-time = "2025-11-30T20:23:32.209Z" }, - { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670, upload-time = "2025-11-30T20:23:33.742Z" }, - { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005, upload-time = "2025-11-30T20:23:35.253Z" }, - { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112, upload-time = "2025-11-30T20:23:36.842Z" }, - { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049, upload-time = "2025-11-30T20:23:38.343Z" }, - { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661, upload-time = "2025-11-30T20:23:40.263Z" }, - { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606, upload-time = "2025-11-30T20:23:42.186Z" }, - { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126, upload-time = "2025-11-30T20:23:44.086Z" }, - { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371, upload-time = "2025-11-30T20:23:46.004Z" }, - { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298, upload-time = "2025-11-30T20:23:47.696Z" }, - { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604, upload-time = "2025-11-30T20:23:49.501Z" }, - { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391, upload-time = "2025-11-30T20:23:50.96Z" }, - { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868, upload-time = "2025-11-30T20:23:52.494Z" }, - { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747, upload-time = "2025-11-30T20:23:54.036Z" }, - { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795, upload-time = "2025-11-30T20:23:55.556Z" }, - { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330, upload-time = "2025-11-30T20:23:57.033Z" }, - { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194, upload-time = "2025-11-30T20:23:58.637Z" }, - { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340, upload-time = "2025-11-30T20:24:00.2Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765, upload-time = "2025-11-30T20:24:01.759Z" }, - { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834, upload-time = "2025-11-30T20:24:03.687Z" }, - { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470, upload-time = "2025-11-30T20:24:05.232Z" }, - { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630, upload-time = "2025-11-30T20:24:06.878Z" }, - { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148, upload-time = "2025-11-30T20:24:08.445Z" }, - { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030, upload-time = "2025-11-30T20:24:10.956Z" }, - { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570, upload-time = "2025-11-30T20:24:12.735Z" }, - { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532, upload-time = "2025-11-30T20:24:14.634Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/e9/67/cae617f1351490c25a4b8ac3b8b63a4dda609295d8222bad12242dfdc629/rich-14.3.4.tar.gz", hash = "sha256:817e02727f2b25b40ef56f5aa2217f400c8489f79ca8f46ea2b70dd5e14558a9", size = 230524, upload-time = "2026-04-11T02:57:45.419Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/76/6d163cfac87b632216f71879e6b2cf17163f773ff59c00b5ff4900a80fa3/rich-14.3.4-py3-none-any.whl", hash = "sha256:07e7adb4690f68864777b1450859253bed81a99a31ac321ac1817b2313558952", size = 310480, upload-time = "2026-04-11T02:57:47.484Z" }, ] [[package]] name = "rsl-rl-lib" -version = "5.0.1" +version = "5.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "gitpython" }, { name = "numpy" }, { name = "onnx" }, { name = "onnxscript" }, + { name = "tensorboard" }, { name = "tensordict" }, { name = "torch", version = "2.9.1", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform == 'darwin'" }, { name = "torch", version = "2.9.1+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "sys_platform != 'darwin'" }, { name = "torchvision" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d5/23/dae404688e571e73662b7754ab2a8bddcc48f625c338b7f55576ad528653/rsl_rl_lib-5.0.1.tar.gz", hash = "sha256:b6e1fce8f4481c6118d53c7b03b80c8070d0a1edd3df3efbec5e5e6ff7c92132", size = 58623, upload-time = "2026-03-04T08:19:49.671Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ad/25/293a8ffd929d40accab30057b962084601133bd8a6779c156c2f58eb93bc/rsl_rl_lib-5.0.1-py3-none-any.whl", hash = "sha256:2f71a4f753537faf7826d0b74815160e37c409b39ab80a4581524b7ef7bd8539", size = 84202, upload-time = "2026-03-04T08:19:48.044Z" }, -] - -[[package]] -name = "rtree" -version = "1.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/95/09/7302695875a019514de9a5dd17b8320e7a19d6e7bc8f85dcfb79a4ce2da3/rtree-1.4.1.tar.gz", hash = "sha256:c6b1b3550881e57ebe530cc6cffefc87cd9bf49c30b37b894065a9f810875e46", size = 52425, upload-time = "2025-08-13T19:32:01.413Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/51/2d4c95b3642c0f659fbcddcd17fa0401903733250fa382f51f9b913bb85f/rsl_rl_lib-5.4.0.tar.gz", hash = "sha256:e1aa5cd5771f2d9a9e7a7ba5456b942ab588410cfd397b3c63e32d00a0744f0d", size = 65902, upload-time = "2026-05-27T10:42:39.656Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/04/d9/108cd989a4c0954e60b3cdc86fd2826407702b5375f6dfdab2802e5fed98/rtree-1.4.1-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:d672184298527522d4914d8ae53bf76982b86ca420b0acde9298a7a87d81d4a4", size = 468484, upload-time = "2025-08-13T19:31:50.593Z" }, - { url = "https://files.pythonhosted.org/packages/f3/cf/2710b6fd6b07ea0aef317b29f335790ba6adf06a28ac236078ed9bd8a91d/rtree-1.4.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a7e48d805e12011c2cf739a29d6a60ae852fb1de9fc84220bbcef67e6e595d7d", size = 436325, upload-time = "2025-08-13T19:31:52.367Z" }, - { url = "https://files.pythonhosted.org/packages/55/e1/4d075268a46e68db3cac51846eb6a3ab96ed481c585c5a1ad411b3c23aad/rtree-1.4.1-py3-none-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:efa8c4496e31e9ad58ff6c7df89abceac7022d906cb64a3e18e4fceae6b77f65", size = 459789, upload-time = "2025-08-13T19:31:53.926Z" }, - { url = "https://files.pythonhosted.org/packages/d1/75/e5d44be90525cd28503e7f836d077ae6663ec0687a13ba7810b4114b3668/rtree-1.4.1-py3-none-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:12de4578f1b3381a93a655846900be4e3d5f4cd5e306b8b00aa77c1121dc7e8c", size = 507644, upload-time = "2025-08-13T19:31:55.164Z" }, - { url = "https://files.pythonhosted.org/packages/fd/85/b8684f769a142163b52859a38a486493b05bafb4f2fb71d4f945de28ebf9/rtree-1.4.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b558edda52eca3e6d1ee629042192c65e6b7f2c150d6d6cd207ce82f85be3967", size = 1454478, upload-time = "2025-08-13T19:31:56.808Z" }, - { url = "https://files.pythonhosted.org/packages/e9/a4/c2292b95246b9165cc43a0c3757e80995d58bc9b43da5cb47ad6e3535213/rtree-1.4.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:f155bc8d6bac9dcd383481dee8c130947a4866db1d16cb6dff442329a038a0dc", size = 1555140, upload-time = "2025-08-13T19:31:58.031Z" }, - { url = "https://files.pythonhosted.org/packages/74/25/5282c8270bfcd620d3e73beb35b40ac4ab00f0a898d98ebeb41ef0989ec8/rtree-1.4.1-py3-none-win_amd64.whl", hash = "sha256:efe125f416fd27150197ab8521158662943a40f87acab8028a1aac4ad667a489", size = 389358, upload-time = "2025-08-13T19:31:59.247Z" }, - { url = "https://files.pythonhosted.org/packages/3f/50/0a9e7e7afe7339bd5e36911f0ceb15fed51945836ed803ae5afd661057fd/rtree-1.4.1-py3-none-win_arm64.whl", hash = "sha256:3d46f55729b28138e897ffef32f7ce93ac335cb67f9120125ad3742a220800f0", size = 355253, upload-time = "2025-08-13T19:32:00.296Z" }, + { url = "https://files.pythonhosted.org/packages/f8/dd/6797be77ce0cc12881f29cd9363b791f15276fde6a136a73443b6b6c2ce3/rsl_rl_lib-5.4.0-py3-none-any.whl", hash = "sha256:b30a0e59dac0ef7236f8f793c9bedfa8f2b8f8d29c2965140feeb1439153ebab", size = 92871, upload-time = "2026-05-27T10:42:38.415Z" }, ] [[package]] name = "ruff" -version = "0.15.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e6/97/e9f1ca355108ef7194e38c812ef40ba98c7208f47b13ad78d023caa583da/ruff-0.15.9.tar.gz", hash = "sha256:29cbb1255a9797903f6dde5ba0188c707907ff44a9006eb273b5a17bfa0739a2", size = 4617361, upload-time = "2026-04-02T18:17:20.829Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/1f/9cdfd0ac4b9d1e5a6cf09bedabdf0b56306ab5e333c85c87281273e7b041/ruff-0.15.9-py3-none-linux_armv6l.whl", hash = "sha256:6efbe303983441c51975c243e26dff328aca11f94b70992f35b093c2e71801e1", size = 10511206, upload-time = "2026-04-02T18:16:41.574Z" }, - { url = "https://files.pythonhosted.org/packages/3d/f6/32bfe3e9c136b35f02e489778d94384118bb80fd92c6d92e7ccd97db12ce/ruff-0.15.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:4965bac6ac9ea86772f4e23587746f0b7a395eccabb823eb8bfacc3fa06069f7", size = 10923307, upload-time = "2026-04-02T18:17:08.645Z" }, - { url = "https://files.pythonhosted.org/packages/ca/25/de55f52ab5535d12e7aaba1de37a84be6179fb20bddcbe71ec091b4a3243/ruff-0.15.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:eaf05aad70ca5b5a0a4b0e080df3a6b699803916d88f006efd1f5b46302daab8", size = 10316722, upload-time = "2026-04-02T18:16:44.206Z" }, - { url = "https://files.pythonhosted.org/packages/48/11/690d75f3fd6278fe55fff7c9eb429c92d207e14b25d1cae4064a32677029/ruff-0.15.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9439a342adb8725f32f92732e2bafb6d5246bd7a5021101166b223d312e8fc59", size = 10623674, upload-time = "2026-04-02T18:16:50.951Z" }, - { url = "https://files.pythonhosted.org/packages/bd/ec/176f6987be248fc5404199255522f57af1b4a5a1b57727e942479fec98ad/ruff-0.15.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9c5e6faf9d97c8edc43877c3f406f47446fc48c40e1442d58cfcdaba2acea745", size = 10351516, upload-time = "2026-04-02T18:16:57.206Z" }, - { url = "https://files.pythonhosted.org/packages/b2/fc/51cffbd2b3f240accc380171d51446a32aa2ea43a40d4a45ada67368fbd2/ruff-0.15.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b34a9766aeec27a222373d0b055722900fbc0582b24f39661aa96f3fe6ad901", size = 11150202, upload-time = "2026-04-02T18:17:06.452Z" }, - { url = "https://files.pythonhosted.org/packages/d6/d4/25292a6dfc125f6b6528fe6af31f5e996e19bf73ca8e3ce6eb7fa5b95885/ruff-0.15.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:89dd695bc72ae76ff484ae54b7e8b0f6b50f49046e198355e44ea656e521fef9", size = 11988891, upload-time = "2026-04-02T18:17:18.575Z" }, - { url = "https://files.pythonhosted.org/packages/13/e1/1eebcb885c10e19f969dcb93d8413dfee8172578709d7ee933640f5e7147/ruff-0.15.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce187224ef1de1bd225bc9a152ac7102a6171107f026e81f317e4257052916d5", size = 11480576, upload-time = "2026-04-02T18:16:52.986Z" }, - { url = "https://files.pythonhosted.org/packages/ff/6b/a1548ac378a78332a4c3dcf4a134c2475a36d2a22ddfa272acd574140b50/ruff-0.15.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b0c7c341f68adb01c488c3b7d4b49aa8ea97409eae6462d860a79cf55f431b6", size = 11254525, upload-time = "2026-04-02T18:17:02.041Z" }, - { url = "https://files.pythonhosted.org/packages/42/aa/4bb3af8e61acd9b1281db2ab77e8b2c3c5e5599bf2a29d4a942f1c62b8d6/ruff-0.15.9-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:55cc15eee27dc0eebdfcb0d185a6153420efbedc15eb1d38fe5e685657b0f840", size = 11204072, upload-time = "2026-04-02T18:17:13.581Z" }, - { url = "https://files.pythonhosted.org/packages/69/48/d550dc2aa6e423ea0bcc1d0ff0699325ffe8a811e2dba156bd80750b86dc/ruff-0.15.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a6537f6eed5cda688c81073d46ffdfb962a5f29ecb6f7e770b2dc920598997ed", size = 10594998, upload-time = "2026-04-02T18:16:46.369Z" }, - { url = "https://files.pythonhosted.org/packages/63/47/321167e17f5344ed5ec6b0aa2cff64efef5f9e985af8f5622cfa6536043f/ruff-0.15.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:6d3fcbca7388b066139c523bda744c822258ebdcfbba7d24410c3f454cc9af71", size = 10359769, upload-time = "2026-04-02T18:17:10.994Z" }, - { url = "https://files.pythonhosted.org/packages/67/5e/074f00b9785d1d2c6f8c22a21e023d0c2c1817838cfca4c8243200a1fa87/ruff-0.15.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:058d8e99e1bfe79d8a0def0b481c56059ee6716214f7e425d8e737e412d69677", size = 10850236, upload-time = "2026-04-02T18:16:48.749Z" }, - { url = "https://files.pythonhosted.org/packages/76/37/804c4135a2a2caf042925d30d5f68181bdbd4461fd0d7739da28305df593/ruff-0.15.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8e1ddb11dbd61d5983fa2d7d6370ef3eb210951e443cace19594c01c72abab4c", size = 11358343, upload-time = "2026-04-02T18:16:55.068Z" }, - { url = "https://files.pythonhosted.org/packages/88/3d/1364fcde8656962782aa9ea93c92d98682b1ecec2f184e625a965ad3b4a6/ruff-0.15.9-py3-none-win32.whl", hash = "sha256:bde6ff36eaf72b700f32b7196088970bf8fdb2b917b7accd8c371bfc0fd573ec", size = 10583382, upload-time = "2026-04-02T18:17:04.261Z" }, - { url = "https://files.pythonhosted.org/packages/4c/56/5c7084299bd2cacaa07ae63a91c6f4ba66edc08bf28f356b24f6b717c799/ruff-0.15.9-py3-none-win_amd64.whl", hash = "sha256:45a70921b80e1c10cf0b734ef09421f71b5aa11d27404edc89d7e8a69505e43d", size = 11744969, upload-time = "2026-04-02T18:16:59.611Z" }, - { url = "https://files.pythonhosted.org/packages/03/36/76704c4f312257d6dbaae3c959add2a622f63fcca9d864659ce6d8d97d3d/ruff-0.15.9-py3-none-win_arm64.whl", hash = "sha256:0694e601c028fd97dc5c6ee244675bc241aeefced7ef80cd9c6935a871078f53", size = 11005870, upload-time = "2026-04-02T18:17:15.773Z" }, +version = "0.16.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/85/c8e12473c93018f92d19dd988a294202e1c27426c47ec4de53ffb847b8d8/ruff-0.16.5.tar.gz", hash = "sha256:1b88500f9ffbcab3dedb0082c9f9492e91ec3d618aac1236a3e0189938f7040b", size = 4912003, upload-time = "2026-08-27T16:34:18.258Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/b6/77c90a970fe2dae17a723acbd011043ea97c98d7deacccefdc4ba74ec512/ruff-0.16.5-py3-none-linux_armv6l.whl", hash = "sha256:12e5f673e774c35fbb62f288809c7653b73445f8ecec6b6063fd6ea3521aa14b", size = 10011941, upload-time = "2026-08-27T16:33:41.287Z" }, + { url = "https://files.pythonhosted.org/packages/4b/46/6cf67cf6411885a1d6f7f6d801682f155536a85176d10b605e2ceffed8bd/ruff-0.16.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:eda58a5802de40e7ed5b32b64e0b32539338cc6fcd2c78f61e3ad6a0d79f51c3", size = 10204049, upload-time = "2026-08-27T16:33:44.056Z" }, + { url = "https://files.pythonhosted.org/packages/46/fd/c8720ca7a090abf0c2fef4abe8a5ef6e5127ed15196d8886ff75a2b370e2/ruff-0.16.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c5ae9a7b9a8875131f40f8fe967cc86abf899779efd663cb7ce3d572d01da7eb", size = 9809037, upload-time = "2026-08-27T16:33:46.257Z" }, + { url = "https://files.pythonhosted.org/packages/43/45/a684caacdedaca180f52bacccc40bf0789d2c5a7c75f25324853e9eaedb5/ruff-0.16.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b719b0a1f4d59710d283ab2965f621684a108a9e41da622e3b23f0326cd0025", size = 9964129, upload-time = "2026-08-27T16:33:48.352Z" }, + { url = "https://files.pythonhosted.org/packages/9e/f2/5d2bcdaca6b5b93d1b4dfc166cd2aebf7680143a1b38a28759df13a94d31/ruff-0.16.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2298f2780ed1be0c5cb1361e32ab7b1467f3cce7dabe101d2210a314f2fe42e9", size = 9821518, upload-time = "2026-08-27T16:33:50.57Z" }, + { url = "https://files.pythonhosted.org/packages/aa/ff/011cce29accf9257d5974145b733fc653a37985ed6825413a3987cefbfe0/ruff-0.16.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:258f29035a2dd021e7861e631b227a5b3f14e50c1184c9a6a122c5f4576154d7", size = 10534835, upload-time = "2026-08-27T16:33:52.522Z" }, + { url = "https://files.pythonhosted.org/packages/d7/5a/f0cf109bada9bba0e96c90c21c9f9251803f57225c32d293327a03c710d6/ruff-0.16.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9a4f0432966834019c74d1b7e5c51224305d7713f3d7faf3e7451f1a3be3cde", size = 11252550, upload-time = "2026-08-27T16:33:54.521Z" }, + { url = "https://files.pythonhosted.org/packages/63/4d/1d481aaea2046c6a7ed7c291f9004c669cce3c087b6b376ed5b08271e3fe/ruff-0.16.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b5eb3a8c3d0ade9cea42b591fd530368e8798380e30e0a308b85a5cf718f09ea", size = 10777949, upload-time = "2026-08-27T16:33:56.88Z" }, + { url = "https://files.pythonhosted.org/packages/ee/34/ee245ca55f64443233034b3d02b03236b19242004281247c079390b7facd/ruff-0.16.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef0f69e191a13a3c9816f63163c88790cb12cd157bbbb384e9c44745702ab105", size = 10311656, upload-time = "2026-08-27T16:33:59.12Z" }, + { url = "https://files.pythonhosted.org/packages/a7/4d/c33a333e341c0a2b96c715b52d89a606f5a34cd4ac493cd9b8d0187186b8/ruff-0.16.5-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:0eeab41fbea2c42f98dfb9822cdccda9d24ba38d49f6dc945b5c236d48f0ef29", size = 10532125, upload-time = "2026-08-27T16:34:01.166Z" }, + { url = "https://files.pythonhosted.org/packages/30/e1/a64cef78b40192497bb98a27a8aa8f2c98ee9ee15bc97f7712d94ef32937/ruff-0.16.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f0768e9df4300713fff30733c87575f68b6f1d8de41184e505b7fdd9c0c95eaf", size = 10097648, upload-time = "2026-08-27T16:34:03.16Z" }, + { url = "https://files.pythonhosted.org/packages/cc/4e/4cdc9ed3c3e109d2f71e62572a37457298d7bc7501ec3138babb7ed32bbd/ruff-0.16.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:95cc70cdc7aa80c338de356279d2adbeb2de0f520b9ecd8aba75b94e95e02f91", size = 9829344, upload-time = "2026-08-27T16:34:05.134Z" }, + { url = "https://files.pythonhosted.org/packages/39/4a/31ed35ce31729955fc583ee0d176d6e784c1290cb0b0a75cb2134c1ab72a/ruff-0.16.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d185c8398ded1bfd91c0c2cb258346307571eccc473a8490af8c3977399c384a", size = 10277117, upload-time = "2026-08-27T16:34:07.425Z" }, + { url = "https://files.pythonhosted.org/packages/a8/a0/60356d86687b4b666d593df213f4dc3041750d024cb7bf2cfa81cfd65c2e/ruff-0.16.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:fb8e3a3c4c6a784150a7ced53b015f4b253fc2bf97a610886419ead64b4756ef", size = 10711653, upload-time = "2026-08-27T16:34:09.712Z" }, + { url = "https://files.pythonhosted.org/packages/ed/20/656d67f5b25ca9bda4e02b1de25867b2954e1d19e03648060f167ad0f4cc/ruff-0.16.5-py3-none-win32.whl", hash = "sha256:288b0a5f080492fe5635db849f9e2e84aa3cce7b7f0e955997d416c507c76a26", size = 10034250, upload-time = "2026-08-27T16:34:11.8Z" }, + { url = "https://files.pythonhosted.org/packages/5b/42/ee8e68a207b9127fcde6c3d7e197def432f346cb1af159e1fa14ca0d1cdc/ruff-0.16.5-py3-none-win_amd64.whl", hash = "sha256:ddc6385fb2137f616357ca03d6c74f4be987f80fed4008566b754f6032b8546f", size = 10516714, upload-time = "2026-08-27T16:34:13.963Z" }, + { url = "https://files.pythonhosted.org/packages/73/e3/7df5a396e445b9ba49ce9a9437439a4d80042c61c0ade199abf8d16de1ac/ruff-0.16.5-py3-none-win_arm64.whl", hash = "sha256:a64abe90968719b851bb7cedffaa8753fbdbdadab483089682db623f3edc587e", size = 10391564, upload-time = "2026-08-27T16:34:16.064Z" }, ] [[package]] name = "scipy" -version = "1.17.1" +version = "1.18.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822, upload-time = "2026-02-23T00:26:24.851Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/76/27/07ee1b57b65e92645f219b37148a7e7928b82e2b5dbeccecb4dff7c64f0b/scipy-1.17.1-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:5e3c5c011904115f88a39308379c17f91546f77c1667cea98739fe0fccea804c", size = 31590199, upload-time = "2026-02-23T00:19:17.192Z" }, - { url = "https://files.pythonhosted.org/packages/ec/ae/db19f8ab842e9b724bf5dbb7db29302a91f1e55bc4d04b1025d6d605a2c5/scipy-1.17.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:6fac755ca3d2c3edcb22f479fceaa241704111414831ddd3bc6056e18516892f", size = 28154001, upload-time = "2026-02-23T00:19:22.241Z" }, - { url = "https://files.pythonhosted.org/packages/5b/58/3ce96251560107b381cbd6e8413c483bbb1228a6b919fa8652b0d4090e7f/scipy-1.17.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:7ff200bf9d24f2e4d5dc6ee8c3ac64d739d3a89e2326ba68aaf6c4a2b838fd7d", size = 20325719, upload-time = "2026-02-23T00:19:26.329Z" }, - { url = "https://files.pythonhosted.org/packages/b2/83/15087d945e0e4d48ce2377498abf5ad171ae013232ae31d06f336e64c999/scipy-1.17.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4b400bdc6f79fa02a4d86640310dde87a21fba0c979efff5248908c6f15fad1b", size = 22683595, upload-time = "2026-02-23T00:19:30.304Z" }, - { url = "https://files.pythonhosted.org/packages/b4/e0/e58fbde4a1a594c8be8114eb4aac1a55bcd6587047efc18a61eb1f5c0d30/scipy-1.17.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b64ca7d4aee0102a97f3ba22124052b4bd2152522355073580bf4845e2550b6", size = 32896429, upload-time = "2026-02-23T00:19:35.536Z" }, - { url = "https://files.pythonhosted.org/packages/f5/5f/f17563f28ff03c7b6799c50d01d5d856a1d55f2676f537ca8d28c7f627cd/scipy-1.17.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:581b2264fc0aa555f3f435a5944da7504ea3a065d7029ad60e7c3d1ae09c5464", size = 35203952, upload-time = "2026-02-23T00:19:42.259Z" }, - { url = "https://files.pythonhosted.org/packages/8d/a5/9afd17de24f657fdfe4df9a3f1ea049b39aef7c06000c13db1530d81ccca/scipy-1.17.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:beeda3d4ae615106d7094f7e7cef6218392e4465cc95d25f900bebabfded0950", size = 34979063, upload-time = "2026-02-23T00:19:47.547Z" }, - { url = "https://files.pythonhosted.org/packages/8b/13/88b1d2384b424bf7c924f2038c1c409f8d88bb2a8d49d097861dd64a57b2/scipy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6609bc224e9568f65064cfa72edc0f24ee6655b47575954ec6339534b2798369", size = 37598449, upload-time = "2026-02-23T00:19:53.238Z" }, - { url = "https://files.pythonhosted.org/packages/35/e5/d6d0e51fc888f692a35134336866341c08655d92614f492c6860dc45bb2c/scipy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:37425bc9175607b0268f493d79a292c39f9d001a357bebb6b88fdfaff13f6448", size = 36510943, upload-time = "2026-02-23T00:20:50.89Z" }, - { url = "https://files.pythonhosted.org/packages/2a/fd/3be73c564e2a01e690e19cc618811540ba5354c67c8680dce3281123fb79/scipy-1.17.1-cp313-cp313-win_arm64.whl", hash = "sha256:5cf36e801231b6a2059bf354720274b7558746f3b1a4efb43fcf557ccd484a87", size = 24545621, upload-time = "2026-02-23T00:20:55.871Z" }, - { url = "https://files.pythonhosted.org/packages/6f/6b/17787db8b8114933a66f9dcc479a8272e4b4da75fe03b0c282f7b0ade8cd/scipy-1.17.1-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:d59c30000a16d8edc7e64152e30220bfbd724c9bbb08368c054e24c651314f0a", size = 31936708, upload-time = "2026-02-23T00:19:58.694Z" }, - { url = "https://files.pythonhosted.org/packages/38/2e/524405c2b6392765ab1e2b722a41d5da33dc5c7b7278184a8ad29b6cb206/scipy-1.17.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:010f4333c96c9bb1a4516269e33cb5917b08ef2166d5556ca2fd9f082a9e6ea0", size = 28570135, upload-time = "2026-02-23T00:20:03.934Z" }, - { url = "https://files.pythonhosted.org/packages/fd/c3/5bd7199f4ea8556c0c8e39f04ccb014ac37d1468e6cfa6a95c6b3562b76e/scipy-1.17.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:2ceb2d3e01c5f1d83c4189737a42d9cb2fc38a6eeed225e7515eef71ad301dce", size = 20741977, upload-time = "2026-02-23T00:20:07.935Z" }, - { url = "https://files.pythonhosted.org/packages/d9/b8/8ccd9b766ad14c78386599708eb745f6b44f08400a5fd0ade7cf89b6fc93/scipy-1.17.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:844e165636711ef41f80b4103ed234181646b98a53c8f05da12ca5ca289134f6", size = 23029601, upload-time = "2026-02-23T00:20:12.161Z" }, - { url = "https://files.pythonhosted.org/packages/6d/a0/3cb6f4d2fb3e17428ad2880333cac878909ad1a89f678527b5328b93c1d4/scipy-1.17.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:158dd96d2207e21c966063e1635b1063cd7787b627b6f07305315dd73d9c679e", size = 33019667, upload-time = "2026-02-23T00:20:17.208Z" }, - { url = "https://files.pythonhosted.org/packages/f3/c3/2d834a5ac7bf3a0c806ad1508efc02dda3c8c61472a56132d7894c312dea/scipy-1.17.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74cbb80d93260fe2ffa334efa24cb8f2f0f622a9b9febf8b483c0b865bfb3475", size = 35264159, upload-time = "2026-02-23T00:20:23.087Z" }, - { url = "https://files.pythonhosted.org/packages/4d/77/d3ed4becfdbd217c52062fafe35a72388d1bd82c2d0ba5ca19d6fcc93e11/scipy-1.17.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:dbc12c9f3d185f5c737d801da555fb74b3dcfa1a50b66a1a93e09190f41fab50", size = 35102771, upload-time = "2026-02-23T00:20:28.636Z" }, - { url = "https://files.pythonhosted.org/packages/bd/12/d19da97efde68ca1ee5538bb261d5d2c062f0c055575128f11a2730e3ac1/scipy-1.17.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:94055a11dfebe37c656e70317e1996dc197e1a15bbcc351bcdd4610e128fe1ca", size = 37665910, upload-time = "2026-02-23T00:20:34.743Z" }, - { url = "https://files.pythonhosted.org/packages/06/1c/1172a88d507a4baaf72c5a09bb6c018fe2ae0ab622e5830b703a46cc9e44/scipy-1.17.1-cp313-cp313t-win_amd64.whl", hash = "sha256:e30bdeaa5deed6bc27b4cc490823cd0347d7dae09119b8803ae576ea0ce52e4c", size = 36562980, upload-time = "2026-02-23T00:20:40.575Z" }, - { url = "https://files.pythonhosted.org/packages/70/b0/eb757336e5a76dfa7911f63252e3b7d1de00935d7705cf772db5b45ec238/scipy-1.17.1-cp313-cp313t-win_arm64.whl", hash = "sha256:a720477885a9d2411f94a93d16f9d89bad0f28ca23c3f8daa521e2dcc3f44d49", size = 24856543, upload-time = "2026-02-23T00:20:45.313Z" }, - { url = "https://files.pythonhosted.org/packages/cf/83/333afb452af6f0fd70414dc04f898647ee1423979ce02efa75c3b0f2c28e/scipy-1.17.1-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:a48a72c77a310327f6a3a920092fa2b8fd03d7deaa60f093038f22d98e096717", size = 31584510, upload-time = "2026-02-23T00:21:01.015Z" }, - { url = "https://files.pythonhosted.org/packages/ed/a6/d05a85fd51daeb2e4ea71d102f15b34fedca8e931af02594193ae4fd25f7/scipy-1.17.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:45abad819184f07240d8a696117a7aacd39787af9e0b719d00285549ed19a1e9", size = 28170131, upload-time = "2026-02-23T00:21:05.888Z" }, - { url = "https://files.pythonhosted.org/packages/db/7b/8624a203326675d7746a254083a187398090a179335b2e4a20e2ddc46e83/scipy-1.17.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:3fd1fcdab3ea951b610dc4cef356d416d5802991e7e32b5254828d342f7b7e0b", size = 20342032, upload-time = "2026-02-23T00:21:09.904Z" }, - { url = "https://files.pythonhosted.org/packages/c9/35/2c342897c00775d688d8ff3987aced3426858fd89d5a0e26e020b660b301/scipy-1.17.1-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:7bdf2da170b67fdf10bca777614b1c7d96ae3ca5794fd9587dce41eb2966e866", size = 22678766, upload-time = "2026-02-23T00:21:14.313Z" }, - { url = "https://files.pythonhosted.org/packages/ef/f2/7cdb8eb308a1a6ae1e19f945913c82c23c0c442a462a46480ce487fdc0ac/scipy-1.17.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:adb2642e060a6549c343603a3851ba76ef0b74cc8c079a9a58121c7ec9fe2350", size = 32957007, upload-time = "2026-02-23T00:21:19.663Z" }, - { url = "https://files.pythonhosted.org/packages/0b/2e/7eea398450457ecb54e18e9d10110993fa65561c4f3add5e8eccd2b9cd41/scipy-1.17.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eee2cfda04c00a857206a4330f0c5e3e56535494e30ca445eb19ec624ae75118", size = 35221333, upload-time = "2026-02-23T00:21:25.278Z" }, - { url = "https://files.pythonhosted.org/packages/d9/77/5b8509d03b77f093a0d52e606d3c4f79e8b06d1d38c441dacb1e26cacf46/scipy-1.17.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d2650c1fb97e184d12d8ba010493ee7b322864f7d3d00d3f9bb97d9c21de4068", size = 35042066, upload-time = "2026-02-23T00:21:31.358Z" }, - { url = "https://files.pythonhosted.org/packages/f9/df/18f80fb99df40b4070328d5ae5c596f2f00fffb50167e31439e932f29e7d/scipy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:08b900519463543aa604a06bec02461558a6e1cef8fdbb8098f77a48a83c8118", size = 37612763, upload-time = "2026-02-23T00:21:37.247Z" }, - { url = "https://files.pythonhosted.org/packages/4b/39/f0e8ea762a764a9dc52aa7dabcfad51a354819de1f0d4652b6a1122424d6/scipy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:3877ac408e14da24a6196de0ddcace62092bfc12a83823e92e49e40747e52c19", size = 37290984, upload-time = "2026-02-23T00:22:35.023Z" }, - { url = "https://files.pythonhosted.org/packages/7c/56/fe201e3b0f93d1a8bcf75d3379affd228a63d7e2d80ab45467a74b494947/scipy-1.17.1-cp314-cp314-win_arm64.whl", hash = "sha256:f8885db0bc2bffa59d5c1b72fad7a6a92d3e80e7257f967dd81abb553a90d293", size = 25192877, upload-time = "2026-02-23T00:22:39.798Z" }, - { url = "https://files.pythonhosted.org/packages/96/ad/f8c414e121f82e02d76f310f16db9899c4fcde36710329502a6b2a3c0392/scipy-1.17.1-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:1cc682cea2ae55524432f3cdff9e9a3be743d52a7443d0cba9017c23c87ae2f6", size = 31949750, upload-time = "2026-02-23T00:21:42.289Z" }, - { url = "https://files.pythonhosted.org/packages/7c/b0/c741e8865d61b67c81e255f4f0a832846c064e426636cd7de84e74d209be/scipy-1.17.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:2040ad4d1795a0ae89bfc7e8429677f365d45aa9fd5e4587cf1ea737f927b4a1", size = 28585858, upload-time = "2026-02-23T00:21:47.706Z" }, - { url = "https://files.pythonhosted.org/packages/ed/1b/3985219c6177866628fa7c2595bfd23f193ceebbe472c98a08824b9466ff/scipy-1.17.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:131f5aaea57602008f9822e2115029b55d4b5f7c070287699fe45c661d051e39", size = 20757723, upload-time = "2026-02-23T00:21:52.039Z" }, - { url = "https://files.pythonhosted.org/packages/c0/19/2a04aa25050d656d6f7b9e7b685cc83d6957fb101665bfd9369ca6534563/scipy-1.17.1-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:9cdc1a2fcfd5c52cfb3045feb399f7b3ce822abdde3a193a6b9a60b3cb5854ca", size = 23043098, upload-time = "2026-02-23T00:21:56.185Z" }, - { url = "https://files.pythonhosted.org/packages/86/f1/3383beb9b5d0dbddd030335bf8a8b32d4317185efe495374f134d8be6cce/scipy-1.17.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e3dcd57ab780c741fde8dc68619de988b966db759a3c3152e8e9142c26295ad", size = 33030397, upload-time = "2026-02-23T00:22:01.404Z" }, - { url = "https://files.pythonhosted.org/packages/41/68/8f21e8a65a5a03f25a79165ec9d2b28c00e66dc80546cf5eb803aeeff35b/scipy-1.17.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a9956e4d4f4a301ebf6cde39850333a6b6110799d470dbbb1e25326ac447f52a", size = 35281163, upload-time = "2026-02-23T00:22:07.024Z" }, - { url = "https://files.pythonhosted.org/packages/84/8d/c8a5e19479554007a5632ed7529e665c315ae7492b4f946b0deb39870e39/scipy-1.17.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:a4328d245944d09fd639771de275701ccadf5f781ba0ff092ad141e017eccda4", size = 35116291, upload-time = "2026-02-23T00:22:12.585Z" }, - { url = "https://files.pythonhosted.org/packages/52/52/e57eceff0e342a1f50e274264ed47497b59e6a4e3118808ee58ddda7b74a/scipy-1.17.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a77cbd07b940d326d39a1d1b37817e2ee4d79cb30e7338f3d0cddffae70fcaa2", size = 37682317, upload-time = "2026-02-23T00:22:18.513Z" }, - { url = "https://files.pythonhosted.org/packages/11/2f/b29eafe4a3fbc3d6de9662b36e028d5f039e72d345e05c250e121a230dd4/scipy-1.17.1-cp314-cp314t-win_amd64.whl", hash = "sha256:eb092099205ef62cd1782b006658db09e2fed75bffcae7cc0d44052d8aa0f484", size = 37345327, upload-time = "2026-02-23T00:22:24.442Z" }, - { url = "https://files.pythonhosted.org/packages/07/39/338d9219c4e87f3e708f18857ecd24d22a0c3094752393319553096b98af/scipy-1.17.1-cp314-cp314t-win_arm64.whl", hash = "sha256:200e1050faffacc162be6a486a984a0497866ec54149a01270adc8a59b7c7d21", size = 25489165, upload-time = "2026-02-23T00:22:29.563Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/7e/74/66de6258867beb2ef08f35f9f2ac017a52cacd5081714d239ff1a442d458/scipy-1.18.1.tar.gz", hash = "sha256:52c4b7422442aba924d03ad4019852b08a92e64ea187b933135687bfe2747307", size = 30781235, upload-time = "2026-08-21T23:28:50.599Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/55/4540ee0f9c42a9ad7109d0d1a8cc70de54c3572b01c6693a2b1c70e90ceb/scipy-1.18.1-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:3ab3523da44749156e1f68b464dc56af11ae4cbc5c739a49d05f32b982eca9f3", size = 31089958, upload-time = "2026-08-21T23:24:35.8Z" }, + { url = "https://files.pythonhosted.org/packages/2a/f5/769f36d14922b8071a43e95d24d18b6bdafad10d7f5cf647867e1ac052bc/scipy-1.18.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:e6fb6a55cc0ba97b59a1f288fb86dc6fce8bdfc0fffcbfd015e3a954bf2a2d93", size = 28715106, upload-time = "2026-08-21T23:24:40.775Z" }, + { url = "https://files.pythonhosted.org/packages/9a/d7/21d890274f75ea37a8209d5519e72da3da90302e3b9fb8397a0918386a62/scipy-1.18.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:ea324d9dd34c38bfb9bec8ca4d1b407db97dbb74029f566b8e322b1b6fe56fe6", size = 20456846, upload-time = "2026-08-21T23:24:45.066Z" }, + { url = "https://files.pythonhosted.org/packages/ec/01/798430ecea2e78ec7c02663d5f71c007bb6abeca931080debd40d7fa55ea/scipy-1.18.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:75b00eb8fb802090aa903f4ea1c7f5a584779f967361e68b7e98e531cc2d7174", size = 23087986, upload-time = "2026-08-21T23:24:49.539Z" }, + { url = "https://files.pythonhosted.org/packages/e6/5f/4634e9d35c68496e4e34cb6946eafab044458e6cedab42b40b6588e475b6/scipy-1.18.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d416b16cccfd70fbf62400e84d0bb2f4e6af519a45557f1692c749b37f14b315", size = 33998146, upload-time = "2026-08-21T23:24:54.714Z" }, + { url = "https://files.pythonhosted.org/packages/41/48/6450ed9243315322bbc19ac57b9b70d66a20bf1d38d124c96bc4bf6af9ea/scipy-1.18.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fdaf5ea890a6183d0565f51a61799d67081bd5b1cf03c5f4b3fd3732108625c9", size = 35312578, upload-time = "2026-08-21T23:25:00.44Z" }, + { url = "https://files.pythonhosted.org/packages/00/bd/bf5a4be6a3525676499f6dff307991739ff6fdcad1481b1aeb6745339f58/scipy-1.18.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c825cef2f49e46753726a7181a8e199804a912b29519ada542c6ebc654951899", size = 35612621, upload-time = "2026-08-21T23:25:06.144Z" }, + { url = "https://files.pythonhosted.org/packages/bd/4e/3c45c33e00a77996c4b1cb707929f833ba7b1d522ee29f882512c330676d/scipy-1.18.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e3b417bf8c2c7c16e8f58ad91db17783ec911ac16e7b50eb6eab6e809b4f5b07", size = 37457323, upload-time = "2026-08-21T23:25:12.483Z" }, + { url = "https://files.pythonhosted.org/packages/93/0e/e0348fbc0dbab65c114cf78957e7dfeb49f8e8b556b4d930cc12ff195e18/scipy-1.18.1-cp313-cp313-win_amd64.whl", hash = "sha256:559ed65f60c1af5a03f3912605a1b5114f522c7c32fb23c3376ae8f03219fe28", size = 36622841, upload-time = "2026-08-21T23:25:18.722Z" }, + { url = "https://files.pythonhosted.org/packages/50/a8/6a77f5f267c555108f0a864b6db714363dab567a8266422a79a385f9232b/scipy-1.18.1-cp313-cp313-win_arm64.whl", hash = "sha256:cd479fc04dd9401e3b4f49e76518768ef99c4f517a98c284eb091fd725719adf", size = 24399315, upload-time = "2026-08-21T23:25:23.458Z" }, ] [[package]] name = "sentry-sdk" -version = "2.57.0" +version = "2.68.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4f/87/46c0406d8b5ddd026f73adaf5ab75ce144219c41a4830b52df4b9ab55f7f/sentry_sdk-2.57.0.tar.gz", hash = "sha256:4be8d1e71c32fb27f79c577a337ac8912137bba4bcbc64a4ec1da4d6d8dc5199", size = 435288, upload-time = "2026-03-31T09:39:29.264Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/e7/c504a4bd2d95df2e0ab73714a9161ff1cf6ff1486922685e5f46dfd9eba8/sentry_sdk-2.68.1.tar.gz", hash = "sha256:6a97895230b04bc35d4d8d2e51e3b9e21902dfb0086ccf1f131a80c15c7b997a", size = 1019262, upload-time = "2026-08-24T13:09:38.108Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c9/64/982e07b93219cb52e1cca5d272cb579e2f3eb001956c9e7a9a6d106c9473/sentry_sdk-2.57.0-py2.py3-none-any.whl", hash = "sha256:812c8bf5ff3d2f0e89c82f5ce80ab3a6423e102729c4706af7413fd1eb480585", size = 456489, upload-time = "2026-03-31T09:39:27.524Z" }, + { url = "https://files.pythonhosted.org/packages/2b/28/465ad9382be98f2172e691f5836cf87f936773913ad7ab85ba1ba1d6706e/sentry_sdk-2.68.1-py3-none-any.whl", hash = "sha256:775b78871783a0ffd758276ad01b3bb2b1ebcdad8f9d2f0a7723f76b73c99b65", size = 520851, upload-time = "2026-08-24T13:09:36.186Z" }, ] [[package]] name = "setuptools" -version = "82.0.1" +version = "84.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4f/db/cfac1baf10650ab4d1c111714410d2fbb77ac5a616db26775db562c8fab2/setuptools-82.0.1.tar.gz", hash = "sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9", size = 1152316, upload-time = "2026-03-09T12:47:17.221Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/44/f5da03a8ef95d369145c5bb53050e7877c9f3d312e128605fd9504829143/setuptools-84.0.0.tar.gz", hash = "sha256:f4695c21257f0d9b537ec2692c941d02ee143b7cc1276941349a546573b2ef73", size = 1168449, upload-time = "2026-08-08T18:27:58.365Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl", hash = "sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb", size = 1006223, upload-time = "2026-03-09T12:47:15.026Z" }, -] - -[[package]] -name = "shapely" -version = "2.1.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/4d/bc/0989043118a27cccb4e906a46b7565ce36ca7b57f5a18b78f4f1b0f72d9d/shapely-2.1.2.tar.gz", hash = "sha256:2ed4ecb28320a433db18a5bf029986aa8afcfd740745e78847e330d5d94922a9", size = 315489, upload-time = "2025-09-24T13:51:41.432Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/90/98ef257c23c46425dc4d1d31005ad7c8d649fe423a38b917db02c30f1f5a/shapely-2.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b510dda1a3672d6879beb319bc7c5fd302c6c354584690973c838f46ec3e0fa8", size = 1832644, upload-time = "2025-09-24T13:50:44.886Z" }, - { url = "https://files.pythonhosted.org/packages/6d/ab/0bee5a830d209adcd3a01f2d4b70e587cdd9fd7380d5198c064091005af8/shapely-2.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8cff473e81017594d20ec55d86b54bc635544897e13a7cfc12e36909c5309a2a", size = 1642887, upload-time = "2025-09-24T13:50:46.735Z" }, - { url = "https://files.pythonhosted.org/packages/2d/5e/7d7f54ba960c13302584c73704d8c4d15404a51024631adb60b126a4ae88/shapely-2.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fe7b77dc63d707c09726b7908f575fc04ff1d1ad0f3fb92aec212396bc6cfe5e", size = 2970931, upload-time = "2025-09-24T13:50:48.374Z" }, - { url = "https://files.pythonhosted.org/packages/f2/a2/83fc37e2a58090e3d2ff79175a95493c664bcd0b653dd75cb9134645a4e5/shapely-2.1.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7ed1a5bbfb386ee8332713bf7508bc24e32d24b74fc9a7b9f8529a55db9f4ee6", size = 3082855, upload-time = "2025-09-24T13:50:50.037Z" }, - { url = "https://files.pythonhosted.org/packages/44/2b/578faf235a5b09f16b5f02833c53822294d7f21b242f8e2d0cf03fb64321/shapely-2.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a84e0582858d841d54355246ddfcbd1fce3179f185da7470f41ce39d001ee1af", size = 3979960, upload-time = "2025-09-24T13:50:51.74Z" }, - { url = "https://files.pythonhosted.org/packages/4d/04/167f096386120f692cc4ca02f75a17b961858997a95e67a3cb6a7bbd6b53/shapely-2.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dc3487447a43d42adcdf52d7ac73804f2312cbfa5d433a7d2c506dcab0033dfd", size = 4142851, upload-time = "2025-09-24T13:50:53.49Z" }, - { url = "https://files.pythonhosted.org/packages/48/74/fb402c5a6235d1c65a97348b48cdedb75fb19eca2b1d66d04969fc1c6091/shapely-2.1.2-cp313-cp313-win32.whl", hash = "sha256:9c3a3c648aedc9f99c09263b39f2d8252f199cb3ac154fadc173283d7d111350", size = 1541890, upload-time = "2025-09-24T13:50:55.337Z" }, - { url = "https://files.pythonhosted.org/packages/41/47/3647fe7ad990af60ad98b889657a976042c9988c2807cf322a9d6685f462/shapely-2.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:ca2591bff6645c216695bdf1614fca9c82ea1144d4a7591a466fef64f28f0715", size = 1722151, upload-time = "2025-09-24T13:50:57.153Z" }, - { url = "https://files.pythonhosted.org/packages/3c/49/63953754faa51ffe7d8189bfbe9ca34def29f8c0e34c67cbe2a2795f269d/shapely-2.1.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2d93d23bdd2ed9dc157b46bc2f19b7da143ca8714464249bef6771c679d5ff40", size = 1834130, upload-time = "2025-09-24T13:50:58.49Z" }, - { url = "https://files.pythonhosted.org/packages/7f/ee/dce001c1984052970ff60eb4727164892fb2d08052c575042a47f5a9e88f/shapely-2.1.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:01d0d304b25634d60bd7cf291828119ab55a3bab87dc4af1e44b07fb225f188b", size = 1642802, upload-time = "2025-09-24T13:50:59.871Z" }, - { url = "https://files.pythonhosted.org/packages/da/e7/fc4e9a19929522877fa602f705706b96e78376afb7fad09cad5b9af1553c/shapely-2.1.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8d8382dd120d64b03698b7298b89611a6ea6f55ada9d39942838b79c9bc89801", size = 3018460, upload-time = "2025-09-24T13:51:02.08Z" }, - { url = "https://files.pythonhosted.org/packages/a1/18/7519a25db21847b525696883ddc8e6a0ecaa36159ea88e0fef11466384d0/shapely-2.1.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:19efa3611eef966e776183e338b2d7ea43569ae99ab34f8d17c2c054d3205cc0", size = 3095223, upload-time = "2025-09-24T13:51:04.472Z" }, - { url = "https://files.pythonhosted.org/packages/48/de/b59a620b1f3a129c3fecc2737104a0a7e04e79335bd3b0a1f1609744cf17/shapely-2.1.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:346ec0c1a0fcd32f57f00e4134d1200e14bf3f5ae12af87ba83ca275c502498c", size = 4030760, upload-time = "2025-09-24T13:51:06.455Z" }, - { url = "https://files.pythonhosted.org/packages/96/b3/c6655ee7232b417562bae192ae0d3ceaadb1cc0ffc2088a2ddf415456cc2/shapely-2.1.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6305993a35989391bd3476ee538a5c9a845861462327efe00dd11a5c8c709a99", size = 4170078, upload-time = "2025-09-24T13:51:08.584Z" }, - { url = "https://files.pythonhosted.org/packages/a0/8e/605c76808d73503c9333af8f6cbe7e1354d2d238bda5f88eea36bfe0f42a/shapely-2.1.2-cp313-cp313t-win32.whl", hash = "sha256:c8876673449f3401f278c86eb33224c5764582f72b653a415d0e6672fde887bf", size = 1559178, upload-time = "2025-09-24T13:51:10.73Z" }, - { url = "https://files.pythonhosted.org/packages/36/f7/d317eb232352a1f1444d11002d477e54514a4a6045536d49d0c59783c0da/shapely-2.1.2-cp313-cp313t-win_amd64.whl", hash = "sha256:4a44bc62a10d84c11a7a3d7c1c4fe857f7477c3506e24c9062da0db0ae0c449c", size = 1739756, upload-time = "2025-09-24T13:51:12.105Z" }, - { url = "https://files.pythonhosted.org/packages/fc/c4/3ce4c2d9b6aabd27d26ec988f08cb877ba9e6e96086eff81bfea93e688c7/shapely-2.1.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:9a522f460d28e2bf4e12396240a5fc1518788b2fcd73535166d748399ef0c223", size = 1831290, upload-time = "2025-09-24T13:51:13.56Z" }, - { url = "https://files.pythonhosted.org/packages/17/b9/f6ab8918fc15429f79cb04afa9f9913546212d7fb5e5196132a2af46676b/shapely-2.1.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1ff629e00818033b8d71139565527ced7d776c269a49bd78c9df84e8f852190c", size = 1641463, upload-time = "2025-09-24T13:51:14.972Z" }, - { url = "https://files.pythonhosted.org/packages/a5/57/91d59ae525ca641e7ac5551c04c9503aee6f29b92b392f31790fcb1a4358/shapely-2.1.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f67b34271dedc3c653eba4e3d7111aa421d5be9b4c4c7d38d30907f796cb30df", size = 2970145, upload-time = "2025-09-24T13:51:16.961Z" }, - { url = "https://files.pythonhosted.org/packages/8a/cb/4948be52ee1da6927831ab59e10d4c29baa2a714f599f1f0d1bc747f5777/shapely-2.1.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:21952dc00df38a2c28375659b07a3979d22641aeb104751e769c3ee825aadecf", size = 3073806, upload-time = "2025-09-24T13:51:18.712Z" }, - { url = "https://files.pythonhosted.org/packages/03/83/f768a54af775eb41ef2e7bec8a0a0dbe7d2431c3e78c0a8bdba7ab17e446/shapely-2.1.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1f2f33f486777456586948e333a56ae21f35ae273be99255a191f5c1fa302eb4", size = 3980803, upload-time = "2025-09-24T13:51:20.37Z" }, - { url = "https://files.pythonhosted.org/packages/9f/cb/559c7c195807c91c79d38a1f6901384a2878a76fbdf3f1048893a9b7534d/shapely-2.1.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:cf831a13e0d5a7eb519e96f58ec26e049b1fad411fc6fc23b162a7ce04d9cffc", size = 4133301, upload-time = "2025-09-24T13:51:21.887Z" }, - { url = "https://files.pythonhosted.org/packages/80/cd/60d5ae203241c53ef3abd2ef27c6800e21afd6c94e39db5315ea0cbafb4a/shapely-2.1.2-cp314-cp314-win32.whl", hash = "sha256:61edcd8d0d17dd99075d320a1dd39c0cb9616f7572f10ef91b4b5b00c4aeb566", size = 1583247, upload-time = "2025-09-24T13:51:23.401Z" }, - { url = "https://files.pythonhosted.org/packages/74/d4/135684f342e909330e50d31d441ace06bf83c7dc0777e11043f99167b123/shapely-2.1.2-cp314-cp314-win_amd64.whl", hash = "sha256:a444e7afccdb0999e203b976adb37ea633725333e5b119ad40b1ca291ecf311c", size = 1773019, upload-time = "2025-09-24T13:51:24.873Z" }, - { url = "https://files.pythonhosted.org/packages/a3/05/a44f3f9f695fa3ada22786dc9da33c933da1cbc4bfe876fe3a100bafe263/shapely-2.1.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:5ebe3f84c6112ad3d4632b1fd2290665aa75d4cef5f6c5d77c4c95b324527c6a", size = 1834137, upload-time = "2025-09-24T13:51:26.665Z" }, - { url = "https://files.pythonhosted.org/packages/52/7e/4d57db45bf314573427b0a70dfca15d912d108e6023f623947fa69f39b72/shapely-2.1.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5860eb9f00a1d49ebb14e881f5caf6c2cf472c7fd38bd7f253bbd34f934eb076", size = 1642884, upload-time = "2025-09-24T13:51:28.029Z" }, - { url = "https://files.pythonhosted.org/packages/5a/27/4e29c0a55d6d14ad7422bf86995d7ff3f54af0eba59617eb95caf84b9680/shapely-2.1.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b705c99c76695702656327b819c9660768ec33f5ce01fa32b2af62b56ba400a1", size = 3018320, upload-time = "2025-09-24T13:51:29.903Z" }, - { url = "https://files.pythonhosted.org/packages/9f/bb/992e6a3c463f4d29d4cd6ab8963b75b1b1040199edbd72beada4af46bde5/shapely-2.1.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a1fd0ea855b2cf7c9cddaf25543e914dd75af9de08785f20ca3085f2c9ca60b0", size = 3094931, upload-time = "2025-09-24T13:51:32.699Z" }, - { url = "https://files.pythonhosted.org/packages/9c/16/82e65e21070e473f0ed6451224ed9fa0be85033d17e0c6e7213a12f59d12/shapely-2.1.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:df90e2db118c3671a0754f38e36802db75fe0920d211a27481daf50a711fdf26", size = 4030406, upload-time = "2025-09-24T13:51:34.189Z" }, - { url = "https://files.pythonhosted.org/packages/7c/75/c24ed871c576d7e2b64b04b1fe3d075157f6eb54e59670d3f5ffb36e25c7/shapely-2.1.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:361b6d45030b4ac64ddd0a26046906c8202eb60d0f9f53085f5179f1d23021a0", size = 4169511, upload-time = "2025-09-24T13:51:36.297Z" }, - { url = "https://files.pythonhosted.org/packages/b1/f7/b3d1d6d18ebf55236eec1c681ce5e665742aab3c0b7b232720a7d43df7b6/shapely-2.1.2-cp314-cp314t-win32.whl", hash = "sha256:b54df60f1fbdecc8ebc2c5b11870461a6417b3d617f555e5033f1505d36e5735", size = 1602607, upload-time = "2025-09-24T13:51:37.757Z" }, - { url = "https://files.pythonhosted.org/packages/9a/f6/f09272a71976dfc138129b8faf435d064a811ae2f708cb147dccdf7aacdb/shapely-2.1.2-cp314-cp314t-win_amd64.whl", hash = "sha256:0036ac886e0923417932c2e6369b6c52e38e0ff5d9120b90eef5cd9a5fc5cae9", size = 1796682, upload-time = "2025-09-24T13:51:39.233Z" }, + { url = "https://files.pythonhosted.org/packages/95/9c/c510029fc6ef33a6275cd2c5d3cecd6613dfd6aa401d57c54f1c18852ccf/setuptools-84.0.0-py3-none-any.whl", hash = "sha256:51a52592b3b99e102b609654876bd65f19f999935166d1352678931132b0c670", size = 818216, upload-time = "2026-08-08T18:27:56.719Z" }, ] [[package]] @@ -2354,15 +1609,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" }, ] -[[package]] -name = "svg-path" -version = "7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/66/b9/649abbe870842c185b12920e937e9b95d4c2b18de50af98d2c140df3e179/svg_path-7.0.tar.gz", hash = "sha256:9037486957cb1dcf4375ef42206499a47c111b8ffcbac6e3e55f9d079d875bb0", size = 23552, upload-time = "2025-07-06T15:20:40.823Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/83/4f5b250220e1a5acd31345a5ec1c95a7769725d0d8135276f399f44062f8/svg_path-7.0-py2.py3-none-any.whl", hash = "sha256:447cb1e16a95acea2dd867fe737fa99cb75d587b4fc64dbee709a8dd6891ad9c", size = 18208, upload-time = "2025-07-06T15:20:39.59Z" }, -] - [[package]] name = "sympy" version = "1.14.0" @@ -2377,7 +1623,7 @@ wheels = [ [[package]] name = "tensorboard" -version = "2.20.0" +version = "2.21.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "absl-py" }, @@ -2392,7 +1638,7 @@ dependencies = [ { name = "werkzeug" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/9c/d9/a5db55f88f258ac669a92858b70a714bbbd5acd993820b41ec4a96a4d77f/tensorboard-2.20.0-py3-none-any.whl", hash = "sha256:9dc9f978cb84c0723acf9a345d96c184f0293d18f166bb8d59ee098e6cfaaba6", size = 5525680, upload-time = "2025-07-17T19:20:49.638Z" }, + { url = "https://files.pythonhosted.org/packages/37/4b/cd2eec9642781a8f5b2fb9994e3933a7b259ab18e9d49aeede9b5acf6311/tensorboard-2.21.0-py3-none-any.whl", hash = "sha256:7279316dcb6bd5bc391d623dea841531299cde1887310e8133bc34a996d32255", size = 5516204, upload-time = "2026-06-29T20:48:04.472Z" }, ] [[package]] @@ -2407,7 +1653,7 @@ wheels = [ [[package]] name = "tensordict" -version = "0.11.0" +version = "0.14.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cloudpickle" }, @@ -2419,22 +1665,10 @@ dependencies = [ { name = "torch", version = "2.9.1+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "sys_platform != 'darwin'" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/2b/8d/64b04f4c3ae77cd1330f697950b8ac9785f815be152805b126321f4c9483/tensordict-0.11.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:fa1f77fc63b37c19fe8e3e684d7d9dcd0e7d39beaa1d4dd09e6369aab4f47036", size = 815987, upload-time = "2026-01-26T11:36:13.277Z" }, - { url = "https://files.pythonhosted.org/packages/53/6f/4ef78fdd6d0d33c1cbc9b13e7f3079bf46f1c9e53a728e986c6f664be774/tensordict-0.11.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:526a1391f4a13ec82b781078a9190fc0626bfdeedaf30a32ba84264db76fd5fb", size = 460959, upload-time = "2026-01-26T11:36:14.439Z" }, - { url = "https://files.pythonhosted.org/packages/96/62/6322a759fc4b62c2ded50b3330bcb1e541d86734b86603d3e4c4c1442b16/tensordict-0.11.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:525bff4b95539b63fdb45e82c166c7481d039df604007baab69fbcfcb1310ac4", size = 465438, upload-time = "2026-01-26T11:36:15.971Z" }, - { url = "https://files.pythonhosted.org/packages/b6/d1/2d00adaa35a0a37f9c796709a739904ab1c032f721dcef736eb8bd72a999/tensordict-0.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:5a63f53f20aa90ea23cac69ca4daf8db97d12dd0c1b51b855424bc48e411914c", size = 510202, upload-time = "2026-01-26T11:36:17.196Z" }, - { url = "https://files.pythonhosted.org/packages/26/20/014904cd5e5b851ea7b1c9a46b91a5c3a850fc6807b640d7a9a09c8714aa/tensordict-0.11.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:0c925186aabb04aaa080a1b0160f48dad0911d3dc42bfb5dabdc9ed60518fbd7", size = 822881, upload-time = "2026-01-26T11:36:18.381Z" }, - { url = "https://files.pythonhosted.org/packages/60/85/4e54d398a53520f624d291f8a498d389fbbdf740d3a6b018d67c50feef55/tensordict-0.11.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:c6b3d465f72ddc8fac2b1f031f873f38d073dcca897d1c8751fa4e95142d848f", size = 462403, upload-time = "2026-01-26T11:36:19.506Z" }, - { url = "https://files.pythonhosted.org/packages/8a/ec/bfc5384cea17fecca6980ee9e6fe5b75e55bab09bfe1975795107d8491ff/tensordict-0.11.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:06a0df47c227c81ce6a3d7a7960a0ca2a9ab832a3460e9a4a88a3c5b929903e4", size = 465141, upload-time = "2026-01-26T11:36:20.658Z" }, - { url = "https://files.pythonhosted.org/packages/a7/d8/b84caf450e1cce55f9b3cd64c3f9d56b3d0cd9265ea728500605fd71a971/tensordict-0.11.0-cp313-cp313t-win_amd64.whl", hash = "sha256:e963e114e8c03d9b2a93b41899af6598a27db1c5fa17c78aeb0cc16ab9e143c5", size = 520028, upload-time = "2026-01-26T11:36:21.904Z" }, - { url = "https://files.pythonhosted.org/packages/22/eb/a30a548306ddb90010bb8440d463ebff1b3a1a6682e7cf8b47a3a9d6b8a2/tensordict-0.11.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:b90bfef3d83e36771b2bb3d888abc2102a09eb0cb43d24141396272ce1cb76dd", size = 818890, upload-time = "2026-01-26T11:36:23.053Z" }, - { url = "https://files.pythonhosted.org/packages/dc/44/48f8ece93bdd6009e8b02f88db818c9a2ca14064c339adeaf7f42f0551f4/tensordict-0.11.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:70bd7e583fb9fb7ee28dd7a783170411dde5f47191cefa87063cff414357ad24", size = 461355, upload-time = "2026-01-26T11:36:24.418Z" }, - { url = "https://files.pythonhosted.org/packages/f6/31/37c668e4477db51322f73289a3b3b45eba1ff5d0fb593f7cb258553a382e/tensordict-0.11.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:27169933ad38f549b6317a4bd9a6cee92a72cb2e9d5f5f909d42a3fc81111105", size = 465724, upload-time = "2026-01-26T11:36:25.737Z" }, - { url = "https://files.pythonhosted.org/packages/8a/61/3e219bac522cf8a33fe41fdf45682fbccf660ba3426e7a80d787277f4bc7/tensordict-0.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:f2effe50ce519d2bebb4e2cfddef7e1faf265524e8ada3177ca4f7f6e91098d7", size = 510486, upload-time = "2026-01-26T11:36:26.826Z" }, - { url = "https://files.pythonhosted.org/packages/fa/25/addc886b14cf16469ef40cb1647f8f36ab3fb993e9b3053c056a105dcc55/tensordict-0.11.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:805c416e4771aea3fa4cca074c4b0db06816098f5fa2bbde3682e06a438106a1", size = 822879, upload-time = "2026-01-26T11:36:28.343Z" }, - { url = "https://files.pythonhosted.org/packages/c2/e7/a1af12078d33d2f834fb3b4a52a17808e91c0be1439ef111a4858b1a2881/tensordict-0.11.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:c669e18fad8966125d900e5591af59f8da1177618db9d22cab246e0b393c7d2d", size = 462405, upload-time = "2026-01-26T11:36:30.094Z" }, - { url = "https://files.pythonhosted.org/packages/55/be/88f8a8aa1056fbf8ec17af5e9e31cf25e3c60f04943bcb8af89cb2d44528/tensordict-0.11.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:da98ecbd706b630c77911595abb45829eb8d9006c50751dff506179a132517bd", size = 465139, upload-time = "2026-01-26T11:36:31.271Z" }, - { url = "https://files.pythonhosted.org/packages/b0/1e/b556a5b4dc88ff3f63e744f6487b9f84e4a149a4a5f2a6628f472dd2d4d3/tensordict-0.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:3b5f95b726405e2da308bdb2e81523d1f4b8e85baf88856620500af689d41064", size = 520145, upload-time = "2026-01-26T11:36:32.423Z" }, + { url = "https://files.pythonhosted.org/packages/23/bf/a969e631cdae4234266242f3d74023a25a8087e58e3971f0407e2f115cc4/tensordict-0.14.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:7b9458005b6868d932242eeca93b2fcea9f47bb8f6d37bd53cdd1f493fb783a4", size = 945332, upload-time = "2026-08-14T21:27:57.952Z" }, + { url = "https://files.pythonhosted.org/packages/12/b7/58fd3c27d462693c44e8eea64bd5607382ea3f427201a12bb096bf7d2ab2/tensordict-0.14.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:6f67bf69b4aebc7a1369e6fc2f66206c8386bc8a26a43ceedff4ebcc1abca432", size = 588643, upload-time = "2026-08-14T21:27:59.327Z" }, + { url = "https://files.pythonhosted.org/packages/f3/1c/c78fdf8c2e1ac118921338619e0c1d35f0f6614fb04d7645f353f42e8bf0/tensordict-0.14.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:2f7dceb6dfac5578c130201c847c43ab0c22c0ba60dc086ddd43d69022ab69eb", size = 593998, upload-time = "2026-08-14T21:28:00.71Z" }, + { url = "https://files.pythonhosted.org/packages/90/f8/3215bd911a57bdfb947f9a537e02e511364d2b63cd91866021c6e73e596c/tensordict-0.14.0-cp313-cp313-win_amd64.whl", hash = "sha256:a828b52fbf2c90d638b20a8094f6a70d72f2a0e5a668c1ae5fcf87d9d9bee84d", size = 643671, upload-time = "2026-08-14T21:28:02.051Z" }, ] [[package]] @@ -2442,8 +1676,7 @@ name = "torch" version = "2.9.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "platform_machine != 's390x' and sys_platform == 'darwin'", - "platform_machine == 's390x' and sys_platform == 'darwin'", + "sys_platform == 'darwin'", ] dependencies = [ { name = "filelock", marker = "sys_platform == 'darwin'" }, @@ -2457,8 +1690,6 @@ dependencies = [ wheels = [ { url = "https://files.pythonhosted.org/packages/28/0e/2a37247957e72c12151b33a01e4df651d9d155dd74d8cfcbfad15a79b44a/torch-2.9.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5be4bf7496f1e3ffb1dd44b672adb1ac3f081f204c5ca81eba6442f5f634df8e", size = 74830751, upload-time = "2025-11-12T15:21:43.792Z" }, { url = "https://files.pythonhosted.org/packages/40/60/71c698b466dd01e65d0e9514b5405faae200c52a76901baf6906856f17e4/torch-2.9.1-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:2c14b3da5df416cf9cb5efab83aa3056f5b8cd8620b8fde81b4987ecab730587", size = 74480347, upload-time = "2025-11-12T15:21:57.648Z" }, - { url = "https://files.pythonhosted.org/packages/48/50/c4b5112546d0d13cc9eaa1c732b823d676a9f49ae8b6f97772f795874a03/torch-2.9.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1edee27a7c9897f4e0b7c14cfc2f3008c571921134522d5b9b5ec4ebbc69041a", size = 74433245, upload-time = "2025-11-12T15:22:39.027Z" }, - { url = "https://files.pythonhosted.org/packages/bd/b2/2d15a52516b2ea3f414643b8de68fa4cb220d3877ac8b1028c83dc8ca1c4/torch-2.9.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:cb10896a1f7fedaddbccc2017ce6ca9ecaaf990f0973bdfcf405439750118d2c", size = 74823558, upload-time = "2025-11-12T15:22:43.392Z" }, ] [[package]] @@ -2466,8 +1697,7 @@ name = "torch" version = "2.9.1+cu128" source = { registry = "https://download.pytorch.org/whl/cu128" } resolution-markers = [ - "platform_machine != 's390x' and sys_platform != 'darwin'", - "platform_machine == 's390x' and sys_platform != 'darwin'", + "sys_platform != 'darwin'", ] dependencies = [ { name = "filelock", marker = "sys_platform != 'darwin'" }, @@ -2495,23 +1725,17 @@ dependencies = [ { name = "typing-extensions", marker = "sys_platform != 'darwin'" }, ] wheels = [ - { url = "https://download.pytorch.org/whl/cu128/torch-2.9.1%2Bcu128-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:0b80b7555dcd0a75b7b06016991f01281a0bb078cf28fa2d1dfb949fad2fbd07" }, - { url = "https://download.pytorch.org/whl/cu128/torch-2.9.1%2Bcu128-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:63381a109a569b280ed3319da89d3afe5cf9ab5c879936382a212affb5c90552" }, - { url = "https://download.pytorch.org/whl/cu128/torch-2.9.1%2Bcu128-cp313-cp313-win_amd64.whl", hash = "sha256:ad9183864acdd99fc5143d7ca9d3d2e7ddfc9a9600ff43217825d4e5e9855ccc" }, - { url = "https://download.pytorch.org/whl/cu128/torch-2.9.1%2Bcu128-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:2314521c74d76e513c53bb72c0ce3511ef0295ff657a432790df6c207e5d7962" }, - { url = "https://download.pytorch.org/whl/cu128/torch-2.9.1%2Bcu128-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:4454a4faca31af81566e3a4208f10f20b8a6d9cfe42791b0ca7ff134326468fc" }, - { url = "https://download.pytorch.org/whl/cu128/torch-2.9.1%2Bcu128-cp313-cp313t-win_amd64.whl", hash = "sha256:24420e430e77136f7079354134b34e7ba9d87e539f5ac84c33b08e5c13412ebe" }, - { url = "https://download.pytorch.org/whl/cu128/torch-2.9.1%2Bcu128-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:32c036296c557f19a1537ce981c40533650097114e1720a321a39a3b08d9df56" }, - { url = "https://download.pytorch.org/whl/cu128/torch-2.9.1%2Bcu128-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:7788d3d03d939cf00f93ac0da5ab520846f66411e339cfbf519a806e8facf519" }, - { url = "https://download.pytorch.org/whl/cu128/torch-2.9.1%2Bcu128-cp314-cp314-win_amd64.whl", hash = "sha256:7bcd40cbffac475b478d6ce812f03da84e9a4894956efb89c3b7bcca5dbd4f91" }, - { url = "https://download.pytorch.org/whl/cu128/torch-2.9.1%2Bcu128-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:e88c78e5b08ae9303aa15da43b68b44287ecbec16d898d9fad6998832fe626a5" }, - { url = "https://download.pytorch.org/whl/cu128/torch-2.9.1%2Bcu128-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:7d8769bdf3200ca16a92f14df404c3370171ac3732996528a8973d753eac562f" }, - { url = "https://download.pytorch.org/whl/cu128/torch-2.9.1%2Bcu128-cp314-cp314t-win_amd64.whl", hash = "sha256:0c784b600959ec70ee01cb23e8bc870a0e0475af30378ff5e39f4abed8b7c1cc" }, + { url = "https://download-r2.pytorch.org/whl/cu128/torch-2.9.1%2Bcu128-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:0b80b7555dcd0a75b7b06016991f01281a0bb078cf28fa2d1dfb949fad2fbd07", upload-time = "2026-01-26T16:54:37Z" }, + { url = "https://download-r2.pytorch.org/whl/cu128/torch-2.9.1%2Bcu128-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:63381a109a569b280ed3319da89d3afe5cf9ab5c879936382a212affb5c90552", upload-time = "2026-01-26T16:54:52Z" }, + { url = "https://download-r2.pytorch.org/whl/cu128/torch-2.9.1%2Bcu128-cp313-cp313-win_amd64.whl", hash = "sha256:ad9183864acdd99fc5143d7ca9d3d2e7ddfc9a9600ff43217825d4e5e9855ccc", upload-time = "2026-01-26T16:55:00Z" }, + { url = "https://download-r2.pytorch.org/whl/cu128/torch-2.9.1%2Bcu128-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:2314521c74d76e513c53bb72c0ce3511ef0295ff657a432790df6c207e5d7962", upload-time = "2026-01-26T16:55:25Z" }, + { url = "https://download-r2.pytorch.org/whl/cu128/torch-2.9.1%2Bcu128-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:4454a4faca31af81566e3a4208f10f20b8a6d9cfe42791b0ca7ff134326468fc", upload-time = "2026-01-26T16:55:28Z" }, + { url = "https://download-r2.pytorch.org/whl/cu128/torch-2.9.1%2Bcu128-cp313-cp313t-win_amd64.whl", hash = "sha256:24420e430e77136f7079354134b34e7ba9d87e539f5ac84c33b08e5c13412ebe", upload-time = "2026-01-26T16:55:48Z" }, ] [[package]] name = "torchrunx" -version = "0.3.4" +version = "0.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cloudpickle" }, @@ -2519,11 +1743,10 @@ dependencies = [ { name = "numpy" }, { name = "torch", version = "2.9.1", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform == 'darwin'" }, { name = "torch", version = "2.9.1+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "sys_platform != 'darwin'" }, - { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/65/60/ce6ccaf618e56775905e75e3fe7f2c8adfb61916d2946e854850c1d19a0d/torchrunx-0.3.4.tar.gz", hash = "sha256:6f2333fa17f7ef1f43f6c65d2b008b8479b29d972a8ed209da613d830dffdc45", size = 41312, upload-time = "2025-11-19T02:36:13.54Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/80/b760a13db3e41fc2645911c30717cef05f1b200367d98f979226802ab10e/torchrunx-0.4.0.tar.gz", hash = "sha256:adcdbfd82ef633e549ba6e7694bc6eb258f55a6a64c8c59776794b922ae7b3ff", size = 41553, upload-time = "2026-08-04T07:55:09.264Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/0d/9f5e24043f2562fd4dc15d04614cb5f3b4a1ffe327cb040b9f95b03fa84d/torchrunx-0.3.4-py3-none-any.whl", hash = "sha256:a157ec139f5a0bdfaa5ece50d987ff0a3212a4657d791367f11fdd383ccdbd5b", size = 34172, upload-time = "2025-11-19T02:36:12.354Z" }, + { url = "https://files.pythonhosted.org/packages/43/81/c03ed239f02e1612f6b60525602b8d7f22c22ef332ba47d55178b1b5694e/torchrunx-0.4.0-py3-none-any.whl", hash = "sha256:a8aa42292941ddb7d68fe42a5eabe25800176a12250d417192bd7501b839e52f", size = 34276, upload-time = "2026-08-04T07:55:08.3Z" }, ] [[package]] @@ -2545,68 +1768,39 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a9/fe/ff27d2ed1b524078164bea1062f23d2618a5fc3208e247d6153c18c91a76/torchvision-0.24.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:f231f6a4f2aa6522713326d0d2563538fa72d613741ae364f9913027fa52ea35", size = 2341708, upload-time = "2025-11-12T15:25:25.08Z" }, { url = "https://files.pythonhosted.org/packages/b1/b9/d6c903495cbdfd2533b3ef6f7b5643ff589ea062f8feb5c206ee79b9d9e5/torchvision-0.24.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:1540a9e7f8cf55fe17554482f5a125a7e426347b71de07327d5de6bfd8d17caa", size = 8177239, upload-time = "2025-11-12T15:25:18.554Z" }, { url = "https://files.pythonhosted.org/packages/4f/2b/ba02e4261369c3798310483028495cf507e6cb3f394f42e4796981ecf3a7/torchvision-0.24.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d83e16d70ea85d2f196d678bfb702c36be7a655b003abed84e465988b6128938", size = 4251604, upload-time = "2025-11-12T15:25:34.069Z" }, - { url = "https://files.pythonhosted.org/packages/42/84/577b2cef8f32094add5f52887867da4c2a3e6b4261538447e9b48eb25812/torchvision-0.24.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:cccf4b4fec7fdfcd3431b9ea75d1588c0a8596d0333245dafebee0462abe3388", size = 2005319, upload-time = "2025-11-12T15:25:23.827Z" }, - { url = "https://files.pythonhosted.org/packages/5f/34/ecb786bffe0159a3b49941a61caaae089853132f3cd1e8f555e3621f7e6f/torchvision-0.24.1-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:1b495edd3a8f9911292424117544f0b4ab780452e998649425d1f4b2bed6695f", size = 2338844, upload-time = "2025-11-12T15:25:32.625Z" }, - { url = "https://files.pythonhosted.org/packages/51/99/a84623786a6969504c87f2dc3892200f586ee13503f519d282faab0bb4f0/torchvision-0.24.1-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:ab211e1807dc3e53acf8f6638df9a7444c80c0ad050466e8d652b3e83776987b", size = 8175144, upload-time = "2025-11-12T15:25:31.355Z" }, - { url = "https://files.pythonhosted.org/packages/6d/ba/8fae3525b233e109317ce6a9c1de922ab2881737b029a7e88021f81e068f/torchvision-0.24.1-cp314-cp314-win_amd64.whl", hash = "sha256:18f9cb60e64b37b551cd605a3d62c15730c086362b40682d23e24b616a697d41", size = 4234459, upload-time = "2025-11-12T15:25:19.859Z" }, - { url = "https://files.pythonhosted.org/packages/50/33/481602c1c72d0485d4b3a6b48c9534b71c2957c9d83bf860eb837bf5a620/torchvision-0.24.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ec9d7379c519428395e4ffda4dbb99ec56be64b0a75b95989e00f9ec7ae0b2d7", size = 2005336, upload-time = "2025-11-12T15:25:27.225Z" }, - { url = "https://files.pythonhosted.org/packages/d0/7f/372de60bf3dd8f5593bd0d03f4aecf0d1fd58f5bc6943618d9d913f5e6d5/torchvision-0.24.1-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:af9201184c2712d808bd4eb656899011afdfce1e83721c7cb08000034df353fe", size = 2341704, upload-time = "2025-11-12T15:25:29.857Z" }, - { url = "https://files.pythonhosted.org/packages/36/9b/0f3b9ff3d0225ee2324ec663de0e7fb3eb855615ca958ac1875f22f1f8e5/torchvision-0.24.1-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:9ef95d819fd6df81bc7cc97b8f21a15d2c0d3ac5dbfaab5cbc2d2ce57114b19e", size = 8177422, upload-time = "2025-11-12T15:25:37.357Z" }, - { url = "https://files.pythonhosted.org/packages/d6/ab/e2bcc7c2f13d882a58f8b30ff86f794210b075736587ea50f8c545834f8a/torchvision-0.24.1-cp314-cp314t-win_amd64.whl", hash = "sha256:480b271d6edff83ac2e8d69bbb4cf2073f93366516a50d48f140ccfceedb002e", size = 4335190, upload-time = "2025-11-12T15:25:35.745Z" }, ] [[package]] name = "tqdm" -version = "4.67.3" +version = "4.70.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -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" } +sdist = { url = "https://files.pythonhosted.org/packages/21/3b/6c24bec5be5e743ffd99576daa5cc077722fc7d5bbc00bd133fa0c698dc6/tqdm-4.70.0.tar.gz", hash = "sha256:55b0b0dbd97462d06ebee91e4dac24ed4d4702be82b24f07e6c1d27e08cea220", size = 795438, upload-time = "2026-07-27T11:33:15.271Z" } 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" }, + { url = "https://files.pythonhosted.org/packages/f9/1c/01bfd571a64e7f270e6bab5e33777debe0edc56759233ce84f27dec92d14/tqdm-4.70.0-py3-none-any.whl", hash = "sha256:7f585706bfddbdebf89daac705b2dfcc16890130727d3197ca62c732b4310953", size = 80184, upload-time = "2026-07-27T11:33:13.167Z" }, ] [[package]] name = "traitlets" -version = "5.14.3" +version = "5.16.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621, upload-time = "2024-04-19T11:11:49.746Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/2e/a7fbfe268c8a3b32546930c0297c101d65a4a14c304ad5790a9f478f0e4e/traitlets-5.16.1.tar.gz", hash = "sha256:ed900c2b631aa3a112811139fa97b8d2c3bad5e989656bba4b7e52c7852c18c1", size = 166137, upload-time = "2026-08-03T08:32:36.848Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359, upload-time = "2024-04-19T11:11:46.763Z" }, + { url = "https://files.pythonhosted.org/packages/ad/66/0d785f0bc5e4315a96c989bb476d0fc07ea4f85132550c7b156ca2035d52/traitlets-5.16.1-py3-none-any.whl", hash = "sha256:f775618166caa0396c8e337099240f2bd3e5e917d203b2e6fbe21a58d3cb1f6b", size = 86211, upload-time = "2026-08-03T08:32:34.48Z" }, ] [[package]] name = "trimesh" -version = "4.11.5" +version = "4.12.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0d/bf/53b69f3b6708c20ceb4d1d1250c7dc205733eb646659e5e55771f76ffabd/trimesh-4.11.5.tar.gz", hash = "sha256:b90e6cdd6ada51c52d4a7d32947f4ce44b6751c5b7cab2b04e271ecea1e397d3", size = 836449, upload-time = "2026-03-25T01:08:24.216Z" } +sdist = { url = "https://files.pythonhosted.org/packages/79/37/5cb90f04990260d2caceb6093560c6cefafca1ec522c1e43be01ca658244/trimesh-4.12.2.tar.gz", hash = "sha256:c8ca31571ac00b112e4e160e66a2d4c3491df321f056bd33806be0485d1af9d9", size = 842220, upload-time = "2026-05-01T00:57:43.333Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/24/83/72e812f772daee66651f468c7b2535fa05eac27db26df7e614cae823c832/trimesh-4.11.5-py3-none-any.whl", hash = "sha256:b225a94c8af79569f7167ca7eaaab4fd05c260da58a075599453d655835258ef", size = 740833, upload-time = "2026-03-25T01:08:21.397Z" }, -] - -[package.optional-dependencies] -easy = [ - { name = "charset-normalizer" }, - { name = "colorlog" }, - { name = "embreex", marker = "platform_machine == 'x86_64'" }, - { name = "httpx" }, - { name = "jsonschema" }, - { name = "lxml" }, - { name = "manifold3d", marker = "python_full_version < '3.14'" }, - { name = "mapbox-earcut" }, - { name = "networkx" }, - { name = "pillow" }, - { name = "pycollada" }, - { name = "rtree" }, - { name = "scipy" }, - { name = "shapely" }, - { name = "svg-path" }, - { name = "vhacdx" }, - { name = "xxhash" }, + { url = "https://files.pythonhosted.org/packages/05/98/716a473cfb24750858ddd5d14e6527539dd206583a46408d08eeb2844a75/trimesh-4.12.2-py3-none-any.whl", hash = "sha256:b5b5afa63c5272345f2858f7676bc8c217dc8a89f4fadf6193fe10a81b5ff2aa", size = 741043, upload-time = "2026-05-01T00:57:40.763Z" }, ] [[package]] @@ -2618,127 +1812,92 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/27/46/8c3bbb5b0a19313f50edcaa363b599e5a1a5ac9683ead82b9b80fe497c8d/triton-3.5.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3f4346b6ebbd4fad18773f5ba839114f4826037c9f2f34e0148894cd5dd3dba", size = 170470410, upload-time = "2025-11-11T17:41:06.319Z" }, { url = "https://files.pythonhosted.org/packages/84/1e/7df59baef41931e21159371c481c31a517ff4c2517343b62503d0cd2be99/triton-3.5.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:02c770856f5e407d24d28ddc66e33cf026e6f4d360dcb8b2fabe6ea1fc758621", size = 160072799, upload-time = "2025-11-11T17:52:07.293Z" }, { url = "https://files.pythonhosted.org/packages/37/92/e97fcc6b2c27cdb87ce5ee063d77f8f26f19f06916aa680464c8104ef0f6/triton-3.5.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0b4d2c70127fca6a23e247f9348b8adde979d2e7a20391bfbabaac6aebc7e6a8", size = 170579924, upload-time = "2025-11-11T17:41:12.455Z" }, - { url = "https://files.pythonhosted.org/packages/14/f9/0430e879c1e63a1016cb843261528fd3187c872c3a9539132efc39514753/triton-3.5.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f617aa7925f9ea9968ec2e1adaf93e87864ff51549c8f04ce658f29bbdb71e2d", size = 159956163, upload-time = "2025-11-11T17:52:12.999Z" }, - { url = "https://files.pythonhosted.org/packages/a4/e6/c595c35e5c50c4bc56a7bac96493dad321e9e29b953b526bbbe20f9911d0/triton-3.5.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0637b1efb1db599a8e9dc960d53ab6e4637db7d4ab6630a0974705d77b14b60", size = 170480488, upload-time = "2025-11-11T17:41:18.222Z" }, - { url = "https://files.pythonhosted.org/packages/41/1e/63d367c576c75919e268e4fbc33c1cb33b6dc12bb85e8bfe531c2a8bd5d3/triton-3.5.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8932391d7f93698dfe5bc9bead77c47a24f97329e9f20c10786bb230a9083f56", size = 160073620, upload-time = "2025-11-11T17:52:18.403Z" }, - { url = "https://files.pythonhosted.org/packages/16/b5/b0d3d8b901b6a04ca38df5e24c27e53afb15b93624d7fd7d658c7cd9352a/triton-3.5.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bac7f7d959ad0f48c0e97d6643a1cc0fd5786fe61cb1f83b537c6b2d54776478", size = 170582192, upload-time = "2025-11-11T17:41:23.963Z" }, ] [[package]] name = "ty" -version = "0.0.28" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/19/c2/a60543fb172ac7adaa3ae43b8db1d0dcd70aa67df254b70bf42f852a24f6/ty-0.0.28.tar.gz", hash = "sha256:1fbde7bc5d154d6f047b570d95665954fa83b75a0dce50d88cf081b40a27ea32", size = 5447781, upload-time = "2026-04-02T21:34:33.556Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/15/c2aa3d4633e6153a2e300d7dd0ebdedf904a60241d1922566f31c5f7f211/ty-0.0.28-py3-none-linux_armv6l.whl", hash = "sha256:6dbfb27524195ab1715163d7be065cc45037509fe529d9763aff6732c919f0d8", size = 10556282, upload-time = "2026-04-02T21:35:04.165Z" }, - { url = "https://files.pythonhosted.org/packages/60/9c/f6183838df89e9692235a71a69a9d4e0f12481bbdf1883f47010075793b0/ty-0.0.28-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:8c72a899ba94f7438bd07e897a84b36526b385aaf01d6f3eb6504e869232b3a6", size = 10425770, upload-time = "2026-04-02T21:34:49.144Z" }, - { url = "https://files.pythonhosted.org/packages/68/82/e9208383412f8a320537ef4c44a768d2cb6c1330d9ab33087f0b932ccd1b/ty-0.0.28-py3-none-macosx_11_0_arm64.whl", hash = "sha256:eef67f9cdfd31677bde801b611741dde779271ec6f471f818c7c6eccf515237f", size = 9899999, upload-time = "2026-04-02T21:34:40.297Z" }, - { url = "https://files.pythonhosted.org/packages/4d/26/0442f49589ba393fbd3b50751f8bb82137b036bc509762884f7b21c511d1/ty-0.0.28-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70e7b98a91d8245641be1e4b55af8bc9b1ae82ec189794d35e14e546f1e15e66", size = 10400725, upload-time = "2026-04-02T21:34:42.779Z" }, - { url = "https://files.pythonhosted.org/packages/57/d9/64128f1a7ceba72e49f35dd562533f44d4c56d0cf62efb21692377819dbc/ty-0.0.28-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9bd83d4ad9f99078b830aabb47792fac6dc39368bb0f72f3cc14607173ed6e25", size = 10387410, upload-time = "2026-04-02T21:34:46.889Z" }, - { url = "https://files.pythonhosted.org/packages/cc/52/498b6bdd1d0a985fd14ce83c31186f3b838ad79efdf68ce928f441a6962b/ty-0.0.28-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0172984fc2fcd3e47ccd5da69f36f632cddc410f9a093144a05ad07d67cf06ed", size = 10880982, upload-time = "2026-04-02T21:34:53.687Z" }, - { url = "https://files.pythonhosted.org/packages/f4/c8/fefd616f38a250b28f62ba73728cb6061715f03df0a610dce558a0fdfc0a/ty-0.0.28-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0bbf47d2bea82a09cab2ca4f48922d6c16a36608447acdc64163cd19beb28d3", size = 11459056, upload-time = "2026-04-02T21:34:31.642Z" }, - { url = "https://files.pythonhosted.org/packages/16/15/9e18d763a5ef9c6a69396876586589fd5e0fd0acba35fae8a9a169680f48/ty-0.0.28-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1774c9a0fb071607e3bdfa0ce8365488ac46809fc04ad1706562a8709a023247", size = 11156341, upload-time = "2026-04-02T21:35:01.824Z" }, - { url = "https://files.pythonhosted.org/packages/89/29/8ac0281fc44c3297f0e58699ebf993c13621e32a0fab1025439d3ea8a2f1/ty-0.0.28-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2849d6d212af78175430e8cc51a962a53851458182eb44a981b0e3981163177", size = 11006089, upload-time = "2026-04-02T21:34:38.111Z" }, - { url = "https://files.pythonhosted.org/packages/dd/de/5b5fdbe3bdb5c6f4918b33f1c55cd975b3d606057089a822439d5151bf93/ty-0.0.28-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3c576c15b867b3913c4a1d9be30ade4682303e24a576d2cc99bfd8f25ae838e9", size = 10367739, upload-time = "2026-04-02T21:34:57.679Z" }, - { url = "https://files.pythonhosted.org/packages/80/82/abdfb27ab988e6bd09502a4573f64a7e72db3e83acd7886af54448703c97/ty-0.0.28-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:2e5f13d10b3436bee3ea35851e5af400123f6693bfae48294ddfbbf553fa51ef", size = 10399528, upload-time = "2026-04-02T21:34:51.398Z" }, - { url = "https://files.pythonhosted.org/packages/ba/74/3ccbe468e8480ba53f83a1e52481d3e11756415f0ca1297fb2da65e29612/ty-0.0.28-py3-none-musllinux_1_2_i686.whl", hash = "sha256:759db467e399faedc7d5f1ca4b383dd8ecc71d7d79b2ca6ea6db4ac8e643378a", size = 10586771, upload-time = "2026-04-02T21:34:35.912Z" }, - { url = "https://files.pythonhosted.org/packages/ee/79/545c76dcef0c3f89fb733ec46118aed2a700e79d4e22cb142e3b5a80286c/ty-0.0.28-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:0cd44e3c857951cbf3f8647722ca87475614fac8ac0371eb1f200a942315a2c2", size = 11110550, upload-time = "2026-04-02T21:34:55.65Z" }, - { url = "https://files.pythonhosted.org/packages/2c/e4/e3c6f71c95a2cbabd7d88fd698b00b8af48e39aa10e0b10b839410fc3c6d/ty-0.0.28-py3-none-win32.whl", hash = "sha256:88e2c784ec5e0e2fb01b137d92fd595cdc27b98a553f4bb34b8bf138bac1be1e", size = 9985411, upload-time = "2026-04-02T21:34:44.763Z" }, - { url = "https://files.pythonhosted.org/packages/8c/e5/79dbab4856d3d15e5173314ff1846be65d58b31de6efe62ef1c25c663b32/ty-0.0.28-py3-none-win_amd64.whl", hash = "sha256:faaffbef127cb67560ad6dbc6a8f8845a4033b818bcc78ad7af923e02df199db", size = 10986548, upload-time = "2026-04-02T21:34:59.886Z" }, - { url = "https://files.pythonhosted.org/packages/01/b2/cc987aaf5babacc55caf0aeb751c83401e86e05e22ce82dace5a7e7e5354/ty-0.0.28-py3-none-win_arm64.whl", hash = "sha256:34a18ea09ee09612fb6555deccf1eed810e6f770b61a41243b494bcb7f624a1c", size = 10388573, upload-time = "2026-04-02T21:34:29.219Z" }, +version = "0.0.75" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/81/d0/d0c96f898d6974a4a3569ab3efdf9512c04ad99f9203effb55f72497fe97/ty-0.0.75.tar.gz", hash = "sha256:4c5eead33dfbf6e2ebb4f400f74b51ffc9bab702a6f23ddb648a1cbb740387e3", size = 6868326, upload-time = "2026-08-26T20:23:40.399Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/6c/b12d03505f17581f0cfa3c12273fe34c1d67b36dfda1bc561a6bdc16512b/ty-0.0.75-py3-none-linux_armv6l.whl", hash = "sha256:e5409f50db2246fd4bd039d93d261e0cfa1daa554a4fb77256f91072c570349a", size = 12972606, upload-time = "2026-08-26T20:22:59.716Z" }, + { url = "https://files.pythonhosted.org/packages/d1/aa/30f11eecd9215a9f87e8fe8baaf48f3ce905f5d75b8e4aac70f0091f130c/ty-0.0.75-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5e7b8b3472fb9bb2eeab314984b265df08a7a9d518867a9e6020eebc06570be2", size = 12527158, upload-time = "2026-08-26T20:23:02.767Z" }, + { url = "https://files.pythonhosted.org/packages/f2/11/7fd7001b0b5c6610bfbad7357e47d5fe6f82d4e84e94c53776a478f5e9f8/ty-0.0.75-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c6ccf34169821fe0d23e3360deeef981d217963412f1d087b9bdd32ec57f7a57", size = 12400533, upload-time = "2026-08-26T20:23:04.965Z" }, + { url = "https://files.pythonhosted.org/packages/fd/7f/1e284ea3d348d7be02f12d83bc22ed9ef193033f863f05b64db99027f141/ty-0.0.75-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:842ebb41e9c6c334b40768704e20b1a69d5c6b08805b289d5e0e2565f49f2de1", size = 12420592, upload-time = "2026-08-26T20:23:07.427Z" }, + { url = "https://files.pythonhosted.org/packages/2d/ab/d813271543370c47fd74b5118f2066ab32b0983e907b1821f3f9a6d0fa7f/ty-0.0.75-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cf7a5a723c5f1e0fab4ffbfe9bd95123a526ed48f206e5f25cb2161ca294007a", size = 12739219, upload-time = "2026-08-26T20:23:09.809Z" }, + { url = "https://files.pythonhosted.org/packages/31/5b/95b49cc5570fd92a7bf63732f649b31906158721e03c7fcb1b5be74ee3bf/ty-0.0.75-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54382f98e5da292fcd7104391afef5105c35bb2f312e29bea6f5fa419935255c", size = 13494046, upload-time = "2026-08-26T20:23:12.191Z" }, + { url = "https://files.pythonhosted.org/packages/2c/0d/502d2dd68173cf020e1ad2bdbab9544c86776de0b0e2ed15f8c2fe006e3d/ty-0.0.75-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac13b180dc2aade2cd243f56b01650e78bf091a2e522ad3bc947245d7837c613", size = 13938899, upload-time = "2026-08-26T20:23:14.764Z" }, + { url = "https://files.pythonhosted.org/packages/20/5b/f3b12a25c07224456219fc2bd20db0ad7e40b304be0ff6aad728da0135f9/ty-0.0.75-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:752df7951a443219d7f1ff817e3723c85d428565ff449e08a7a93ba821661526", size = 13656711, upload-time = "2026-08-26T20:23:17.145Z" }, + { url = "https://files.pythonhosted.org/packages/51/7b/f090ad306e2b15a07b332d647138c5264b89d9758855ecce8b8a10bcb153/ty-0.0.75-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fd399feedf7cee816563c1baec45fc1c0b3c89f1ea42364920b688004b5b7da", size = 13093499, upload-time = "2026-08-26T20:23:19.489Z" }, + { url = "https://files.pythonhosted.org/packages/f1/4b/f69b99aaaca0c7c65d5f114b186b26b21666f767b0c69eec99a2bdccc061/ty-0.0.75-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:d7625f6f56c7dc1e873579fdc9e432a0e21e302afe847ab60704d2303442a92e", size = 13520580, upload-time = "2026-08-26T20:23:21.789Z" }, + { url = "https://files.pythonhosted.org/packages/b6/e7/692c5f905c0345a15d2255fc74066d660f030254ae8dcdaf33f5a5c2f279/ty-0.0.75-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:89e7d527e95a2534b70cae29e94c104b84082760ea05927d23bb87280969c104", size = 12524095, upload-time = "2026-08-26T20:23:24.026Z" }, + { url = "https://files.pythonhosted.org/packages/ba/9a/f42b12cf265ea95344bf554764c4791cfb273bdd628aadd7c209af7cadc3/ty-0.0.75-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:0843f134440740706e01bee5f88f4cfc10e9b018bddb9e4ef4c12dc9fc0c9aef", size = 12756591, upload-time = "2026-08-26T20:23:26.126Z" }, + { url = "https://files.pythonhosted.org/packages/7c/5e/9b180c133cb9cce48179a7d2bf9e1802d992aa8176a918e0e05205760b42/ty-0.0.75-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1bd0ec0e50ee1376875c88891efe6f549c3560fa5b2ddad79a425cd5a6218b9c", size = 12998754, upload-time = "2026-08-26T20:23:28.353Z" }, + { url = "https://files.pythonhosted.org/packages/39/f6/3c6ef5dd550103e29905121c67fb96a374564f31a2f44c6faa1af98c2d61/ty-0.0.75-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1f9eafd561f90110d5e29f589ec3e956c4686e2f6631348d99276436f5cbe4d1", size = 13316474, upload-time = "2026-08-26T20:23:30.857Z" }, + { url = "https://files.pythonhosted.org/packages/bb/52/12776337874c821076bd5368e352ccd9e67174790abe3b856f749cb3524b/ty-0.0.75-py3-none-win32.whl", hash = "sha256:05063a6fafe2154b794a7f964515d148e51acd186d72d4a3acd347ee9fa19336", size = 12316315, upload-time = "2026-08-26T20:23:33.528Z" }, + { url = "https://files.pythonhosted.org/packages/53/e6/bb51e16af5c7138c9f52f8f3d0a401a371c6798d092e3b74926f186a9814/ty-0.0.75-py3-none-win_amd64.whl", hash = "sha256:81cf1ba5f6b7536ad56747865214255d9bc8e80533a689dbb9ddeaad464b09f1", size = 12917267, upload-time = "2026-08-26T20:23:35.978Z" }, + { url = "https://files.pythonhosted.org/packages/39/73/4542f829107468b5de4231af67f29927c093bfad11f3c1e5b2c08fb1206b/ty-0.0.75-py3-none-win_arm64.whl", hash = "sha256:541c9af5b7a0ad23d15ec315a7da81150833c359f48124ed3789ff25eacd6f42", size = 12711024, upload-time = "2026-08-26T20:23:38.159Z" }, ] [[package]] name = "typeguard" -version = "4.5.1" +version = "4.6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2b/e8/66e25efcc18542d58706ce4e50415710593721aae26e794ab1dec34fb66f/typeguard-4.5.1.tar.gz", hash = "sha256:f6f8ecbbc819c9bc749983cc67c02391e16a9b43b8b27f15dc70ed7c4a007274", size = 80121, upload-time = "2026-02-19T16:09:03.392Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b4/de/4420db493fa8fc0856d5e5c1b159c63a323d2de2317babe36b01568928e8/typeguard-4.6.0.tar.gz", hash = "sha256:e7414f09111317de3e335de92cd397c5c0ca00b1cc1676de12e1d444a79b3f21", size = 82330, upload-time = "2026-07-26T08:40:23.207Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/91/88/b55b3117287a8540b76dbdd87733808d4d01c8067a3b339408c250bb3600/typeguard-4.5.1-py3-none-any.whl", hash = "sha256:44d2bf329d49a244110a090b55f5f91aa82d9a9834ebfd30bcc73651e4a8cc40", size = 36745, upload-time = "2026-02-19T16:09:01.6Z" }, + { url = "https://files.pythonhosted.org/packages/8f/eb/461d5f167b6f5c7d97696f397c82f82e3480e003fce3f0a1cd1dd26e2eb2/typeguard-4.6.0-py3-none-any.whl", hash = "sha256:79878165bb86f2cf5d41d159a0ff1792a796cf496882d2fe1b1c6c7049b9cdd7", size = 36884, upload-time = "2026-07-26T08:40:21.868Z" }, ] [[package]] name = "typing-extensions" -version = "4.15.0" +version = "4.16.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" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" } 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" }, + { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" }, ] [[package]] name = "typing-inspection" -version = "0.4.2" +version = "0.4.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -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" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/26/b09b8010994eccc3c09092e6b34058f36a460eea2d4c3e8b910c695975a0/typing_inspection-0.4.4.tar.gz", hash = "sha256:547274fa6b0a561ccf549cc9524b999a578e737d015d8709d021f9d0d13bea47", size = 76928, upload-time = "2026-08-12T12:37:25.997Z" } 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" }, + { url = "https://files.pythonhosted.org/packages/67/81/4add07e5172b7ac40d8ed5ff580409a7801a4fe26d529bdd915401dabfbe/typing_inspection-0.4.4-py3-none-any.whl", hash = "sha256:65b8397ba37ccbce054456aaccddfc91e6e3083c92824df348d96ca832f3f147", size = 14750, upload-time = "2026-08-12T12:37:24.648Z" }, ] [[package]] name = "tyro" -version = "1.0.12" +version = "1.0.16" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "docstring-parser" }, { name = "typeguard" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/aa/3e/96397637152a9ce368a575e88e1782a9fe4640a3950e26805ec9cca569c1/tyro-1.0.12.tar.gz", hash = "sha256:b49177ab9668e0c9255025d3936b6647cc07537ca04a4ed34362099907508ba2", size = 489159, upload-time = "2026-04-02T09:48:58.294Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3c/83/4008dff806dc894fc0422017316982d3f14709046bcb0dfa720cb64501aa/tyro-1.0.16.tar.gz", hash = "sha256:edf23c9c3eecba5a2b5af152df8f34a723b689a001ff5b7311f93eff1893a636", size = 603382, upload-time = "2026-08-23T01:19:26.643Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/8e/fa21200c7fcebdbc5a1f7ebe2d437d69b0723bfaeea843c846b99248b2ba/tyro-1.0.12-py3-none-any.whl", hash = "sha256:d9c7b65e05c1b6760204bcb4d0b1ef76f235b16f1423d056a0d36814c2df902d", size = 185245, upload-time = "2026-04-02T09:48:56.808Z" }, + { url = "https://files.pythonhosted.org/packages/23/d9/ea12ca0392c9eaeff7421efcc665f350dbc2ef30a113ee13c7afea21cf37/tyro-1.0.16-py3-none-any.whl", hash = "sha256:2f9087196113699647190459a12b6b4f2784dd0703c50ab1f7bf0f85a3a794ea", size = 216925, upload-time = "2026-08-23T01:19:25.168Z" }, ] [[package]] name = "urllib3" -version = "2.6.3" +version = "2.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } +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/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, -] - -[[package]] -name = "vhacdx" -version = "0.0.10" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/15/e3/d2abc3dc4c1cb216c2efdc70b36f80efeb1bdbd7d420a676ddc9d9d980e1/vhacdx-0.0.10.tar.gz", hash = "sha256:fcc23201e319d79fe25e064847efc254bd39ac30af28cc761409e1f9142dd033", size = 58125, upload-time = "2025-12-02T20:58:45.358Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/54/c2fc08d9324bbd92735caf9207cbabada3a8dd9d270d6e46ca69eb7f883d/vhacdx-0.0.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0599bc2a96de8fc9aeff460b3e88b8572e84ae95b8fc6c9888ef4b92023c22d5", size = 225014, upload-time = "2025-12-02T20:58:00.938Z" }, - { url = "https://files.pythonhosted.org/packages/3b/9e/42adb642a12915acc9cb2bfab21710a6aabf045c26967ba0ff0e08a872d0/vhacdx-0.0.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dc648829a1e973f34ee11393a4f334ab55e3e0e9c4b9f6d6349af966fdf1895a", size = 211127, upload-time = "2025-12-02T20:58:02.107Z" }, - { url = "https://files.pythonhosted.org/packages/51/3d/63e090cd966817b89643d7e52e13df45043b22a42c7fbf702866bdd75bc0/vhacdx-0.0.10-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:74c03f7315a434ec83cd0bff1e6bce6af4c01df61d677f48f3ffb36800606ee7", size = 239471, upload-time = "2025-12-02T20:58:03.173Z" }, - { url = "https://files.pythonhosted.org/packages/b8/b4/07ab1c828bae0eb5c72cd9a4cbe8b0376d374509be3c7055e1a399bf85c3/vhacdx-0.0.10-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fcd02acc3733ec3a0a0d28ca7f526d4c56f14a3ceb4b12fce45acf72c09054a", size = 253019, upload-time = "2025-12-02T20:58:04.318Z" }, - { url = "https://files.pythonhosted.org/packages/05/cb/bc8a8858b300d2c092da11096ae0586ece446b4c41cb26620bf00d1d8232/vhacdx-0.0.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4b9f8a80ca4c54d7fa76419a2ebd9e9386cd177dc4d2b97f2208ac57c9a7e8aa", size = 1226933, upload-time = "2025-12-02T20:58:05.907Z" }, - { url = "https://files.pythonhosted.org/packages/15/52/213230883874615f1661903bce1ace5013d03b34696efce8d53c662a3358/vhacdx-0.0.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:847bd43e82afb439dd3fa972618d786d0b98d8ef04a8e8a6381f6945204d2505", size = 1288871, upload-time = "2025-12-02T20:58:07.432Z" }, - { url = "https://files.pythonhosted.org/packages/32/25/f0e6806824f88d47ab8bc1c9bf6f11634fd7b382d635d0696825f3b5672f/vhacdx-0.0.10-cp313-cp313-win_amd64.whl", hash = "sha256:ab300c5f3fe4e54f99af92f9ea27c977b09df5f59190b0a3e025161110f71ce7", size = 195091, upload-time = "2025-12-02T20:58:08.783Z" }, - { url = "https://files.pythonhosted.org/packages/b7/b8/5137c048728fddd3315a79c94ba8663f3707f9268af9af15b15e1ef3cd85/vhacdx-0.0.10-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:147030c7683be4f21a3cdfb202b121c01716694b61ddad794345fcd9fa43d0ec", size = 225247, upload-time = "2025-12-02T20:58:09.918Z" }, - { url = "https://files.pythonhosted.org/packages/1b/08/5c731863db402e9878380f68be8722fabbcaf8cfe8d06237aaf15f116d95/vhacdx-0.0.10-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:069eb4381917b790921a33d4cc6ed07f7ed5362474232110baf8dd3dadcd768d", size = 211339, upload-time = "2025-12-02T20:58:10.951Z" }, - { url = "https://files.pythonhosted.org/packages/04/3a/e93ce9b653a9f435c530df8d5ad68a80b8bdc2b8518abc225fef9e7f349a/vhacdx-0.0.10-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7702892008b1150619c1f66a62ef88d1cb8f92b09d9c39a0bfb87d147f1c5ae2", size = 239974, upload-time = "2025-12-02T20:58:12.101Z" }, - { url = "https://files.pythonhosted.org/packages/77/dc/ef34f97a65385bc1f8ed4718fa5f7d96313e299e76761f1b69efaf597797/vhacdx-0.0.10-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b4d550dfc377471b36f11065fc12cfbbd1750d63b10a336644bfdcbf27aa8382", size = 253245, upload-time = "2025-12-02T20:58:13.303Z" }, - { url = "https://files.pythonhosted.org/packages/f4/6c/57051066bd0589b7fe68c32061821180f520b6a7ef4efa072b755dde63d3/vhacdx-0.0.10-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:edce8f0ff516a111b6f1d644782a1d496b3e9e34ff4ce09849c9b8071627bca5", size = 1227432, upload-time = "2025-12-02T20:58:14.73Z" }, - { url = "https://files.pythonhosted.org/packages/1d/49/3488f2bd991027bd86f072cf776935c80b4e630bd3bc43c3289bc6eeeba0/vhacdx-0.0.10-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4c463abbdce73d5d0b94eab2c9f43f2b55a4d0e788d87af659cc78029b960bf9", size = 1289126, upload-time = "2025-12-02T20:58:16.009Z" }, - { url = "https://files.pythonhosted.org/packages/77/56/2f4506382a1133bf441cba2010017064e8f7af940d100141799d7e867e58/vhacdx-0.0.10-cp314-cp314-win_amd64.whl", hash = "sha256:b93c834f2bf1fa6630da5d3f77e94ea8e542fca31e385244a7ec905a32155549", size = 198706, upload-time = "2025-12-02T20:58:17.378Z" }, - { url = "https://files.pythonhosted.org/packages/db/f6/4fabfe65f3123abda09adc416a396caf8c2ad1b29c34a5178ec71754a163/vhacdx-0.0.10-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:4c0f1bafc53e156472b0367533c2d3ec7a96b676b6d57aa92dd3e37519331b07", size = 228276, upload-time = "2025-12-02T20:58:18.545Z" }, - { url = "https://files.pythonhosted.org/packages/dc/70/bdc742628adcf9966cea81be7a651300bc399b492d10a763781af6d27041/vhacdx-0.0.10-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b0f8643dcb1f0774320fc1389ead0d0da4536e4c0fecfd4c8133baec0b6fa556", size = 214287, upload-time = "2025-12-02T20:58:19.696Z" }, - { url = "https://files.pythonhosted.org/packages/84/6a/f2e37ad333d3f671e1d59ba76bb61edc5520146539d52ee29e555becb4ac/vhacdx-0.0.10-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4547f3e55eb935d163d89c10ffdcadf8797c3b435a9dc82be4e0e27b1e3abff0", size = 240923, upload-time = "2025-12-02T20:58:21.105Z" }, - { url = "https://files.pythonhosted.org/packages/5b/7a/f0325cd7ece95dbbc10d0c3f6d241d47beb3b99ae4dafe2e450082cd7bd9/vhacdx-0.0.10-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee09c4f2b6385546001b5e8609f428417fac147cfd3ea020fbc7dec0f11c489b", size = 254257, upload-time = "2025-12-02T20:58:22.176Z" }, - { url = "https://files.pythonhosted.org/packages/ac/56/53347b910351eb4cf32a797e177f18b8d82b1ef4e4325607254cfe88ad2a/vhacdx-0.0.10-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8b94d198e4716f9220985523f374617432ef5530910f3730051f3e7fcba71798", size = 1228434, upload-time = "2025-12-02T20:58:23.302Z" }, - { url = "https://files.pythonhosted.org/packages/be/f5/f86c63da38b0446ef6652e8e72b84451e440418eaac0f554737e159ae36e/vhacdx-0.0.10-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:39c6d31ed27e3f33e9411927e1567ba37a18ba7ce9295efd1b24414b7313b503", size = 1288854, upload-time = "2025-12-02T20:58:24.46Z" }, - { url = "https://files.pythonhosted.org/packages/0c/d1/b30dec954a24b41358297a3fbe7c30d8e2e818831f552cb34c904baa04e4/vhacdx-0.0.10-cp314-cp314t-win_amd64.whl", hash = "sha256:fc6a613082ec522a020e4f6a09f39ed42546de9aebe99548aa84938b1440871c", size = 204896, upload-time = "2025-12-02T20:58:25.825Z" }, + { 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 = "viser" -version = "1.0.26" +version = "1.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "imageio" }, @@ -2750,21 +1909,20 @@ dependencies = [ { name = "trimesh" }, { name = "typing-extensions" }, { name = "websockets" }, - { name = "yourdfpy" }, { name = "zstandard" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/12/ce/82a0e50fae21f5e02fcc5d9aff2ab59dccb9c319b6c4cf528f2228049b05/viser-1.0.26.tar.gz", hash = "sha256:dc08c6f505e70324b0603bdddf9714c00ac828c259ee49abd8ad094bfc90c91c", size = 4828261, upload-time = "2026-03-30T11:43:19.513Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/4d/e3d9bb3cb71a3f5de21b9f35fb4db09c6f873885c02e9d9ace20d221aae1/viser-1.1.0.tar.gz", hash = "sha256:28c27da73f391fdc7a1ffc3e3d122fd8eede9d15508128bc53c7e02904169412", size = 5310056, upload-time = "2026-08-16T21:15:10.628Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/91/f7/762a2d5fab509d0c632b271e21e634462397cc02cca649771c3e9d2e0bcc/viser-1.0.26-py3-none-any.whl", hash = "sha256:03b177b4ef584f58f7b74fdf44cccb165b8a220ffd90728ef5c1e3d1b1fcf258", size = 4922888, upload-time = "2026-03-30T11:43:21.355Z" }, + { url = "https://files.pythonhosted.org/packages/73/b1/37b6fefb2f2fe5f11ae305d0a428e1cdb4f5868112c42ab29c1ce1629f06/viser-1.1.0-py3-none-any.whl", hash = "sha256:d683accd18d457d3971e107d6269b5a9e2dcc93d2a8661544ac433542e4550e8", size = 5464160, upload-time = "2026-08-16T21:15:08.786Z" }, ] [[package]] name = "wandb" -version = "0.25.1" +version = "0.29.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, - { name = "gitpython" }, + { name = "opentelemetry-api" }, { name = "packaging" }, { name = "platformdirs" }, { name = "protobuf" }, @@ -2773,77 +1931,83 @@ dependencies = [ { name = "requests" }, { name = "sentry-sdk" }, { name = "typing-extensions" }, + { name = "xxhash" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/60/bb/eb579bf9abac70934a014a9d4e45346aab307994f3021d201bebe5fa25ec/wandb-0.25.1.tar.gz", hash = "sha256:b2a95cd777ecbe7499599a43158834983448a0048329bc7210ef46ca18d21994", size = 43983308, upload-time = "2026-03-10T23:51:44.227Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c8/f2/ae1d136972c93c20b87f0caa8cfcacf922d2c650272d0f5af412847955a9/wandb-0.29.0.tar.gz", hash = "sha256:44981b504427ba312e0823fa62c02ff96fb9740a83e4657e693e3e2097cde603", size = 41230058, upload-time = "2026-08-26T23:43:12.669Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/d8/873553b6818499d1b1de314067d528b892897baf0dc81fedc0e845abc2dd/wandb-0.25.1-py3-none-macosx_12_0_arm64.whl", hash = "sha256:9bb0679a3e2dcd96db9d9b6d3e17d046241d8d122974b24facb85cc93309a8c9", size = 23615900, upload-time = "2026-03-10T23:51:06.278Z" }, - { url = "https://files.pythonhosted.org/packages/71/ea/b131f319aaa5d0bf7572b6bfcff3dd89e1cf92b17eee443bbab71d12d74c/wandb-0.25.1-py3-none-macosx_12_0_x86_64.whl", hash = "sha256:0fb13ed18914027523e7b4fc20380c520e0d10da0ee452f924a13f84509fbe12", size = 25576144, upload-time = "2026-03-10T23:51:11.527Z" }, - { url = "https://files.pythonhosted.org/packages/70/5f/81508581f0bb77b0495665c1c78e77606a48e66e855ca71ba7c8ae29efa4/wandb-0.25.1-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:cc4521eb5223429ddab5e8eee9b42fdf4caabdf0bc4e0e809042720e5fbef0ed", size = 23070425, upload-time = "2026-03-10T23:51:15.71Z" }, - { url = "https://files.pythonhosted.org/packages/f2/c7/445155ef010e2e35d190797d7c36ff441e062a5b566a6da4778e22233395/wandb-0.25.1-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:e73b4c55b947edae349232d5845204d30fac88e18eb4ad1d4b96bf7cf898405a", size = 25628142, upload-time = "2026-03-10T23:51:19.326Z" }, - { url = "https://files.pythonhosted.org/packages/d5/63/f5c55ee00cf481ef1ccd3c385a0585ad52e7840d08419d4f82ddbeeea959/wandb-0.25.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:22b84065aa398e1624d2e5ad79e08bc4d2af41a6db61697b03b3aaba332977c6", size = 23123172, upload-time = "2026-03-10T23:51:23.418Z" }, - { url = "https://files.pythonhosted.org/packages/3e/d9/19eb7974c0e9253bcbaee655222c0f0e1a52e63e9479ee711b4208f8ac31/wandb-0.25.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:005c4c6b5126ef8f4b4110e5372d950918b00637d6dc4b615ad17445f9739478", size = 25714479, upload-time = "2026-03-10T23:51:27.421Z" }, - { url = "https://files.pythonhosted.org/packages/11/19/466c1d03323a4a0ed7d4036a59b18d6b6f67cb5032e444205927e226b18d/wandb-0.25.1-py3-none-win32.whl", hash = "sha256:8f2d04f16b88d65bfba9d79fb945f6c64e2686215469a841936e0972be8ec6a5", size = 24967338, upload-time = "2026-03-10T23:51:31.833Z" }, - { url = "https://files.pythonhosted.org/packages/89/22/680d34c1587f3a979c701b66d71aa7c42b4ef2fdf0774f67034e618e834e/wandb-0.25.1-py3-none-win_amd64.whl", hash = "sha256:62db5166de14456156d7a85953a58733a631228e6d4248a753605f75f75fb845", size = 24967343, upload-time = "2026-03-10T23:51:36.026Z" }, - { url = "https://files.pythonhosted.org/packages/c4/e8/76836b75d401ff5912aaf513176e64557ceaec4c4946bfd38a698ff84d48/wandb-0.25.1-py3-none-win_arm64.whl", hash = "sha256:cc7c34b70cf4b7be4d395541e82e325fd9d2be978d62c9ec01f1a7141523b6bb", size = 22080774, upload-time = "2026-03-10T23:51:40.196Z" }, + { url = "https://files.pythonhosted.org/packages/f0/ff/b1ca84260ff441652389188510b8ad8fab0e978b936aa8b7fb7557808d4a/wandb-0.29.0-py3-none-macosx_13_0_arm64.whl", hash = "sha256:c9efa966c3bf7b7a80bf461e4856546f0592cbb6a47e6f292db4d8e7270dab73", size = 26899829, upload-time = "2026-08-26T23:42:52.271Z" }, + { url = "https://files.pythonhosted.org/packages/e4/9c/fb0799f91830441e65616902c3c633f5f72166732e57c137dc6c2625179e/wandb-0.29.0-py3-none-macosx_13_0_x86_64.whl", hash = "sha256:2adae9c36a6d992c767c2248c175a1963db4771e650acdf3da471314c33d3d41", size = 28892707, upload-time = "2026-08-26T23:42:55.37Z" }, + { url = "https://files.pythonhosted.org/packages/5b/15/7270dd7ae17dd914f25794088b6d9583bc989626be472153770ca6272cf0/wandb-0.29.0-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:095b17df32ead3b459069b460665cea1d1781c63ad649be042894bc8e4c17b0b", size = 27547745, upload-time = "2026-08-26T23:42:57.795Z" }, + { url = "https://files.pythonhosted.org/packages/32/fc/d9a9978acffb34ae2a2e5065f421a6e62f3ad6e27ad41229e86025b56fe3/wandb-0.29.0-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:fdfd8845539302d2e3a880bc3d2586c2fd2da9515939cbc69e173d88f9e8c024", size = 29847397, upload-time = "2026-08-26T23:43:00.218Z" }, + { url = "https://files.pythonhosted.org/packages/dc/21/a4034ec06ed5a72cb85e3f1db013b1a8366b91ffd629bc807eb900527c73/wandb-0.29.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:217236c7347cd9c9c9e0332d5cf6d7342ca47d9d81faf2bcd1674d72988870c3", size = 27583732, upload-time = "2026-08-26T23:43:02.643Z" }, + { url = "https://files.pythonhosted.org/packages/c0/9a/6da88bb45ea28c83eeb889473f84d0d505ed5f15be40726f125621fa7467/wandb-0.29.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:2bdb72ba809192a1a170830bea1071804d762769448f3ceb822b41160b9f6098", size = 30074151, upload-time = "2026-08-26T23:43:05.026Z" }, + { url = "https://files.pythonhosted.org/packages/c7/f9/96746f484690cd23fbb84c6fd0364e8c462e804dede9efe50a98b4f27ddd/wandb-0.29.0-py3-none-win_amd64.whl", hash = "sha256:92dca672417404d2cb882fe34f38d602ddcb54ece38716f7c76630e06fc62360", size = 27040796, upload-time = "2026-08-26T23:43:07.444Z" }, + { url = "https://files.pythonhosted.org/packages/94/74/e11e2ad886ac35bb5d568308f279357868de233e0dfaec11b9ddeed53164/wandb-0.29.0-py3-none-win_arm64.whl", hash = "sha256:ac523a1cd863f1a883852146f451ec9b950b0d2278d6e3bbedd099c713d14047", size = 24663195, upload-time = "2026-08-26T23:43:09.896Z" }, ] [[package]] name = "warp-lang" -version = "1.12.0" +version = "1.16.0" source = { registry = "https://pypi.nvidia.com/" } +resolution-markers = [ + "sys_platform != 'darwin'", +] dependencies = [ - { name = "numpy" }, + { name = "numpy", marker = "sys_platform != 'darwin'" }, +] +wheels = [ + { url = "https://pypi.nvidia.com/warp-lang/warp_lang-1.16.0-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:96449fc1e3b354185e2f09434fb794b5953ab2e8673b104d7e7f48d5d418bb35" }, + { url = "https://pypi.nvidia.com/warp-lang/warp_lang-1.16.0-py3-none-manylinux_2_34_aarch64.whl", hash = "sha256:d4715171bd6821436b82293d774c9a082ace08215c189572b4348d1e48fb8ee8" }, + { url = "https://pypi.nvidia.com/warp-lang/warp_lang-1.16.0-py3-none-win_amd64.whl", hash = "sha256:8690c9096e0a271985339d4aa4b37675e7d62852620ca861ac032dc85b124542" }, +] + +[[package]] +name = "warp-lang" +version = "1.16.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "sys_platform == 'darwin'", +] +dependencies = [ + { name = "numpy", marker = "sys_platform == 'darwin'" }, ] wheels = [ - { url = "https://pypi.nvidia.com/warp-lang/warp_lang-1.12.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c78c3701d5cad86c30ef5017410d294ec46a396bb0d502ee1c98743494f3a62f" }, - { url = "https://pypi.nvidia.com/warp-lang/warp_lang-1.12.0-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:a1436f60a1881cd94f787e751a83fc0987626be2d3e2b4e74c64a6947c6d1266" }, - { url = "https://pypi.nvidia.com/warp-lang/warp_lang-1.12.0-py3-none-manylinux_2_34_aarch64.whl", hash = "sha256:a2d6decba693aba5b828573c4414fd6a3f4c4a934db9c322736ef2b3fa99fe76" }, - { url = "https://pypi.nvidia.com/warp-lang/warp_lang-1.12.0-py3-none-win_amd64.whl", hash = "sha256:697248edd2f1e2952f50e3db33b214af76173641a8894aacc467bed6dc247f8a" }, + { url = "https://files.pythonhosted.org/packages/73/8a/8be711f3b5431a6e0bdb97117d681b03cf2bb95d6fcf8869861900898d88/warp_lang-1.16.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:19a7590c4484e8250ab19165311d2a1774138663b361c41e8be2865c7515c4ae", size = 25221601, upload-time = "2026-08-03T02:26:38.389Z" }, ] [[package]] name = "wcwidth" -version = "0.6.0" +version = "0.8.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/35/a2/8e3becb46433538a38726c948d3399905a4c7cabd0df578ede5dc51f0ec2/wcwidth-0.6.0.tar.gz", hash = "sha256:cdc4e4262d6ef9a1a57e018384cbeb1208d8abbc64176027e2c2455c81313159", size = 159684, upload-time = "2026-02-06T19:19:40.919Z" } +sdist = { url = "https://files.pythonhosted.org/packages/34/74/c6428f875774288bec1396f5bfcbc2d925700a4dad61727fd5f2b12f249d/wcwidth-0.8.2.tar.gz", hash = "sha256:91fbef97204b96a3d4d421609b80340b760cf33e26da123ff243d76b1fda8dda", size = 1466253, upload-time = "2026-06-29T18:11:11.601Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl", hash = "sha256:1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad", size = 94189, upload-time = "2026-02-06T19:19:39.646Z" }, + { url = "https://files.pythonhosted.org/packages/96/42/3e5985a0a7e57de470b320c6d6a1a67c844f6737a587f3d44dd13d1819e7/wcwidth-0.8.2-py3-none-any.whl", hash = "sha256:d63947694a0539a1d51e01eda7caf800c291020e6cdd7e28ad7b14dd33ad4f85", size = 323166, upload-time = "2026-06-29T18:11:09.888Z" }, ] [[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/cc/9c/baa8456050d1c1b08dd0ec7346026668cbc6f145ab4e314d707bb845bf0d/websockets-16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:878b336ac47938b474c8f982ac2f7266a540adc3fa4ad74ae96fea9823a02cc9", size = 177364, upload-time = "2026-01-10T09:22:59.333Z" }, - { url = "https://files.pythonhosted.org/packages/7e/0c/8811fc53e9bcff68fe7de2bcbe75116a8d959ac699a3200f4847a8925210/websockets-16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52a0fec0e6c8d9a784c2c78276a48a2bdf099e4ccc2a4cad53b27718dbfd0230", size = 175039, upload-time = "2026-01-10T09:23:01.171Z" }, - { url = "https://files.pythonhosted.org/packages/aa/82/39a5f910cb99ec0b59e482971238c845af9220d3ab9fa76dd9162cda9d62/websockets-16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e6578ed5b6981005df1860a56e3617f14a6c307e6a71b4fff8c48fdc50f3ed2c", size = 175323, upload-time = "2026-01-10T09:23:02.341Z" }, - { url = "https://files.pythonhosted.org/packages/bd/28/0a25ee5342eb5d5f297d992a77e56892ecb65e7854c7898fb7d35e9b33bd/websockets-16.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95724e638f0f9c350bb1c2b0a7ad0e83d9cc0c9259f3ea94e40d7b02a2179ae5", size = 184975, upload-time = "2026-01-10T09:23:03.756Z" }, - { url = "https://files.pythonhosted.org/packages/f9/66/27ea52741752f5107c2e41fda05e8395a682a1e11c4e592a809a90c6a506/websockets-16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0204dc62a89dc9d50d682412c10b3542d748260d743500a85c13cd1ee4bde82", size = 186203, upload-time = "2026-01-10T09:23:05.01Z" }, - { url = "https://files.pythonhosted.org/packages/37/e5/8e32857371406a757816a2b471939d51c463509be73fa538216ea52b792a/websockets-16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:52ac480f44d32970d66763115edea932f1c5b1312de36df06d6b219f6741eed8", size = 185653, upload-time = "2026-01-10T09:23:06.301Z" }, - { url = "https://files.pythonhosted.org/packages/9b/67/f926bac29882894669368dc73f4da900fcdf47955d0a0185d60103df5737/websockets-16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6e5a82b677f8f6f59e8dfc34ec06ca6b5b48bc4fcda346acd093694cc2c24d8f", size = 184920, upload-time = "2026-01-10T09:23:07.492Z" }, - { url = "https://files.pythonhosted.org/packages/3c/a1/3d6ccdcd125b0a42a311bcd15a7f705d688f73b2a22d8cf1c0875d35d34a/websockets-16.0-cp313-cp313-win32.whl", hash = "sha256:abf050a199613f64c886ea10f38b47770a65154dc37181bfaff70c160f45315a", size = 178255, upload-time = "2026-01-10T09:23:09.245Z" }, - { url = "https://files.pythonhosted.org/packages/6b/ae/90366304d7c2ce80f9b826096a9e9048b4bb760e44d3b873bb272cba696b/websockets-16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3425ac5cf448801335d6fdc7ae1eb22072055417a96cc6b31b3861f455fbc156", size = 178689, upload-time = "2026-01-10T09:23:10.483Z" }, - { url = "https://files.pythonhosted.org/packages/f3/1d/e88022630271f5bd349ed82417136281931e558d628dd52c4d8621b4a0b2/websockets-16.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8cc451a50f2aee53042ac52d2d053d08bf89bcb31ae799cb4487587661c038a0", size = 177406, upload-time = "2026-01-10T09:23:12.178Z" }, - { url = "https://files.pythonhosted.org/packages/f2/78/e63be1bf0724eeb4616efb1ae1c9044f7c3953b7957799abb5915bffd38e/websockets-16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:daa3b6ff70a9241cf6c7fc9e949d41232d9d7d26fd3522b1ad2b4d62487e9904", size = 175085, upload-time = "2026-01-10T09:23:13.511Z" }, - { url = "https://files.pythonhosted.org/packages/bb/f4/d3c9220d818ee955ae390cf319a7c7a467beceb24f05ee7aaaa2414345ba/websockets-16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fd3cb4adb94a2a6e2b7c0d8d05cb94e6f1c81a0cf9dc2694fb65c7e8d94c42e4", size = 175328, upload-time = "2026-01-10T09:23:14.727Z" }, - { url = "https://files.pythonhosted.org/packages/63/bc/d3e208028de777087e6fb2b122051a6ff7bbcca0d6df9d9c2bf1dd869ae9/websockets-16.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:781caf5e8eee67f663126490c2f96f40906594cb86b408a703630f95550a8c3e", size = 185044, upload-time = "2026-01-10T09:23:15.939Z" }, - { url = "https://files.pythonhosted.org/packages/ad/6e/9a0927ac24bd33a0a9af834d89e0abc7cfd8e13bed17a86407a66773cc0e/websockets-16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:caab51a72c51973ca21fa8a18bd8165e1a0183f1ac7066a182ff27107b71e1a4", size = 186279, upload-time = "2026-01-10T09:23:17.148Z" }, - { url = "https://files.pythonhosted.org/packages/b9/ca/bf1c68440d7a868180e11be653c85959502efd3a709323230314fda6e0b3/websockets-16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19c4dc84098e523fd63711e563077d39e90ec6702aff4b5d9e344a60cb3c0cb1", size = 185711, upload-time = "2026-01-10T09:23:18.372Z" }, - { url = "https://files.pythonhosted.org/packages/c4/f8/fdc34643a989561f217bb477cbc47a3a07212cbda91c0e4389c43c296ebf/websockets-16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a5e18a238a2b2249c9a9235466b90e96ae4795672598a58772dd806edc7ac6d3", size = 184982, upload-time = "2026-01-10T09:23:19.652Z" }, - { url = "https://files.pythonhosted.org/packages/dd/d1/574fa27e233764dbac9c52730d63fcf2823b16f0856b3329fc6268d6ae4f/websockets-16.0-cp314-cp314-win32.whl", hash = "sha256:a069d734c4a043182729edd3e9f247c3b2a4035415a9172fd0f1b71658a320a8", size = 177915, upload-time = "2026-01-10T09:23:21.458Z" }, - { url = "https://files.pythonhosted.org/packages/8a/f1/ae6b937bf3126b5134ce1f482365fde31a357c784ac51852978768b5eff4/websockets-16.0-cp314-cp314-win_amd64.whl", hash = "sha256:c0ee0e63f23914732c6d7e0cce24915c48f3f1512ec1d079ed01fc629dab269d", size = 178381, upload-time = "2026-01-10T09:23:22.715Z" }, - { url = "https://files.pythonhosted.org/packages/06/9b/f791d1db48403e1f0a27577a6beb37afae94254a8c6f08be4a23e4930bc0/websockets-16.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:a35539cacc3febb22b8f4d4a99cc79b104226a756aa7400adc722e83b0d03244", size = 177737, upload-time = "2026-01-10T09:23:24.523Z" }, - { url = "https://files.pythonhosted.org/packages/bd/40/53ad02341fa33b3ce489023f635367a4ac98b73570102ad2cdd770dacc9a/websockets-16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b784ca5de850f4ce93ec85d3269d24d4c82f22b7212023c974c401d4980ebc5e", size = 175268, upload-time = "2026-01-10T09:23:25.781Z" }, - { url = "https://files.pythonhosted.org/packages/74/9b/6158d4e459b984f949dcbbb0c5d270154c7618e11c01029b9bbd1bb4c4f9/websockets-16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:569d01a4e7fba956c5ae4fc988f0d4e187900f5497ce46339c996dbf24f17641", size = 175486, upload-time = "2026-01-10T09:23:27.033Z" }, - { url = "https://files.pythonhosted.org/packages/e5/2d/7583b30208b639c8090206f95073646c2c9ffd66f44df967981a64f849ad/websockets-16.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:50f23cdd8343b984957e4077839841146f67a3d31ab0d00e6b824e74c5b2f6e8", size = 185331, upload-time = "2026-01-10T09:23:28.259Z" }, - { url = "https://files.pythonhosted.org/packages/45/b0/cce3784eb519b7b5ad680d14b9673a31ab8dcb7aad8b64d81709d2430aa8/websockets-16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:152284a83a00c59b759697b7f9e9cddf4e3c7861dd0d964b472b70f78f89e80e", size = 186501, upload-time = "2026-01-10T09:23:29.449Z" }, - { url = "https://files.pythonhosted.org/packages/19/60/b8ebe4c7e89fb5f6cdf080623c9d92789a53636950f7abacfc33fe2b3135/websockets-16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc59589ab64b0022385f429b94697348a6a234e8ce22544e3681b2e9331b5944", size = 186062, upload-time = "2026-01-10T09:23:31.368Z" }, - { url = "https://files.pythonhosted.org/packages/88/a8/a080593f89b0138b6cba1b28f8df5673b5506f72879322288b031337c0b8/websockets-16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:32da954ffa2814258030e5a57bc73a3635463238e797c7375dc8091327434206", size = 185356, upload-time = "2026-01-10T09:23:32.627Z" }, - { url = "https://files.pythonhosted.org/packages/c2/b6/b9afed2afadddaf5ebb2afa801abf4b0868f42f8539bfe4b071b5266c9fe/websockets-16.0-cp314-cp314t-win32.whl", hash = "sha256:5a4b4cc550cb665dd8a47f868c8d04c8230f857363ad3c9caf7a0c3bf8c61ca6", size = 178085, upload-time = "2026-01-10T09:23:33.816Z" }, - { url = "https://files.pythonhosted.org/packages/9f/3e/28135a24e384493fa804216b79a6a6759a38cc4ff59118787b9fb693df93/websockets-16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b14dc141ed6d2dde437cddb216004bcac6a1df0935d79656387bd41632ba0bbd", size = 178531, upload-time = "2026-01-10T09:23:35.016Z" }, - { 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" }, +version = "16.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/f7/bc3a25c5ec26ce62ce487690becc2f3710bbc7b33338f005ad390db0b986/websockets-16.1.1.tar.gz", hash = "sha256:db234eda965dcce15df96bb9709f587cd87d4d52aaf0e80e2f34ec04c7670c57", size = 182204, upload-time = "2026-07-17T22:51:05.858Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/fd/6ec6c6d2850aea25b1b2aa9901a016980bb87d01e89b3eb00470b1b5d471/websockets-16.1.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ab59169ace05dcb49a1d4118f0bde139557adf45091bd85747e36bf5de984dd1", size = 179587, upload-time = "2026-07-17T22:49:38.959Z" }, + { url = "https://files.pythonhosted.org/packages/5f/d8/1d299d2dd34087db39831a34cc645ef8a6f89d78efada6983093513cd81c/websockets-16.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5e3b7d601f6f84156b08cc4a5e541c2b50ad7b36cfc302b657a12477c904a5df", size = 177272, upload-time = "2026-07-17T22:49:40.293Z" }, + { url = "https://files.pythonhosted.org/packages/3d/86/0a70d3ae2f0f2256bb41302d9804dbca65d4360281e7feb3e1f94102ac46/websockets-16.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cd2ca96a082a36964aca83e992f72abeb61b7306c1a6cba4c7d06a7b93750cac", size = 177530, upload-time = "2026-07-17T22:49:41.786Z" }, + { url = "https://files.pythonhosted.org/packages/b5/c2/c676c69444d9db448b3f0a55a98dcc534affce0bce961d9d2f0b8499b10a/websockets-16.1.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f5d497865f05bb222cab7016c6034542e84e5f29f49c6fd3f4939cda7197b5b8", size = 187197, upload-time = "2026-07-17T22:49:43.658Z" }, + { url = "https://files.pythonhosted.org/packages/0b/13/88137fbaf726ebe29d62c1117fa11fa2bbb6209dc79d4ad738efbe36a2aa/websockets-16.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bae954c382e013d5ea5b190d2830526bfa45ad121c326da0049b8c769f185db6", size = 188433, upload-time = "2026-07-17T22:49:45.147Z" }, + { url = "https://files.pythonhosted.org/packages/01/6d/46c2f2ce6751cb26f39293e1ecbf8544cb01321397cd476c2756b98c216d/websockets-16.1.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e09f753a169951eb4f28c2c774f71069304f66e7277e0f5a2892423599cfa854", size = 189868, upload-time = "2026-07-17T22:49:46.581Z" }, + { url = "https://files.pythonhosted.org/packages/29/2b/170a9e8097636cfde4dc3c592b6e00b18a44a2f5407606d96ca542dd5838/websockets-16.1.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:024193f8551a2b0eafbdd160911012c4e6c228c28430c84433253299a9e42d6a", size = 189059, upload-time = "2026-07-17T22:49:47.972Z" }, + { url = "https://files.pythonhosted.org/packages/a7/48/f0d4ebc9ab4b473b8861b9e20fdb663d515d42f7befdf62cdb60fee7a1ec/websockets-16.1.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:aabe464bfd13bd25f4821faf111da6fefdc389f870265a53105580e45b0a2e49", size = 187814, upload-time = "2026-07-17T22:49:49.344Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ba/39a41d3ae8e72696a9492581900611c5a91e2b07563b0bcd2523adea9854/websockets-16.1.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a28fcbc9b6baf54a2e23f8655f308e4ccc6afdd7266f8fe7954f320dcda0f785", size = 185229, upload-time = "2026-07-17T22:49:50.787Z" }, + { url = "https://files.pythonhosted.org/packages/3c/36/ac15b604f850d1907f0a85ed721cefe47cd45034b3620069b829746cccbe/websockets-16.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:79eace538c6a97e96d0d03d4f9d314f9677f5ed85a8a984992ffd90b13cb8a56", size = 187874, upload-time = "2026-07-17T22:49:52.228Z" }, + { url = "https://files.pythonhosted.org/packages/a8/f3/3fbd5d71d59299c3770faa5884d4f45070236ca5a35ab3a61830812c409a/websockets-16.1.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:496af849a472b531f758dbd4d61338f5000538cb1a7b3d20d9d32a264517f509", size = 186469, upload-time = "2026-07-17T22:49:53.776Z" }, + { url = "https://files.pythonhosted.org/packages/b4/fc/dd90349bba58af2a53ef2ddd9c32716c81eb6d59a0687939fff561860878/websockets-16.1.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5283810d2646741a0d8da2aa733d6aefa0545809afccb2a5d105a26bc45125f1", size = 188347, upload-time = "2026-07-17T22:49:55.202Z" }, + { url = "https://files.pythonhosted.org/packages/4c/f3/f73ba86427682da59b78c11d77ba56d5b801c32e84afe79b274bbd6a9bb2/websockets-16.1.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:4e3b680b1e0a27457e727a0d572fd81dffa87b6dbf8b228ab57da64f7d85aead", size = 185903, upload-time = "2026-07-17T22:49:56.75Z" }, + { url = "https://files.pythonhosted.org/packages/34/7c/f95eb20e80104173b3a0a092291f89ea4047ef6e608e0a57ca06eb14eecb/websockets-16.1.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:69159730a823dde3ea8d08783e8d47ef135a6d7e8d44eb127e32b321c9db8e3e", size = 186855, upload-time = "2026-07-17T22:49:58.467Z" }, + { url = "https://files.pythonhosted.org/packages/b0/35/dd875b3e050ff232d60fa377707f890e369f74d134f1be32e8f68879747c/websockets-16.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ed5bb271084b46530ee2ddc0410537a9961152c5ccba2fc98c5276d992ccba87", size = 187140, upload-time = "2026-07-17T22:50:00.016Z" }, + { url = "https://files.pythonhosted.org/packages/e8/dc/5cbfcb41824502f6af93b8f3943a4d06c67c23c7d2e31eb18748c4a5b2a7/websockets-16.1.1-cp313-cp313-win32.whl", hash = "sha256:cfb70b4eb56cac4da0a83588f3ad50d46beb0690391082f3d4e2d488c70b68ea", size = 179928, upload-time = "2026-07-17T22:50:01.685Z" }, + { url = "https://files.pythonhosted.org/packages/b0/c1/71e5deb5b7f8f226997ab64908c184ac3105c0155ce2d486f318e5dd08a8/websockets-16.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:d9531d9cbeac99af6f038fb1bc351403531f7d634a2c2e10e2f7c854c6ed5b68", size = 180242, upload-time = "2026-07-17T22:50:03.117Z" }, + { url = "https://files.pythonhosted.org/packages/be/4d/2d0d67834092e354d2b0498f014a41249a89556bc406cf86f3e1557bb463/websockets-16.1.1-py3-none-any.whl", hash = "sha256:6abbd3e82c731c8e531714466acd5d87b5e88ac3243465337ba71d68e23ae7e3", size = 173814, upload-time = "2026-07-17T22:51:04.184Z" }, ] [[package]] @@ -2860,147 +2024,75 @@ wheels = [ [[package]] name = "wrapt" -version = "2.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2e/64/925f213fdcbb9baeb1530449ac71a4d57fc361c053d06bf78d0c5c7cd80c/wrapt-2.1.2.tar.gz", hash = "sha256:3996a67eecc2c68fd47b4e3c564405a5777367adfd9b8abb58387b63ee83b21e", size = 81678, upload-time = "2026-03-06T02:53:25.134Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4c/7a/d936840735c828b38d26a854e85d5338894cda544cb7a85a9d5b8b9c4df7/wrapt-2.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:787fd6f4d67befa6fe2abdffcbd3de2d82dfc6fb8a6d850407c53332709d030b", size = 61259, upload-time = "2026-03-06T02:53:41.922Z" }, - { url = "https://files.pythonhosted.org/packages/5e/88/9a9b9a90ac8ca11c2fdb6a286cb3a1fc7dd774c00ed70929a6434f6bc634/wrapt-2.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4bdf26e03e6d0da3f0e9422fd36bcebf7bc0eeb55fdf9c727a09abc6b9fe472e", size = 61851, upload-time = "2026-03-06T02:52:48.672Z" }, - { url = "https://files.pythonhosted.org/packages/03/a9/5b7d6a16fd6533fed2756900fc8fc923f678179aea62ada6d65c92718c00/wrapt-2.1.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bbac24d879aa22998e87f6b3f481a5216311e7d53c7db87f189a7a0266dafffb", size = 121446, upload-time = "2026-03-06T02:54:14.013Z" }, - { url = "https://files.pythonhosted.org/packages/45/bb/34c443690c847835cfe9f892be78c533d4f32366ad2888972c094a897e39/wrapt-2.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:16997dfb9d67addc2e3f41b62a104341e80cac52f91110dece393923c0ebd5ca", size = 123056, upload-time = "2026-03-06T02:54:10.829Z" }, - { url = "https://files.pythonhosted.org/packages/93/b9/ff205f391cb708f67f41ea148545f2b53ff543a7ac293b30d178af4d2271/wrapt-2.1.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:162e4e2ba7542da9027821cb6e7c5e068d64f9a10b5f15512ea28e954893a267", size = 117359, upload-time = "2026-03-06T02:53:03.623Z" }, - { url = "https://files.pythonhosted.org/packages/1f/3d/1ea04d7747825119c3c9a5e0874a40b33594ada92e5649347c457d982805/wrapt-2.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f29c827a8d9936ac320746747a016c4bc66ef639f5cd0d32df24f5eacbf9c69f", size = 121479, upload-time = "2026-03-06T02:53:45.844Z" }, - { url = "https://files.pythonhosted.org/packages/78/cc/ee3a011920c7a023b25e8df26f306b2484a531ab84ca5c96260a73de76c0/wrapt-2.1.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:a9dd9813825f7ecb018c17fd147a01845eb330254dff86d3b5816f20f4d6aaf8", size = 116271, upload-time = "2026-03-06T02:54:46.356Z" }, - { url = "https://files.pythonhosted.org/packages/98/fd/e5ff7ded41b76d802cf1191288473e850d24ba2e39a6ec540f21ae3b57cb/wrapt-2.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6f8dbdd3719e534860d6a78526aafc220e0241f981367018c2875178cf83a413", size = 120573, upload-time = "2026-03-06T02:52:50.163Z" }, - { url = "https://files.pythonhosted.org/packages/47/c5/242cae3b5b080cd09bacef0591691ba1879739050cc7c801ff35c8886b66/wrapt-2.1.2-cp313-cp313-win32.whl", hash = "sha256:5c35b5d82b16a3bc6e0a04349b606a0582bc29f573786aebe98e0c159bc48db6", size = 58205, upload-time = "2026-03-06T02:53:47.494Z" }, - { url = "https://files.pythonhosted.org/packages/12/69/c358c61e7a50f290958809b3c61ebe8b3838ea3e070d7aac9814f95a0528/wrapt-2.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:f8bc1c264d8d1cf5b3560a87bbdd31131573eb25f9f9447bb6252b8d4c44a3a1", size = 60452, upload-time = "2026-03-06T02:53:30.038Z" }, - { url = "https://files.pythonhosted.org/packages/8e/66/c8a6fcfe321295fd8c0ab1bd685b5a01462a9b3aa2f597254462fc2bc975/wrapt-2.1.2-cp313-cp313-win_arm64.whl", hash = "sha256:3beb22f674550d5634642c645aba4c72a2c66fb185ae1aebe1e955fae5a13baf", size = 58842, upload-time = "2026-03-06T02:52:52.114Z" }, - { url = "https://files.pythonhosted.org/packages/da/55/9c7052c349106e0b3f17ae8db4b23a691a963c334de7f9dbd60f8f74a831/wrapt-2.1.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0fc04bc8664a8bc4c8e00b37b5355cffca2535209fba1abb09ae2b7c76ddf82b", size = 63075, upload-time = "2026-03-06T02:53:19.108Z" }, - { url = "https://files.pythonhosted.org/packages/09/a8/ce7b4006f7218248dd71b7b2b732d0710845a0e49213b18faef64811ffef/wrapt-2.1.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a9b9d50c9af998875a1482a038eb05755dfd6fe303a313f6a940bb53a83c3f18", size = 63719, upload-time = "2026-03-06T02:54:33.452Z" }, - { url = "https://files.pythonhosted.org/packages/e4/e5/2ca472e80b9e2b7a17f106bb8f9df1db11e62101652ce210f66935c6af67/wrapt-2.1.2-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2d3ff4f0024dd224290c0eabf0240f1bfc1f26363431505fb1b0283d3b08f11d", size = 152643, upload-time = "2026-03-06T02:52:42.721Z" }, - { url = "https://files.pythonhosted.org/packages/36/42/30f0f2cefca9d9cbf6835f544d825064570203c3e70aa873d8ae12e23791/wrapt-2.1.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3278c471f4468ad544a691b31bb856374fbdefb7fee1a152153e64019379f015", size = 158805, upload-time = "2026-03-06T02:54:25.441Z" }, - { url = "https://files.pythonhosted.org/packages/bb/67/d08672f801f604889dcf58f1a0b424fe3808860ede9e03affc1876b295af/wrapt-2.1.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a8914c754d3134a3032601c6984db1c576e6abaf3fc68094bb8ab1379d75ff92", size = 145990, upload-time = "2026-03-06T02:53:57.456Z" }, - { url = "https://files.pythonhosted.org/packages/68/a7/fd371b02e73babec1de6ade596e8cd9691051058cfdadbfd62a5898f3295/wrapt-2.1.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ff95d4264e55839be37bafe1536db2ab2de19da6b65f9244f01f332b5286cfbf", size = 155670, upload-time = "2026-03-06T02:54:55.309Z" }, - { url = "https://files.pythonhosted.org/packages/86/2d/9fe0095dfdb621009f40117dcebf41d7396c2c22dca6eac779f4c007b86c/wrapt-2.1.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:76405518ca4e1b76fbb1b9f686cff93aebae03920cc55ceeec48ff9f719c5f67", size = 144357, upload-time = "2026-03-06T02:54:24.092Z" }, - { url = "https://files.pythonhosted.org/packages/0e/b6/ec7b4a254abbe4cde9fa15c5d2cca4518f6b07d0f1b77d4ee9655e30280e/wrapt-2.1.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c0be8b5a74c5824e9359b53e7e58bef71a729bacc82e16587db1c4ebc91f7c5a", size = 150269, upload-time = "2026-03-06T02:53:31.268Z" }, - { url = "https://files.pythonhosted.org/packages/6e/6b/2fabe8ebf148f4ee3c782aae86a795cc68ffe7d432ef550f234025ce0cfa/wrapt-2.1.2-cp313-cp313t-win32.whl", hash = "sha256:f01277d9a5fc1862f26f7626da9cf443bebc0abd2f303f41c5e995b15887dabd", size = 59894, upload-time = "2026-03-06T02:54:15.391Z" }, - { url = "https://files.pythonhosted.org/packages/ca/fb/9ba66fc2dedc936de5f8073c0217b5d4484e966d87723415cc8262c5d9c2/wrapt-2.1.2-cp313-cp313t-win_amd64.whl", hash = "sha256:84ce8f1c2104d2f6daa912b1b5b039f331febfeee74f8042ad4e04992bd95c8f", size = 63197, upload-time = "2026-03-06T02:54:41.943Z" }, - { url = "https://files.pythonhosted.org/packages/c0/1c/012d7423c95d0e337117723eb8ecf73c622ce15a97847e84cf3f8f26cd7e/wrapt-2.1.2-cp313-cp313t-win_arm64.whl", hash = "sha256:a93cd767e37faeddbe07d8fc4212d5cba660af59bdb0f6372c93faaa13e6e679", size = 60363, upload-time = "2026-03-06T02:54:48.093Z" }, - { url = "https://files.pythonhosted.org/packages/39/25/e7ea0b417db02bb796182a5316398a75792cd9a22528783d868755e1f669/wrapt-2.1.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1370e516598854e5b4366e09ce81e08bfe94d42b0fd569b88ec46cc56d9164a9", size = 61418, upload-time = "2026-03-06T02:53:55.706Z" }, - { url = "https://files.pythonhosted.org/packages/ec/0f/fa539e2f6a770249907757eaeb9a5ff4deb41c026f8466c1c6d799088a9b/wrapt-2.1.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6de1a3851c27e0bd6a04ca993ea6f80fc53e6c742ee1601f486c08e9f9b900a9", size = 61914, upload-time = "2026-03-06T02:52:53.37Z" }, - { url = "https://files.pythonhosted.org/packages/53/37/02af1867f5b1441aaeda9c82deed061b7cd1372572ddcd717f6df90b5e93/wrapt-2.1.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:de9f1a2bbc5ac7f6012ec24525bdd444765a2ff64b5985ac6e0692144838542e", size = 120417, upload-time = "2026-03-06T02:54:30.74Z" }, - { url = "https://files.pythonhosted.org/packages/c3/b7/0138a6238c8ba7476c77cf786a807f871672b37f37a422970342308276e7/wrapt-2.1.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:970d57ed83fa040d8b20c52fe74a6ae7e3775ae8cff5efd6a81e06b19078484c", size = 122797, upload-time = "2026-03-06T02:54:51.539Z" }, - { url = "https://files.pythonhosted.org/packages/e1/ad/819ae558036d6a15b7ed290d5b14e209ca795dd4da9c58e50c067d5927b0/wrapt-2.1.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3969c56e4563c375861c8df14fa55146e81ac11c8db49ea6fb7f2ba58bc1ff9a", size = 117350, upload-time = "2026-03-06T02:54:37.651Z" }, - { url = "https://files.pythonhosted.org/packages/8b/2d/afc18dc57a4600a6e594f77a9ae09db54f55ba455440a54886694a84c71b/wrapt-2.1.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:57d7c0c980abdc5f1d98b11a2aa3bb159790add80258c717fa49a99921456d90", size = 121223, upload-time = "2026-03-06T02:54:35.221Z" }, - { url = "https://files.pythonhosted.org/packages/b9/5b/5ec189b22205697bc56eb3b62aed87a1e0423e9c8285d0781c7a83170d15/wrapt-2.1.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:776867878e83130c7a04237010463372e877c1c994d449ca6aaafeab6aab2586", size = 116287, upload-time = "2026-03-06T02:54:19.654Z" }, - { url = "https://files.pythonhosted.org/packages/f7/2d/f84939a7c9b5e6cdd8a8d0f6a26cabf36a0f7e468b967720e8b0cd2bdf69/wrapt-2.1.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:fab036efe5464ec3291411fabb80a7a39e2dd80bae9bcbeeca5087fdfa891e19", size = 119593, upload-time = "2026-03-06T02:54:16.697Z" }, - { url = "https://files.pythonhosted.org/packages/0b/fe/ccd22a1263159c4ac811ab9374c061bcb4a702773f6e06e38de5f81a1bdc/wrapt-2.1.2-cp314-cp314-win32.whl", hash = "sha256:e6ed62c82ddf58d001096ae84ce7f833db97ae2263bff31c9b336ba8cfe3f508", size = 58631, upload-time = "2026-03-06T02:53:06.498Z" }, - { url = "https://files.pythonhosted.org/packages/65/0a/6bd83be7bff2e7efaac7b4ac9748da9d75a34634bbbbc8ad077d527146df/wrapt-2.1.2-cp314-cp314-win_amd64.whl", hash = "sha256:467e7c76315390331c67073073d00662015bb730c566820c9ca9b54e4d67fd04", size = 60875, upload-time = "2026-03-06T02:53:50.252Z" }, - { url = "https://files.pythonhosted.org/packages/6c/c0/0b3056397fe02ff80e5a5d72d627c11eb885d1ca78e71b1a5c1e8c7d45de/wrapt-2.1.2-cp314-cp314-win_arm64.whl", hash = "sha256:da1f00a557c66225d53b095a97eace0fc5349e3bfda28fa34ffae238978ee575", size = 59164, upload-time = "2026-03-06T02:53:59.128Z" }, - { url = "https://files.pythonhosted.org/packages/71/ed/5d89c798741993b2371396eb9d4634f009ff1ad8a6c78d366fe2883ea7a6/wrapt-2.1.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:62503ffbc2d3a69891cf29beeaccdb4d5e0a126e2b6a851688d4777e01428dbb", size = 63163, upload-time = "2026-03-06T02:52:54.873Z" }, - { url = "https://files.pythonhosted.org/packages/c6/8c/05d277d182bf36b0a13d6bd393ed1dec3468a25b59d01fba2dd70fe4d6ae/wrapt-2.1.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c7e6cd120ef837d5b6f860a6ea3745f8763805c418bb2f12eeb1fa6e25f22d22", size = 63723, upload-time = "2026-03-06T02:52:56.374Z" }, - { url = "https://files.pythonhosted.org/packages/f4/27/6c51ec1eff4413c57e72d6106bb8dec6f0c7cdba6503d78f0fa98767bcc9/wrapt-2.1.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3769a77df8e756d65fbc050333f423c01ae012b4f6731aaf70cf2bef61b34596", size = 152652, upload-time = "2026-03-06T02:53:23.79Z" }, - { url = "https://files.pythonhosted.org/packages/db/4c/d7dd662d6963fc7335bfe29d512b02b71cdfa23eeca7ab3ac74a67505deb/wrapt-2.1.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a76d61a2e851996150ba0f80582dd92a870643fa481f3b3846f229de88caf044", size = 158807, upload-time = "2026-03-06T02:53:35.742Z" }, - { url = "https://files.pythonhosted.org/packages/b4/4d/1e5eea1a78d539d346765727422976676615814029522c76b87a95f6bcdd/wrapt-2.1.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6f97edc9842cf215312b75fe737ee7c8adda75a89979f8e11558dfff6343cc4b", size = 146061, upload-time = "2026-03-06T02:52:57.574Z" }, - { url = "https://files.pythonhosted.org/packages/89/bc/62cabea7695cd12a288023251eeefdcb8465056ddaab6227cb78a2de005b/wrapt-2.1.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4006c351de6d5007aa33a551f600404ba44228a89e833d2fadc5caa5de8edfbf", size = 155667, upload-time = "2026-03-06T02:53:39.422Z" }, - { url = "https://files.pythonhosted.org/packages/e9/99/6f2888cd68588f24df3a76572c69c2de28287acb9e1972bf0c83ce97dbc1/wrapt-2.1.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:a9372fc3639a878c8e7d87e1556fa209091b0a66e912c611e3f833e2c4202be2", size = 144392, upload-time = "2026-03-06T02:54:22.41Z" }, - { url = "https://files.pythonhosted.org/packages/40/51/1dfc783a6c57971614c48e361a82ca3b6da9055879952587bc99fe1a7171/wrapt-2.1.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3144b027ff30cbd2fca07c0a87e67011adb717eb5f5bd8496325c17e454257a3", size = 150296, upload-time = "2026-03-06T02:54:07.848Z" }, - { url = "https://files.pythonhosted.org/packages/6c/38/cbb8b933a0201076c1f64fc42883b0023002bdc14a4964219154e6ff3350/wrapt-2.1.2-cp314-cp314t-win32.whl", hash = "sha256:3b8d15e52e195813efe5db8cec156eebe339aaf84222f4f4f051a6c01f237ed7", size = 60539, upload-time = "2026-03-06T02:54:00.594Z" }, - { url = "https://files.pythonhosted.org/packages/82/dd/e5176e4b241c9f528402cebb238a36785a628179d7d8b71091154b3e4c9e/wrapt-2.1.2-cp314-cp314t-win_amd64.whl", hash = "sha256:08ffa54146a7559f5b8df4b289b46d963a8e74ed16ba3687f99896101a3990c5", size = 63969, upload-time = "2026-03-06T02:54:39Z" }, - { url = "https://files.pythonhosted.org/packages/5c/99/79f17046cf67e4a95b9987ea129632ba8bcec0bc81f3fb3d19bdb0bd60cd/wrapt-2.1.2-cp314-cp314t-win_arm64.whl", hash = "sha256:72aaa9d0d8e4ed0e2e98019cea47a21f823c9dd4b43c7b77bba6679ffcca6a00", size = 60554, upload-time = "2026-03-06T02:53:14.132Z" }, - { url = "https://files.pythonhosted.org/packages/1a/c7/8528ac2dfa2c1e6708f647df7ae144ead13f0a31146f43c7264b4942bf12/wrapt-2.1.2-py3-none-any.whl", hash = "sha256:b8fd6fa2b2c4e7621808f8c62e8317f4aae56e59721ad933bac5239d913cf0e8", size = 43993, upload-time = "2026-03-06T02:53:12.905Z" }, +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2b/b0/c1f5a970721f06b85c0cd5142e0ff8fe067708abd779b0c4f4be7d61d09f/wrapt-2.3.0.tar.gz", hash = "sha256:681a2d0eefd721998f90642762b8e75c2159ec531b20ad5e437245ea7b06a107", size = 131509, upload-time = "2026-07-28T06:06:14.895Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/6e/0f88a072483e76b881e3fdcd6b6ffb4a5791002514fe541e72b1b73c859a/wrapt-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0d3fb71e65b001adfc42684522eeccd9c21d8ba679945abc993439567b66e59f", size = 81960, upload-time = "2026-07-28T06:04:49.622Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ff/b7e2776e7c294075eb712cc9ef573d1b818f393006d09787262b8fc871c4/wrapt-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:51a7a4181c1295774812271fbcd7c909df372bc25579d4ed9eb875caaf0ae86f", size = 82435, upload-time = "2026-07-28T06:04:50.9Z" }, + { url = "https://files.pythonhosted.org/packages/d8/90/343bb5d0f1f9669bc252a6073f085b4abf862511bd5c9c9eaec754341f1d/wrapt-2.3.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9045917809c63fdf7abe3a2ceaed3d670b8ee4500ddd9291192d30aeb34467c5", size = 170350, upload-time = "2026-07-28T06:04:52.187Z" }, + { url = "https://files.pythonhosted.org/packages/59/f8/13b79a392930bd0dd6b86cbfbfe1c40944110456e1dc6d809e5c46ece904/wrapt-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:54ca1d5573f69b5fe1d74f1f65799c68015e82f685efec9fd8cfa40a094c44d0", size = 170022, upload-time = "2026-07-28T06:04:53.599Z" }, + { url = "https://files.pythonhosted.org/packages/b2/fc/4f1b6918f5290db959d6e0c07f77385d87cede29c39c9cf8f145e9c82954/wrapt-2.3.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:242b60c21e30866e6a2fa606c612b47c553fa60c0eaeeeb7797fb842ac0ce609", size = 161043, upload-time = "2026-07-28T06:04:54.936Z" }, + { url = "https://files.pythonhosted.org/packages/01/e1/45d3cf74414780bdff6d0380467e003f6eb0f028b6c9403db868dbc7209c/wrapt-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3f3d7ec0a51fbfe00d3aef047641ff2c58b25565b4717fc1f90e050be01cba8", size = 168576, upload-time = "2026-07-28T06:04:56.261Z" }, + { url = "https://files.pythonhosted.org/packages/f3/73/2fa58dd97f191c997755e2c6d569a68f0c433db4e4b36099bdd7227b6cac/wrapt-2.3.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:261f53870cd4fb2bf38f9f972c56c728fd224cb7c65721307de59d9e7e6741ae", size = 159140, upload-time = "2026-07-28T06:04:57.754Z" }, + { url = "https://files.pythonhosted.org/packages/29/a8/08a56e2000a8816d449dcbad8c8b081697acbbd490821ceca0f9d8e8d20c/wrapt-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8159ec0b0cb7608175eb150de94c19e34f4d47ac655f5ca9baf45df6b688ffd3", size = 169263, upload-time = "2026-07-28T06:04:59.161Z" }, + { url = "https://files.pythonhosted.org/packages/9e/d4/354e1725e35a73b2af4fa70a3e024c7a5d1bf1802dfb862dcb668aae0253/wrapt-2.3.0-cp313-cp313-win32.whl", hash = "sha256:10461884b3014fbfc8eb7d09a93c5f246363e6711d9d881f95eb8c27fdef049f", size = 78241, upload-time = "2026-07-28T06:05:00.507Z" }, + { url = "https://files.pythonhosted.org/packages/6c/7e/34c87fa2174848dfee820322aaa318bab08913998ccecc8d2f57b4ad4639/wrapt-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:ac870cc97b73bb00ac353329e9559a4bebc47c4c86792ed9b23b58c15b6ad838", size = 81113, upload-time = "2026-07-28T06:05:01.839Z" }, + { url = "https://files.pythonhosted.org/packages/11/86/fcc9a530579e008c9478bb565a6cdfbfd33536660f069c8b91a6607c5050/wrapt-2.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:a65e8db2b4e90c2e7ade931086351c98ef420bf7a94ee08c95ac8a3cbbc43579", size = 80182, upload-time = "2026-07-28T06:05:03.152Z" }, + { url = "https://files.pythonhosted.org/packages/96/50/3864848b95b28ef73e17551fc8dccbff2628a834f52cf26a57f9c419fb83/wrapt-2.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:fd1f2f557dd3491fe75905e578f4db967393d40d1a8f468edc4d40ac7f2d5944", size = 83921, upload-time = "2026-07-28T06:05:04.476Z" }, + { url = "https://files.pythonhosted.org/packages/3b/4c/3d1921a60c3e8c71c540ff136e6a47a1fbccf7f671e818394889f7871d9c/wrapt-2.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:9f5d2aec29dfc76c37e23897dee92766a3fd4f3bff3ae7fc9c6b4bf37d8c1360", size = 84412, upload-time = "2026-07-28T06:05:05.921Z" }, + { url = "https://files.pythonhosted.org/packages/fa/1a/4a796ff7adb26ada6d4b758c94d47a38320b085e7099afc088efbbcdb006/wrapt-2.3.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:646d20d413ffcd1b0a2f700076e2d0252d872dcb7754860a73e45a59ea883614", size = 207168, upload-time = "2026-07-28T06:05:07.256Z" }, + { url = "https://files.pythonhosted.org/packages/1d/3e/d7777776806c579b761bac2f91721dda9f04c7a1b380213c5935cc750ae6/wrapt-2.3.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:379f670f45b7bb8993edd9f6fc36c6cc65edb81cffa0b504be34acb0303fff0a", size = 214351, upload-time = "2026-07-28T06:05:08.945Z" }, + { url = "https://files.pythonhosted.org/packages/63/27/2d64d394df7bf181955b3bb562bf33c4492fb4be113f53071106d43ad8b5/wrapt-2.3.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6208f302f110295d64b22a7ac96500c791bf492dce4366e622e4912b077c9687", size = 199020, upload-time = "2026-07-28T06:05:10.418Z" }, + { url = "https://files.pythonhosted.org/packages/3e/3d/fb31d3db7d9834d265fb1a27a2adf0ddf51557c67458c97b22439ad6ae3d/wrapt-2.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ed635a9ca4f3a5a2b900c10c69e823373bc00ebc114b459383596d3487da3570", size = 209969, upload-time = "2026-07-28T06:05:11.983Z" }, + { url = "https://files.pythonhosted.org/packages/1f/d1/8724b5da582e62070dc9bf4d8bf1972f317297eefd7ba1f2b5c6393ccf6c/wrapt-2.3.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:e3b9eaa742ae7a0aaaaad4ca4b69469d757af2d6e6663ef1dadc47adec0aeb41", size = 196324, upload-time = "2026-07-28T06:05:13.557Z" }, + { url = "https://files.pythonhosted.org/packages/0d/5c/3d9ef411149543016ee6bcf3af707f787cebd946527452b94bf122e9b7b4/wrapt-2.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d0f7284f88f4833705132d06d3b425a43095c2cbd07c58166aac3ab646ba12a4", size = 202610, upload-time = "2026-07-28T06:05:15.048Z" }, + { url = "https://files.pythonhosted.org/packages/13/9b/4fc042ceb757866dd4a5fc057b3b736f2b360d3703ce9f830d83dc9226e0/wrapt-2.3.0-cp313-cp313t-win32.whl", hash = "sha256:7ebb274aba688b043429eb1500ff8a76ce0cb8ac0812ca3e301f06247b8722b3", size = 79178, upload-time = "2026-07-28T06:05:16.469Z" }, + { url = "https://files.pythonhosted.org/packages/6b/ff/b94878f8eed809ca042685276bcea9f24e8c2ca7c9653bb80bbb920a68a5/wrapt-2.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:c4bded758ad6f03b965830944a2f0bc5b2eb3767fe5a7310134315d1a6610e98", size = 82634, upload-time = "2026-07-28T06:05:18.026Z" }, + { url = "https://files.pythonhosted.org/packages/80/fb/663e1de5332a71685a729754312d327d4cada767c36e1c5a2db4c8de49e6/wrapt-2.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:d2cc64539da63e39ffb9c7ede849b6e8ddaaf7b3876b5cfb04efd85a5f3f4eb6", size = 81387, upload-time = "2026-07-28T06:05:19.417Z" }, + { url = "https://files.pythonhosted.org/packages/00/39/3daf9f47be208606586de4568ba6713db53ebc8fd7a575aea1fe57983b69/wrapt-2.3.0-py3-none-any.whl", hash = "sha256:d8c7ed08477429752b8c44991f40ad7838b18332a160698740a6bfbc10d998a2", size = 61866, upload-time = "2026-07-28T06:06:12.9Z" }, ] [[package]] name = "xxhash" -version = "3.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/02/84/30869e01909fb37a6cc7e18688ee8bf1e42d57e7e0777636bd47524c43c7/xxhash-3.6.0.tar.gz", hash = "sha256:f0162a78b13a0d7617b2845b90c763339d1f1d82bb04a4b07f4ab535cc5e05d6", size = 85160, upload-time = "2025-10-02T14:37:08.097Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/33/76/35d05267ac82f53ae9b0e554da7c5e281ee61f3cad44c743f0fcd354f211/xxhash-3.6.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:599e64ba7f67472481ceb6ee80fa3bd828fd61ba59fb11475572cc5ee52b89ec", size = 32738, upload-time = "2025-10-02T14:34:55.839Z" }, - { url = "https://files.pythonhosted.org/packages/31/a8/3fbce1cd96534a95e35d5120637bf29b0d7f5d8fa2f6374e31b4156dd419/xxhash-3.6.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7d8b8aaa30fca4f16f0c84a5c8d7ddee0e25250ec2796c973775373257dde8f1", size = 30821, upload-time = "2025-10-02T14:34:57.219Z" }, - { url = "https://files.pythonhosted.org/packages/0c/ea/d387530ca7ecfa183cb358027f1833297c6ac6098223fd14f9782cd0015c/xxhash-3.6.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d597acf8506d6e7101a4a44a5e428977a51c0fadbbfd3c39650cca9253f6e5a6", size = 194127, upload-time = "2025-10-02T14:34:59.21Z" }, - { url = "https://files.pythonhosted.org/packages/ba/0c/71435dcb99874b09a43b8d7c54071e600a7481e42b3e3ce1eb5226a5711a/xxhash-3.6.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:858dc935963a33bc33490128edc1c12b0c14d9c7ebaa4e387a7869ecc4f3e263", size = 212975, upload-time = "2025-10-02T14:35:00.816Z" }, - { url = "https://files.pythonhosted.org/packages/84/7a/c2b3d071e4bb4a90b7057228a99b10d51744878f4a8a6dd643c8bd897620/xxhash-3.6.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba284920194615cb8edf73bf52236ce2e1664ccd4a38fdb543506413529cc546", size = 212241, upload-time = "2025-10-02T14:35:02.207Z" }, - { url = "https://files.pythonhosted.org/packages/81/5f/640b6eac0128e215f177df99eadcd0f1b7c42c274ab6a394a05059694c5a/xxhash-3.6.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4b54219177f6c6674d5378bd862c6aedf64725f70dd29c472eaae154df1a2e89", size = 445471, upload-time = "2025-10-02T14:35:03.61Z" }, - { url = "https://files.pythonhosted.org/packages/5e/1e/3c3d3ef071b051cc3abbe3721ffb8365033a172613c04af2da89d5548a87/xxhash-3.6.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:42c36dd7dbad2f5238950c377fcbf6811b1cdb1c444fab447960030cea60504d", size = 193936, upload-time = "2025-10-02T14:35:05.013Z" }, - { url = "https://files.pythonhosted.org/packages/2c/bd/4a5f68381939219abfe1c22a9e3a5854a4f6f6f3c4983a87d255f21f2e5d/xxhash-3.6.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f22927652cba98c44639ffdc7aaf35828dccf679b10b31c4ad72a5b530a18eb7", size = 210440, upload-time = "2025-10-02T14:35:06.239Z" }, - { url = "https://files.pythonhosted.org/packages/eb/37/b80fe3d5cfb9faff01a02121a0f4d565eb7237e9e5fc66e73017e74dcd36/xxhash-3.6.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b45fad44d9c5c119e9c6fbf2e1c656a46dc68e280275007bbfd3d572b21426db", size = 197990, upload-time = "2025-10-02T14:35:07.735Z" }, - { url = "https://files.pythonhosted.org/packages/d7/fd/2c0a00c97b9e18f72e1f240ad4e8f8a90fd9d408289ba9c7c495ed7dc05c/xxhash-3.6.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:6f2580ffab1a8b68ef2b901cde7e55fa8da5e4be0977c68f78fc80f3c143de42", size = 210689, upload-time = "2025-10-02T14:35:09.438Z" }, - { url = "https://files.pythonhosted.org/packages/93/86/5dd8076a926b9a95db3206aba20d89a7fc14dd5aac16e5c4de4b56033140/xxhash-3.6.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:40c391dd3cd041ebc3ffe6f2c862f402e306eb571422e0aa918d8070ba31da11", size = 414068, upload-time = "2025-10-02T14:35:11.162Z" }, - { url = "https://files.pythonhosted.org/packages/af/3c/0bb129170ee8f3650f08e993baee550a09593462a5cddd8e44d0011102b1/xxhash-3.6.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f205badabde7aafd1a31e8ca2a3e5a763107a71c397c4481d6a804eb5063d8bd", size = 191495, upload-time = "2025-10-02T14:35:12.971Z" }, - { url = "https://files.pythonhosted.org/packages/e9/3a/6797e0114c21d1725e2577508e24006fd7ff1d8c0c502d3b52e45c1771d8/xxhash-3.6.0-cp313-cp313-win32.whl", hash = "sha256:2577b276e060b73b73a53042ea5bd5203d3e6347ce0d09f98500f418a9fcf799", size = 30620, upload-time = "2025-10-02T14:35:14.129Z" }, - { url = "https://files.pythonhosted.org/packages/86/15/9bc32671e9a38b413a76d24722a2bf8784a132c043063a8f5152d390b0f9/xxhash-3.6.0-cp313-cp313-win_amd64.whl", hash = "sha256:757320d45d2fbcce8f30c42a6b2f47862967aea7bf458b9625b4bbe7ee390392", size = 31542, upload-time = "2025-10-02T14:35:15.21Z" }, - { url = "https://files.pythonhosted.org/packages/39/c5/cc01e4f6188656e56112d6a8e0dfe298a16934b8c47a247236549a3f7695/xxhash-3.6.0-cp313-cp313-win_arm64.whl", hash = "sha256:457b8f85dec5825eed7b69c11ae86834a018b8e3df5e77783c999663da2f96d6", size = 27880, upload-time = "2025-10-02T14:35:16.315Z" }, - { url = "https://files.pythonhosted.org/packages/f3/30/25e5321c8732759e930c555176d37e24ab84365482d257c3b16362235212/xxhash-3.6.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a42e633d75cdad6d625434e3468126c73f13f7584545a9cf34e883aa1710e702", size = 32956, upload-time = "2025-10-02T14:35:17.413Z" }, - { url = "https://files.pythonhosted.org/packages/9f/3c/0573299560d7d9f8ab1838f1efc021a280b5ae5ae2e849034ef3dee18810/xxhash-3.6.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:568a6d743219e717b07b4e03b0a828ce593833e498c3b64752e0f5df6bfe84db", size = 31072, upload-time = "2025-10-02T14:35:18.844Z" }, - { url = "https://files.pythonhosted.org/packages/7a/1c/52d83a06e417cd9d4137722693424885cc9878249beb3a7c829e74bf7ce9/xxhash-3.6.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bec91b562d8012dae276af8025a55811b875baace6af510412a5e58e3121bc54", size = 196409, upload-time = "2025-10-02T14:35:20.31Z" }, - { url = "https://files.pythonhosted.org/packages/e3/8e/c6d158d12a79bbd0b878f8355432075fc82759e356ab5a111463422a239b/xxhash-3.6.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:78e7f2f4c521c30ad5e786fdd6bae89d47a32672a80195467b5de0480aa97b1f", size = 215736, upload-time = "2025-10-02T14:35:21.616Z" }, - { url = "https://files.pythonhosted.org/packages/bc/68/c4c80614716345d55071a396cf03d06e34b5f4917a467faf43083c995155/xxhash-3.6.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3ed0df1b11a79856df5ffcab572cbd6b9627034c1c748c5566fa79df9048a7c5", size = 214833, upload-time = "2025-10-02T14:35:23.32Z" }, - { url = "https://files.pythonhosted.org/packages/7e/e9/ae27c8ffec8b953efa84c7c4a6c6802c263d587b9fc0d6e7cea64e08c3af/xxhash-3.6.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0e4edbfc7d420925b0dd5e792478ed393d6e75ff8fc219a6546fb446b6a417b1", size = 448348, upload-time = "2025-10-02T14:35:25.111Z" }, - { url = "https://files.pythonhosted.org/packages/d7/6b/33e21afb1b5b3f46b74b6bd1913639066af218d704cc0941404ca717fc57/xxhash-3.6.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fba27a198363a7ef87f8c0f6b171ec36b674fe9053742c58dd7e3201c1ab30ee", size = 196070, upload-time = "2025-10-02T14:35:26.586Z" }, - { url = "https://files.pythonhosted.org/packages/96/b6/fcabd337bc5fa624e7203aa0fa7d0c49eed22f72e93229431752bddc83d9/xxhash-3.6.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:794fe9145fe60191c6532fa95063765529770edcdd67b3d537793e8004cabbfd", size = 212907, upload-time = "2025-10-02T14:35:28.087Z" }, - { url = "https://files.pythonhosted.org/packages/4b/d3/9ee6160e644d660fcf176c5825e61411c7f62648728f69c79ba237250143/xxhash-3.6.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:6105ef7e62b5ac73a837778efc331a591d8442f8ef5c7e102376506cb4ae2729", size = 200839, upload-time = "2025-10-02T14:35:29.857Z" }, - { url = "https://files.pythonhosted.org/packages/0d/98/e8de5baa5109394baf5118f5e72ab21a86387c4f89b0e77ef3e2f6b0327b/xxhash-3.6.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:f01375c0e55395b814a679b3eea205db7919ac2af213f4a6682e01220e5fe292", size = 213304, upload-time = "2025-10-02T14:35:31.222Z" }, - { url = "https://files.pythonhosted.org/packages/7b/1d/71056535dec5c3177eeb53e38e3d367dd1d16e024e63b1cee208d572a033/xxhash-3.6.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:d706dca2d24d834a4661619dcacf51a75c16d65985718d6a7d73c1eeeb903ddf", size = 416930, upload-time = "2025-10-02T14:35:32.517Z" }, - { url = "https://files.pythonhosted.org/packages/dc/6c/5cbde9de2cd967c322e651c65c543700b19e7ae3e0aae8ece3469bf9683d/xxhash-3.6.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5f059d9faeacd49c0215d66f4056e1326c80503f51a1532ca336a385edadd033", size = 193787, upload-time = "2025-10-02T14:35:33.827Z" }, - { url = "https://files.pythonhosted.org/packages/19/fa/0172e350361d61febcea941b0cc541d6e6c8d65d153e85f850a7b256ff8a/xxhash-3.6.0-cp313-cp313t-win32.whl", hash = "sha256:1244460adc3a9be84731d72b8e80625788e5815b68da3da8b83f78115a40a7ec", size = 30916, upload-time = "2025-10-02T14:35:35.107Z" }, - { url = "https://files.pythonhosted.org/packages/ad/e6/e8cf858a2b19d6d45820f072eff1bea413910592ff17157cabc5f1227a16/xxhash-3.6.0-cp313-cp313t-win_amd64.whl", hash = "sha256:b1e420ef35c503869c4064f4a2f2b08ad6431ab7b229a05cce39d74268bca6b8", size = 31799, upload-time = "2025-10-02T14:35:36.165Z" }, - { url = "https://files.pythonhosted.org/packages/56/15/064b197e855bfb7b343210e82490ae672f8bc7cdf3ddb02e92f64304ee8a/xxhash-3.6.0-cp313-cp313t-win_arm64.whl", hash = "sha256:ec44b73a4220623235f67a996c862049f375df3b1052d9899f40a6382c32d746", size = 28044, upload-time = "2025-10-02T14:35:37.195Z" }, - { url = "https://files.pythonhosted.org/packages/7e/5e/0138bc4484ea9b897864d59fce9be9086030825bc778b76cb5a33a906d37/xxhash-3.6.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:a40a3d35b204b7cc7643cbcf8c9976d818cb47befcfac8bbefec8038ac363f3e", size = 32754, upload-time = "2025-10-02T14:35:38.245Z" }, - { url = "https://files.pythonhosted.org/packages/18/d7/5dac2eb2ec75fd771957a13e5dda560efb2176d5203f39502a5fc571f899/xxhash-3.6.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a54844be970d3fc22630b32d515e79a90d0a3ddb2644d8d7402e3c4c8da61405", size = 30846, upload-time = "2025-10-02T14:35:39.6Z" }, - { url = "https://files.pythonhosted.org/packages/fe/71/8bc5be2bb00deb5682e92e8da955ebe5fa982da13a69da5a40a4c8db12fb/xxhash-3.6.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:016e9190af8f0a4e3741343777710e3d5717427f175adfdc3e72508f59e2a7f3", size = 194343, upload-time = "2025-10-02T14:35:40.69Z" }, - { url = "https://files.pythonhosted.org/packages/e7/3b/52badfb2aecec2c377ddf1ae75f55db3ba2d321c5e164f14461c90837ef3/xxhash-3.6.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4f6f72232f849eb9d0141e2ebe2677ece15adfd0fa599bc058aad83c714bb2c6", size = 213074, upload-time = "2025-10-02T14:35:42.29Z" }, - { url = "https://files.pythonhosted.org/packages/a2/2b/ae46b4e9b92e537fa30d03dbc19cdae57ed407e9c26d163895e968e3de85/xxhash-3.6.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:63275a8aba7865e44b1813d2177e0f5ea7eadad3dd063a21f7cf9afdc7054063", size = 212388, upload-time = "2025-10-02T14:35:43.929Z" }, - { url = "https://files.pythonhosted.org/packages/f5/80/49f88d3afc724b4ac7fbd664c8452d6db51b49915be48c6982659e0e7942/xxhash-3.6.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cd01fa2aa00d8b017c97eb46b9a794fbdca53fc14f845f5a328c71254b0abb7", size = 445614, upload-time = "2025-10-02T14:35:45.216Z" }, - { url = "https://files.pythonhosted.org/packages/ed/ba/603ce3961e339413543d8cd44f21f2c80e2a7c5cfe692a7b1f2cccf58f3c/xxhash-3.6.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0226aa89035b62b6a86d3c68df4d7c1f47a342b8683da2b60cedcddb46c4d95b", size = 194024, upload-time = "2025-10-02T14:35:46.959Z" }, - { url = "https://files.pythonhosted.org/packages/78/d1/8e225ff7113bf81545cfdcd79eef124a7b7064a0bba53605ff39590b95c2/xxhash-3.6.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c6e193e9f56e4ca4923c61238cdaced324f0feac782544eb4c6d55ad5cc99ddd", size = 210541, upload-time = "2025-10-02T14:35:48.301Z" }, - { url = "https://files.pythonhosted.org/packages/6f/58/0f89d149f0bad89def1a8dd38feb50ccdeb643d9797ec84707091d4cb494/xxhash-3.6.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9176dcaddf4ca963d4deb93866d739a343c01c969231dbe21680e13a5d1a5bf0", size = 198305, upload-time = "2025-10-02T14:35:49.584Z" }, - { url = "https://files.pythonhosted.org/packages/11/38/5eab81580703c4df93feb5f32ff8fa7fe1e2c51c1f183ee4e48d4bb9d3d7/xxhash-3.6.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c1ce4009c97a752e682b897aa99aef84191077a9433eb237774689f14f8ec152", size = 210848, upload-time = "2025-10-02T14:35:50.877Z" }, - { url = "https://files.pythonhosted.org/packages/5e/6b/953dc4b05c3ce678abca756416e4c130d2382f877a9c30a20d08ee6a77c0/xxhash-3.6.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:8cb2f4f679b01513b7adbb9b1b2f0f9cdc31b70007eaf9d59d0878809f385b11", size = 414142, upload-time = "2025-10-02T14:35:52.15Z" }, - { url = "https://files.pythonhosted.org/packages/08/a9/238ec0d4e81a10eb5026d4a6972677cbc898ba6c8b9dbaec12ae001b1b35/xxhash-3.6.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:653a91d7c2ab54a92c19ccf43508b6a555440b9be1bc8be553376778be7f20b5", size = 191547, upload-time = "2025-10-02T14:35:53.547Z" }, - { url = "https://files.pythonhosted.org/packages/f1/ee/3cf8589e06c2164ac77c3bf0aa127012801128f1feebf2a079272da5737c/xxhash-3.6.0-cp314-cp314-win32.whl", hash = "sha256:a756fe893389483ee8c394d06b5ab765d96e68fbbfe6fde7aa17e11f5720559f", size = 31214, upload-time = "2025-10-02T14:35:54.746Z" }, - { url = "https://files.pythonhosted.org/packages/02/5d/a19552fbc6ad4cb54ff953c3908bbc095f4a921bc569433d791f755186f1/xxhash-3.6.0-cp314-cp314-win_amd64.whl", hash = "sha256:39be8e4e142550ef69629c9cd71b88c90e9a5db703fecbcf265546d9536ca4ad", size = 32290, upload-time = "2025-10-02T14:35:55.791Z" }, - { url = "https://files.pythonhosted.org/packages/b1/11/dafa0643bc30442c887b55baf8e73353a344ee89c1901b5a5c54a6c17d39/xxhash-3.6.0-cp314-cp314-win_arm64.whl", hash = "sha256:25915e6000338999236f1eb68a02a32c3275ac338628a7eaa5a269c401995679", size = 28795, upload-time = "2025-10-02T14:35:57.162Z" }, - { url = "https://files.pythonhosted.org/packages/2c/db/0e99732ed7f64182aef4a6fb145e1a295558deec2a746265dcdec12d191e/xxhash-3.6.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c5294f596a9017ca5a3e3f8884c00b91ab2ad2933cf288f4923c3fd4346cf3d4", size = 32955, upload-time = "2025-10-02T14:35:58.267Z" }, - { url = "https://files.pythonhosted.org/packages/55/f4/2a7c3c68e564a099becfa44bb3d398810cc0ff6749b0d3cb8ccb93f23c14/xxhash-3.6.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1cf9dcc4ab9cff01dfbba78544297a3a01dafd60f3bde4e2bfd016cf7e4ddc67", size = 31072, upload-time = "2025-10-02T14:35:59.382Z" }, - { url = "https://files.pythonhosted.org/packages/c6/d9/72a29cddc7250e8a5819dad5d466facb5dc4c802ce120645630149127e73/xxhash-3.6.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:01262da8798422d0685f7cef03b2bd3f4f46511b02830861df548d7def4402ad", size = 196579, upload-time = "2025-10-02T14:36:00.838Z" }, - { url = "https://files.pythonhosted.org/packages/63/93/b21590e1e381040e2ca305a884d89e1c345b347404f7780f07f2cdd47ef4/xxhash-3.6.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51a73fb7cb3a3ead9f7a8b583ffd9b8038e277cdb8cb87cf890e88b3456afa0b", size = 215854, upload-time = "2025-10-02T14:36:02.207Z" }, - { url = "https://files.pythonhosted.org/packages/ce/b8/edab8a7d4fa14e924b29be877d54155dcbd8b80be85ea00d2be3413a9ed4/xxhash-3.6.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b9c6df83594f7df8f7f708ce5ebeacfc69f72c9fbaaababf6cf4758eaada0c9b", size = 214965, upload-time = "2025-10-02T14:36:03.507Z" }, - { url = "https://files.pythonhosted.org/packages/27/67/dfa980ac7f0d509d54ea0d5a486d2bb4b80c3f1bb22b66e6a05d3efaf6c0/xxhash-3.6.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:627f0af069b0ea56f312fd5189001c24578868643203bca1abbc2c52d3a6f3ca", size = 448484, upload-time = "2025-10-02T14:36:04.828Z" }, - { url = "https://files.pythonhosted.org/packages/8c/63/8ffc2cc97e811c0ca5d00ab36604b3ea6f4254f20b7bc658ca825ce6c954/xxhash-3.6.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aa912c62f842dfd013c5f21a642c9c10cd9f4c4e943e0af83618b4a404d9091a", size = 196162, upload-time = "2025-10-02T14:36:06.182Z" }, - { url = "https://files.pythonhosted.org/packages/4b/77/07f0e7a3edd11a6097e990f6e5b815b6592459cb16dae990d967693e6ea9/xxhash-3.6.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b465afd7909db30168ab62afe40b2fcf79eedc0b89a6c0ab3123515dc0df8b99", size = 213007, upload-time = "2025-10-02T14:36:07.733Z" }, - { url = "https://files.pythonhosted.org/packages/ae/d8/bc5fa0d152837117eb0bef6f83f956c509332ce133c91c63ce07ee7c4873/xxhash-3.6.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:a881851cf38b0a70e7c4d3ce81fc7afd86fbc2a024f4cfb2a97cf49ce04b75d3", size = 200956, upload-time = "2025-10-02T14:36:09.106Z" }, - { url = "https://files.pythonhosted.org/packages/26/a5/d749334130de9411783873e9b98ecc46688dad5db64ca6e04b02acc8b473/xxhash-3.6.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:9b3222c686a919a0f3253cfc12bb118b8b103506612253b5baeaac10d8027cf6", size = 213401, upload-time = "2025-10-02T14:36:10.585Z" }, - { url = "https://files.pythonhosted.org/packages/89/72/abed959c956a4bfc72b58c0384bb7940663c678127538634d896b1195c10/xxhash-3.6.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:c5aa639bc113e9286137cec8fadc20e9cd732b2cc385c0b7fa673b84fc1f2a93", size = 417083, upload-time = "2025-10-02T14:36:12.276Z" }, - { url = "https://files.pythonhosted.org/packages/0c/b3/62fd2b586283b7d7d665fb98e266decadf31f058f1cf6c478741f68af0cb/xxhash-3.6.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5c1343d49ac102799905e115aee590183c3921d475356cb24b4de29a4bc56518", size = 193913, upload-time = "2025-10-02T14:36:14.025Z" }, - { url = "https://files.pythonhosted.org/packages/9a/9a/c19c42c5b3f5a4aad748a6d5b4f23df3bed7ee5445accc65a0fb3ff03953/xxhash-3.6.0-cp314-cp314t-win32.whl", hash = "sha256:5851f033c3030dd95c086b4a36a2683c2ff4a799b23af60977188b057e467119", size = 31586, upload-time = "2025-10-02T14:36:15.603Z" }, - { url = "https://files.pythonhosted.org/packages/03/d6/4cc450345be9924fd5dc8c590ceda1db5b43a0a889587b0ae81a95511360/xxhash-3.6.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0444e7967dac37569052d2409b00a8860c2135cff05502df4da80267d384849f", size = 32526, upload-time = "2025-10-02T14:36:16.708Z" }, - { url = "https://files.pythonhosted.org/packages/0f/c9/7243eb3f9eaabd1a88a5a5acadf06df2d83b100c62684b7425c6a11bcaa8/xxhash-3.6.0-cp314-cp314t-win_arm64.whl", hash = "sha256:bb79b1e63f6fd84ec778a4b1916dfe0a7c3fdb986c06addd5db3a0d413819d95", size = 28898, upload-time = "2025-10-02T14:36:17.843Z" }, -] - -[[package]] -name = "yourdfpy" -version = "0.0.60" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "lxml" }, - { name = "numpy" }, - { name = "six" }, - { name = "trimesh", extra = ["easy"] }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ff/19/20c50861f30aff7720f9a601f386d73760c2df9961de1f98d0dbf3b85e69/yourdfpy-0.0.60.tar.gz", hash = "sha256:2af2d8bdeea1b85b642590a3b4236fdb35746d7b3e38ce460a169c18d9c4f868", size = 538238, upload-time = "2026-01-23T07:32:47.856Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/60/4ea0d6df0b497d51bf2ef87eaab0eb26f8bc3b3313c012da5df3383cced9/yourdfpy-0.0.60-py3-none-any.whl", hash = "sha256:8a187a8b18c98db87c76e9a950581b3c875b761e00df83942526c17ea693166c", size = 22194, upload-time = "2026-01-23T07:32:46.481Z" }, +version = "4.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/a5/1386f35da1475fcaeef42581deae73417c6d2a6a0b2d2e8914de18844dcd/xxhash-4.0.1.tar.gz", hash = "sha256:d55bf4ef10eb09b8b6866790e083d26d087d84caa3cc0946ba87c3ca7ecaf7b7", size = 101513, upload-time = "2026-08-17T08:24:08.557Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/dd/c707286b527722f776e1fb81dd202c45623355ba1a2972337a2a26075b2b/xxhash-4.0.1-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:8c9fe122444e129881afd1d4d1c7ac0d3ce2d91b68c2b40173b6025ff1c31f9a", size = 43639, upload-time = "2026-08-17T08:20:54.945Z" }, + { url = "https://files.pythonhosted.org/packages/1b/3b/bb71639a0f95635f61936a6f2653599c4261b645ddddd8d00f9dfe3613e2/xxhash-4.0.1-cp313-cp313-android_24_x86_64.whl", hash = "sha256:1f3346c5c287ac3c7f38b20380f55e8768230e7252af59fabcf3b87ab21e4256", size = 40657, upload-time = "2026-08-17T08:22:12.616Z" }, + { url = "https://files.pythonhosted.org/packages/3c/91/76f3f5385faa9886a36f21fcc603f40b4c0c40ce622382f133160c48b4d9/xxhash-4.0.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:4e5141543c7f7fe3087500bbb4ac2845cb528a980aa91f8f1e661e2292ff4a5d", size = 34708, upload-time = "2026-08-17T08:35:24.614Z" }, + { url = "https://files.pythonhosted.org/packages/9a/4a/f48f0e3e1b1ab072979fff2a5be899234e28090883e8b519d0b10215d708/xxhash-4.0.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:f09ee747e2a5f876cc5ad56947734811828335e13b403dd8ea1e06d77a9dd48d", size = 35650, upload-time = "2026-08-17T08:21:09.337Z" }, + { url = "https://files.pythonhosted.org/packages/c4/53/b73d7472b196101ad1f57ed0674af3af803ac3e9ec2feadd650a7b262562/xxhash-4.0.1-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:acf52474b2494ef66dc7e0fb6d5e2b50c18313039ad4d275fbf9f9907c804bc5", size = 37958, upload-time = "2026-08-17T08:22:10.616Z" }, + { url = "https://files.pythonhosted.org/packages/d0/f2/024946ad8fa532074af4e4380179da54b7ec9facc8bd0b279ec0fac4e63a/xxhash-4.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1b3cccf75eeb5b01639b2feadb042a8e07889293b7ca72fa2985e7dcb64763cf", size = 38032, upload-time = "2026-08-17T08:22:09.535Z" }, + { url = "https://files.pythonhosted.org/packages/da/e0/934af8d99bb5885711006bec30a691f728edd513d2c40f053f887d8e7577/xxhash-4.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cd878d32f5c6cbce9783f8d6897561fb772211edba9dde49d85672b88ed45276", size = 35895, upload-time = "2026-08-17T08:35:16.53Z" }, + { url = "https://files.pythonhosted.org/packages/20/5f/a8011f6a1558f7ca66d9077bb4f192b1871afcea62fbd5733605d2015755/xxhash-4.0.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:41e579025a6e13a99e6d71e39c9cfc621a0dcdbbf19106325e145fa858f2d794", size = 259464, upload-time = "2026-08-17T08:21:06.72Z" }, + { url = "https://files.pythonhosted.org/packages/ff/89/9665a44397547e7a3d58c0942425a976d58dcfd4b538f33220a312bf6912/xxhash-4.0.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:74379a577a9f3b6afbdedf1b90e5c7764467051977f18a326d7d607336d743bd", size = 283949, upload-time = "2026-08-17T08:22:17.003Z" }, + { url = "https://files.pythonhosted.org/packages/34/2d/78774141266457468f29f3f5803092df4db87d8148ba74e4debd041649db/xxhash-4.0.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:acb31ecdd1a97fab5cd39a84ee9f515e727d319f796fec48703b8339b9998360", size = 303898, upload-time = "2026-08-17T08:35:27.951Z" }, + { url = "https://files.pythonhosted.org/packages/59/48/d78d22de576b42528bff87c14207de50de4f0b888221a50ff7c9d675d670/xxhash-4.0.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5b7875ac1a2edcb691f27642b8b94b904baa6bcecb7d79c72df2228ba8cb5c51", size = 287241, upload-time = "2026-08-17T08:21:13.042Z" }, + { url = "https://files.pythonhosted.org/packages/4c/de/7a1755a59c59fd46176f293bbdd99e399a6537ba9537fc723aa4d1bf6e27/xxhash-4.0.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4751f1d7eecae6b2d2a773630f1a7248f125c9a92a456694d03c15bceffc9d68", size = 519856, upload-time = "2026-08-17T08:22:15.35Z" }, + { url = "https://files.pythonhosted.org/packages/6f/fb/76580c08e916507859b0f335393cb5fdc59452c4402edbc6bcca6e47e7df/xxhash-4.0.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9a51b061d54cda8b83e62c44458bfbf0dabbef9b975dd9649952ba5076b9f349", size = 268572, upload-time = "2026-08-17T08:22:14.533Z" }, + { url = "https://files.pythonhosted.org/packages/d0/2b/1abde3e07b8f2077a38b4fbfaf764115008bfe0ff03bc7756a52c9fd0607/xxhash-4.0.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:74a164e8b63f1e9cf35c9a7809d082b033d1a00e7375d5d814415436e7867e57", size = 344967, upload-time = "2026-08-17T08:35:23.569Z" }, + { url = "https://files.pythonhosted.org/packages/5c/15/80b6ddf0732eef48a8b5fe717398274794392bd6dbe82af38d189d214772/xxhash-4.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f5e5c6df4b703afcbe9352d238a51efd97c3b91fdc3a2052e40fdacb1e7505f", size = 279956, upload-time = "2026-08-17T08:21:24.97Z" }, + { url = "https://files.pythonhosted.org/packages/77/e0/11cbc43c205bf81fad50d69c7319cd1b1ccc01a66cd4fb8766357126c43d/xxhash-4.0.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:d54b8ae068af532c8cdf56abb9e09a60fbe7b10792444c9c27987bb6d3b450fa", size = 307583, upload-time = "2026-08-17T08:22:22.541Z" }, + { url = "https://files.pythonhosted.org/packages/1c/11/cf0bc07feb2791045b6ac075d4bf64f1a5beedef2f46ae70d7104d63a19f/xxhash-4.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1749f0688020209fe0d357ce1e1cd9ec9c6161ed0405ea949d24581c4c43fa91", size = 265848, upload-time = "2026-08-17T08:35:31.298Z" }, + { url = "https://files.pythonhosted.org/packages/d4/c4/7ada4bea2a2795073dfc42d96842930efbe7a0c1857ef4b522e4e90e5d83/xxhash-4.0.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:94ac8a6b8c47951173f0b67bf862bcb971bf24e493b9fbbdb0e010cbbc7d9f54", size = 284409, upload-time = "2026-08-17T08:21:23.156Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f4/d8ce83dd6b99ccfbdadaf2db968ae40334d2e5f73a0297e593b9ddb3df39/xxhash-4.0.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:a33de7633c948ab2dc144af370a66e7e7af29b425dcd0f7e4f59689fb9391b53", size = 335921, upload-time = "2026-08-17T08:22:21.802Z" }, + { url = "https://files.pythonhosted.org/packages/a6/9f/f47d8724bd8bc45b395b06b7cacea2dae0d00031af1b707184a091161df6/xxhash-4.0.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:247ece770647c0aef080561fa996f9774b4dadce2d0c42eeb98229db7dcf820d", size = 487023, upload-time = "2026-08-17T08:22:19.729Z" }, + { url = "https://files.pythonhosted.org/packages/57/54/2d87098f3371cc1e42dd04d2285ad56bca4c56667bc501bff02d2b9fd6b5/xxhash-4.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a4553d36cc0b7fce1f35ba8a94dfd775aa3ed12f5eab2dc3b46ac75a0706b0bb", size = 264333, upload-time = "2026-08-17T08:35:27.001Z" }, + { url = "https://files.pythonhosted.org/packages/27/b8/93795ca5898ec7d7d0455283ad261c0fc76b4f0c0a69e86233bd7badb0bd/xxhash-4.0.1-cp313-cp313-pyemscripten_2025_0_wasm32.whl", hash = "sha256:87aa309a93bd5ec13f14309a305ff4e9bf74c5363fc46c264c0a22edfd5b0670", size = 20581, upload-time = "2026-08-17T08:21:39.207Z" }, + { url = "https://files.pythonhosted.org/packages/b6/96/926f7335a0a1647952c00421e8da877f658094f61336306c7cadc335c94d/xxhash-4.0.1-cp313-cp313-win32.whl", hash = "sha256:cba763d84b06bda2c38d5185dee76f1b9dfdc0789e96e476d9e10005526d0788", size = 34449, upload-time = "2026-08-17T08:22:29.362Z" }, + { url = "https://files.pythonhosted.org/packages/ea/61/8a5aeb811de093bab3434e77eff0e9461624a1a56a6a93d315d080aab2aa/xxhash-4.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:97b94fb29abf21f5f0bde15f7dbdd3a4aa2dc59f37026adc7b4bee8563b84375", size = 36520, upload-time = "2026-08-17T08:35:34.852Z" }, + { url = "https://files.pythonhosted.org/packages/04/14/97f3c74000ca36955e9cb86f6d270dcd5848b5c65afa623453f5cf2d83d6/xxhash-4.0.1-cp313-cp313-win_arm64.whl", hash = "sha256:08ed8da18cd4fd0a6a5d6a444852d8fbd0e565388a74a4937085451b5f1a312a", size = 33428, upload-time = "2026-08-17T08:21:31.713Z" }, ] [[package]] name = "zipp" -version = "3.23.0" +version = "4.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/d8/eab98a517c14134c0b2eb4e2387bc5f457334293ec5d2dd3857ec2966802/zipp-4.1.0.tar.gz", hash = "sha256:4cb57381f544315db7688e976e922a2b18cdb513d21cc194eb42232ba2a3e602", size = 26214, upload-time = "2026-05-18T20:08:57.967Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, + { url = "https://files.pythonhosted.org/packages/3a/13/547360d81e6d88d58492968ffda9f9542854f11310ee556fef14260cc886/zipp-4.1.0-py3-none-any.whl", hash = "sha256:25ad4e16390cd314347dd8f1de67a2ac538ae658ed4ab9db16029c07c188e97f", size = 10238, upload-time = "2026-05-18T20:08:57.045Z" }, ] [[package]] @@ -3026,19 +2118,4 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8e/e0/69a553d2047f9a2c7347caa225bb3a63b6d7704ad74610cb7823baa08ed7/zstandard-0.25.0-cp313-cp313-win32.whl", hash = "sha256:7030defa83eef3e51ff26f0b7bfb229f0204b66fe18e04359ce3474ac33cbc09", size = 436936, upload-time = "2025-09-14T22:17:52.658Z" }, { url = "https://files.pythonhosted.org/packages/d9/82/b9c06c870f3bd8767c201f1edbdf9e8dc34be5b0fbc5682c4f80fe948475/zstandard-0.25.0-cp313-cp313-win_amd64.whl", hash = "sha256:1f830a0dac88719af0ae43b8b2d6aef487d437036468ef3c2ea59c51f9d55fd5", size = 506232, upload-time = "2025-09-14T22:17:50.402Z" }, { url = "https://files.pythonhosted.org/packages/d4/57/60c3c01243bb81d381c9916e2a6d9e149ab8627c0c7d7abb2d73384b3c0c/zstandard-0.25.0-cp313-cp313-win_arm64.whl", hash = "sha256:85304a43f4d513f5464ceb938aa02c1e78c2943b29f44a750b48b25ac999a049", size = 462671, upload-time = "2025-09-14T22:17:51.533Z" }, - { url = "https://files.pythonhosted.org/packages/3d/5c/f8923b595b55fe49e30612987ad8bf053aef555c14f05bb659dd5dbe3e8a/zstandard-0.25.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e29f0cf06974c899b2c188ef7f783607dbef36da4c242eb6c82dcd8b512855e3", size = 795887, upload-time = "2025-09-14T22:17:54.198Z" }, - { url = "https://files.pythonhosted.org/packages/8d/09/d0a2a14fc3439c5f874042dca72a79c70a532090b7ba0003be73fee37ae2/zstandard-0.25.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:05df5136bc5a011f33cd25bc9f506e7426c0c9b3f9954f056831ce68f3b6689f", size = 640658, upload-time = "2025-09-14T22:17:55.423Z" }, - { url = "https://files.pythonhosted.org/packages/5d/7c/8b6b71b1ddd517f68ffb55e10834388d4f793c49c6b83effaaa05785b0b4/zstandard-0.25.0-cp314-cp314-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:f604efd28f239cc21b3adb53eb061e2a205dc164be408e553b41ba2ffe0ca15c", size = 5379849, upload-time = "2025-09-14T22:17:57.372Z" }, - { url = "https://files.pythonhosted.org/packages/a4/86/a48e56320d0a17189ab7a42645387334fba2200e904ee47fc5a26c1fd8ca/zstandard-0.25.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223415140608d0f0da010499eaa8ccdb9af210a543fac54bce15babbcfc78439", size = 5058095, upload-time = "2025-09-14T22:17:59.498Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ad/eb659984ee2c0a779f9d06dbfe45e2dc39d99ff40a319895df2d3d9a48e5/zstandard-0.25.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e54296a283f3ab5a26fc9b8b5d4978ea0532f37b231644f367aa588930aa043", size = 5551751, upload-time = "2025-09-14T22:18:01.618Z" }, - { url = "https://files.pythonhosted.org/packages/61/b3/b637faea43677eb7bd42ab204dfb7053bd5c4582bfe6b1baefa80ac0c47b/zstandard-0.25.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ca54090275939dc8ec5dea2d2afb400e0f83444b2fc24e07df7fdef677110859", size = 6364818, upload-time = "2025-09-14T22:18:03.769Z" }, - { url = "https://files.pythonhosted.org/packages/31/dc/cc50210e11e465c975462439a492516a73300ab8caa8f5e0902544fd748b/zstandard-0.25.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e09bb6252b6476d8d56100e8147b803befa9a12cea144bbe629dd508800d1ad0", size = 5560402, upload-time = "2025-09-14T22:18:05.954Z" }, - { url = "https://files.pythonhosted.org/packages/c9/ae/56523ae9c142f0c08efd5e868a6da613ae76614eca1305259c3bf6a0ed43/zstandard-0.25.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a9ec8c642d1ec73287ae3e726792dd86c96f5681eb8df274a757bf62b750eae7", size = 4955108, upload-time = "2025-09-14T22:18:07.68Z" }, - { url = "https://files.pythonhosted.org/packages/98/cf/c899f2d6df0840d5e384cf4c4121458c72802e8bda19691f3b16619f51e9/zstandard-0.25.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a4089a10e598eae6393756b036e0f419e8c1d60f44a831520f9af41c14216cf2", size = 5269248, upload-time = "2025-09-14T22:18:09.753Z" }, - { url = "https://files.pythonhosted.org/packages/1b/c0/59e912a531d91e1c192d3085fc0f6fb2852753c301a812d856d857ea03c6/zstandard-0.25.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f67e8f1a324a900e75b5e28ffb152bcac9fbed1cc7b43f99cd90f395c4375344", size = 5430330, upload-time = "2025-09-14T22:18:11.966Z" }, - { url = "https://files.pythonhosted.org/packages/a0/1d/7e31db1240de2df22a58e2ea9a93fc6e38cc29353e660c0272b6735d6669/zstandard-0.25.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:9654dbc012d8b06fc3d19cc825af3f7bf8ae242226df5f83936cb39f5fdc846c", size = 5811123, upload-time = "2025-09-14T22:18:13.907Z" }, - { url = "https://files.pythonhosted.org/packages/f6/49/fac46df5ad353d50535e118d6983069df68ca5908d4d65b8c466150a4ff1/zstandard-0.25.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4203ce3b31aec23012d3a4cf4a2ed64d12fea5269c49aed5e4c3611b938e4088", size = 5359591, upload-time = "2025-09-14T22:18:16.465Z" }, - { url = "https://files.pythonhosted.org/packages/c2/38/f249a2050ad1eea0bb364046153942e34abba95dd5520af199aed86fbb49/zstandard-0.25.0-cp314-cp314-win32.whl", hash = "sha256:da469dc041701583e34de852d8634703550348d5822e66a0c827d39b05365b12", size = 444513, upload-time = "2025-09-14T22:18:20.61Z" }, - { url = "https://files.pythonhosted.org/packages/3a/43/241f9615bcf8ba8903b3f0432da069e857fc4fd1783bd26183db53c4804b/zstandard-0.25.0-cp314-cp314-win_amd64.whl", hash = "sha256:c19bcdd826e95671065f8692b5a4aa95c52dc7a02a4c5a0cac46deb879a017a2", size = 516118, upload-time = "2025-09-14T22:18:17.849Z" }, - { url = "https://files.pythonhosted.org/packages/f0/ef/da163ce2450ed4febf6467d77ccb4cd52c4c30ab45624bad26ca0a27260c/zstandard-0.25.0-cp314-cp314-win_arm64.whl", hash = "sha256:d7541afd73985c630bafcd6338d2518ae96060075f9463d7dc14cfb33514383d", size = 476940, upload-time = "2025-09-14T22:18:19.088Z" }, ]